84bc1e26c3ea0d9d4a9f2a9a47b39200f0c9426a
[fishladder.git] / dr_soft / miniaudio.h
1 /*
2 Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
3 miniaudio - v0.10.4 - 2020-04-12
4
5 David Reid - davidreidsoftware@gmail.com
6
7 Website: https://miniaud.io
8 GitHub: https://github.com/dr-soft/miniaudio
9 */
10
11 /*
12 RELEASE NOTES - VERSION 0.10.x
13 ==============================
14 Version 0.10 includes major API changes and refactoring, mostly concerned with the data conversion system. Data conversion is performed internally to convert
15 audio data between the format requested when initializing the `ma_device` object and the format of the internal device used by the backend. The same applies
16 to the `ma_decoder` object. The previous design has several design flaws and missing features which necessitated a complete redesign.
17
18
19 Changes to Data Conversion
20 --------------------------
21 The previous data conversion system used callbacks to deliver input data for conversion. This design works well in some specific situations, but in other
22 situations it has some major readability and maintenance issues. The decision was made to replace this with a more iterative approach where you just pass in a
23 pointer to the input data directly rather than dealing with a callback.
24
25 The following are the data conversion APIs that have been removed and their replacements:
26
27 - ma_format_converter -> ma_convert_pcm_frames_format()
28 - ma_channel_router -> ma_channel_converter
29 - ma_src -> ma_resampler
30 - ma_pcm_converter -> ma_data_converter
31
32 The previous conversion APIs accepted a callback in their configs. There are no longer any callbacks to deal with. Instead you just pass the data into the
33 `*_process_pcm_frames()` function as a pointer to a buffer.
34
35 The simplest aspect of data conversion is sample format conversion. To convert between two formats, just call `ma_convert_pcm_frames_format()`. Channel
36 conversion is also simple which you can do with `ma_channel_converter` via `ma_channel_converter_process_pcm_frames()`.
37
38 Resampling is more complicated because the number of output frames that are processed is different to the number of input frames that are consumed. When you
39 call `ma_resampler_process_pcm_frames()` you need to pass in the number of input frames available for processing and the number of output frames you want to
40 output. Upon returning they will receive the number of input frames that were consumed and the number of output frames that were generated.
41
42 The `ma_data_converter` API is a wrapper around format, channel and sample rate conversion and handles all of the data conversion you'll need which probably
43 makes it the best option if you need to do data conversion.
44
45 In addition to changes to the API design, a few other changes have been made to the data conversion pipeline:
46
47 - The sinc resampler has been removed. This was completely broken and never actually worked properly.
48 - The linear resampler now uses low-pass filtering to remove aliasing. The quality of the low-pass filter can be controlled via the resampler config with the
49 `lpfOrder` option, which has a maximum value of MA_MAX_FILTER_ORDER.
50 - Data conversion now supports s16 natively which runs through a fixed point pipeline. Previously everything needed to be converted to floating point before
51 processing, whereas now both s16 and f32 are natively supported. Other formats still require conversion to either s16 or f32 prior to processing, however
52 `ma_data_converter` will handle this for you.
53
54
55 Custom Memory Allocators
56 ------------------------
57 miniaudio has always supported macro level customization for memory allocation via MA_MALLOC, MA_REALLOC and MA_FREE, however some scenarios require more
58 flexibility by allowing a user data pointer to be passed to the custom allocation routines. Support for this has been added to version 0.10 via the
59 `ma_allocation_callbacks` structure. Anything making use of heap allocations has been updated to accept this new structure.
60
61 The `ma_context_config` structure has been updated with a new member called `allocationCallbacks`. Leaving this set to it's defaults returned by
62 `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
63 way, and leaving everything as-is after `ma_decoder_config_init()` will cause it to use the same defaults.
64
65 The following APIs have been updated to take a pointer to a `ma_allocation_callbacks` object. Setting this parameter to NULL will cause it to use defaults.
66 Otherwise they will use the relevant callback in the structure.
67
68 - ma_malloc()
69 - ma_realloc()
70 - ma_free()
71 - ma_aligned_malloc()
72 - ma_aligned_free()
73 - ma_rb_init() / ma_rb_init_ex()
74 - ma_pcm_rb_init() / ma_pcm_rb_init_ex()
75
76 Note that you can continue to use MA_MALLOC, MA_REALLOC and MA_FREE as per normal. These will continue to be used by default if you do not specify custom
77 allocation callbacks.
78
79
80 Buffer and Period Configuration Changes
81 ---------------------------------------
82 The way in which the size of the internal buffer and periods are specified in the device configuration have changed. In previous versions, the config variables
83 `bufferSizeInFrames` and `bufferSizeInMilliseconds` defined the size of the entire buffer, with the size of a period being the size of this variable divided by
84 the period count. This became confusing because people would expect the value of `bufferSizeInFrames` or `bufferSizeInMilliseconds` to independantly determine
85 latency, when in fact it was that value divided by the period count that determined it. These variables have been removed and replaced with new ones called
86 `periodSizeInFrames` and `periodSizeInMilliseconds`.
87
88 These new configuration variables work in the same way as their predecessors in that if one is set to 0, the other will be used, but the main difference is
89 that you now set these to you desired latency rather than the size of the entire buffer. The benefit of this is that it's much easier and less confusing to
90 configure latency.
91
92 The following unused APIs have been removed:
93
94 ma_get_default_buffer_size_in_milliseconds()
95 ma_get_default_buffer_size_in_frames()
96
97 The following macros have been removed:
98
99 MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
100 MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
101
102
103 Other API Changes
104 -----------------
105 Other less major API changes have also been made in version 0.10.
106
107 `ma_device_set_stop_callback()` has been removed. If you require a stop callback, you must now set it via the device config just like the data callback.
108
109 The `ma_sine_wave` API has been replaced with a more general API called `ma_waveform`. This supports generation of different types of waveforms, including
110 sine, square, triangle and sawtooth. Use `ma_waveform_init()` in place of `ma_sine_wave_init()` to initialize the waveform object. This takes a configuration
111 object called `ma_waveform_config` which defines the properties of the waveform. Use `ma_waveform_config_init()` to initialize a `ma_waveform_config` object.
112 Use `ma_waveform_read_pcm_frames()` in place of `ma_sine_wave_read_f32()` and `ma_sine_wave_read_f32_ex()`.
113
114 `ma_convert_frames()` and `ma_convert_frames_ex()` have been changed. Both of these functions now take a new parameter called `frameCountOut` which specifies
115 the size of the output buffer in PCM frames. This has been added for safety. In addition to this, the parameters for `ma_convert_frames_ex()` have changed to
116 take a pointer to a `ma_data_converter_config` object to specify the input and output formats to convert between. This was done to make it more flexible, to
117 prevent the parameter list getting too long, and to prevent API breakage whenever a new conversion property is added.
118
119 `ma_calculate_frame_count_after_src()` has been renamed to `ma_calculate_frame_count_after_resampling()` for consistency with the new `ma_resampler` API.
120
121
122 Filters
123 -------
124 The following filters have been added:
125
126 |-------------|-------------------------------------------------------------------|
127 | API | Description |
128 |-------------|-------------------------------------------------------------------|
129 | ma_biquad | Biquad filter (transposed direct form 2) |
130 | ma_lpf1 | First order low-pass filter |
131 | ma_lpf2 | Second order low-pass filter |
132 | ma_lpf | High order low-pass filter (Butterworth) |
133 | ma_hpf1 | First order high-pass filter |
134 | ma_hpf2 | Second order high-pass filter |
135 | ma_hpf | High order high-pass filter (Butterworth) |
136 | ma_bpf2 | Second order band-pass filter |
137 | ma_bpf | High order band-pass filter |
138 | ma_peak2 | Second order peaking filter |
139 | ma_notch2 | Second order notching filter |
140 | ma_loshelf2 | Second order low shelf filter |
141 | ma_hishelf2 | Second order high shelf filter |
142 |-------------|-------------------------------------------------------------------|
143
144 These filters all support 32-bit floating point and 16-bit signed integer formats natively. Other formats need to be converted beforehand.
145
146
147 Sine, Square, Triangle and Sawtooth Waveforms
148 ---------------------------------------------
149 Previously miniaudio supported only sine wave generation. This has now been generalized to support sine, square, triangle and sawtooth waveforms. The old
150 `ma_sine_wave` API has been removed and replaced with the `ma_waveform` API. Use `ma_waveform_config_init()` to initialize a config object, and then pass it
151 into `ma_waveform_init()`. Then use `ma_waveform_read_pcm_frames()` to read PCM data.
152
153
154 Noise Generation
155 ----------------
156 A noise generation API has been added. This is used via the `ma_noise` API. Currently white, pink and Brownian noise is supported. The `ma_noise` API is
157 similar to the waveform API. Use `ma_noise_config_init()` to initialize a config object, and then pass it into `ma_noise_init()` to initialize a `ma_noise`
158 object. Then use `ma_noise_read_pcm_frames()` to read PCM data.
159
160
161 Miscellaneous Changes
162 ---------------------
163 The MA_NO_STDIO option has been removed. This would disable file I/O APIs, however this has proven to be too hard to maintain for it's perceived value and was
164 therefore removed.
165
166 Internal functions have all been made static where possible. If you get warnings about unused functions, please submit a bug report.
167
168 The `ma_device` structure is no longer defined as being aligned to MA_SIMD_ALIGNMENT. This resulted in a possible crash when allocating a `ma_device` object on
169 the heap, but not aligning it to MA_SIMD_ALIGNMENT. This crash would happen due to the compiler seeing the alignment specified on the structure and assuming it
170 was always aligned as such and thinking it was safe to emit alignment-dependant SIMD instructions. Since miniaudio's philosophy is for things to just work,
171 this has been removed from all structures.
172
173 Results codes have been overhauled. Unnecessary result codes have been removed, and some have been renumbered for organisation purposes. If you are are binding
174 maintainer you will need to update your result codes. Support has also been added for retrieving a human readable description of a given result code via the
175 `ma_result_description()` API.
176
177 ALSA: The automatic format conversion, channel conversion and resampling performed by ALSA is now disabled by default as they were causing some compatibility
178 issues with certain devices and configurations. These can be individually enabled via the device config:
179
180 ```c
181 deviceConfig.alsa.noAutoFormat = MA_TRUE;
182 deviceConfig.alsa.noAutoChannels = MA_TRUE;
183 deviceConfig.alsa.noAutoResample = MA_TRUE;
184 ```
185 */
186
187
188 /*
189 Introduction
190 ============
191 miniaudio is a single file library for audio playback and capture. To use it, do the following in one .c file:
192
193 ```c
194 #define MINIAUDIO_IMPLEMENTATION
195 #include "miniaudio.h
196 ```
197
198 You can #include miniaudio.h in other parts of the program just like any other header.
199
200 miniaudio uses the concept of a "device" as the abstraction for physical devices. The idea is that you choose a physical device to emit or capture audio from,
201 and then move data to/from the device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a callback which you specify when
202 initializing the device.
203
204 When initializing the device you first need to configure it. The device configuration allows you to specify things like the format of the data delivered via
205 the callback, the size of the internal buffer and the ID of the device you want to emit or capture audio from.
206
207 Once you have the device configuration set up you can initialize the device. When initializing a device you need to allocate memory for the device object
208 beforehand. This gives the application complete control over how the memory is allocated. In the example below we initialize a playback device on the stack,
209 but you could allocate it on the heap if that suits your situation better.
210
211 ```c
212 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
213 {
214 // 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
215 // move data from pInput into pOutput. Never process more than frameCount frames.
216 }
217
218 ...
219
220 ma_device_config config = ma_device_config_init(ma_device_type_playback);
221 config.playback.format = MY_FORMAT;
222 config.playback.channels = MY_CHANNEL_COUNT;
223 config.sampleRate = MY_SAMPLE_RATE;
224 config.dataCallback = data_callback;
225 config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData).
226
227 ma_device device;
228 if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
229 ... An error occurred ...
230 }
231
232 ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually.
233
234 ...
235
236 ma_device_uninit(&device); // This will stop the device so no need to do that manually.
237 ```
238
239 In the example above, `data_callback()` is where audio data is written and read from the device. The idea is in playback mode you cause sound to be emitted
240 from the speakers by writing audio data to the output buffer (`pOutput` in the example). In capture mode you read data from the input buffer (`pInput`) to
241 extract sound captured by the microphone. The `frameCount` parameter tells you how many frames can be written to the output buffer and read from the input
242 buffer. A "frame" is one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 samples: one for the left, one for the right.
243 The channel count is defined by the device config. The size in bytes of an individual sample is defined by the sample format which is also specified in the
244 device config. Multi-channel audio data is always interleaved, which means the samples for each frame are stored next to each other in memory. For example, in
245 a stereo stream the first pair of samples will be the left and right samples for the first frame, the second pair of samples will be the left and right samples
246 for the second frame, etc.
247
248 The configuration of the device is defined by the `ma_device_config` structure. The config object is always initialized with `ma_device_config_init()`. It's
249 important to always initialize the config with this function as it initializes it with logical defaults and ensures your program doesn't break when new members
250 are added to the `ma_device_config` structure. The example above uses a fairly simple and standard device configuration. The call to `ma_device_config_init()`
251 takes a single parameter, which is whether or not the device is a playback, capture, duplex or loopback device (loopback devices are not supported on all
252 backends). The `config.playback.format` member sets the sample format which can be one of the following (all formats are native-endian):
253
254 |---------------|----------------------------------------|---------------------------|
255 | Symbol | Description | Range |
256 |---------------|----------------------------------------|---------------------------|
257 | ma_format_f32 | 32-bit floating point | [-1, 1] |
258 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
259 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
260 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
261 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
262 |---------------|----------------------------------------|---------------------------|
263
264 The `config.playback.channels` member sets the number of channels to use with the device. The channel count cannot exceed MA_MAX_CHANNELS. The
265 `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
266 44100 or 48000, but can be set to anything. It's recommended to keep this between 8000 and 384000, however.
267
268 Note that leaving the format, channel count and/or sample rate at their default values will result in the internal device's native configuration being used
269 which is useful if you want to avoid the overhead of miniaudio's automatic data conversion.
270
271 In addition to the sample format, channel count and sample rate, the data callback and user data pointer are also set via the config. The user data pointer is
272 not passed into the callback as a parameter, but is instead set to the `pUserData` member of `ma_device` which you can access directly since all miniaudio
273 structures are transparent.
274
275 Initializing the device is done with `ma_device_init()`. This will return a result code telling you what went wrong, if anything. On success it will return
276 `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
277 it, which is what the example above does, but you can also stop the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again.
278 Note that it's important to never stop or start the device from inside the callback. This will result in a deadlock. Instead you set a variable or signal an
279 event indicating that the device needs to stop and handle it in a different thread. The following APIs must never be called inside the callback:
280
281 ma_device_init()
282 ma_device_init_ex()
283 ma_device_uninit()
284 ma_device_start()
285 ma_device_stop()
286
287 You must never try uninitializing and reinitializing a device inside the callback. You must also never try to stop and start it from inside the callback. There
288 are a few other things you shouldn't do in the callback depending on your requirements, however this isn't so much a thread-safety thing, but rather a real-
289 time processing thing which is beyond the scope of this introduction.
290
291 The example above demonstrates the initialization of a playback device, but it works exactly the same for capture. All you need to do is change the device type
292 from `ma_device_type_playback` to `ma_device_type_capture` when setting up the config, like so:
293
294 ```c
295 ma_device_config config = ma_device_config_init(ma_device_type_capture);
296 config.capture.format = MY_FORMAT;
297 config.capture.channels = MY_CHANNEL_COUNT;
298 ```
299
300 In the data callback you just read from the input buffer (`pInput` in the example above) and leave the output buffer alone (it will be set to NULL when the
301 device type is set to `ma_device_type_capture`).
302
303 These are the available device types and how you should handle the buffers in the callback:
304
305 |-------------------------|--------------------------------------------------------|
306 | Device Type | Callback Behavior |
307 |-------------------------|--------------------------------------------------------|
308 | ma_device_type_playback | Write to output buffer, leave input buffer untouched. |
309 | ma_device_type_capture | Read from input buffer, leave output buffer untouched. |
310 | ma_device_type_duplex | Read from input buffer, write to output buffer. |
311 | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. |
312 |-------------------------|--------------------------------------------------------|
313
314 You will notice in the example above that the sample format and channel count is specified separately for playback and capture. This is to support different
315 data formats between the playback and capture devices in a full-duplex system. An example may be that you want to capture audio data as a monaural stream (one
316 channel), but output sound to a stereo speaker system. Note that if you use different formats between playback and capture in a full-duplex configuration you
317 will need to convert the data yourself. There are functions available to help you do this which will be explained later.
318
319 The example above did not specify a physical device to connect to which means it will use the operating system's default device. If you have multiple physical
320 devices connected and you want to use a specific one you will need to specify the device ID in the configuration, like so:
321
322 ```
323 config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device.
324 config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device.
325 ```
326
327 To retrieve the device ID you will need to perform device enumeration, however this requires the use of a new concept called the "context". Conceptually
328 speaking the context sits above the device. There is one context to many devices. The purpose of the context is to represent the backend at a more global level
329 and to perform operations outside the scope of an individual device. Mainly it is used for performing run-time linking against backend libraries, initializing
330 backends and enumerating devices. The example below shows how to enumerate devices.
331
332 ```c
333 ma_context context;
334 if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {
335 // Error.
336 }
337
338 ma_device_info* pPlaybackDeviceInfos;
339 ma_uint32 playbackDeviceCount;
340 ma_device_info* pCaptureDeviceInfos;
341 ma_uint32 captureDeviceCount;
342 if (ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, &pCaptureDeviceInfos, &captureDeviceCount) != MA_SUCCESS) {
343 // Error.
344 }
345
346 // Loop over 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
347 // opportunity to choose which device they'd prefer.
348 for (ma_uint32 iDevice = 0; iDevice < playbackDeviceCount; iDevice += 1) {
349 printf("%d - %s\n", iDevice, pPlaybackDeviceInfos[iDevice].name);
350 }
351
352 ma_device_config config = ma_device_config_init(ma_device_type_playback);
353 config.playback.pDeviceID = &pPlaybackDeviceInfos[chosenPlaybackDeviceIndex].id;
354 config.playback.format = MY_FORMAT;
355 config.playback.channels = MY_CHANNEL_COUNT;
356 config.sampleRate = MY_SAMPLE_RATE;
357 config.dataCallback = data_callback;
358 config.pUserData = pMyCustomData;
359
360 ma_device device;
361 if (ma_device_init(&context, &config, &device) != MA_SUCCESS) {
362 // Error
363 }
364
365 ...
366
367 ma_device_uninit(&device);
368 ma_context_uninit(&context);
369 ```
370
371 The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. The first parameter is a pointer to a list of `ma_backend`
372 values which are used to override the default backend priorities. When this is NULL, as in this example, miniaudio's default priorities are used. The second
373 parameter is the number of backends listed in the array pointed to by the first parameter. The third parameter is a pointer to a `ma_context_config` object
374 which can be NULL, in which case defaults are used. The context configuration is used for setting the logging callback, custom memory allocation callbacks,
375 user-defined data and some backend-specific configurations.
376
377 Once the context has been initialized you can enumerate devices. In the example above we use the simpler `ma_context_get_devices()`, however you can also use a
378 callback for handling devices by using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer to a pointer that will,
379 upon output, be set to a pointer to a buffer containing a list of `ma_device_info` structures. You also provide a pointer to an unsigned integer that will
380 receive the number of items in the returned buffer. Do not free the returned buffers as their memory is managed internally by miniaudio.
381
382 The `ma_device_info` structure contains an `id` member which is the ID you pass to the device config. It also contains the name of the device which is useful
383 for presenting a list of devices to the user via the UI.
384
385 When creating your own context you will want to pass it to `ma_device_init()` when initializing the device. Passing in NULL, like we do in the first example,
386 will result in miniaudio creating the context for you, which you don't want to do since you've already created a context. Note that internally the context is
387 only tracked by it's pointer which means you must not change the location of the `ma_context` object. If this is an issue, consider using `malloc()` to
388 allocate memory for the context.
389
390
391
392 Building
393 ========
394 miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details.
395
396
397 Windows
398 -------
399 The Windows build should compile cleanly on all popular compilers without the need to configure any include paths nor link to any libraries.
400
401 macOS and iOS
402 -------------
403 The macOS build should compile cleanly without the need to download any dependencies nor link to any libraries or frameworks. The iOS build needs to be
404 compiled as Objective-C (sorry) and will need to link the relevant frameworks but should Just Work with Xcode. Compiling through the command line requires
405 linking to -lpthread and -lm.
406
407 Linux
408 -----
409 The Linux build only requires linking to -ldl, -lpthread and -lm. You do not need any development packages.
410
411 BSD
412 ---
413 The BSD build only requires linking to -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and FreeBSD uses OSS.
414
415 Android
416 -------
417 AAudio is the highest priority backend on Android. This should work out of the box without needing any kind of compiler configuration. Support for AAudio
418 starts with Android 8 which means older versions will fall back to OpenSL|ES which requires API level 16+.
419
420 Emscripten
421 ----------
422 The Emscripten build emits Web Audio JavaScript directly and should Just Work without any configuration. You cannot use -std=c* compiler flags, nor -ansi.
423
424
425 Build Options
426 -------------
427 #define these options before including miniaudio.h.
428
429 #define MA_NO_WASAPI
430 Disables the WASAPI backend.
431
432 #define MA_NO_DSOUND
433 Disables the DirectSound backend.
434
435 #define MA_NO_WINMM
436 Disables the WinMM backend.
437
438 #define MA_NO_ALSA
439 Disables the ALSA backend.
440
441 #define MA_NO_PULSEAUDIO
442 Disables the PulseAudio backend.
443
444 #define MA_NO_JACK
445 Disables the JACK backend.
446
447 #define MA_NO_COREAUDIO
448 Disables the Core Audio backend.
449
450 #define MA_NO_SNDIO
451 Disables the sndio backend.
452
453 #define MA_NO_AUDIO4
454 Disables the audio(4) backend.
455
456 #define MA_NO_OSS
457 Disables the OSS backend.
458
459 #define MA_NO_AAUDIO
460 Disables the AAudio backend.
461
462 #define MA_NO_OPENSL
463 Disables the OpenSL|ES backend.
464
465 #define MA_NO_WEBAUDIO
466 Disables the Web Audio backend.
467
468 #define MA_NO_NULL
469 Disables the null backend.
470
471 #define MA_NO_DECODING
472 Disables the decoding APIs.
473
474 #define MA_NO_DEVICE_IO
475 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
476 decoding APIs.
477
478 #define MA_NO_SSE2
479 Disables SSE2 optimizations.
480
481 #define MA_NO_AVX2
482 Disables AVX2 optimizations.
483
484 #define MA_NO_AVX512
485 Disables AVX-512 optimizations.
486
487 #define MA_NO_NEON
488 Disables NEON optimizations.
489
490 #define MA_LOG_LEVEL <Level>
491 Sets the logging level. Set level to one of the following:
492 MA_LOG_LEVEL_VERBOSE
493 MA_LOG_LEVEL_INFO
494 MA_LOG_LEVEL_WARNING
495 MA_LOG_LEVEL_ERROR
496
497 #define MA_DEBUG_OUTPUT
498 Enable printf() debug output.
499
500 #define MA_COINIT_VALUE
501 Windows only. The value to pass to internal calls to CoInitializeEx(). Defaults to COINIT_MULTITHREADED.
502
503 #define MA_API
504 Controls how public APIs should be decorated. Defaults to `extern`.
505
506 #define MA_DLL
507 If set, configures MA_API to either import or export APIs depending on whether or not the implementation is being defined. If defining the implementation,
508 MA_API will be configured to export. Otherwise it will be configured to import. This has no effect if MA_API is defined externally.
509
510
511
512
513 Definitions
514 ===========
515 This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms throughout the audio space, so this
516 section is intended to clarify how miniaudio uses each term.
517
518 Sample
519 ------
520 A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit floating point number.
521
522 Frame / PCM Frame
523 -----------------
524 A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 samples, a mono frame is 1 sample, a 5.1 surround sound frame
525 is 6 samples, etc. The terms "frame" and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. If ever miniaudio
526 needs to refer to a compressed frame, such as a FLAC frame, it will always clarify what it's referring to with something like "FLAC frame".
527
528 Channel
529 -------
530 A stream of monaural audio that is emitted from an individual speaker in a speaker system, or received from an individual microphone in a microphone system. A
531 stereo stream has two channels (a left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio systems refer to a channel as
532 a complex audio stream that's mixed with other channels to produce the final mix - this is completely different to miniaudio's use of the term "channel" and
533 should not be confused.
534
535 Sample Rate
536 -----------
537 The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number of PCM frames that are processed per second.
538
539 Formats
540 -------
541 Throughout miniaudio you will see references to different sample formats:
542
543 |---------------|----------------------------------------|---------------------------|
544 | Symbol | Description | Range |
545 |---------------|----------------------------------------|---------------------------|
546 | ma_format_f32 | 32-bit floating point | [-1, 1] |
547 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
548 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
549 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
550 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
551 |---------------|----------------------------------------|---------------------------|
552
553 All formats are native-endian.
554
555
556
557 Decoding
558 ========
559 The `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
560 implementation of miniaudio. You can find copies of these in the "extras" folder in the miniaudio repository (https://github.com/dr-soft/miniaudio).
561
562 The table below are the supported decoding backends:
563
564 |--------|-----------------|
565 | Type | Backend Library |
566 |--------|-----------------|
567 | WAV | dr_wav.h |
568 | FLAC | dr_flac.h |
569 | MP3 | dr_mp3.h |
570 | Vorbis | stb_vorbis.c |
571 |--------|-----------------|
572
573 The code below is an example of how to enable decoding backends:
574
575 ```c
576 #include "dr_flac.h" // Enables FLAC decoding.
577 #include "dr_mp3.h" // Enables MP3 decoding.
578 #include "dr_wav.h" // Enables WAV decoding.
579
580 #define MINIAUDIO_IMPLEMENTATION
581 #include "miniaudio.h"
582 ```
583
584 A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with `ma_decoder_init_memory()`, or from data delivered via callbacks
585 with `ma_decoder_init()`. Here is an example for loading a decoder from a file:
586
587 ```c
588 ma_decoder decoder;
589 ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder);
590 if (result != MA_SUCCESS) {
591 return false; // An error occurred.
592 }
593
594 ...
595
596 ma_decoder_uninit(&decoder);
597 ```
598
599 When initializing a decoder, you can optionally pass in a pointer to a ma_decoder_config object (the NULL argument in the example above) which allows you to
600 configure the output format, channel count, sample rate and channel map:
601
602 ```c
603 ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000);
604 ```
605
606 When passing in NULL for decoder config in `ma_decoder_init*()`, the output format will be the same as that defined by the decoding backend.
607
608 Data is read from the decoder as PCM frames:
609
610 ```c
611 ma_uint64 framesRead = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead);
612 ```
613
614 You can also seek to a specific frame like so:
615
616 ```c
617 ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame);
618 if (result != MA_SUCCESS) {
619 return false; // An error occurred.
620 }
621 ```
622
623 When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding backend. This can be unnecessarily inefficient if the type
624 is already known. In this case you can use the `_wav`, `_mp3`, etc. varients of the aforementioned initialization APIs:
625
626 ```c
627 ma_decoder_init_wav()
628 ma_decoder_init_mp3()
629 ma_decoder_init_memory_wav()
630 ma_decoder_init_memory_mp3()
631 ma_decoder_init_file_wav()
632 ma_decoder_init_file_mp3()
633 etc.
634 ```
635
636 The `ma_decoder_init_file()` API will try using the file extension to determine which decoding backend to prefer.
637
638
639
640 Encoding
641 ========
642 The `ma_encoding` API is used for writing audio files. To enable an encoder you must #include the header of the relevant backend library before the
643 implementation of miniaudio. You can find copies of these in the "extras" folder in the miniaudio repository (https://github.com/dr-soft/miniaudio).
644
645 The table below are the supported encoding backends:
646
647 |--------|-----------------|
648 | Type | Backend Library |
649 |--------|-----------------|
650 | WAV | dr_wav.h |
651 |--------|-----------------|
652
653 The code below is an example of how to enable encoding backends:
654
655 ```c
656 #include "dr_wav.h" // Enables WAV encoding.
657
658 #define MINIAUDIO_IMPLEMENTATION
659 #include "miniaudio.h"
660 ```
661
662 An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data delivered via callbacks with `ma_encoder_init()`. Below is an
663 example for initializing an encoder to output to a file.
664
665 ```c
666 ma_encoder_config config = ma_encoder_config_init(ma_resource_format_wav, FORMAT, CHANNELS, SAMPLE_RATE);
667 ma_encoder encoder;
668 ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder);
669 if (result != MA_SUCCESS) {
670 // Error
671 }
672
673 ...
674
675 ma_encoder_uninit(&encoder);
676 ```
677
678 When initializing an encoder you must specify a config which is initialized with `ma_encoder_config_init()`. Here you must specify the file type, the output
679 sample format, output channel count and output sample rate. The following file types are supported:
680
681 |------------------------|-------------|
682 | Enum | Description |
683 |------------------------|-------------|
684 | ma_resource_format_wav | WAV |
685 |------------------------|-------------|
686
687 If the format, channel count or sample rate is not supported by the output file type an error will be returned. The encoder will not perform data conversion so
688 you will need to convert it before outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the example below:
689
690 ```c
691 framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite);
692 ```
693
694 Encoders must be uninitialized with `ma_encoder_uninit()`.
695
696
697
698 Sample Format Conversion
699 ========================
700 Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and `ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()`
701 to convert between two specific formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use `ma_convert_pcm_frames_format()` to convert
702 PCM frames where you want to specify the frame count and channel count as a variable instead of the total sample count.
703
704 Dithering
705 ---------
706 Dithering can be set using the ditherMode parameter.
707
708 The different dithering modes include the following, in order of efficiency:
709
710 |-----------|--------------------------|
711 | Type | Enum Token |
712 |-----------|--------------------------|
713 | None | ma_dither_mode_none |
714 | Rectangle | ma_dither_mode_rectangle |
715 | Triangle | ma_dither_mode_triangle |
716 |-----------|--------------------------|
717
718 Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be ignored for conversions where dithering is not needed.
719 Dithering is available for the following conversions:
720
721 s16 -> u8
722 s24 -> u8
723 s32 -> u8
724 f32 -> u8
725 s24 -> s16
726 s32 -> s16
727 f32 -> s16
728
729 Note that it is not an error to pass something other than ma_dither_mode_none for conversions where dither is not used. It will just be ignored.
730
731
732
733 Channel Conversion
734 ==================
735 Channel conversion is used for channel rearrangement and conversion from one channel count to another. The `ma_channel_converter` API is used for channel
736 conversion. Below is an example of initializing a simple channel converter which converts from mono to stereo.
737
738 ```c
739 ma_channel_converter_config config = ma_channel_converter_config_init(ma_format, 1, NULL, 2, NULL, ma_channel_mix_mode_default, NULL);
740 result = ma_channel_converter_init(&config, &converter);
741 if (result != MA_SUCCESS) {
742 // Error.
743 }
744 ```
745
746 To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so:
747
748 ```c
749 ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount);
750 if (result != MA_SUCCESS) {
751 // Error.
752 }
753 ```
754
755 It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM frames.
756
757 The 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
758 `ma_pcm_convert()`, etc.
759
760 Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
761
762
763 Channel Mapping
764 ---------------
765 In addition to converting from one channel count to another, like the example above, The channel converter can also be used to rearrange channels. When
766 initializing the channel converter, you can optionally pass in channel maps for both the input and output frames. If the channel counts are the same, and each
767 channel map contains the same channel positions with the exception that they're in a different order, a simple shuffling of the channels will be performed. If,
768 however, there is not a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed based on a mixing mode which is
769 specified when initializing the `ma_channel_converter_config` object.
770
771 When converting from mono to multi-channel, the mono channel is simply copied to each output channel. When going the other way around, the audio of each output
772 channel is simply averaged and copied to the mono channel.
773
774 In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess channels and silence extra channels. For example, converting
775 from 4 to 2 channels, the 3rd and 4th channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and 4th channels.
776
777 The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a simple distribution between input and output. Imagine sitting
778 in the middle of a room, with speakers on the walls representing channel positions. The MA_CHANNEL_FRONT_LEFT position can be thought of as being in the corner
779 of the front and left walls.
780
781 Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined weights. Custom weights can be passed in as the last parameter of
782 `ma_channel_converter_config_init()`.
783
784 Predefined channel maps can be retrieved with `ma_get_standard_channel_map()`. This takes a `ma_standard_channel_map` enum as it's first parameter, which can
785 be one of the following:
786
787 |-----------------------------------|-----------------------------------------------------------|
788 | Name | Description |
789 |-----------------------------------|-----------------------------------------------------------|
790 | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. |
791 | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. |
792 | ma_standard_channel_map_alsa | Default ALSA channel map. |
793 | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. |
794 | ma_standard_channel_map_flac | FLAC channel map. |
795 | ma_standard_channel_map_vorbis | Vorbis channel map. |
796 | ma_standard_channel_map_sound4 | FreeBSD's sound(4). |
797 | ma_standard_channel_map_sndio | sndio channel map. www.sndio.org/tips.html |
798 | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering |
799 |-----------------------------------|-----------------------------------------------------------|
800
801 Below are the channel maps used by default in miniaudio (ma_standard_channel_map_default):
802
803 |---------------|------------------------------|
804 | Channel Count | Mapping |
805 |---------------|------------------------------|
806 | 1 (Mono) | 0: MA_CHANNEL_MONO |
807 |---------------|------------------------------|
808 | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT |
809 | | 1: MA_CHANNEL_FRONT_RIGHT |
810 |---------------|------------------------------|
811 | 3 | 0: MA_CHANNEL_FRONT_LEFT |
812 | | 1: MA_CHANNEL_FRONT_RIGHT |
813 | | 2: MA_CHANNEL_FRONT_CENTER |
814 |---------------|------------------------------|
815 | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT |
816 | | 1: MA_CHANNEL_FRONT_RIGHT |
817 | | 2: MA_CHANNEL_FRONT_CENTER |
818 | | 3: MA_CHANNEL_BACK_CENTER |
819 |---------------|------------------------------|
820 | 5 | 0: MA_CHANNEL_FRONT_LEFT |
821 | | 1: MA_CHANNEL_FRONT_RIGHT |
822 | | 2: MA_CHANNEL_FRONT_CENTER |
823 | | 3: MA_CHANNEL_BACK_LEFT |
824 | | 4: MA_CHANNEL_BACK_RIGHT |
825 |---------------|------------------------------|
826 | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT |
827 | | 1: MA_CHANNEL_FRONT_RIGHT |
828 | | 2: MA_CHANNEL_FRONT_CENTER |
829 | | 3: MA_CHANNEL_LFE |
830 | | 4: MA_CHANNEL_SIDE_LEFT |
831 | | 5: MA_CHANNEL_SIDE_RIGHT |
832 |---------------|------------------------------|
833 | 7 | 0: MA_CHANNEL_FRONT_LEFT |
834 | | 1: MA_CHANNEL_FRONT_RIGHT |
835 | | 2: MA_CHANNEL_FRONT_CENTER |
836 | | 3: MA_CHANNEL_LFE |
837 | | 4: MA_CHANNEL_BACK_CENTER |
838 | | 4: MA_CHANNEL_SIDE_LEFT |
839 | | 5: MA_CHANNEL_SIDE_RIGHT |
840 |---------------|------------------------------|
841 | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT |
842 | | 1: MA_CHANNEL_FRONT_RIGHT |
843 | | 2: MA_CHANNEL_FRONT_CENTER |
844 | | 3: MA_CHANNEL_LFE |
845 | | 4: MA_CHANNEL_BACK_LEFT |
846 | | 5: MA_CHANNEL_BACK_RIGHT |
847 | | 6: MA_CHANNEL_SIDE_LEFT |
848 | | 7: MA_CHANNEL_SIDE_RIGHT |
849 |---------------|------------------------------|
850 | Other | All channels set to 0. This |
851 | | is equivalent to the same |
852 | | mapping as the device. |
853 |---------------|------------------------------|
854
855
856
857 Resampling
858 ==========
859 Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something like the following:
860
861 ```c
862 ma_resampler_config config = ma_resampler_config_init(ma_format_s16, channels, sampleRateIn, sampleRateOut, ma_resample_algorithm_linear);
863 ma_resampler resampler;
864 ma_result result = ma_resampler_init(&config, &resampler);
865 if (result != MA_SUCCESS) {
866 // An error occurred...
867 }
868 ```
869
870 Do the following to uninitialize the resampler:
871
872 ```c
873 ma_resampler_uninit(&resampler);
874 ```
875
876 The following example shows how data can be processed
877
878 ```c
879 ma_uint64 frameCountIn = 1000;
880 ma_uint64 frameCountOut = 2000;
881 ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
882 if (result != MA_SUCCESS) {
883 // An error occurred...
884 }
885
886 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number of output frames written.
887 ```
888
889 To initialize the resampler you first need to set up a config (`ma_resampler_config`) with `ma_resampler_config_init()`. You need to specify the sample format
890 you want to use, the number of channels, the input and output sample rate, and the algorithm.
891
892 The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format you will need to perform pre- and post-conversions yourself
893 where necessary. Note that the format is the same for both input and output. The format cannot be changed after initialization.
894
895 The resampler supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization.
896
897 The sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the
898 only configuration property that can be changed after initialization.
899
900 The miniaudio resampler supports multiple algorithms:
901
902 |-----------|------------------------------|
903 | Algorithm | Enum Token |
904 |-----------|------------------------------|
905 | Linear | ma_resample_algorithm_linear |
906 | Speex | ma_resample_algorithm_speex |
907 |-----------|------------------------------|
908
909 Because Speex is not public domain it is strictly opt-in and the code is stored in separate files. if you opt-in to the Speex backend you will need to consider
910 it's license, the text of which can be found in it's source files in "extras/speex_resampler". Details on how to opt-in to the Speex resampler is explained in
911 the Speex Resampler section below.
912
913 The algorithm cannot be changed after initialization.
914
915 Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process
916 frames, use `ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number of
917 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
918 number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large
919 buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek.
920
921 The sample rate can be changed dynamically on the fly. You can change this with explicit sample rates with `ma_resampler_set_rate()` and also with a decimal
922 ratio with `ma_resampler_set_rate_ratio()`. The ratio is in/out.
923
924 Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with
925 `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
926 input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`.
927
928 Due to the nature of how resampling works, the resampler introduces some latency. This can be retrieved in terms of both the input rate and the output rate
929 with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.
930
931
932 Resampling Algorithms
933 ---------------------
934 The choice of resampling algorithm depends on your situation and requirements. The linear resampler is the most efficient and has the least amount of latency,
935 but at the expense of poorer quality. The Speex resampler is higher quality, but slower with more latency. It also performs several heap allocations internally
936 for memory management.
937
938
939 Linear Resampling
940 -----------------
941 The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, some control over the quality of the linear resampler which
942 may make it a suitable option depending on your requirements.
943
944 The linear resampler performs low-pass filtering before or after downsampling or upsampling, depending on the sample rates you're converting between. When
945 decreasing the sample rate, the low-pass filter will be applied before downsampling. When increasing the rate it will be performed after upsampling. By default
946 a fourth order low-pass filter will be applied. This can be configured via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering.
947
948 The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of the input and output sample rates (Nyquist Frequency). This
949 can be controlled with the `lpfNyquistFactor` config variable. This defaults to 1, and should be in the range of 0..1, although a value of 0 does not make
950 sense and should be avoided. A value of 1 will use the Nyquist Frequency as the cutoff. A value of 0.5 will use half the Nyquist Frequency as the cutoff, etc.
951 Values less than 1 will result in more washed out sound due to more of the higher frequencies being removed. This config variable has no impact on performance
952 and is a purely perceptual configuration.
953
954 The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`.
955
956
957 Speex Resampling
958 ----------------
959 The Speex resampler is made up of third party code which is released under the BSD license. Because it is licensed differently to miniaudio, which is public
960 domain, it is strictly opt-in and all of it's code is stored in separate files. If you opt-in to the Speex resampler you must consider the license text in it's
961 source files. To opt-in, you must first #include the following file before the implementation of miniaudio.h:
962
963 #include "extras/speex_resampler/ma_speex_resampler.h"
964
965 Both the header and implementation is contained within the same file. The implementation can be included in your program like so:
966
967 #define MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION
968 #include "extras/speex_resampler/ma_speex_resampler.h"
969
970 Note that even if you opt-in to the Speex backend, miniaudio won't use it unless you explicitly ask for it in the respective config of the object you are
971 initializing. If you try to use the Speex resampler without opting in, initialization of the `ma_resampler` object will fail with `MA_NO_BACKEND`.
972
973 The only configuration option to consider with the Speex resampler is the `speex.quality` config variable. This is a value between 0 and 10, with 0 being
974 the fastest with the poorest quality and 10 being the slowest with the highest quality. The default value is 3.
975
976
977
978 General Data Conversion
979 =======================
980 The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and resampling into one operation. This is what miniaudio uses
981 internally to convert between the format requested when the device was initialized and the format of the backend's native device. The API for general data
982 conversion is very similar to the resampling API. Create a `ma_data_converter` object like this:
983
984 ```c
985 ma_data_converter_config config = ma_data_converter_config_init(inputFormat, outputFormat, inputChannels, outputChannels, inputSampleRate, outputSampleRate);
986 ma_data_converter converter;
987 ma_result result = ma_data_converter_init(&config, &converter);
988 if (result != MA_SUCCESS) {
989 // An error occurred...
990 }
991 ```
992
993 In the example above we use `ma_data_converter_config_init()` to initialize the config, however there's many more properties that can be configured, such as
994 channel maps and resampling quality. Something like the following may be more suitable depending on your requirements:
995
996 ```c
997 ma_data_converter_config config = ma_data_converter_config_init_default();
998 config.formatIn = inputFormat;
999 config.formatOut = outputFormat;
1000 config.channelsIn = inputChannels;
1001 config.channelsOut = outputChannels;
1002 config.sampleRateIn = inputSampleRate;
1003 config.sampleRateOut = outputSampleRate;
1004 ma_get_standard_channel_map(ma_standard_channel_map_flac, config.channelCountIn, config.channelMapIn);
1005 config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER;
1006 ```
1007
1008 Do the following to uninitialize the data converter:
1009
1010 ```c
1011 ma_data_converter_uninit(&converter);
1012 ```
1013
1014 The following example shows how data can be processed
1015
1016 ```c
1017 ma_uint64 frameCountIn = 1000;
1018 ma_uint64 frameCountOut = 2000;
1019 ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
1020 if (result != MA_SUCCESS) {
1021 // An error occurred...
1022 }
1023
1024 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number of output frames written.
1025 ```
1026
1027 The data converter supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization.
1028
1029 Sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the only
1030 configuration property that can be changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of `ma_data_converter_config` is
1031 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 resampling
1032 algorithm cannot be changed after initialization.
1033
1034 Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process
1035 frames, use `ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number
1036 of input frames contained in the input buffer. On output these variables contain the number of output frames that were written to the output buffer and the
1037 number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large
1038 buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek.
1039
1040 Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with
1041 `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
1042 input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`.
1043
1044 Due to the nature of how resampling works, the data converter introduces some latency if resampling is required. This can be retrieved in terms of both the
1045 input rate and the output rate with `ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`.
1046
1047
1048
1049 Filtering
1050 =========
1051
1052 Biquad Filtering
1053 ----------------
1054 Biquad filtering is achieved with the `ma_biquad` API. Example:
1055
1056 ```c
1057 ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
1058 ma_result result = ma_biquad_init(&config, &biquad);
1059 if (result != MA_SUCCESS) {
1060 // Error.
1061 }
1062
1063 ...
1064
1065 ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount);
1066 ```
1067
1068 Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, b1 and b2, and the denominator coefficients are a0, a1 and
1069 a2. The a0 coefficient is required and coefficients must not be pre-normalized.
1070
1071 Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format you need to convert it yourself beforehand. When using
1072 `ma_format_s16` the biquad filter will use fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used.
1073
1074 Input and output frames are always interleaved.
1075
1076 Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so:
1077
1078 ```c
1079 ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount);
1080 ```
1081
1082 If you need to change the values of the coefficients, but maintain the values in the registers you can do so with `ma_biquad_reinit()`. This is useful if you
1083 need to change the properties of the filter while keeping the values of registers valid to avoid glitching. Do not use `ma_biquad_init()` for this as it will
1084 do a full initialization which involves clearing the registers to 0. Note that changing the format or channel count after initialization is invalid and will
1085 result in an error.
1086
1087
1088 Low-Pass Filtering
1089 ------------------
1090 Low-pass filtering is achieved with the following APIs:
1091
1092 |---------|------------------------------------------|
1093 | API | Description |
1094 |---------|------------------------------------------|
1095 | ma_lpf1 | First order low-pass filter |
1096 | ma_lpf2 | Second order low-pass filter |
1097 | ma_lpf | High order low-pass filter (Butterworth) |
1098 |---------|------------------------------------------|
1099
1100 Low-pass filter example:
1101
1102 ```c
1103 ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
1104 ma_result result = ma_lpf_init(&config, &lpf);
1105 if (result != MA_SUCCESS) {
1106 // Error.
1107 }
1108
1109 ...
1110
1111 ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount);
1112 ```
1113
1114 Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format you need to convert it yourself beforehand. Input and output
1115 frames are always interleaved.
1116
1117 Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so:
1118
1119 ```c
1120 ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);
1121 ```
1122
1123 The maximum filter order is limited to MA_MAX_FILTER_ORDER which is set to 8. If you need more, you can chain first and second order filters together.
1124
1125 ```c
1126 for (iFilter = 0; iFilter < filterCount; iFilter += 1) {
1127 ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount);
1128 }
1129 ```
1130
1131 If you need to change the configuration of the filter, but need to maintain the state of internal registers you can do so with `ma_lpf_reinit()`. This may be
1132 useful if you need to change the sample rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the format or channel
1133 count after initialization is invalid and will result in an error.
1134
1135 The `ma_lpf` object supports a configurable order, but if you only need a first order filter you may want to consider using `ma_lpf1`. Likewise, if you only
1136 need a second order filter you can use `ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient.
1137
1138 If an even filter order is specified, a series of second order filters will be processed in a chain. If an odd filter order is specified, a first order filter
1139 will be applied, followed by a series of second order filters in a chain.
1140
1141
1142 High-Pass Filtering
1143 -------------------
1144 High-pass filtering is achieved with the following APIs:
1145
1146 |---------|-------------------------------------------|
1147 | API | Description |
1148 |---------|-------------------------------------------|
1149 | ma_hpf1 | First order high-pass filter |
1150 | ma_hpf2 | Second order high-pass filter |
1151 | ma_hpf | High order high-pass filter (Butterworth) |
1152 |---------|-------------------------------------------|
1153
1154 High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, `ma_hpf2` and `ma_hpf`. See example code for low-pass filters
1155 for example usage.
1156
1157
1158 Band-Pass Filtering
1159 -------------------
1160 Band-pass filtering is achieved with the following APIs:
1161
1162 |---------|-------------------------------|
1163 | API | Description |
1164 |---------|-------------------------------|
1165 | ma_bpf2 | Second order band-pass filter |
1166 | ma_bpf | High order band-pass filter |
1167 |---------|-------------------------------|
1168
1169 Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and `ma_hpf`. See example code for low-pass filters for example
1170 usage. Note that the order for band-pass filters must be an even number which means there is no first order band-pass filter, unlike low-pass and high-pass
1171 filters.
1172
1173
1174 Notch Filtering
1175 ---------------
1176 Notch filtering is achieved with the following APIs:
1177
1178 |-----------|------------------------------------------|
1179 | API | Description |
1180 |-----------|------------------------------------------|
1181 | ma_notch2 | Second order notching filter |
1182 |-----------|------------------------------------------|
1183
1184
1185 Peaking EQ Filtering
1186 --------------------
1187 Peaking filtering is achieved with the following APIs:
1188
1189 |----------|------------------------------------------|
1190 | API | Description |
1191 |----------|------------------------------------------|
1192 | ma_peak2 | Second order peaking filter |
1193 |----------|------------------------------------------|
1194
1195
1196 Low Shelf Filtering
1197 -------------------
1198 Low shelf filtering is achieved with the following APIs:
1199
1200 |-------------|------------------------------------------|
1201 | API | Description |
1202 |-------------|------------------------------------------|
1203 | ma_loshelf2 | Second order low shelf filter |
1204 |-------------|------------------------------------------|
1205
1206 Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to just turn them down rather than eliminate them entirely.
1207
1208
1209 High Shelf Filtering
1210 --------------------
1211 High shelf filtering is achieved with the following APIs:
1212
1213 |-------------|------------------------------------------|
1214 | API | Description |
1215 |-------------|------------------------------------------|
1216 | ma_hishelf2 | Second order high shelf filter |
1217 |-------------|------------------------------------------|
1218
1219 The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` instead of `ma_loshelf`. Where a low shelf filter is used to
1220 adjust the volume of low frequencies, the high shelf filter does the same thing for high frequencies.
1221
1222
1223
1224
1225 Waveform and Noise Generation
1226 =============================
1227
1228 Waveforms
1229 ---------
1230 miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved with the `ma_waveform` API. Example:
1231
1232 ```c
1233 ma_waveform_config config = ma_waveform_config_init(FORMAT, CHANNELS, SAMPLE_RATE, ma_waveform_type_sine, amplitude, frequency);
1234
1235 ma_waveform waveform;
1236 ma_result result = ma_waveform_init(&config, &waveform);
1237 if (result != MA_SUCCESS) {
1238 // Error.
1239 }
1240
1241 ...
1242
1243 ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount);
1244 ```
1245
1246 The amplitude, frequency and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()` and
1247 `ma_waveform_set_sample_rate()` respectively.
1248
1249 You can reverse the waveform by setting the amplitude to a negative value. You can use this to control whether or not a sawtooth has a positive or negative
1250 ramp, for example.
1251
1252 Below are the supported waveform types:
1253
1254 |---------------------------|
1255 | Enum Name |
1256 |---------------------------|
1257 | ma_waveform_type_sine |
1258 | ma_waveform_type_square |
1259 | ma_waveform_type_triangle |
1260 | ma_waveform_type_sawtooth |
1261 |---------------------------|
1262
1263
1264
1265 Noise
1266 -----
1267 miniaudio supports generation of white, pink and brownian noise via the `ma_noise` API. Example:
1268
1269 ```c
1270 ma_noise_config config = ma_noise_config_init(FORMAT, CHANNELS, ma_noise_type_white, SEED, amplitude);
1271
1272 ma_noise noise;
1273 ma_result result = ma_noise_init(&config, &noise);
1274 if (result != MA_SUCCESS) {
1275 // Error.
1276 }
1277
1278 ...
1279
1280 ma_noise_read_pcm_frames(&noise, pOutput, frameCount);
1281 ```
1282
1283 The noise API uses simple LCG random number generation. It supports a custom seed which is useful for things like automated testing requiring reproducibility.
1284 Setting the seed to zero will default to MA_DEFAULT_LCG_SEED.
1285
1286 By default, the noise API will use different values for different channels. So, for example, the left side in a stereo stream will be different to the right
1287 side. To instead have each channel use the same random value, set the `duplicateChannels` member of the noise config to true, like so:
1288
1289 ```c
1290 config.duplicateChannels = MA_TRUE;
1291 ```
1292
1293 Below are the supported noise types.
1294
1295 |------------------------|
1296 | Enum Name |
1297 |------------------------|
1298 | ma_noise_type_white |
1299 | ma_noise_type_pink |
1300 | ma_noise_type_brownian |
1301 |------------------------|
1302
1303
1304
1305 Ring Buffers
1306 ============
1307 miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates
1308 on bytes, whereas the `ma_pcm_rb` operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around `ma_rb`.
1309
1310 Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved streams. The caller can also allocate their own backing memory for
1311 the ring buffer to use internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for you.
1312
1313 The examples below use the PCM frame variant of the ring buffer since that's most likely the one you will want to use. To initialize a ring buffer, do
1314 something like the following:
1315
1316 ```c
1317 ma_pcm_rb rb;
1318 ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb);
1319 if (result != MA_SUCCESS) {
1320 // Error
1321 }
1322 ```
1323
1324 The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because it's the PCM varient of the ring buffer API. For the regular
1325 ring buffer that operates on bytes you would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes instead of frames. The
1326 fourth parameter is an optional pre-allocated buffer and the fifth parameter is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation
1327 routines. Passing in NULL for this results in MA_MALLOC() and MA_FREE() being used.
1328
1329 Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is offset from each other based on the stride. To manage your sub-
1330 buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`.
1331
1332 Use 'ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section of the ring buffer. You specify the number of frames you
1333 need, and on output it will set to what was actually acquired. If the read or write pointer is positioned such that the number of frames requested will require
1334 a loop, it will be clamped to the end of the buffer. Therefore, the number of frames you're given may be less than the number you requested.
1335
1336 After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the buffer and then "commit" it with `ma_pcm_rb_commit_read()` or
1337 `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
1338 call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and
1339 `ma_pcm_rb_commit_write()` is what's used to increment the pointers.
1340
1341 If you want to correct for drift between the write pointer and the read pointer you can use a combination of `ma_pcm_rb_pointer_distance()`,
1342 `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
1343 the consumer thread, and the write pointer forward by the producer thread. If there is too much space between the pointers, move the read pointer forward. If
1344 there is too little space between the pointers, move the write pointer forward.
1345
1346 You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` API. This is exactly the sample, only you will use the `ma_rb`
1347 functions instead of `ma_pcm_rb` and instead of frame counts you'll pass around byte counts.
1348
1349 The maximum size of the buffer in bytes is 0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1) due to the most significant bit being used to encode a flag and the internally
1350 managed buffers always being aligned to MA_SIMD_ALIGNMENT.
1351
1352 Note that the ring buffer is only thread safe when used by a single consumer thread and single producer thread.
1353
1354
1355
1356 Backends
1357 ========
1358 The following backends are supported by miniaudio.
1359
1360 |-------------|-----------------------|--------------------------------------------------------|
1361 | Name | Enum Name | Supported Operating Systems |
1362 |-------------|-----------------------|--------------------------------------------------------|
1363 | WASAPI | ma_backend_wasapi | Windows Vista+ |
1364 | DirectSound | ma_backend_dsound | Windows XP+ |
1365 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
1366 | Core Audio | ma_backend_coreaudio | macOS, iOS |
1367 | ALSA | ma_backend_alsa | Linux |
1368 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
1369 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
1370 | sndio | ma_backend_sndio | OpenBSD |
1371 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
1372 | OSS | ma_backend_oss | FreeBSD |
1373 | AAudio | ma_backend_aaudio | Android 8+ |
1374 | OpenSL|ES | ma_backend_opensl | Android (API level 16+) |
1375 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
1376 | Null | ma_backend_null | Cross Platform (not used on Web) |
1377 |-------------|-----------------------|--------------------------------------------------------|
1378
1379 Some backends have some nuance details you may want to be aware of.
1380
1381 WASAPI
1382 ------
1383 - 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
1384 this, set wasapi.noAutoConvertSRC to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing when the
1385 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM flag is specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's lower quality internal resampler being used
1386 instead which will in turn enable the use of low-latency shared mode.
1387
1388 PulseAudio
1389 ----------
1390 - If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
1391 https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling
1392 Alternatively, consider using a different backend such as ALSA.
1393
1394 Android
1395 -------
1396 - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
1397 <uses-permission android:name="android.permission.RECORD_AUDIO" />
1398 - With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a limitation with OpenSL|ES.
1399 - 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
1400 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().
1401 - 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
1402 potential device-specific optimizations the driver may implement.
1403
1404 UWP
1405 ---
1406 - UWP only supports default playback and capture devices.
1407 - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):
1408 <Package ...>
1409 ...
1410 <Capabilities>
1411 <DeviceCapability Name="microphone" />
1412 </Capabilities>
1413 </Package>
1414
1415 Web Audio / Emscripten
1416 ----------------------
1417 - You cannot use -std=c* compiler flags, nor -ansi. This only applies to the Emscripten build.
1418 - 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.
1419 - Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated.
1420 - 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
1421 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
1422 without first handling some kind of user input.
1423
1424
1425
1426 Miscellaneous Notes
1427 ===================
1428 - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI and Core Audio, however other backends such as
1429 PulseAudio may naturally support it, though not all have been tested.
1430 - The contents of the output buffer passed into the data callback will always be pre-initialized to zero unless the noPreZeroedOutputBuffer config variable in
1431 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.
1432 - 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
1433 clipping yourself, you can disable this overhead by setting noClip to true in the device config.
1434 - The sndio backend is currently only enabled on OpenBSD builds.
1435 - The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it.
1436 - Note that GCC and Clang requires "-msse2", "-mavx2", etc. for SIMD optimizations.
1437 */
1438
1439 #ifndef miniaudio_h
1440 #define miniaudio_h
1441
1442 #ifdef __cplusplus
1443 extern "C" {
1444 #endif
1445
1446 #if defined(_MSC_VER) && !defined(__clang__)
1447 #pragma warning(push)
1448 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
1449 #pragma warning(disable:4324) /* structure was padded due to alignment specifier */
1450 #else
1451 #pragma GCC diagnostic push
1452 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
1453 #if defined(__clang__)
1454 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
1455 #endif
1456 #endif
1457
1458 /* Platform/backend detection. */
1459 #ifdef _WIN32
1460 #define MA_WIN32
1461 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
1462 #define MA_WIN32_UWP
1463 #else
1464 #define MA_WIN32_DESKTOP
1465 #endif
1466 #else
1467 #define MA_POSIX
1468
1469 /* We only use multi-threading with the device IO API, so no need to include these headers otherwise. */
1470 #if !defined(MA_NO_DEVICE_IO)
1471 #include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
1472 #include <semaphore.h>
1473 #endif
1474
1475 #ifdef __unix__
1476 #define MA_UNIX
1477 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
1478 #define MA_BSD
1479 #endif
1480 #endif
1481 #ifdef __linux__
1482 #define MA_LINUX
1483 #endif
1484 #ifdef __APPLE__
1485 #define MA_APPLE
1486 #endif
1487 #ifdef __ANDROID__
1488 #define MA_ANDROID
1489 #endif
1490 #ifdef __EMSCRIPTEN__
1491 #define MA_EMSCRIPTEN
1492 #endif
1493 #endif
1494
1495 #include <stddef.h> /* For size_t. */
1496
1497 /* Sized types. Prefer built-in types. Fall back to stdint. */
1498 #ifdef _MSC_VER
1499 #if defined(__clang__)
1500 #pragma GCC diagnostic push
1501 #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
1502 #pragma GCC diagnostic ignored "-Wlong-long"
1503 #pragma GCC diagnostic ignored "-Wc++11-long-long"
1504 #endif
1505 typedef signed __int8 ma_int8;
1506 typedef unsigned __int8 ma_uint8;
1507 typedef signed __int16 ma_int16;
1508 typedef unsigned __int16 ma_uint16;
1509 typedef signed __int32 ma_int32;
1510 typedef unsigned __int32 ma_uint32;
1511 typedef signed __int64 ma_int64;
1512 typedef unsigned __int64 ma_uint64;
1513 #if defined(__clang__)
1514 #pragma GCC diagnostic pop
1515 #endif
1516 #else
1517 #define MA_HAS_STDINT
1518 #include <stdint.h>
1519 typedef int8_t ma_int8;
1520 typedef uint8_t ma_uint8;
1521 typedef int16_t ma_int16;
1522 typedef uint16_t ma_uint16;
1523 typedef int32_t ma_int32;
1524 typedef uint32_t ma_uint32;
1525 typedef int64_t ma_int64;
1526 typedef uint64_t ma_uint64;
1527 #endif
1528
1529 #ifdef MA_HAS_STDINT
1530 typedef uintptr_t ma_uintptr;
1531 #else
1532 #if defined(_WIN32)
1533 #if defined(_WIN64)
1534 typedef ma_uint64 ma_uintptr;
1535 #else
1536 typedef ma_uint32 ma_uintptr;
1537 #endif
1538 #elif defined(__GNUC__)
1539 #if defined(__LP64__)
1540 typedef ma_uint64 ma_uintptr;
1541 #else
1542 typedef ma_uint32 ma_uintptr;
1543 #endif
1544 #else
1545 typedef ma_uint64 ma_uintptr; /* Fallback. */
1546 #endif
1547 #endif
1548
1549 typedef ma_uint8 ma_bool8;
1550 typedef ma_uint32 ma_bool32;
1551 #define MA_TRUE 1
1552 #define MA_FALSE 0
1553
1554 typedef void* ma_handle;
1555 typedef void* ma_ptr;
1556 typedef void (* ma_proc)(void);
1557
1558 #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
1559 typedef ma_uint16 wchar_t;
1560 #endif
1561
1562 /* Define NULL for some compilers. */
1563 #ifndef NULL
1564 #define NULL 0
1565 #endif
1566
1567 #if defined(SIZE_MAX)
1568 #define MA_SIZE_MAX SIZE_MAX
1569 #else
1570 #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
1571 #endif
1572
1573
1574 #ifdef _MSC_VER
1575 #define MA_INLINE __forceinline
1576 #elif defined(__GNUC__)
1577 /*
1578 I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
1579 the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
1580 case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
1581 command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
1582 I am using "__inline__" only when we're compiling in strict ANSI mode.
1583 */
1584 #if defined(__STRICT_ANSI__)
1585 #define MA_INLINE __inline__ __attribute__((always_inline))
1586 #else
1587 #define MA_INLINE inline __attribute__((always_inline))
1588 #endif
1589 #else
1590 #define MA_INLINE
1591 #endif
1592
1593 #if !defined(MA_API)
1594 #if defined(MA_DLL)
1595 #if defined(_WIN32)
1596 #define MA_DLL_IMPORT __declspec(dllimport)
1597 #define MA_DLL_EXPORT __declspec(dllexport)
1598 #define MA_DLL_PRIVATE static
1599 #else
1600 #if defined(__GNUC__) && __GNUC__ >= 4
1601 #define MA_DLL_IMPORT __attribute__((visibility("default")))
1602 #define MA_DLL_EXPORT __attribute__((visibility("default")))
1603 #define MA_DLL_PRIVATE __attribute__((visibility("hidden")))
1604 #else
1605 #define MA_DLL_IMPORT
1606 #define MA_DLL_EXPORT
1607 #define MA_DLL_PRIVATE static
1608 #endif
1609 #endif
1610
1611 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
1612 #define MA_API MA_DLL_EXPORT
1613 #else
1614 #define MA_API MA_DLL_IMPORT
1615 #endif
1616 #define MA_PRIVATE MA_DLL_PRIVATE
1617 #else
1618 #define MA_API extern
1619 #define MA_PRIVATE static
1620 #endif
1621 #endif
1622
1623 /* SIMD alignment in bytes. Currently set to 64 bytes in preparation for future AVX-512 optimizations. */
1624 #define MA_SIMD_ALIGNMENT 64
1625
1626
1627 /* Logging levels */
1628 #define MA_LOG_LEVEL_VERBOSE 4
1629 #define MA_LOG_LEVEL_INFO 3
1630 #define MA_LOG_LEVEL_WARNING 2
1631 #define MA_LOG_LEVEL_ERROR 1
1632
1633 #ifndef MA_LOG_LEVEL
1634 #define MA_LOG_LEVEL MA_LOG_LEVEL_ERROR
1635 #endif
1636
1637 typedef struct ma_context ma_context;
1638 typedef struct ma_device ma_device;
1639
1640 typedef ma_uint8 ma_channel;
1641 #define MA_CHANNEL_NONE 0
1642 #define MA_CHANNEL_MONO 1
1643 #define MA_CHANNEL_FRONT_LEFT 2
1644 #define MA_CHANNEL_FRONT_RIGHT 3
1645 #define MA_CHANNEL_FRONT_CENTER 4
1646 #define MA_CHANNEL_LFE 5
1647 #define MA_CHANNEL_BACK_LEFT 6
1648 #define MA_CHANNEL_BACK_RIGHT 7
1649 #define MA_CHANNEL_FRONT_LEFT_CENTER 8
1650 #define MA_CHANNEL_FRONT_RIGHT_CENTER 9
1651 #define MA_CHANNEL_BACK_CENTER 10
1652 #define MA_CHANNEL_SIDE_LEFT 11
1653 #define MA_CHANNEL_SIDE_RIGHT 12
1654 #define MA_CHANNEL_TOP_CENTER 13
1655 #define MA_CHANNEL_TOP_FRONT_LEFT 14
1656 #define MA_CHANNEL_TOP_FRONT_CENTER 15
1657 #define MA_CHANNEL_TOP_FRONT_RIGHT 16
1658 #define MA_CHANNEL_TOP_BACK_LEFT 17
1659 #define MA_CHANNEL_TOP_BACK_CENTER 18
1660 #define MA_CHANNEL_TOP_BACK_RIGHT 19
1661 #define MA_CHANNEL_AUX_0 20
1662 #define MA_CHANNEL_AUX_1 21
1663 #define MA_CHANNEL_AUX_2 22
1664 #define MA_CHANNEL_AUX_3 23
1665 #define MA_CHANNEL_AUX_4 24
1666 #define MA_CHANNEL_AUX_5 25
1667 #define MA_CHANNEL_AUX_6 26
1668 #define MA_CHANNEL_AUX_7 27
1669 #define MA_CHANNEL_AUX_8 28
1670 #define MA_CHANNEL_AUX_9 29
1671 #define MA_CHANNEL_AUX_10 30
1672 #define MA_CHANNEL_AUX_11 31
1673 #define MA_CHANNEL_AUX_12 32
1674 #define MA_CHANNEL_AUX_13 33
1675 #define MA_CHANNEL_AUX_14 34
1676 #define MA_CHANNEL_AUX_15 35
1677 #define MA_CHANNEL_AUX_16 36
1678 #define MA_CHANNEL_AUX_17 37
1679 #define MA_CHANNEL_AUX_18 38
1680 #define MA_CHANNEL_AUX_19 39
1681 #define MA_CHANNEL_AUX_20 40
1682 #define MA_CHANNEL_AUX_21 41
1683 #define MA_CHANNEL_AUX_22 42
1684 #define MA_CHANNEL_AUX_23 43
1685 #define MA_CHANNEL_AUX_24 44
1686 #define MA_CHANNEL_AUX_25 45
1687 #define MA_CHANNEL_AUX_26 46
1688 #define MA_CHANNEL_AUX_27 47
1689 #define MA_CHANNEL_AUX_28 48
1690 #define MA_CHANNEL_AUX_29 49
1691 #define MA_CHANNEL_AUX_30 50
1692 #define MA_CHANNEL_AUX_31 51
1693 #define MA_CHANNEL_LEFT MA_CHANNEL_FRONT_LEFT
1694 #define MA_CHANNEL_RIGHT MA_CHANNEL_FRONT_RIGHT
1695 #define MA_CHANNEL_POSITION_COUNT (MA_CHANNEL_AUX_31 + 1)
1696
1697
1698 typedef int ma_result;
1699 #define MA_SUCCESS 0
1700 #define MA_ERROR -1 /* A generic error. */
1701 #define MA_INVALID_ARGS -2
1702 #define MA_INVALID_OPERATION -3
1703 #define MA_OUT_OF_MEMORY -4
1704 #define MA_OUT_OF_RANGE -5
1705 #define MA_ACCESS_DENIED -6
1706 #define MA_DOES_NOT_EXIST -7
1707 #define MA_ALREADY_EXISTS -8
1708 #define MA_TOO_MANY_OPEN_FILES -9
1709 #define MA_INVALID_FILE -10
1710 #define MA_TOO_BIG -11
1711 #define MA_PATH_TOO_LONG -12
1712 #define MA_NAME_TOO_LONG -13
1713 #define MA_NOT_DIRECTORY -14
1714 #define MA_IS_DIRECTORY -15
1715 #define MA_DIRECTORY_NOT_EMPTY -16
1716 #define MA_END_OF_FILE -17
1717 #define MA_NO_SPACE -18
1718 #define MA_BUSY -19
1719 #define MA_IO_ERROR -20
1720 #define MA_INTERRUPT -21
1721 #define MA_UNAVAILABLE -22
1722 #define MA_ALREADY_IN_USE -23
1723 #define MA_BAD_ADDRESS -24
1724 #define MA_BAD_SEEK -25
1725 #define MA_BAD_PIPE -26
1726 #define MA_DEADLOCK -27
1727 #define MA_TOO_MANY_LINKS -28
1728 #define MA_NOT_IMPLEMENTED -29
1729 #define MA_NO_MESSAGE -30
1730 #define MA_BAD_MESSAGE -31
1731 #define MA_NO_DATA_AVAILABLE -32
1732 #define MA_INVALID_DATA -33
1733 #define MA_TIMEOUT -34
1734 #define MA_NO_NETWORK -35
1735 #define MA_NOT_UNIQUE -36
1736 #define MA_NOT_SOCKET -37
1737 #define MA_NO_ADDRESS -38
1738 #define MA_BAD_PROTOCOL -39
1739 #define MA_PROTOCOL_UNAVAILABLE -40
1740 #define MA_PROTOCOL_NOT_SUPPORTED -41
1741 #define MA_PROTOCOL_FAMILY_NOT_SUPPORTED -42
1742 #define MA_ADDRESS_FAMILY_NOT_SUPPORTED -43
1743 #define MA_SOCKET_NOT_SUPPORTED -44
1744 #define MA_CONNECTION_RESET -45
1745 #define MA_ALREADY_CONNECTED -46
1746 #define MA_NOT_CONNECTED -47
1747 #define MA_CONNECTION_REFUSED -48
1748 #define MA_NO_HOST -49
1749 #define MA_IN_PROGRESS -50
1750 #define MA_CANCELLED -51
1751 #define MA_MEMORY_ALREADY_MAPPED -52
1752 #define MA_AT_END -53
1753
1754 /* General miniaudio-specific errors. */
1755 #define MA_FORMAT_NOT_SUPPORTED -100
1756 #define MA_DEVICE_TYPE_NOT_SUPPORTED -101
1757 #define MA_SHARE_MODE_NOT_SUPPORTED -102
1758 #define MA_NO_BACKEND -103
1759 #define MA_NO_DEVICE -104
1760 #define MA_API_NOT_FOUND -105
1761 #define MA_INVALID_DEVICE_CONFIG -106
1762
1763 /* State errors. */
1764 #define MA_DEVICE_NOT_INITIALIZED -200
1765 #define MA_DEVICE_ALREADY_INITIALIZED -201
1766 #define MA_DEVICE_NOT_STARTED -202
1767 #define MA_DEVICE_NOT_STOPPED -203
1768
1769 /* Operation errors. */
1770 #define MA_FAILED_TO_INIT_BACKEND -300
1771 #define MA_FAILED_TO_OPEN_BACKEND_DEVICE -301
1772 #define MA_FAILED_TO_START_BACKEND_DEVICE -302
1773 #define MA_FAILED_TO_STOP_BACKEND_DEVICE -303
1774
1775
1776 /* Standard sample rates. */
1777 #define MA_SAMPLE_RATE_8000 8000
1778 #define MA_SAMPLE_RATE_11025 11025
1779 #define MA_SAMPLE_RATE_16000 16000
1780 #define MA_SAMPLE_RATE_22050 22050
1781 #define MA_SAMPLE_RATE_24000 24000
1782 #define MA_SAMPLE_RATE_32000 32000
1783 #define MA_SAMPLE_RATE_44100 44100
1784 #define MA_SAMPLE_RATE_48000 48000
1785 #define MA_SAMPLE_RATE_88200 88200
1786 #define MA_SAMPLE_RATE_96000 96000
1787 #define MA_SAMPLE_RATE_176400 176400
1788 #define MA_SAMPLE_RATE_192000 192000
1789 #define MA_SAMPLE_RATE_352800 352800
1790 #define MA_SAMPLE_RATE_384000 384000
1791
1792 #define MA_MIN_CHANNELS 1
1793 #define MA_MAX_CHANNELS 32
1794 #define MA_MIN_SAMPLE_RATE MA_SAMPLE_RATE_8000
1795 #define MA_MAX_SAMPLE_RATE MA_SAMPLE_RATE_384000
1796
1797 #ifndef MA_MAX_FILTER_ORDER
1798 #define MA_MAX_FILTER_ORDER 8
1799 #endif
1800
1801 typedef enum
1802 {
1803 ma_stream_format_pcm = 0
1804 } ma_stream_format;
1805
1806 typedef enum
1807 {
1808 ma_stream_layout_interleaved = 0,
1809 ma_stream_layout_deinterleaved
1810 } ma_stream_layout;
1811
1812 typedef enum
1813 {
1814 ma_dither_mode_none = 0,
1815 ma_dither_mode_rectangle,
1816 ma_dither_mode_triangle
1817 } ma_dither_mode;
1818
1819 typedef enum
1820 {
1821 /*
1822 I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
1823 added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().
1824 */
1825 ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */
1826 ma_format_u8 = 1,
1827 ma_format_s16 = 2, /* Seems to be the most widely supported format. */
1828 ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */
1829 ma_format_s32 = 4,
1830 ma_format_f32 = 5,
1831 ma_format_count
1832 } ma_format;
1833
1834 typedef enum
1835 {
1836 ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */
1837 ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */
1838 ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */
1839 ma_channel_mix_mode_planar_blend = ma_channel_mix_mode_rectangular,
1840 ma_channel_mix_mode_default = ma_channel_mix_mode_planar_blend
1841 } ma_channel_mix_mode;
1842
1843 typedef enum
1844 {
1845 ma_standard_channel_map_microsoft,
1846 ma_standard_channel_map_alsa,
1847 ma_standard_channel_map_rfc3551, /* Based off AIFF. */
1848 ma_standard_channel_map_flac,
1849 ma_standard_channel_map_vorbis,
1850 ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */
1851 ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */
1852 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. */
1853 ma_standard_channel_map_default = ma_standard_channel_map_microsoft
1854 } ma_standard_channel_map;
1855
1856 typedef enum
1857 {
1858 ma_performance_profile_low_latency = 0,
1859 ma_performance_profile_conservative
1860 } ma_performance_profile;
1861
1862
1863 typedef struct
1864 {
1865 void* pUserData;
1866 void* (* onMalloc)(size_t sz, void* pUserData);
1867 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
1868 void (* onFree)(void* p, void* pUserData);
1869 } ma_allocation_callbacks;
1870
1871
1872 /**************************************************************************************************************************************************************
1873
1874 Biquad Filtering
1875
1876 **************************************************************************************************************************************************************/
1877 typedef union
1878 {
1879 float f32;
1880 ma_int32 s32;
1881 } ma_biquad_coefficient;
1882
1883 typedef struct
1884 {
1885 ma_format format;
1886 ma_uint32 channels;
1887 double b0;
1888 double b1;
1889 double b2;
1890 double a0;
1891 double a1;
1892 double a2;
1893 } ma_biquad_config;
1894
1895 MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2);
1896
1897 typedef struct
1898 {
1899 ma_format format;
1900 ma_uint32 channels;
1901 ma_biquad_coefficient b0;
1902 ma_biquad_coefficient b1;
1903 ma_biquad_coefficient b2;
1904 ma_biquad_coefficient a1;
1905 ma_biquad_coefficient a2;
1906 ma_biquad_coefficient r1[MA_MAX_CHANNELS];
1907 ma_biquad_coefficient r2[MA_MAX_CHANNELS];
1908 } ma_biquad;
1909
1910 MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ);
1911 MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);
1912 MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1913 MA_API ma_uint32 ma_biquad_get_latency(ma_biquad* pBQ);
1914
1915
1916 /**************************************************************************************************************************************************************
1917
1918 Low-Pass Filtering
1919
1920 **************************************************************************************************************************************************************/
1921 typedef struct
1922 {
1923 ma_format format;
1924 ma_uint32 channels;
1925 ma_uint32 sampleRate;
1926 double cutoffFrequency;
1927 double q;
1928 } ma_lpf1_config, ma_lpf2_config;
1929
1930 MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
1931 MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
1932
1933 typedef struct
1934 {
1935 ma_format format;
1936 ma_uint32 channels;
1937 ma_biquad_coefficient a;
1938 ma_biquad_coefficient r1[MA_MAX_CHANNELS];
1939 } ma_lpf1;
1940
1941 MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, ma_lpf1* pLPF);
1942 MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF);
1943 MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1944 MA_API ma_uint32 ma_lpf1_get_latency(ma_lpf1* pLPF);
1945
1946 typedef struct
1947 {
1948 ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */
1949 } ma_lpf2;
1950
1951 MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, ma_lpf2* pLPF);
1952 MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF);
1953 MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1954 MA_API ma_uint32 ma_lpf2_get_latency(ma_lpf2* pLPF);
1955
1956
1957 typedef struct
1958 {
1959 ma_format format;
1960 ma_uint32 channels;
1961 ma_uint32 sampleRate;
1962 double cutoffFrequency;
1963 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
1964 } ma_lpf_config;
1965
1966 MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
1967
1968 typedef struct
1969 {
1970 ma_format format;
1971 ma_uint32 channels;
1972 ma_uint32 lpf1Count;
1973 ma_uint32 lpf2Count;
1974 ma_lpf1 lpf1[1];
1975 ma_lpf2 lpf2[MA_MAX_FILTER_ORDER/2];
1976 } ma_lpf;
1977
1978 MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF);
1979 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF);
1980 MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
1981 MA_API ma_uint32 ma_lpf_get_latency(ma_lpf* pLPF);
1982
1983
1984 /**************************************************************************************************************************************************************
1985
1986 High-Pass Filtering
1987
1988 **************************************************************************************************************************************************************/
1989 typedef struct
1990 {
1991 ma_format format;
1992 ma_uint32 channels;
1993 ma_uint32 sampleRate;
1994 double cutoffFrequency;
1995 double q;
1996 } ma_hpf1_config, ma_hpf2_config;
1997
1998 MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
1999 MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
2000
2001 typedef struct
2002 {
2003 ma_format format;
2004 ma_uint32 channels;
2005 ma_biquad_coefficient a;
2006 ma_biquad_coefficient r1[MA_MAX_CHANNELS];
2007 } ma_hpf1;
2008
2009 MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, ma_hpf1* pHPF);
2010 MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF);
2011 MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2012 MA_API ma_uint32 ma_hpf1_get_latency(ma_hpf1* pHPF);
2013
2014 typedef struct
2015 {
2016 ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */
2017 } ma_hpf2;
2018
2019 MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, ma_hpf2* pHPF);
2020 MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF);
2021 MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2022 MA_API ma_uint32 ma_hpf2_get_latency(ma_hpf2* pHPF);
2023
2024
2025 typedef struct
2026 {
2027 ma_format format;
2028 ma_uint32 channels;
2029 ma_uint32 sampleRate;
2030 double cutoffFrequency;
2031 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
2032 } ma_hpf_config;
2033
2034 MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2035
2036 typedef struct
2037 {
2038 ma_format format;
2039 ma_uint32 channels;
2040 ma_uint32 hpf1Count;
2041 ma_uint32 hpf2Count;
2042 ma_hpf1 hpf1[1];
2043 ma_hpf2 hpf2[MA_MAX_FILTER_ORDER/2];
2044 } ma_hpf;
2045
2046 MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, ma_hpf* pHPF);
2047 MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF);
2048 MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2049 MA_API ma_uint32 ma_hpf_get_latency(ma_hpf* pHPF);
2050
2051
2052 /**************************************************************************************************************************************************************
2053
2054 Band-Pass Filtering
2055
2056 **************************************************************************************************************************************************************/
2057 typedef struct
2058 {
2059 ma_format format;
2060 ma_uint32 channels;
2061 ma_uint32 sampleRate;
2062 double cutoffFrequency;
2063 double q;
2064 } ma_bpf2_config;
2065
2066 MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
2067
2068 typedef struct
2069 {
2070 ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */
2071 } ma_bpf2;
2072
2073 MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, ma_bpf2* pBPF);
2074 MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF);
2075 MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2076 MA_API ma_uint32 ma_bpf2_get_latency(ma_bpf2* pBPF);
2077
2078
2079 typedef struct
2080 {
2081 ma_format format;
2082 ma_uint32 channels;
2083 ma_uint32 sampleRate;
2084 double cutoffFrequency;
2085 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
2086 } ma_bpf_config;
2087
2088 MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2089
2090 typedef struct
2091 {
2092 ma_format format;
2093 ma_uint32 channels;
2094 ma_uint32 bpf2Count;
2095 ma_bpf2 bpf2[MA_MAX_FILTER_ORDER/2];
2096 } ma_bpf;
2097
2098 MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF);
2099 MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF);
2100 MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2101 MA_API ma_uint32 ma_bpf_get_latency(ma_bpf* pBPF);
2102
2103
2104 /**************************************************************************************************************************************************************
2105
2106 Notching Filter
2107
2108 **************************************************************************************************************************************************************/
2109 typedef struct
2110 {
2111 ma_format format;
2112 ma_uint32 channels;
2113 ma_uint32 sampleRate;
2114 double q;
2115 double frequency;
2116 } ma_notch2_config;
2117
2118 MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);
2119
2120 typedef struct
2121 {
2122 ma_biquad bq;
2123 } ma_notch2;
2124
2125 MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, ma_notch2* pFilter);
2126 MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter);
2127 MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2128 MA_API ma_uint32 ma_notch2_get_latency(ma_notch2* pFilter);
2129
2130
2131 /**************************************************************************************************************************************************************
2132
2133 Peaking EQ Filter
2134
2135 **************************************************************************************************************************************************************/
2136 typedef struct
2137 {
2138 ma_format format;
2139 ma_uint32 channels;
2140 ma_uint32 sampleRate;
2141 double gainDB;
2142 double q;
2143 double frequency;
2144 } ma_peak2_config;
2145
2146 MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
2147
2148 typedef struct
2149 {
2150 ma_biquad bq;
2151 } ma_peak2;
2152
2153 MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter);
2154 MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter);
2155 MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2156 MA_API ma_uint32 ma_peak2_get_latency(ma_peak2* pFilter);
2157
2158
2159 /**************************************************************************************************************************************************************
2160
2161 Low Shelf Filter
2162
2163 **************************************************************************************************************************************************************/
2164 typedef struct
2165 {
2166 ma_format format;
2167 ma_uint32 channels;
2168 ma_uint32 sampleRate;
2169 double gainDB;
2170 double shelfSlope;
2171 double frequency;
2172 } ma_loshelf2_config;
2173
2174 MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);
2175
2176 typedef struct
2177 {
2178 ma_biquad bq;
2179 } ma_loshelf2;
2180
2181 MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter);
2182 MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter);
2183 MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2184 MA_API ma_uint32 ma_loshelf2_get_latency(ma_loshelf2* pFilter);
2185
2186
2187 /**************************************************************************************************************************************************************
2188
2189 High Shelf Filter
2190
2191 **************************************************************************************************************************************************************/
2192 typedef struct
2193 {
2194 ma_format format;
2195 ma_uint32 channels;
2196 ma_uint32 sampleRate;
2197 double gainDB;
2198 double shelfSlope;
2199 double frequency;
2200 } ma_hishelf2_config;
2201
2202 MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);
2203
2204 typedef struct
2205 {
2206 ma_biquad bq;
2207 } ma_hishelf2;
2208
2209 MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter);
2210 MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter);
2211 MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2212 MA_API ma_uint32 ma_hishelf2_get_latency(ma_hishelf2* pFilter);
2213
2214
2215
2216 /************************************************************************************************************************************************************
2217 *************************************************************************************************************************************************************
2218
2219 DATA CONVERSION
2220 ===============
2221
2222 This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.
2223
2224 *************************************************************************************************************************************************************
2225 ************************************************************************************************************************************************************/
2226
2227 /**************************************************************************************************************************************************************
2228
2229 Resampling
2230
2231 **************************************************************************************************************************************************************/
2232 typedef struct
2233 {
2234 ma_format format;
2235 ma_uint32 channels;
2236 ma_uint32 sampleRateIn;
2237 ma_uint32 sampleRateOut;
2238 ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */
2239 double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */
2240 } ma_linear_resampler_config;
2241
2242 MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2243
2244 typedef struct
2245 {
2246 ma_linear_resampler_config config;
2247 ma_uint32 inAdvanceInt;
2248 ma_uint32 inAdvanceFrac;
2249 ma_uint32 inTimeInt;
2250 ma_uint32 inTimeFrac;
2251 union
2252 {
2253 float f32[MA_MAX_CHANNELS];
2254 ma_int16 s16[MA_MAX_CHANNELS];
2255 } x0; /* The previous input frame. */
2256 union
2257 {
2258 float f32[MA_MAX_CHANNELS];
2259 ma_int16 s16[MA_MAX_CHANNELS];
2260 } x1; /* The next input frame. */
2261 ma_lpf lpf;
2262 } ma_linear_resampler;
2263
2264 MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler);
2265 MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler);
2266 MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
2267 MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2268 MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut);
2269 MA_API ma_uint64 ma_linear_resampler_get_required_input_frame_count(ma_linear_resampler* pResampler, ma_uint64 outputFrameCount);
2270 MA_API ma_uint64 ma_linear_resampler_get_expected_output_frame_count(ma_linear_resampler* pResampler, ma_uint64 inputFrameCount);
2271 MA_API ma_uint64 ma_linear_resampler_get_input_latency(ma_linear_resampler* pResampler);
2272 MA_API ma_uint64 ma_linear_resampler_get_output_latency(ma_linear_resampler* pResampler);
2273
2274 typedef enum
2275 {
2276 ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */
2277 ma_resample_algorithm_speex
2278 } ma_resample_algorithm;
2279
2280 typedef struct
2281 {
2282 ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */
2283 ma_uint32 channels;
2284 ma_uint32 sampleRateIn;
2285 ma_uint32 sampleRateOut;
2286 ma_resample_algorithm algorithm;
2287 struct
2288 {
2289 ma_uint32 lpfOrder;
2290 double lpfNyquistFactor;
2291 } linear;
2292 struct
2293 {
2294 int quality; /* 0 to 10. Defaults to 3. */
2295 } speex;
2296 } ma_resampler_config;
2297
2298 MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm);
2299
2300 typedef struct
2301 {
2302 ma_resampler_config config;
2303 union
2304 {
2305 ma_linear_resampler linear;
2306 struct
2307 {
2308 void* pSpeexResamplerState; /* SpeexResamplerState* */
2309 } speex;
2310 } state;
2311 } ma_resampler;
2312
2313 /*
2314 Initializes a new resampler object from a config.
2315 */
2316 MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler);
2317
2318 /*
2319 Uninitializes a resampler.
2320 */
2321 MA_API void ma_resampler_uninit(ma_resampler* pResampler);
2322
2323 /*
2324 Converts the given input data.
2325
2326 Both the input and output frames must be in the format specified in the config when the resampler was initilized.
2327
2328 On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that
2329 were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use
2330 ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames.
2331
2332 On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole
2333 input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames
2334 you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead.
2335
2336 If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of
2337 output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input
2338 frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be
2339 processed. In this case, any internal filter state will be updated as if zeroes were passed in.
2340
2341 It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.
2342
2343 It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
2344 */
2345 MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
2346
2347
2348 /*
2349 Sets the input and output sample sample rate.
2350 */
2351 MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2352
2353 /*
2354 Sets the input and output sample rate as a ratio.
2355
2356 The ration is in/out.
2357 */
2358 MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio);
2359
2360
2361 /*
2362 Calculates the number of whole input frames that would need to be read from the client in order to output the specified
2363 number of output frames.
2364
2365 The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
2366 read from the input buffer in order to output the specified number of output frames.
2367 */
2368 MA_API ma_uint64 ma_resampler_get_required_input_frame_count(ma_resampler* pResampler, ma_uint64 outputFrameCount);
2369
2370 /*
2371 Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
2372 input frames.
2373 */
2374 MA_API ma_uint64 ma_resampler_get_expected_output_frame_count(ma_resampler* pResampler, ma_uint64 inputFrameCount);
2375
2376
2377 /*
2378 Retrieves the latency introduced by the resampler in input frames.
2379 */
2380 MA_API ma_uint64 ma_resampler_get_input_latency(ma_resampler* pResampler);
2381
2382 /*
2383 Retrieves the latency introduced by the resampler in output frames.
2384 */
2385 MA_API ma_uint64 ma_resampler_get_output_latency(ma_resampler* pResampler);
2386
2387
2388
2389 /**************************************************************************************************************************************************************
2390
2391 Channel Conversion
2392
2393 **************************************************************************************************************************************************************/
2394 typedef struct
2395 {
2396 ma_format format;
2397 ma_uint32 channelsIn;
2398 ma_uint32 channelsOut;
2399 ma_channel channelMapIn[MA_MAX_CHANNELS];
2400 ma_channel channelMapOut[MA_MAX_CHANNELS];
2401 ma_channel_mix_mode mixingMode;
2402 float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
2403 } ma_channel_converter_config;
2404
2405 MA_API ma_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);
2406
2407 typedef struct
2408 {
2409 ma_format format;
2410 ma_uint32 channelsIn;
2411 ma_uint32 channelsOut;
2412 ma_channel channelMapIn[MA_MAX_CHANNELS];
2413 ma_channel channelMapOut[MA_MAX_CHANNELS];
2414 ma_channel_mix_mode mixingMode;
2415 union
2416 {
2417 float f32[MA_MAX_CHANNELS][MA_MAX_CHANNELS];
2418 ma_int32 s16[MA_MAX_CHANNELS][MA_MAX_CHANNELS];
2419 } weights;
2420 ma_bool32 isPassthrough : 1;
2421 ma_bool32 isSimpleShuffle : 1;
2422 ma_bool32 isSimpleMonoExpansion : 1;
2423 ma_bool32 isStereoToMono : 1;
2424 ma_uint8 shuffleTable[MA_MAX_CHANNELS];
2425 } ma_channel_converter;
2426
2427 MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter);
2428 MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter);
2429 MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2430
2431
2432 /**************************************************************************************************************************************************************
2433
2434 Data Conversion
2435
2436 **************************************************************************************************************************************************************/
2437 typedef struct
2438 {
2439 ma_format formatIn;
2440 ma_format formatOut;
2441 ma_uint32 channelsIn;
2442 ma_uint32 channelsOut;
2443 ma_uint32 sampleRateIn;
2444 ma_uint32 sampleRateOut;
2445 ma_channel channelMapIn[MA_MAX_CHANNELS];
2446 ma_channel channelMapOut[MA_MAX_CHANNELS];
2447 ma_dither_mode ditherMode;
2448 ma_channel_mix_mode channelMixMode;
2449 float channelWeights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when channelMixMode is set to ma_channel_mix_mode_custom_weights. */
2450 struct
2451 {
2452 ma_resample_algorithm algorithm;
2453 ma_bool32 allowDynamicSampleRate;
2454 struct
2455 {
2456 ma_uint32 lpfOrder;
2457 double lpfNyquistFactor;
2458 } linear;
2459 struct
2460 {
2461 int quality;
2462 } speex;
2463 } resampling;
2464 } ma_data_converter_config;
2465
2466 MA_API ma_data_converter_config ma_data_converter_config_init_default(void);
2467 MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2468
2469 typedef struct
2470 {
2471 ma_data_converter_config config;
2472 ma_channel_converter channelConverter;
2473 ma_resampler resampler;
2474 ma_bool32 hasPreFormatConversion : 1;
2475 ma_bool32 hasPostFormatConversion : 1;
2476 ma_bool32 hasChannelConverter : 1;
2477 ma_bool32 hasResampler : 1;
2478 ma_bool32 isPassthrough : 1;
2479 } ma_data_converter;
2480
2481 MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter);
2482 MA_API void ma_data_converter_uninit(ma_data_converter* pConverter);
2483 MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
2484 MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2485 MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut);
2486 MA_API ma_uint64 ma_data_converter_get_required_input_frame_count(ma_data_converter* pConverter, ma_uint64 outputFrameCount);
2487 MA_API ma_uint64 ma_data_converter_get_expected_output_frame_count(ma_data_converter* pConverter, ma_uint64 inputFrameCount);
2488 MA_API ma_uint64 ma_data_converter_get_input_latency(ma_data_converter* pConverter);
2489 MA_API ma_uint64 ma_data_converter_get_output_latency(ma_data_converter* pConverter);
2490
2491
2492 /************************************************************************************************************************************************************
2493
2494 Format Conversion
2495
2496 ************************************************************************************************************************************************************/
2497 MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2498 MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2499 MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2500 MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2501 MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2502 MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2503 MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2504 MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2505 MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2506 MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2507 MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2508 MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2509 MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2510 MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2511 MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2512 MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2513 MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2514 MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2515 MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2516 MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2517 MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);
2518 MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode);
2519
2520 /*
2521 Deinterleaves an interleaved buffer.
2522 */
2523 MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
2524
2525 /*
2526 Interleaves a group of deinterleaved buffers.
2527 */
2528 MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
2529
2530 /************************************************************************************************************************************************************
2531
2532 Channel Maps
2533
2534 ************************************************************************************************************************************************************/
2535
2536 /*
2537 Helper for retrieving a standard channel map.
2538 */
2539 MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS]);
2540
2541 /*
2542 Copies a channel map.
2543 */
2544 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
2545
2546
2547 /*
2548 Determines whether or not a channel map is valid.
2549
2550 A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but
2551 is usually treated as a passthrough.
2552
2553 Invalid channel maps:
2554 - A channel map with no channels
2555 - A channel map with more than one channel and a mono channel
2556 */
2557 MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS]);
2558
2559 /*
2560 Helper for comparing two channel maps for equality.
2561
2562 This assumes the channel count is the same between the two.
2563 */
2564 MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel channelMapA[MA_MAX_CHANNELS], const ma_channel channelMapB[MA_MAX_CHANNELS]);
2565
2566 /*
2567 Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
2568 */
2569 MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS]);
2570
2571 /*
2572 Helper for determining whether or not a channel is present in the given channel map.
2573 */
2574 MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS], ma_channel channelPosition);
2575
2576
2577 /************************************************************************************************************************************************************
2578
2579 Conversion Helpers
2580
2581 ************************************************************************************************************************************************************/
2582
2583 /*
2584 High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to
2585 determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is
2586 ignored.
2587
2588 A return value of 0 indicates an error.
2589
2590 This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead.
2591 */
2592 MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn);
2593 MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig);
2594
2595
2596 /************************************************************************************************************************************************************
2597
2598 Ring Buffer
2599
2600 ************************************************************************************************************************************************************/
2601 typedef struct
2602 {
2603 void* pBuffer;
2604 ma_uint32 subbufferSizeInBytes;
2605 ma_uint32 subbufferCount;
2606 ma_uint32 subbufferStrideInBytes;
2607 volatile ma_uint32 encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. */
2608 volatile ma_uint32 encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. */
2609 ma_bool32 ownsBuffer : 1; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */
2610 ma_bool32 clearOnWriteAcquire : 1; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */
2611 ma_allocation_callbacks allocationCallbacks;
2612 } ma_rb;
2613
2614 MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
2615 MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
2616 MA_API void ma_rb_uninit(ma_rb* pRB);
2617 MA_API void ma_rb_reset(ma_rb* pRB);
2618 MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
2619 MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
2620 MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
2621 MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
2622 MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);
2623 MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);
2624 MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */
2625 MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB);
2626 MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB);
2627 MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB);
2628 MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB);
2629 MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);
2630 MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);
2631
2632
2633 typedef struct
2634 {
2635 ma_rb rb;
2636 ma_format format;
2637 ma_uint32 channels;
2638 } ma_pcm_rb;
2639
2640 MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB);
2641 MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB);
2642 MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB);
2643 MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB);
2644 MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
2645 MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
2646 MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
2647 MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
2648 MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
2649 MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
2650 MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */
2651 MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB);
2652 MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB);
2653 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB);
2654 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB);
2655 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex);
2656 MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);
2657
2658
2659 /************************************************************************************************************************************************************
2660
2661 Miscellaneous Helpers
2662
2663 ************************************************************************************************************************************************************/
2664 /*
2665 Retrieves a human readable description of the given result code.
2666 */
2667 MA_API const char* ma_result_description(ma_result result);
2668
2669 /*
2670 malloc(). Calls MA_MALLOC().
2671 */
2672 MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
2673
2674 /*
2675 realloc(). Calls MA_REALLOC().
2676 */
2677 MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
2678
2679 /*
2680 free(). Calls MA_FREE().
2681 */
2682 MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
2683
2684 /*
2685 Performs an aligned malloc, with the assumption that the alignment is a power of 2.
2686 */
2687 MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks);
2688
2689 /*
2690 Free's an aligned malloc'd buffer.
2691 */
2692 MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
2693
2694 /*
2695 Retrieves a friendly name for a format.
2696 */
2697 MA_API const char* ma_get_format_name(ma_format format);
2698
2699 /*
2700 Blends two frames in floating point format.
2701 */
2702 MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);
2703
2704 /*
2705 Retrieves the size of a sample in bytes for the given format.
2706
2707 This API is efficient and is implemented using a lookup table.
2708
2709 Thread Safety: SAFE
2710 This API is pure.
2711 */
2712 MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format);
2713 static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; }
2714
2715 /*
2716 Converts a log level to a string.
2717 */
2718 MA_API const char* ma_log_level_to_string(ma_uint32 logLevel);
2719
2720
2721
2722 /************************************************************************************************************************************************************
2723 *************************************************************************************************************************************************************
2724
2725 DEVICE I/O
2726 ==========
2727
2728 This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.
2729
2730 *************************************************************************************************************************************************************
2731 ************************************************************************************************************************************************************/
2732 #ifndef MA_NO_DEVICE_IO
2733 /* Some backends are only supported on certain platforms. */
2734 #if defined(MA_WIN32)
2735 #define MA_SUPPORT_WASAPI
2736 #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
2737 #define MA_SUPPORT_DSOUND
2738 #define MA_SUPPORT_WINMM
2739 #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
2740 #endif
2741 #endif
2742 #if defined(MA_UNIX)
2743 #if defined(MA_LINUX)
2744 #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */
2745 #define MA_SUPPORT_ALSA
2746 #endif
2747 #endif
2748 #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
2749 #define MA_SUPPORT_PULSEAUDIO
2750 #define MA_SUPPORT_JACK
2751 #endif
2752 #if defined(MA_ANDROID)
2753 #define MA_SUPPORT_AAUDIO
2754 #define MA_SUPPORT_OPENSL
2755 #endif
2756 #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
2757 #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
2758 #endif
2759 #if defined(__NetBSD__) || defined(__OpenBSD__)
2760 #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */
2761 #endif
2762 #if defined(__FreeBSD__) || defined(__DragonFly__)
2763 #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */
2764 #endif
2765 #endif
2766 #if defined(MA_APPLE)
2767 #define MA_SUPPORT_COREAUDIO
2768 #endif
2769 #if defined(MA_EMSCRIPTEN)
2770 #define MA_SUPPORT_WEBAUDIO
2771 #endif
2772
2773 /* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
2774 #if !defined(MA_EMSCRIPTEN)
2775 #define MA_SUPPORT_NULL
2776 #endif
2777
2778
2779 #if !defined(MA_NO_WASAPI) && defined(MA_SUPPORT_WASAPI)
2780 #define MA_ENABLE_WASAPI
2781 #endif
2782 #if !defined(MA_NO_DSOUND) && defined(MA_SUPPORT_DSOUND)
2783 #define MA_ENABLE_DSOUND
2784 #endif
2785 #if !defined(MA_NO_WINMM) && defined(MA_SUPPORT_WINMM)
2786 #define MA_ENABLE_WINMM
2787 #endif
2788 #if !defined(MA_NO_ALSA) && defined(MA_SUPPORT_ALSA)
2789 #define MA_ENABLE_ALSA
2790 #endif
2791 #if !defined(MA_NO_PULSEAUDIO) && defined(MA_SUPPORT_PULSEAUDIO)
2792 #define MA_ENABLE_PULSEAUDIO
2793 #endif
2794 #if !defined(MA_NO_JACK) && defined(MA_SUPPORT_JACK)
2795 #define MA_ENABLE_JACK
2796 #endif
2797 #if !defined(MA_NO_COREAUDIO) && defined(MA_SUPPORT_COREAUDIO)
2798 #define MA_ENABLE_COREAUDIO
2799 #endif
2800 #if !defined(MA_NO_SNDIO) && defined(MA_SUPPORT_SNDIO)
2801 #define MA_ENABLE_SNDIO
2802 #endif
2803 #if !defined(MA_NO_AUDIO4) && defined(MA_SUPPORT_AUDIO4)
2804 #define MA_ENABLE_AUDIO4
2805 #endif
2806 #if !defined(MA_NO_OSS) && defined(MA_SUPPORT_OSS)
2807 #define MA_ENABLE_OSS
2808 #endif
2809 #if !defined(MA_NO_AAUDIO) && defined(MA_SUPPORT_AAUDIO)
2810 #define MA_ENABLE_AAUDIO
2811 #endif
2812 #if !defined(MA_NO_OPENSL) && defined(MA_SUPPORT_OPENSL)
2813 #define MA_ENABLE_OPENSL
2814 #endif
2815 #if !defined(MA_NO_WEBAUDIO) && defined(MA_SUPPORT_WEBAUDIO)
2816 #define MA_ENABLE_WEBAUDIO
2817 #endif
2818 #if !defined(MA_NO_NULL) && defined(MA_SUPPORT_NULL)
2819 #define MA_ENABLE_NULL
2820 #endif
2821
2822 #ifdef MA_SUPPORT_WASAPI
2823 /* We need a IMMNotificationClient object for WASAPI. */
2824 typedef struct
2825 {
2826 void* lpVtbl;
2827 ma_uint32 counter;
2828 ma_device* pDevice;
2829 } ma_IMMNotificationClient;
2830 #endif
2831
2832 /* Backend enums must be in priority order. */
2833 typedef enum
2834 {
2835 ma_backend_wasapi,
2836 ma_backend_dsound,
2837 ma_backend_winmm,
2838 ma_backend_coreaudio,
2839 ma_backend_sndio,
2840 ma_backend_audio4,
2841 ma_backend_oss,
2842 ma_backend_pulseaudio,
2843 ma_backend_alsa,
2844 ma_backend_jack,
2845 ma_backend_aaudio,
2846 ma_backend_opensl,
2847 ma_backend_webaudio,
2848 ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
2849 } ma_backend;
2850
2851 /* Thread priorties should be ordered such that the default priority of the worker thread is 0. */
2852 typedef enum
2853 {
2854 ma_thread_priority_idle = -5,
2855 ma_thread_priority_lowest = -4,
2856 ma_thread_priority_low = -3,
2857 ma_thread_priority_normal = -2,
2858 ma_thread_priority_high = -1,
2859 ma_thread_priority_highest = 0,
2860 ma_thread_priority_realtime = 1,
2861 ma_thread_priority_default = 0
2862 } ma_thread_priority;
2863
2864 typedef struct
2865 {
2866 ma_context* pContext;
2867
2868 union
2869 {
2870 #ifdef MA_WIN32
2871 struct
2872 {
2873 /*HANDLE*/ ma_handle hThread;
2874 } win32;
2875 #endif
2876 #ifdef MA_POSIX
2877 struct
2878 {
2879 pthread_t thread;
2880 } posix;
2881 #endif
2882 int _unused;
2883 };
2884 } ma_thread;
2885
2886 typedef struct
2887 {
2888 ma_context* pContext;
2889
2890 union
2891 {
2892 #ifdef MA_WIN32
2893 struct
2894 {
2895 /*HANDLE*/ ma_handle hMutex;
2896 } win32;
2897 #endif
2898 #ifdef MA_POSIX
2899 struct
2900 {
2901 pthread_mutex_t mutex;
2902 } posix;
2903 #endif
2904 int _unused;
2905 };
2906 } ma_mutex;
2907
2908 typedef struct
2909 {
2910 ma_context* pContext;
2911
2912 union
2913 {
2914 #ifdef MA_WIN32
2915 struct
2916 {
2917 /*HANDLE*/ ma_handle hEvent;
2918 } win32;
2919 #endif
2920 #ifdef MA_POSIX
2921 struct
2922 {
2923 pthread_mutex_t mutex;
2924 pthread_cond_t condition;
2925 ma_uint32 value;
2926 } posix;
2927 #endif
2928 int _unused;
2929 };
2930 } ma_event;
2931
2932 typedef struct
2933 {
2934 ma_context* pContext;
2935
2936 union
2937 {
2938 #ifdef MA_WIN32
2939 struct
2940 {
2941 /*HANDLE*/ ma_handle hSemaphore;
2942 } win32;
2943 #endif
2944 #ifdef MA_POSIX
2945 struct
2946 {
2947 sem_t semaphore;
2948 } posix;
2949 #endif
2950 int _unused;
2951 };
2952 } ma_semaphore;
2953
2954
2955 /*
2956 The callback for processing audio data from the device.
2957
2958 The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data
2959 available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the
2960 callback will be fired with a consistent frame count.
2961
2962
2963 Parameters
2964 ----------
2965 pDevice (in)
2966 A pointer to the relevant device.
2967
2968 pOutput (out)
2969 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
2970 full-duplex device and null for a capture and loopback device.
2971
2972 pInput (in)
2973 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
2974 playback device.
2975
2976 frameCount (in)
2977 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
2978 `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must
2979 not assume this will always be the same value each time the callback is fired.
2980
2981
2982 Remarks
2983 -------
2984 You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the
2985 callback. The following APIs cannot be called from inside the callback:
2986
2987 ma_device_init()
2988 ma_device_init_ex()
2989 ma_device_uninit()
2990 ma_device_start()
2991 ma_device_stop()
2992
2993 The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread.
2994 */
2995 typedef void (* ma_device_callback_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
2996
2997 /*
2998 The callback for when the device has been stopped.
2999
3000 This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces
3001 such as being unplugged or an internal error occuring.
3002
3003
3004 Parameters
3005 ----------
3006 pDevice (in)
3007 A pointer to the device that has just stopped.
3008
3009
3010 Remarks
3011 -------
3012 Do not restart or uninitialize the device from the callback.
3013 */
3014 typedef void (* ma_stop_proc)(ma_device* pDevice);
3015
3016 /*
3017 The callback for handling log messages.
3018
3019
3020 Parameters
3021 ----------
3022 pContext (in)
3023 A pointer to the context the log message originated from.
3024
3025 pDevice (in)
3026 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.
3027
3028 logLevel (in)
3029 The log level. This can be one of the following:
3030
3031 |----------------------|
3032 | Log Level |
3033 |----------------------|
3034 | MA_LOG_LEVEL_VERBOSE |
3035 | MA_LOG_LEVEL_INFO |
3036 | MA_LOG_LEVEL_WARNING |
3037 | MA_LOG_LEVEL_ERROR |
3038 |----------------------|
3039
3040 message (in)
3041 The log message.
3042
3043
3044 Remarks
3045 -------
3046 Do not modify the state of the device from inside the callback.
3047 */
3048 typedef void (* ma_log_proc)(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message);
3049
3050 typedef enum
3051 {
3052 ma_device_type_playback = 1,
3053 ma_device_type_capture = 2,
3054 ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */
3055 ma_device_type_loopback = 4
3056 } ma_device_type;
3057
3058 typedef enum
3059 {
3060 ma_share_mode_shared = 0,
3061 ma_share_mode_exclusive
3062 } ma_share_mode;
3063
3064 /* iOS/tvOS/watchOS session categories. */
3065 typedef enum
3066 {
3067 ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord with AVAudioSessionCategoryOptionDefaultToSpeaker. */
3068 ma_ios_session_category_none, /* Leave the session category unchanged. */
3069 ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */
3070 ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */
3071 ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */
3072 ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */
3073 ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */
3074 ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */
3075 } ma_ios_session_category;
3076
3077 /* iOS/tvOS/watchOS session category options */
3078 typedef enum
3079 {
3080 ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */
3081 ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */
3082 ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */
3083 ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */
3084 ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */
3085 ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */
3086 ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */
3087 } ma_ios_session_category_option;
3088
3089 typedef union
3090 {
3091 ma_int64 counter;
3092 double counterD;
3093 } ma_timer;
3094
3095 typedef union
3096 {
3097 wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
3098 ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
3099 /*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. */
3100 char alsa[256]; /* ALSA uses a name string for identification. */
3101 char pulse[256]; /* PulseAudio uses a name string for identification. */
3102 int jack; /* JACK always uses default devices. */
3103 char coreaudio[256]; /* Core Audio uses a string for identification. */
3104 char sndio[256]; /* "snd/0", etc. */
3105 char audio4[256]; /* "/dev/audio", etc. */
3106 char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */
3107 ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
3108 ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
3109 char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
3110 int nullbackend; /* The null backend uses an integer for device IDs. */
3111 } ma_device_id;
3112
3113 typedef struct
3114 {
3115 /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
3116 ma_device_id id;
3117 char name[256];
3118
3119 /*
3120 Detailed info. As much of this is filled as possible with ma_context_get_device_info(). Note that you are allowed to initialize
3121 a device with settings outside of this range, but it just means the data will be converted using miniaudio's data conversion
3122 pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided
3123 here mainly for informational purposes or in the rare case that someone might find it useful.
3124
3125 These will be set to 0 when returned by ma_context_enumerate_devices() or ma_context_get_devices().
3126 */
3127 ma_uint32 formatCount;
3128 ma_format formats[ma_format_count];
3129 ma_uint32 minChannels;
3130 ma_uint32 maxChannels;
3131 ma_uint32 minSampleRate;
3132 ma_uint32 maxSampleRate;
3133
3134 struct
3135 {
3136 ma_bool32 isDefault;
3137 } _private;
3138 } ma_device_info;
3139
3140 typedef struct
3141 {
3142 ma_device_type deviceType;
3143 ma_uint32 sampleRate;
3144 ma_uint32 periodSizeInFrames;
3145 ma_uint32 periodSizeInMilliseconds;
3146 ma_uint32 periods;
3147 ma_performance_profile performanceProfile;
3148 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. */
3149 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. */
3150 ma_device_callback_proc dataCallback;
3151 ma_stop_proc stopCallback;
3152 void* pUserData;
3153 struct
3154 {
3155 ma_resample_algorithm algorithm;
3156 struct
3157 {
3158 ma_uint32 lpfOrder;
3159 } linear;
3160 struct
3161 {
3162 int quality;
3163 } speex;
3164 } resampling;
3165 struct
3166 {
3167 ma_device_id* pDeviceID;
3168 ma_format format;
3169 ma_uint32 channels;
3170 ma_channel channelMap[MA_MAX_CHANNELS];
3171 ma_share_mode shareMode;
3172 } playback;
3173 struct
3174 {
3175 ma_device_id* pDeviceID;
3176 ma_format format;
3177 ma_uint32 channels;
3178 ma_channel channelMap[MA_MAX_CHANNELS];
3179 ma_share_mode shareMode;
3180 } capture;
3181
3182 struct
3183 {
3184 ma_bool32 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
3185 ma_bool32 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
3186 ma_bool32 noAutoStreamRouting; /* Disables automatic stream routing. */
3187 ma_bool32 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */
3188 } wasapi;
3189 struct
3190 {
3191 ma_bool32 noMMap; /* Disables MMap mode. */
3192 ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */
3193 ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */
3194 ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */
3195 } alsa;
3196 struct
3197 {
3198 const char* pStreamNamePlayback;
3199 const char* pStreamNameCapture;
3200 } pulse;
3201 } ma_device_config;
3202
3203 typedef struct
3204 {
3205 ma_log_proc logCallback;
3206 ma_thread_priority threadPriority;
3207 void* pUserData;
3208 ma_allocation_callbacks allocationCallbacks;
3209 struct
3210 {
3211 ma_bool32 useVerboseDeviceEnumeration;
3212 } alsa;
3213 struct
3214 {
3215 const char* pApplicationName;
3216 const char* pServerName;
3217 ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */
3218 } pulse;
3219 struct
3220 {
3221 ma_ios_session_category sessionCategory;
3222 ma_uint32 sessionCategoryOptions;
3223 } coreaudio;
3224 struct
3225 {
3226 const char* pClientName;
3227 ma_bool32 tryStartServer;
3228 } jack;
3229 } ma_context_config;
3230
3231 /*
3232 The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`.
3233
3234
3235 Parameters
3236 ----------
3237 pContext (in)
3238 A pointer to the context performing the enumeration.
3239
3240 deviceType (in)
3241 The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`.
3242
3243 pInfo (in)
3244 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,
3245 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
3246 is too inefficient.
3247
3248 pUserData (in)
3249 The user data pointer passed into `ma_context_enumerate_devices()`.
3250 */
3251 typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);
3252
3253 struct ma_context
3254 {
3255 ma_backend backend; /* DirectSound, ALSA, etc. */
3256 ma_log_proc logCallback;
3257 ma_thread_priority threadPriority;
3258 void* pUserData;
3259 ma_allocation_callbacks allocationCallbacks;
3260 ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */
3261 ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */
3262 ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */
3263 ma_uint32 playbackDeviceInfoCount;
3264 ma_uint32 captureDeviceInfoCount;
3265 ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */
3266 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. */
3267
3268 ma_result (* onUninit )(ma_context* pContext);
3269 ma_bool32 (* onDeviceIDEqual )(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1);
3270 ma_result (* onEnumDevices )(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); /* Return false from the callback to stop enumeration. */
3271 ma_result (* onGetDeviceInfo )(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo);
3272 ma_result (* onDeviceInit )(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
3273 void (* onDeviceUninit )(ma_device* pDevice);
3274 ma_result (* onDeviceStart )(ma_device* pDevice);
3275 ma_result (* onDeviceStop )(ma_device* pDevice);
3276 ma_result (* onDeviceMainLoop)(ma_device* pDevice);
3277
3278 union
3279 {
3280 #ifdef MA_SUPPORT_WASAPI
3281 struct
3282 {
3283 int _unused;
3284 } wasapi;
3285 #endif
3286 #ifdef MA_SUPPORT_DSOUND
3287 struct
3288 {
3289 ma_handle hDSoundDLL;
3290 ma_proc DirectSoundCreate;
3291 ma_proc DirectSoundEnumerateA;
3292 ma_proc DirectSoundCaptureCreate;
3293 ma_proc DirectSoundCaptureEnumerateA;
3294 } dsound;
3295 #endif
3296 #ifdef MA_SUPPORT_WINMM
3297 struct
3298 {
3299 ma_handle hWinMM;
3300 ma_proc waveOutGetNumDevs;
3301 ma_proc waveOutGetDevCapsA;
3302 ma_proc waveOutOpen;
3303 ma_proc waveOutClose;
3304 ma_proc waveOutPrepareHeader;
3305 ma_proc waveOutUnprepareHeader;
3306 ma_proc waveOutWrite;
3307 ma_proc waveOutReset;
3308 ma_proc waveInGetNumDevs;
3309 ma_proc waveInGetDevCapsA;
3310 ma_proc waveInOpen;
3311 ma_proc waveInClose;
3312 ma_proc waveInPrepareHeader;
3313 ma_proc waveInUnprepareHeader;
3314 ma_proc waveInAddBuffer;
3315 ma_proc waveInStart;
3316 ma_proc waveInReset;
3317 } winmm;
3318 #endif
3319 #ifdef MA_SUPPORT_ALSA
3320 struct
3321 {
3322 ma_handle asoundSO;
3323 ma_proc snd_pcm_open;
3324 ma_proc snd_pcm_close;
3325 ma_proc snd_pcm_hw_params_sizeof;
3326 ma_proc snd_pcm_hw_params_any;
3327 ma_proc snd_pcm_hw_params_set_format;
3328 ma_proc snd_pcm_hw_params_set_format_first;
3329 ma_proc snd_pcm_hw_params_get_format_mask;
3330 ma_proc snd_pcm_hw_params_set_channels_near;
3331 ma_proc snd_pcm_hw_params_set_rate_resample;
3332 ma_proc snd_pcm_hw_params_set_rate_near;
3333 ma_proc snd_pcm_hw_params_set_buffer_size_near;
3334 ma_proc snd_pcm_hw_params_set_periods_near;
3335 ma_proc snd_pcm_hw_params_set_access;
3336 ma_proc snd_pcm_hw_params_get_format;
3337 ma_proc snd_pcm_hw_params_get_channels;
3338 ma_proc snd_pcm_hw_params_get_channels_min;
3339 ma_proc snd_pcm_hw_params_get_channels_max;
3340 ma_proc snd_pcm_hw_params_get_rate;
3341 ma_proc snd_pcm_hw_params_get_rate_min;
3342 ma_proc snd_pcm_hw_params_get_rate_max;
3343 ma_proc snd_pcm_hw_params_get_buffer_size;
3344 ma_proc snd_pcm_hw_params_get_periods;
3345 ma_proc snd_pcm_hw_params_get_access;
3346 ma_proc snd_pcm_hw_params;
3347 ma_proc snd_pcm_sw_params_sizeof;
3348 ma_proc snd_pcm_sw_params_current;
3349 ma_proc snd_pcm_sw_params_get_boundary;
3350 ma_proc snd_pcm_sw_params_set_avail_min;
3351 ma_proc snd_pcm_sw_params_set_start_threshold;
3352 ma_proc snd_pcm_sw_params_set_stop_threshold;
3353 ma_proc snd_pcm_sw_params;
3354 ma_proc snd_pcm_format_mask_sizeof;
3355 ma_proc snd_pcm_format_mask_test;
3356 ma_proc snd_pcm_get_chmap;
3357 ma_proc snd_pcm_state;
3358 ma_proc snd_pcm_prepare;
3359 ma_proc snd_pcm_start;
3360 ma_proc snd_pcm_drop;
3361 ma_proc snd_pcm_drain;
3362 ma_proc snd_device_name_hint;
3363 ma_proc snd_device_name_get_hint;
3364 ma_proc snd_card_get_index;
3365 ma_proc snd_device_name_free_hint;
3366 ma_proc snd_pcm_mmap_begin;
3367 ma_proc snd_pcm_mmap_commit;
3368 ma_proc snd_pcm_recover;
3369 ma_proc snd_pcm_readi;
3370 ma_proc snd_pcm_writei;
3371 ma_proc snd_pcm_avail;
3372 ma_proc snd_pcm_avail_update;
3373 ma_proc snd_pcm_wait;
3374 ma_proc snd_pcm_info;
3375 ma_proc snd_pcm_info_sizeof;
3376 ma_proc snd_pcm_info_get_name;
3377 ma_proc snd_config_update_free_global;
3378
3379 ma_mutex internalDeviceEnumLock;
3380 ma_bool32 useVerboseDeviceEnumeration;
3381 } alsa;
3382 #endif
3383 #ifdef MA_SUPPORT_PULSEAUDIO
3384 struct
3385 {
3386 ma_handle pulseSO;
3387 ma_proc pa_mainloop_new;
3388 ma_proc pa_mainloop_free;
3389 ma_proc pa_mainloop_get_api;
3390 ma_proc pa_mainloop_iterate;
3391 ma_proc pa_mainloop_wakeup;
3392 ma_proc pa_context_new;
3393 ma_proc pa_context_unref;
3394 ma_proc pa_context_connect;
3395 ma_proc pa_context_disconnect;
3396 ma_proc pa_context_set_state_callback;
3397 ma_proc pa_context_get_state;
3398 ma_proc pa_context_get_sink_info_list;
3399 ma_proc pa_context_get_source_info_list;
3400 ma_proc pa_context_get_sink_info_by_name;
3401 ma_proc pa_context_get_source_info_by_name;
3402 ma_proc pa_operation_unref;
3403 ma_proc pa_operation_get_state;
3404 ma_proc pa_channel_map_init_extend;
3405 ma_proc pa_channel_map_valid;
3406 ma_proc pa_channel_map_compatible;
3407 ma_proc pa_stream_new;
3408 ma_proc pa_stream_unref;
3409 ma_proc pa_stream_connect_playback;
3410 ma_proc pa_stream_connect_record;
3411 ma_proc pa_stream_disconnect;
3412 ma_proc pa_stream_get_state;
3413 ma_proc pa_stream_get_sample_spec;
3414 ma_proc pa_stream_get_channel_map;
3415 ma_proc pa_stream_get_buffer_attr;
3416 ma_proc pa_stream_set_buffer_attr;
3417 ma_proc pa_stream_get_device_name;
3418 ma_proc pa_stream_set_write_callback;
3419 ma_proc pa_stream_set_read_callback;
3420 ma_proc pa_stream_flush;
3421 ma_proc pa_stream_drain;
3422 ma_proc pa_stream_is_corked;
3423 ma_proc pa_stream_cork;
3424 ma_proc pa_stream_trigger;
3425 ma_proc pa_stream_begin_write;
3426 ma_proc pa_stream_write;
3427 ma_proc pa_stream_peek;
3428 ma_proc pa_stream_drop;
3429 ma_proc pa_stream_writable_size;
3430 ma_proc pa_stream_readable_size;
3431
3432 char* pApplicationName;
3433 char* pServerName;
3434 ma_bool32 tryAutoSpawn;
3435 } pulse;
3436 #endif
3437 #ifdef MA_SUPPORT_JACK
3438 struct
3439 {
3440 ma_handle jackSO;
3441 ma_proc jack_client_open;
3442 ma_proc jack_client_close;
3443 ma_proc jack_client_name_size;
3444 ma_proc jack_set_process_callback;
3445 ma_proc jack_set_buffer_size_callback;
3446 ma_proc jack_on_shutdown;
3447 ma_proc jack_get_sample_rate;
3448 ma_proc jack_get_buffer_size;
3449 ma_proc jack_get_ports;
3450 ma_proc jack_activate;
3451 ma_proc jack_deactivate;
3452 ma_proc jack_connect;
3453 ma_proc jack_port_register;
3454 ma_proc jack_port_name;
3455 ma_proc jack_port_get_buffer;
3456 ma_proc jack_free;
3457
3458 char* pClientName;
3459 ma_bool32 tryStartServer;
3460 } jack;
3461 #endif
3462 #ifdef MA_SUPPORT_COREAUDIO
3463 struct
3464 {
3465 ma_handle hCoreFoundation;
3466 ma_proc CFStringGetCString;
3467 ma_proc CFRelease;
3468
3469 ma_handle hCoreAudio;
3470 ma_proc AudioObjectGetPropertyData;
3471 ma_proc AudioObjectGetPropertyDataSize;
3472 ma_proc AudioObjectSetPropertyData;
3473 ma_proc AudioObjectAddPropertyListener;
3474 ma_proc AudioObjectRemovePropertyListener;
3475
3476 ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */
3477 ma_proc AudioComponentFindNext;
3478 ma_proc AudioComponentInstanceDispose;
3479 ma_proc AudioComponentInstanceNew;
3480 ma_proc AudioOutputUnitStart;
3481 ma_proc AudioOutputUnitStop;
3482 ma_proc AudioUnitAddPropertyListener;
3483 ma_proc AudioUnitGetPropertyInfo;
3484 ma_proc AudioUnitGetProperty;
3485 ma_proc AudioUnitSetProperty;
3486 ma_proc AudioUnitInitialize;
3487 ma_proc AudioUnitRender;
3488
3489 /*AudioComponent*/ ma_ptr component;
3490 } coreaudio;
3491 #endif
3492 #ifdef MA_SUPPORT_SNDIO
3493 struct
3494 {
3495 ma_handle sndioSO;
3496 ma_proc sio_open;
3497 ma_proc sio_close;
3498 ma_proc sio_setpar;
3499 ma_proc sio_getpar;
3500 ma_proc sio_getcap;
3501 ma_proc sio_start;
3502 ma_proc sio_stop;
3503 ma_proc sio_read;
3504 ma_proc sio_write;
3505 ma_proc sio_onmove;
3506 ma_proc sio_nfds;
3507 ma_proc sio_pollfd;
3508 ma_proc sio_revents;
3509 ma_proc sio_eof;
3510 ma_proc sio_setvol;
3511 ma_proc sio_onvol;
3512 ma_proc sio_initpar;
3513 } sndio;
3514 #endif
3515 #ifdef MA_SUPPORT_AUDIO4
3516 struct
3517 {
3518 int _unused;
3519 } audio4;
3520 #endif
3521 #ifdef MA_SUPPORT_OSS
3522 struct
3523 {
3524 int versionMajor;
3525 int versionMinor;
3526 } oss;
3527 #endif
3528 #ifdef MA_SUPPORT_AAUDIO
3529 struct
3530 {
3531 ma_handle hAAudio; /* libaaudio.so */
3532 ma_proc AAudio_createStreamBuilder;
3533 ma_proc AAudioStreamBuilder_delete;
3534 ma_proc AAudioStreamBuilder_setDeviceId;
3535 ma_proc AAudioStreamBuilder_setDirection;
3536 ma_proc AAudioStreamBuilder_setSharingMode;
3537 ma_proc AAudioStreamBuilder_setFormat;
3538 ma_proc AAudioStreamBuilder_setChannelCount;
3539 ma_proc AAudioStreamBuilder_setSampleRate;
3540 ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
3541 ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
3542 ma_proc AAudioStreamBuilder_setDataCallback;
3543 ma_proc AAudioStreamBuilder_setErrorCallback;
3544 ma_proc AAudioStreamBuilder_setPerformanceMode;
3545 ma_proc AAudioStreamBuilder_openStream;
3546 ma_proc AAudioStream_close;
3547 ma_proc AAudioStream_getState;
3548 ma_proc AAudioStream_waitForStateChange;
3549 ma_proc AAudioStream_getFormat;
3550 ma_proc AAudioStream_getChannelCount;
3551 ma_proc AAudioStream_getSampleRate;
3552 ma_proc AAudioStream_getBufferCapacityInFrames;
3553 ma_proc AAudioStream_getFramesPerDataCallback;
3554 ma_proc AAudioStream_getFramesPerBurst;
3555 ma_proc AAudioStream_requestStart;
3556 ma_proc AAudioStream_requestStop;
3557 } aaudio;
3558 #endif
3559 #ifdef MA_SUPPORT_OPENSL
3560 struct
3561 {
3562 int _unused;
3563 } opensl;
3564 #endif
3565 #ifdef MA_SUPPORT_WEBAUDIO
3566 struct
3567 {
3568 int _unused;
3569 } webaudio;
3570 #endif
3571 #ifdef MA_SUPPORT_NULL
3572 struct
3573 {
3574 int _unused;
3575 } null_backend;
3576 #endif
3577 };
3578
3579 union
3580 {
3581 #ifdef MA_WIN32
3582 struct
3583 {
3584 /*HMODULE*/ ma_handle hOle32DLL;
3585 ma_proc CoInitializeEx;
3586 ma_proc CoUninitialize;
3587 ma_proc CoCreateInstance;
3588 ma_proc CoTaskMemFree;
3589 ma_proc PropVariantClear;
3590 ma_proc StringFromGUID2;
3591
3592 /*HMODULE*/ ma_handle hUser32DLL;
3593 ma_proc GetForegroundWindow;
3594 ma_proc GetDesktopWindow;
3595
3596 /*HMODULE*/ ma_handle hAdvapi32DLL;
3597 ma_proc RegOpenKeyExA;
3598 ma_proc RegCloseKey;
3599 ma_proc RegQueryValueExA;
3600 } win32;
3601 #endif
3602 #ifdef MA_POSIX
3603 struct
3604 {
3605 ma_handle pthreadSO;
3606 ma_proc pthread_create;
3607 ma_proc pthread_join;
3608 ma_proc pthread_mutex_init;
3609 ma_proc pthread_mutex_destroy;
3610 ma_proc pthread_mutex_lock;
3611 ma_proc pthread_mutex_unlock;
3612 ma_proc pthread_cond_init;
3613 ma_proc pthread_cond_destroy;
3614 ma_proc pthread_cond_wait;
3615 ma_proc pthread_cond_signal;
3616 ma_proc pthread_attr_init;
3617 ma_proc pthread_attr_destroy;
3618 ma_proc pthread_attr_setschedpolicy;
3619 ma_proc pthread_attr_getschedparam;
3620 ma_proc pthread_attr_setschedparam;
3621 } posix;
3622 #endif
3623 int _unused;
3624 };
3625 };
3626
3627 struct ma_device
3628 {
3629 ma_context* pContext;
3630 ma_device_type type;
3631 ma_uint32 sampleRate;
3632 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`. */
3633 ma_device_callback_proc onData; /* Set once at initialization time and should not be changed after. */
3634 ma_stop_proc onStop; /* Set once at initialization time and should not be changed after. */
3635 void* pUserData; /* Application defined data. */
3636 ma_mutex lock;
3637 ma_event wakeupEvent;
3638 ma_event startEvent;
3639 ma_event stopEvent;
3640 ma_thread thread;
3641 ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
3642 ma_bool32 usingDefaultSampleRate : 1;
3643 ma_bool32 usingDefaultBufferSize : 1;
3644 ma_bool32 usingDefaultPeriods : 1;
3645 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(). */
3646 ma_bool32 noPreZeroedOutputBuffer : 1;
3647 ma_bool32 noClip : 1;
3648 volatile float masterVolumeFactor; /* Volatile so we can use some thread safety when applying volume to periods. */
3649 struct
3650 {
3651 ma_resample_algorithm algorithm;
3652 struct
3653 {
3654 ma_uint32 lpfOrder;
3655 } linear;
3656 struct
3657 {
3658 int quality;
3659 } speex;
3660 } resampling;
3661 struct
3662 {
3663 char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
3664 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
3665 ma_bool32 usingDefaultFormat : 1;
3666 ma_bool32 usingDefaultChannels : 1;
3667 ma_bool32 usingDefaultChannelMap : 1;
3668 ma_format format;
3669 ma_uint32 channels;
3670 ma_channel channelMap[MA_MAX_CHANNELS];
3671 ma_format internalFormat;
3672 ma_uint32 internalChannels;
3673 ma_uint32 internalSampleRate;
3674 ma_channel internalChannelMap[MA_MAX_CHANNELS];
3675 ma_uint32 internalPeriodSizeInFrames;
3676 ma_uint32 internalPeriods;
3677 ma_data_converter converter;
3678 } playback;
3679 struct
3680 {
3681 char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
3682 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
3683 ma_bool32 usingDefaultFormat : 1;
3684 ma_bool32 usingDefaultChannels : 1;
3685 ma_bool32 usingDefaultChannelMap : 1;
3686 ma_format format;
3687 ma_uint32 channels;
3688 ma_channel channelMap[MA_MAX_CHANNELS];
3689 ma_format internalFormat;
3690 ma_uint32 internalChannels;
3691 ma_uint32 internalSampleRate;
3692 ma_channel internalChannelMap[MA_MAX_CHANNELS];
3693 ma_uint32 internalPeriodSizeInFrames;
3694 ma_uint32 internalPeriods;
3695 ma_data_converter converter;
3696 } capture;
3697
3698 union
3699 {
3700 #ifdef MA_SUPPORT_WASAPI
3701 struct
3702 {
3703 /*IAudioClient**/ ma_ptr pAudioClientPlayback;
3704 /*IAudioClient**/ ma_ptr pAudioClientCapture;
3705 /*IAudioRenderClient**/ ma_ptr pRenderClient;
3706 /*IAudioCaptureClient**/ ma_ptr pCaptureClient;
3707 /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
3708 ma_IMMNotificationClient notificationClient;
3709 /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
3710 /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
3711 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. */
3712 ma_uint32 actualPeriodSizeInFramesCapture;
3713 ma_uint32 originalPeriodSizeInFrames;
3714 ma_uint32 originalPeriodSizeInMilliseconds;
3715 ma_uint32 originalPeriods;
3716 ma_bool32 hasDefaultPlaybackDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
3717 ma_bool32 hasDefaultCaptureDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
3718 ma_uint32 periodSizeInFramesPlayback;
3719 ma_uint32 periodSizeInFramesCapture;
3720 ma_bool32 isStartedCapture; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
3721 ma_bool32 isStartedPlayback; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
3722 ma_bool32 noAutoConvertSRC : 1; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
3723 ma_bool32 noDefaultQualitySRC : 1; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
3724 ma_bool32 noHardwareOffloading : 1;
3725 ma_bool32 allowCaptureAutoStreamRouting : 1;
3726 ma_bool32 allowPlaybackAutoStreamRouting : 1;
3727 } wasapi;
3728 #endif
3729 #ifdef MA_SUPPORT_DSOUND
3730 struct
3731 {
3732 /*LPDIRECTSOUND*/ ma_ptr pPlayback;
3733 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
3734 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
3735 /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
3736 /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
3737 } dsound;
3738 #endif
3739 #ifdef MA_SUPPORT_WINMM
3740 struct
3741 {
3742 /*HWAVEOUT*/ ma_handle hDevicePlayback;
3743 /*HWAVEIN*/ ma_handle hDeviceCapture;
3744 /*HANDLE*/ ma_handle hEventPlayback;
3745 /*HANDLE*/ ma_handle hEventCapture;
3746 ma_uint32 fragmentSizeInFrames;
3747 ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
3748 ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
3749 ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
3750 ma_uint32 headerFramesConsumedCapture; /* ^^^ */
3751 /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
3752 /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
3753 ma_uint8* pIntermediaryBufferPlayback;
3754 ma_uint8* pIntermediaryBufferCapture;
3755 ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
3756 } winmm;
3757 #endif
3758 #ifdef MA_SUPPORT_ALSA
3759 struct
3760 {
3761 /*snd_pcm_t**/ ma_ptr pPCMPlayback;
3762 /*snd_pcm_t**/ ma_ptr pPCMCapture;
3763 ma_bool32 isUsingMMapPlayback : 1;
3764 ma_bool32 isUsingMMapCapture : 1;
3765 } alsa;
3766 #endif
3767 #ifdef MA_SUPPORT_PULSEAUDIO
3768 struct
3769 {
3770 /*pa_mainloop**/ ma_ptr pMainLoop;
3771 /*pa_mainloop_api**/ ma_ptr pAPI;
3772 /*pa_context**/ ma_ptr pPulseContext;
3773 /*pa_stream**/ ma_ptr pStreamPlayback;
3774 /*pa_stream**/ ma_ptr pStreamCapture;
3775 /*pa_context_state*/ ma_uint32 pulseContextState;
3776 void* pMappedBufferPlayback;
3777 const void* pMappedBufferCapture;
3778 ma_uint32 mappedBufferFramesRemainingPlayback;
3779 ma_uint32 mappedBufferFramesRemainingCapture;
3780 ma_uint32 mappedBufferFramesCapacityPlayback;
3781 ma_uint32 mappedBufferFramesCapacityCapture;
3782 ma_bool32 breakFromMainLoop : 1;
3783 } pulse;
3784 #endif
3785 #ifdef MA_SUPPORT_JACK
3786 struct
3787 {
3788 /*jack_client_t**/ ma_ptr pClient;
3789 /*jack_port_t**/ ma_ptr pPortsPlayback[MA_MAX_CHANNELS];
3790 /*jack_port_t**/ ma_ptr pPortsCapture[MA_MAX_CHANNELS];
3791 float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
3792 float* pIntermediaryBufferCapture;
3793 ma_pcm_rb duplexRB;
3794 } jack;
3795 #endif
3796 #ifdef MA_SUPPORT_COREAUDIO
3797 struct
3798 {
3799 ma_uint32 deviceObjectIDPlayback;
3800 ma_uint32 deviceObjectIDCapture;
3801 /*AudioUnit*/ ma_ptr audioUnitPlayback;
3802 /*AudioUnit*/ ma_ptr audioUnitCapture;
3803 /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */
3804 ma_event stopEvent;
3805 ma_uint32 originalPeriodSizeInFrames;
3806 ma_uint32 originalPeriodSizeInMilliseconds;
3807 ma_uint32 originalPeriods;
3808 ma_bool32 isDefaultPlaybackDevice;
3809 ma_bool32 isDefaultCaptureDevice;
3810 ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
3811 ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
3812 ma_pcm_rb duplexRB;
3813 void* pRouteChangeHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */
3814 } coreaudio;
3815 #endif
3816 #ifdef MA_SUPPORT_SNDIO
3817 struct
3818 {
3819 ma_ptr handlePlayback;
3820 ma_ptr handleCapture;
3821 ma_bool32 isStartedPlayback;
3822 ma_bool32 isStartedCapture;
3823 } sndio;
3824 #endif
3825 #ifdef MA_SUPPORT_AUDIO4
3826 struct
3827 {
3828 int fdPlayback;
3829 int fdCapture;
3830 } audio4;
3831 #endif
3832 #ifdef MA_SUPPORT_OSS
3833 struct
3834 {
3835 int fdPlayback;
3836 int fdCapture;
3837 } oss;
3838 #endif
3839 #ifdef MA_SUPPORT_AAUDIO
3840 struct
3841 {
3842 /*AAudioStream**/ ma_ptr pStreamPlayback;
3843 /*AAudioStream**/ ma_ptr pStreamCapture;
3844 ma_pcm_rb duplexRB;
3845 } aaudio;
3846 #endif
3847 #ifdef MA_SUPPORT_OPENSL
3848 struct
3849 {
3850 /*SLObjectItf*/ ma_ptr pOutputMixObj;
3851 /*SLOutputMixItf*/ ma_ptr pOutputMix;
3852 /*SLObjectItf*/ ma_ptr pAudioPlayerObj;
3853 /*SLPlayItf*/ ma_ptr pAudioPlayer;
3854 /*SLObjectItf*/ ma_ptr pAudioRecorderObj;
3855 /*SLRecordItf*/ ma_ptr pAudioRecorder;
3856 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;
3857 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;
3858 ma_bool32 isDrainingCapture;
3859 ma_bool32 isDrainingPlayback;
3860 ma_uint32 currentBufferIndexPlayback;
3861 ma_uint32 currentBufferIndexCapture;
3862 ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
3863 ma_uint8* pBufferCapture;
3864 ma_pcm_rb duplexRB;
3865 } opensl;
3866 #endif
3867 #ifdef MA_SUPPORT_WEBAUDIO
3868 struct
3869 {
3870 int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
3871 int indexCapture;
3872 ma_pcm_rb duplexRB; /* In external capture format. */
3873 } webaudio;
3874 #endif
3875 #ifdef MA_SUPPORT_NULL
3876 struct
3877 {
3878 ma_thread deviceThread;
3879 ma_event operationEvent;
3880 ma_event operationCompletionEvent;
3881 ma_uint32 operation;
3882 ma_result operationResult;
3883 ma_timer timer;
3884 double priorRunTime;
3885 ma_uint32 currentPeriodFramesRemainingPlayback;
3886 ma_uint32 currentPeriodFramesRemainingCapture;
3887 ma_uint64 lastProcessedFramePlayback;
3888 ma_uint32 lastProcessedFrameCapture;
3889 ma_bool32 isStarted;
3890 } null_device;
3891 #endif
3892 };
3893 };
3894 #if defined(_MSC_VER) && !defined(__clang__)
3895 #pragma warning(pop)
3896 #else
3897 #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
3898 #endif
3899
3900 /*
3901 Initializes a `ma_context_config` object.
3902
3903
3904 Return Value
3905 ------------
3906 A `ma_context_config` initialized to defaults.
3907
3908
3909 Remarks
3910 -------
3911 You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio
3912 is updated and new members are added to `ma_context_config`. It also sets logical defaults.
3913
3914 You can override members of the returned object by changing it's members directly.
3915
3916
3917 See Also
3918 --------
3919 ma_context_init()
3920 */
3921 MA_API ma_context_config ma_context_config_init(void);
3922
3923 /*
3924 Initializes a context.
3925
3926 The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual
3927 device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices.
3928
3929
3930 Parameters
3931 ----------
3932 backends (in, optional)
3933 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
3934
3935 backendCount (in, optional)
3936 The number of items in `backend`. Ignored if `backend` is NULL.
3937
3938 pConfig (in, optional)
3939 The context configuration.
3940
3941 pContext (in)
3942 A pointer to the context object being initialized.
3943
3944
3945 Return Value
3946 ------------
3947 MA_SUCCESS if successful; any other error code otherwise.
3948
3949
3950 Thread Safety
3951 -------------
3952 Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
3953
3954
3955 Remarks
3956 -------
3957 When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order:
3958
3959 |-------------|-----------------------|--------------------------------------------------------|
3960 | Name | Enum Name | Supported Operating Systems |
3961 |-------------|-----------------------|--------------------------------------------------------|
3962 | WASAPI | ma_backend_wasapi | Windows Vista+ |
3963 | DirectSound | ma_backend_dsound | Windows XP+ |
3964 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
3965 | Core Audio | ma_backend_coreaudio | macOS, iOS |
3966 | ALSA | ma_backend_alsa | Linux |
3967 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
3968 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
3969 | sndio | ma_backend_sndio | OpenBSD |
3970 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
3971 | OSS | ma_backend_oss | FreeBSD |
3972 | AAudio | ma_backend_aaudio | Android 8+ |
3973 | OpenSL|ES | ma_backend_opensl | Android (API level 16+) |
3974 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
3975 | Null | ma_backend_null | Cross Platform (not used on Web) |
3976 |-------------|-----------------------|--------------------------------------------------------|
3977
3978 The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings
3979 can then be set directly on the structure. Below are the members of the `ma_context_config` object.
3980
3981 logCallback
3982 Callback for handling log messages from miniaudio.
3983
3984 threadPriority
3985 The desired priority to use for the audio thread. Allowable values include the following:
3986
3987 |--------------------------------------|
3988 | Thread Priority |
3989 |--------------------------------------|
3990 | ma_thread_priority_idle |
3991 | ma_thread_priority_lowest |
3992 | ma_thread_priority_low |
3993 | ma_thread_priority_normal |
3994 | ma_thread_priority_high |
3995 | ma_thread_priority_highest (default) |
3996 | ma_thread_priority_realtime |
3997 | ma_thread_priority_default |
3998 |--------------------------------------|
3999
4000 pUserData
4001 A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`.
4002
4003 allocationCallbacks
4004 Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation
4005 callbacks will be used for anything tied to the context, including devices.
4006
4007 alsa.useVerboseDeviceEnumeration
4008 ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique
4009 card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes
4010 it so the ALSA backend includes all devices. Defaults to false.
4011
4012 pulse.pApplicationName
4013 PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`.
4014
4015 pulse.pServerName
4016 PulseAudio only. The name of the server to connect to with `pa_context_connect()`.
4017
4018 pulse.tryAutoSpawn
4019 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
4020 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
4021 intrusive for the end user.
4022
4023 coreaudio.sessionCategory
4024 iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
4025
4026 |-----------------------------------------|-------------------------------------|
4027 | miniaudio Token | Core Audio Token |
4028 |-----------------------------------------|-------------------------------------|
4029 | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient |
4030 | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient |
4031 | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback |
4032 | ma_ios_session_category_record | AVAudioSessionCategoryRecord |
4033 | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord |
4034 | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute |
4035 | ma_ios_session_category_none | AVAudioSessionCategoryAmbient |
4036 | ma_ios_session_category_default | AVAudioSessionCategoryAmbient |
4037 |-----------------------------------------|-------------------------------------|
4038
4039 coreaudio.sessionCategoryOptions
4040 iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
4041
4042 |---------------------------------------------------------------------------|------------------------------------------------------------------|
4043 | miniaudio Token | Core Audio Token |
4044 |---------------------------------------------------------------------------|------------------------------------------------------------------|
4045 | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers |
4046 | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers |
4047 | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth |
4048 | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker |
4049 | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers |
4050 | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP |
4051 | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay |
4052 |---------------------------------------------------------------------------|------------------------------------------------------------------|
4053
4054 jack.pClientName
4055 The name of the client to pass to `jack_client_open()`.
4056
4057 jack.tryStartServer
4058 Whether or not to try auto-starting the JACK server. Defaults to false.
4059
4060
4061 It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the
4062 relevant backends every time it's initialized.
4063
4064 The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The
4065 reason for this is that a pointer to the context is stored in the `ma_device` structure.
4066
4067
4068 Example 1 - Default Initialization
4069 ----------------------------------
4070 The example below shows how to initialize the context using the default configuration.
4071
4072 ```c
4073 ma_context context;
4074 ma_result result = ma_context_init(NULL, 0, NULL, &context);
4075 if (result != MA_SUCCESS) {
4076 // Error.
4077 }
4078 ```
4079
4080
4081 Example 2 - Custom Configuration
4082 --------------------------------
4083 The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program
4084 wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also
4085 want an error to be returned if no valid backend is available which they achieve by excluding the Null backend.
4086
4087 For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface.
4088
4089 ```c
4090 ma_backend backends[] = {
4091 ma_backend_alsa,
4092 ma_backend_pulseaudio,
4093 ma_backend_wasapi,
4094 ma_backend_dsound
4095 };
4096
4097 ma_context_config config = ma_context_config_init();
4098 config.logCallback = my_log_callback;
4099 config.pUserData = pMyUserData;
4100
4101 ma_context context;
4102 ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context);
4103 if (result != MA_SUCCESS) {
4104 // Error.
4105 if (result == MA_NO_BACKEND) {
4106 // Couldn't find an appropriate backend.
4107 }
4108 }
4109 ```
4110
4111
4112 See Also
4113 --------
4114 ma_context_config_init()
4115 ma_context_uninit()
4116 */
4117 MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
4118
4119 /*
4120 Uninitializes a context.
4121
4122
4123 Return Value
4124 ------------
4125 MA_SUCCESS if successful; any other error code otherwise.
4126
4127
4128 Thread Safety
4129 -------------
4130 Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
4131
4132
4133 Remarks
4134 -------
4135 Results are undefined if you call this while any device created by this context is still active.
4136
4137
4138 See Also
4139 --------
4140 ma_context_init()
4141 */
4142 MA_API ma_result ma_context_uninit(ma_context* pContext);
4143
4144 /*
4145 Retrieves the size of the ma_context object.
4146
4147 This is mainly for the purpose of bindings to know how much memory to allocate.
4148 */
4149 MA_API size_t ma_context_sizeof();
4150
4151 /*
4152 Enumerates over every device (both playback and capture).
4153
4154 This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur
4155 an internal heap allocation, or it simply suits your code better.
4156
4157 Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require
4158 opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this,
4159 but don't call it from within the enumeration callback.
4160
4161 Returning false from the callback will stop enumeration. Returning true will continue enumeration.
4162
4163
4164 Parameters
4165 ----------
4166 pContext (in)
4167 A pointer to the context performing the enumeration.
4168
4169 callback (in)
4170 The callback to fire for each enumerated device.
4171
4172 pUserData (in)
4173 A pointer to application-defined data passed to the callback.
4174
4175
4176 Return Value
4177 ------------
4178 MA_SUCCESS if successful; any other error code otherwise.
4179
4180
4181 Thread Safety
4182 -------------
4183 Safe. This is guarded using a simple mutex lock.
4184
4185
4186 Remarks
4187 -------
4188 Do _not_ assume the first enumerated device of a given type is the default device.
4189
4190 Some backends and platforms may only support default playback and capture devices.
4191
4192 In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also,
4193 do not try to call `ma_context_get_device_info()` from within the callback.
4194
4195 Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.
4196
4197
4198 Example 1 - Simple Enumeration
4199 ------------------------------
4200 ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
4201 {
4202 printf("Device Name: %s\n", pInfo->name);
4203 return MA_TRUE;
4204 }
4205
4206 ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);
4207 if (result != MA_SUCCESS) {
4208 // Error.
4209 }
4210
4211
4212 See Also
4213 --------
4214 ma_context_get_devices()
4215 */
4216 MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
4217
4218 /*
4219 Retrieves basic information about every active playback and/or capture device.
4220
4221 This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
4222 parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.
4223
4224
4225 Parameters
4226 ----------
4227 pContext (in)
4228 A pointer to the context performing the enumeration.
4229
4230 ppPlaybackDeviceInfos (out)
4231 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.
4232
4233 pPlaybackDeviceCount (out)
4234 A pointer to an unsigned integer that will receive the number of playback devices.
4235
4236 ppCaptureDeviceInfos (out)
4237 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.
4238
4239 pCaptureDeviceCount (out)
4240 A pointer to an unsigned integer that will receive the number of capture devices.
4241
4242
4243 Return Value
4244 ------------
4245 MA_SUCCESS if successful; any other error code otherwise.
4246
4247
4248 Thread Safety
4249 -------------
4250 Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple
4251 threads. Instead, you need to make a copy of the returned data with your own higher level synchronization.
4252
4253
4254 Remarks
4255 -------
4256 It is _not_ safe to assume the first device in the list is the default device.
4257
4258 You can pass in NULL for the playback or capture lists in which case they'll be ignored.
4259
4260 The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers.
4261
4262
4263 See Also
4264 --------
4265 ma_context_get_devices()
4266 */
4267 MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
4268
4269 /*
4270 Retrieves information about a device of the given type, with the specified ID and share mode.
4271
4272
4273 Parameters
4274 ----------
4275 pContext (in)
4276 A pointer to the context performing the query.
4277
4278 deviceType (in)
4279 The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.
4280
4281 pDeviceID (in)
4282 The ID of the device being queried.
4283
4284 shareMode (in)
4285 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,
4286 set this to `ma_share_mode_shared`.
4287
4288 pDeviceInfo (out)
4289 A pointer to the `ma_device_info` structure that will receive the device information.
4290
4291
4292 Return Value
4293 ------------
4294 MA_SUCCESS if successful; any other error code otherwise.
4295
4296
4297 Thread Safety
4298 -------------
4299 Safe. This is guarded using a simple mutex lock.
4300
4301
4302 Remarks
4303 -------
4304 Do _not_ call this from within the `ma_context_enumerate_devices()` callback.
4305
4306 It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in
4307 shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify
4308 which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if
4309 the requested share mode is unsupported.
4310
4311 This leaves pDeviceInfo unmodified in the result of an error.
4312 */
4313 MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo);
4314
4315 /*
4316 Determines if the given context supports loopback mode.
4317
4318
4319 Parameters
4320 ----------
4321 pContext (in)
4322 A pointer to the context getting queried.
4323
4324
4325 Return Value
4326 ------------
4327 MA_TRUE if the context supports loopback mode; MA_FALSE otherwise.
4328 */
4329 MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext);
4330
4331
4332
4333 /*
4334 Initializes a device config with default settings.
4335
4336
4337 Parameters
4338 ----------
4339 deviceType (in)
4340 The type of the device this config is being initialized for. This must set to one of the following:
4341
4342 |-------------------------|
4343 | Device Type |
4344 |-------------------------|
4345 | ma_device_type_playback |
4346 | ma_device_type_capture |
4347 | ma_device_type_duplex |
4348 | ma_device_type_loopback |
4349 |-------------------------|
4350
4351
4352 Return Value
4353 ------------
4354 A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks.
4355
4356
4357 Thread Safety
4358 -------------
4359 Safe.
4360
4361
4362 Callback Safety
4363 ---------------
4364 Safe, but don't try initializing a device in a callback.
4365
4366
4367 Remarks
4368 -------
4369 The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a
4370 typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change
4371 before initializing the device.
4372
4373 See `ma_device_init()` for details on specific configuration options.
4374
4375
4376 Example 1 - Simple Configuration
4377 --------------------------------
4378 The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and
4379 then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added
4380 to the `ma_device_config` structure.
4381
4382 ```c
4383 ma_device_config config = ma_device_config_init(ma_device_type_playback);
4384 config.playback.format = ma_format_f32;
4385 config.playback.channels = 2;
4386 config.sampleRate = 48000;
4387 config.dataCallback = ma_data_callback;
4388 config.pUserData = pMyUserData;
4389 ```
4390
4391
4392 See Also
4393 --------
4394 ma_device_init()
4395 ma_device_init_ex()
4396 */
4397 MA_API ma_device_config ma_device_config_init(ma_device_type deviceType);
4398
4399
4400 /*
4401 Initializes a device.
4402
4403 A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it
4404 from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be
4405 playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the
4406 device is done via a callback which is fired by miniaudio at periodic time intervals.
4407
4408 The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames
4409 or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and
4410 increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but
4411 miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple
4412 media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the
4413 backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for.
4414
4415 When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the
4416 format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you
4417 can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline.
4418
4419
4420 Parameters
4421 ----------
4422 pContext (in, optional)
4423 A pointer to the context that owns the device. This can be null, in which case it creates a default context internally.
4424
4425 pConfig (in)
4426 A pointer to the device configuration. Cannot be null. See remarks for details.
4427
4428 pDevice (out)
4429 A pointer to the device object being initialized.
4430
4431
4432 Return Value
4433 ------------
4434 MA_SUCCESS if successful; any other error code otherwise.
4435
4436
4437 Thread Safety
4438 -------------
4439 Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to
4440 calling this at the same time as `ma_device_uninit()`.
4441
4442
4443 Callback Safety
4444 ---------------
4445 Unsafe. It is not safe to call this inside any callback.
4446
4447
4448 Remarks
4449 -------
4450 Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so:
4451
4452 ```c
4453 ma_context_init(NULL, 0, NULL, &context);
4454 ```
4455
4456 Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use
4457 device.pContext for the initialization of other devices.
4458
4459 The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can
4460 then be set directly on the structure. Below are the members of the `ma_device_config` object.
4461
4462 deviceType
4463 Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`.
4464
4465 sampleRate
4466 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.
4467
4468 periodSizeInFrames
4469 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
4470 be used depending on the selected performance profile. This value affects latency. See below for details.
4471
4472 periodSizeInMilliseconds
4473 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
4474 used depending on the selected performance profile. The value affects latency. See below for details.
4475
4476 periods
4477 The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by
4478 this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured.
4479
4480 performanceProfile
4481 A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or
4482 `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value.
4483
4484 noPreZeroedOutputBuffer
4485 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
4486 the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data
4487 callback will write to every sample in the output buffer, or if you are doing your own clearing.
4488
4489 noClip
4490 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
4491 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
4492 applies when the playback sample format is f32.
4493
4494 dataCallback
4495 The callback to fire whenever data is ready to be delivered to or from the device.
4496
4497 stopCallback
4498 The callback to fire whenever the device has stopped, either explicitly via `ma_device_stop()`, or implicitly due to things like the device being
4499 disconnected.
4500
4501 pUserData
4502 The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`.
4503
4504 resampling.algorithm
4505 The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The
4506 default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`.
4507
4508 resampling.linear.lpfOrder
4509 The linear resampler applies a low-pass filter as part of it's procesing for anti-aliasing. This setting controls the order of the filter. The higher
4510 the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
4511 `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.
4512
4513 playback.pDeviceID
4514 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
4515 default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
4516
4517 playback.format
4518 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
4519 initialization from the device object directly with `device.playback.format`.
4520
4521 playback.channels
4522 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
4523 from the device object directly with `device.playback.channels`.
4524
4525 playback.channelMap
4526 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
4527 device object direct with `device.playback.channelMap`.
4528
4529 playback.shareMode
4530 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
4531 exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to
4532 ma_share_mode_shared and reinitializing.
4533
4534 capture.pDeviceID
4535 A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's
4536 default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
4537
4538 capture.format
4539 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
4540 initialization from the device object directly with `device.capture.format`.
4541
4542 capture.channels
4543 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
4544 from the device object directly with `device.capture.channels`.
4545
4546 capture.channelMap
4547 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
4548 device object direct with `device.capture.channelMap`.
4549
4550 capture.shareMode
4551 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
4552 exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to
4553 ma_share_mode_shared and reinitializing.
4554
4555 wasapi.noAutoConvertSRC
4556 WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false.
4557
4558 wasapi.noDefaultQualitySRC
4559 WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`.
4560 You should usually leave this set to false, which is the default.
4561
4562 wasapi.noAutoStreamRouting
4563 WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false.
4564
4565 wasapi.noHardwareOffloading
4566 WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false.
4567
4568 alsa.noMMap
4569 ALSA only. When set to true, disables MMap mode. Defaults to false.
4570
4571 alsa.noAutoFormat
4572 ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false.
4573
4574 alsa.noAutoChannels
4575 ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false.
4576
4577 alsa.noAutoResample
4578 ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false.
4579
4580 pulse.pStreamNamePlayback
4581 PulseAudio only. Sets the stream name for playback.
4582
4583 pulse.pStreamNameCapture
4584 PulseAudio only. Sets the stream name for capture.
4585
4586
4587 Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device.
4588
4589 After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`.
4590
4591 If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or
4592 `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or
4593 `ma_performance_profile_conservative`.
4594
4595 If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device
4596 in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the
4597 config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA,
4598 for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user.
4599 Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary.
4600
4601 When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config
4602 and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run
4603 on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`,
4604 `playback/capture.channels` and `sampleRate` members of the device object.
4605
4606 When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message
4607 asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information.
4608
4609 ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture.
4610 If these fail it will try falling back to the "hw" device.
4611
4612
4613 Example 1 - Simple Initialization
4614 ---------------------------------
4615 This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default
4616 playback device this is usually all you need.
4617
4618 ```c
4619 ma_device_config config = ma_device_config_init(ma_device_type_playback);
4620 config.playback.format = ma_format_f32;
4621 config.playback.channels = 2;
4622 config.sampleRate = 48000;
4623 config.dataCallback = ma_data_callback;
4624 config.pMyUserData = pMyUserData;
4625
4626 ma_device device;
4627 ma_result result = ma_device_init(NULL, &config, &device);
4628 if (result != MA_SUCCESS) {
4629 // Error
4630 }
4631 ```
4632
4633
4634 Example 2 - Advanced Initialization
4635 -----------------------------------
4636 This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size
4637 and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device
4638 enumeration.
4639
4640 ```c
4641 ma_context context;
4642 ma_result result = ma_context_init(NULL, 0, NULL, &context);
4643 if (result != MA_SUCCESS) {
4644 // Error
4645 }
4646
4647 ma_device_info* pPlaybackDeviceInfos;
4648 ma_uint32 playbackDeviceCount;
4649 result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);
4650 if (result != MA_SUCCESS) {
4651 // Error
4652 }
4653
4654 // ... choose a device from pPlaybackDeviceInfos ...
4655
4656 ma_device_config config = ma_device_config_init(ma_device_type_playback);
4657 config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices().
4658 config.playback.format = ma_format_f32;
4659 config.playback.channels = 2;
4660 config.sampleRate = 48000;
4661 config.dataCallback = ma_data_callback;
4662 config.pUserData = pMyUserData;
4663 config.periodSizeInMilliseconds = 10;
4664 config.periods = 3;
4665
4666 ma_device device;
4667 result = ma_device_init(&context, &config, &device);
4668 if (result != MA_SUCCESS) {
4669 // Error
4670 }
4671 ```
4672
4673
4674 See Also
4675 --------
4676 ma_device_config_init()
4677 ma_device_uninit()
4678 ma_device_start()
4679 ma_context_init()
4680 ma_context_get_devices()
4681 ma_context_enumerate_devices()
4682 */
4683 MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
4684
4685 /*
4686 Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context.
4687
4688 This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function
4689 allows you to configure the internally created context.
4690
4691
4692 Parameters
4693 ----------
4694 backends (in, optional)
4695 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
4696
4697 backendCount (in, optional)
4698 The number of items in `backend`. Ignored if `backend` is NULL.
4699
4700 pContextConfig (in, optional)
4701 The context configuration.
4702
4703 pConfig (in)
4704 A pointer to the device configuration. Cannot be null. See remarks for details.
4705
4706 pDevice (out)
4707 A pointer to the device object being initialized.
4708
4709
4710 Return Value
4711 ------------
4712 MA_SUCCESS if successful; any other error code otherwise.
4713
4714
4715 Thread Safety
4716 -------------
4717 Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to
4718 calling this at the same time as `ma_device_uninit()`.
4719
4720
4721 Callback Safety
4722 ---------------
4723 Unsafe. It is not safe to call this inside any callback.
4724
4725
4726 Remarks
4727 -------
4728 You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage
4729 your own context.
4730
4731 See the documentation for `ma_context_init()` for information on the different context configuration options.
4732
4733
4734 See Also
4735 --------
4736 ma_device_init()
4737 ma_device_uninit()
4738 ma_device_config_init()
4739 ma_context_init()
4740 */
4741 MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice);
4742
4743 /*
4744 Uninitializes a device.
4745
4746 This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do.
4747
4748
4749 Parameters
4750 ----------
4751 pDevice (in)
4752 A pointer to the device to stop.
4753
4754
4755 Return Value
4756 ------------
4757 MA_SUCCESS if successful; any other error code otherwise.
4758
4759
4760 Thread Safety
4761 -------------
4762 Unsafe. As soon as this API is called the device should be considered undefined.
4763
4764
4765 Callback Safety
4766 ---------------
4767 Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
4768
4769
4770 See Also
4771 --------
4772 ma_device_init()
4773 ma_device_stop()
4774 */
4775 MA_API void ma_device_uninit(ma_device* pDevice);
4776
4777 /*
4778 Starts the device. For playback devices this begins playback. For capture devices it begins recording.
4779
4780 Use `ma_device_stop()` to stop the device.
4781
4782
4783 Parameters
4784 ----------
4785 pDevice (in)
4786 A pointer to the device to start.
4787
4788
4789 Return Value
4790 ------------
4791 MA_SUCCESS if successful; any other error code otherwise.
4792
4793
4794 Thread Safety
4795 -------------
4796 Safe. It's safe to call this from any thread with the exception of the callback thread.
4797
4798
4799 Callback Safety
4800 ---------------
4801 Unsafe. It is not safe to call this inside any callback.
4802
4803
4804 Remarks
4805 -------
4806 For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid
4807 audio data in the buffer, which needs to be done before the device begins playback.
4808
4809 This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety.
4810
4811 Do not call this in any callback.
4812
4813
4814 See Also
4815 --------
4816 ma_device_stop()
4817 */
4818 MA_API ma_result ma_device_start(ma_device* pDevice);
4819
4820 /*
4821 Stops the device. For playback devices this stops playback. For capture devices it stops recording.
4822
4823 Use `ma_device_start()` to start the device again.
4824
4825
4826 Parameters
4827 ----------
4828 pDevice (in)
4829 A pointer to the device to stop.
4830
4831
4832 Return Value
4833 ------------
4834 MA_SUCCESS if successful; any other error code otherwise.
4835
4836
4837 Thread Safety
4838 -------------
4839 Safe. It's safe to call this from any thread with the exception of the callback thread.
4840
4841
4842 Callback Safety
4843 ---------------
4844 Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
4845
4846
4847 Remarks
4848 -------
4849 This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some
4850 backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size
4851 that was specified at initialization time).
4852
4853 Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and
4854 the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the
4855 speakers or received from the microphone which can in turn result in de-syncs.
4856
4857 Do not call this in any callback.
4858
4859 This will be called implicitly by `ma_device_uninit()`.
4860
4861
4862 See Also
4863 --------
4864 ma_device_start()
4865 */
4866 MA_API ma_result ma_device_stop(ma_device* pDevice);
4867
4868 /*
4869 Determines whether or not the device is started.
4870
4871
4872 Parameters
4873 ----------
4874 pDevice (in)
4875 A pointer to the device whose start state is being retrieved.
4876
4877
4878 Return Value
4879 ------------
4880 True if the device is started, false otherwise.
4881
4882
4883 Thread Safety
4884 -------------
4885 Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return
4886 value will be out of sync.
4887
4888
4889 Callback Safety
4890 ---------------
4891 Safe. This is implemented as a simple accessor.
4892
4893
4894 See Also
4895 --------
4896 ma_device_start()
4897 ma_device_stop()
4898 */
4899 MA_API ma_bool32 ma_device_is_started(ma_device* pDevice);
4900
4901 /*
4902 Sets the master volume factor for the device.
4903
4904 The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_gain_db()` to use decibel notation, where 0 is full volume and
4905 values less than 0 decreases the volume.
4906
4907
4908 Parameters
4909 ----------
4910 pDevice (in)
4911 A pointer to the device whose volume is being set.
4912
4913 volume (in)
4914 The new volume factor. Must be within the range of [0, 1].
4915
4916
4917 Return Value
4918 ------------
4919 MA_SUCCESS if the volume was set successfully.
4920 MA_INVALID_ARGS if pDevice is NULL.
4921 MA_INVALID_ARGS if the volume factor is not within the range of [0, 1].
4922
4923
4924 Thread Safety
4925 -------------
4926 Safe. This just sets a local member of the device object.
4927
4928
4929 Callback Safety
4930 ---------------
4931 Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
4932
4933
4934 Remarks
4935 -------
4936 This applies the volume factor across all channels.
4937
4938 This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
4939
4940
4941 See Also
4942 --------
4943 ma_device_get_master_volume()
4944 ma_device_set_master_volume_gain_db()
4945 ma_device_get_master_volume_gain_db()
4946 */
4947 MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume);
4948
4949 /*
4950 Retrieves the master volume factor for the device.
4951
4952
4953 Parameters
4954 ----------
4955 pDevice (in)
4956 A pointer to the device whose volume factor is being retrieved.
4957
4958 pVolume (in)
4959 A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1].
4960
4961
4962 Return Value
4963 ------------
4964 MA_SUCCESS if successful.
4965 MA_INVALID_ARGS if pDevice is NULL.
4966 MA_INVALID_ARGS if pVolume is NULL.
4967
4968
4969 Thread Safety
4970 -------------
4971 Safe. This just a simple member retrieval.
4972
4973
4974 Callback Safety
4975 ---------------
4976 Safe.
4977
4978
4979 Remarks
4980 -------
4981 If an error occurs, `*pVolume` will be set to 0.
4982
4983
4984 See Also
4985 --------
4986 ma_device_set_master_volume()
4987 ma_device_set_master_volume_gain_db()
4988 ma_device_get_master_volume_gain_db()
4989 */
4990 MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume);
4991
4992 /*
4993 Sets the master volume for the device as gain in decibels.
4994
4995 A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume.
4996
4997
4998 Parameters
4999 ----------
5000 pDevice (in)
5001 A pointer to the device whose gain is being set.
5002
5003 gainDB (in)
5004 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.
5005
5006
5007 Return Value
5008 ------------
5009 MA_SUCCESS if the volume was set successfully.
5010 MA_INVALID_ARGS if pDevice is NULL.
5011 MA_INVALID_ARGS if the gain is > 0.
5012
5013
5014 Thread Safety
5015 -------------
5016 Safe. This just sets a local member of the device object.
5017
5018
5019 Callback Safety
5020 ---------------
5021 Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
5022
5023
5024 Remarks
5025 -------
5026 This applies the gain across all channels.
5027
5028 This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
5029
5030
5031 See Also
5032 --------
5033 ma_device_get_master_volume_gain_db()
5034 ma_device_set_master_volume()
5035 ma_device_get_master_volume()
5036 */
5037 MA_API ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB);
5038
5039 /*
5040 Retrieves the master gain in decibels.
5041
5042
5043 Parameters
5044 ----------
5045 pDevice (in)
5046 A pointer to the device whose gain is being retrieved.
5047
5048 pGainDB (in)
5049 A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0.
5050
5051
5052 Return Value
5053 ------------
5054 MA_SUCCESS if successful.
5055 MA_INVALID_ARGS if pDevice is NULL.
5056 MA_INVALID_ARGS if pGainDB is NULL.
5057
5058
5059 Thread Safety
5060 -------------
5061 Safe. This just a simple member retrieval.
5062
5063
5064 Callback Safety
5065 ---------------
5066 Safe.
5067
5068
5069 Remarks
5070 -------
5071 If an error occurs, `*pGainDB` will be set to 0.
5072
5073
5074 See Also
5075 --------
5076 ma_device_set_master_volume_gain_db()
5077 ma_device_set_master_volume()
5078 ma_device_get_master_volume()
5079 */
5080 MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB);
5081
5082
5083
5084 /************************************************************************************************************************************************************
5085
5086 Utiltities
5087
5088 ************************************************************************************************************************************************************/
5089
5090 /*
5091 Creates a mutex.
5092
5093 A mutex must be created from a valid context. A mutex is initially unlocked.
5094 */
5095 MA_API ma_result ma_mutex_init(ma_context* pContext, ma_mutex* pMutex);
5096
5097 /*
5098 Deletes a mutex.
5099 */
5100 MA_API void ma_mutex_uninit(ma_mutex* pMutex);
5101
5102 /*
5103 Locks a mutex with an infinite timeout.
5104 */
5105 MA_API void ma_mutex_lock(ma_mutex* pMutex);
5106
5107 /*
5108 Unlocks a mutex.
5109 */
5110 MA_API void ma_mutex_unlock(ma_mutex* pMutex);
5111
5112
5113 /*
5114 Retrieves a friendly name for a backend.
5115 */
5116 MA_API const char* ma_get_backend_name(ma_backend backend);
5117
5118 /*
5119 Determines whether or not loopback mode is support by a backend.
5120 */
5121 MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend);
5122
5123
5124 /*
5125 Adjust buffer size based on a scaling factor.
5126
5127 This just multiplies the base size by the scaling factor, making sure it's a size of at least 1.
5128 */
5129 MA_API ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale);
5130
5131 /*
5132 Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
5133 */
5134 MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);
5135
5136 /*
5137 Calculates a buffer size in frames from the specified number of milliseconds and sample rate.
5138 */
5139 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate);
5140
5141 /*
5142 Copies silent frames into the given buffer.
5143 */
5144 MA_API void ma_zero_pcm_frames(void* p, ma_uint32 frameCount, ma_format format, ma_uint32 channels);
5145
5146 /*
5147 Clips f32 samples.
5148 */
5149 MA_API void ma_clip_samples_f32(float* p, ma_uint32 sampleCount);
5150 static MA_INLINE void ma_clip_pcm_frames_f32(float* p, ma_uint32 frameCount, ma_uint32 channels) { ma_clip_samples_f32(p, frameCount*channels); }
5151
5152 /*
5153 Helper for applying a volume factor to samples.
5154
5155 Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place.
5156 */
5157 MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint32 sampleCount, float factor);
5158 MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint32 sampleCount, float factor);
5159 MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint32 sampleCount, float factor);
5160 MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint32 sampleCount, float factor);
5161 MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint32 sampleCount, float factor);
5162
5163 MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint32 sampleCount, float factor);
5164 MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint32 sampleCount, float factor);
5165 MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint32 sampleCount, float factor);
5166 MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint32 sampleCount, float factor);
5167 MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint32 sampleCount, float factor);
5168
5169 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
5170 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
5171 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
5172 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
5173 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
5174 MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor);
5175
5176 MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
5177 MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
5178 MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
5179 MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
5180 MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
5181 MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor);
5182
5183
5184 /*
5185 Helper for converting a linear factor to gain in decibels.
5186 */
5187 MA_API float ma_factor_to_gain_db(float factor);
5188
5189 /*
5190 Helper for converting gain in decibels to a linear factor.
5191 */
5192 MA_API float ma_gain_db_to_factor(float gain);
5193
5194 #endif /* MA_NO_DEVICE_IO */
5195
5196
5197 #if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
5198 typedef enum
5199 {
5200 ma_seek_origin_start,
5201 ma_seek_origin_current
5202 } ma_seek_origin;
5203
5204 typedef enum
5205 {
5206 ma_resource_format_wav
5207 } ma_resource_format;
5208 #endif
5209
5210 /************************************************************************************************************************************************************
5211
5212 Decoding
5213 ========
5214
5215 Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless
5216 you do your own synchronization.
5217
5218 ************************************************************************************************************************************************************/
5219 #ifndef MA_NO_DECODING
5220 typedef struct ma_decoder ma_decoder;
5221
5222 typedef size_t (* ma_decoder_read_proc) (ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */
5223 typedef ma_bool32 (* ma_decoder_seek_proc) (ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin);
5224 typedef ma_uint64 (* ma_decoder_read_pcm_frames_proc) (ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount); /* Returns the number of frames read. Output data is in internal format. */
5225 typedef ma_result (* ma_decoder_seek_to_pcm_frame_proc) (ma_decoder* pDecoder, ma_uint64 frameIndex);
5226 typedef ma_result (* ma_decoder_uninit_proc) (ma_decoder* pDecoder);
5227 typedef ma_uint64 (* ma_decoder_get_length_in_pcm_frames_proc)(ma_decoder* pDecoder);
5228
5229 typedef struct
5230 {
5231 ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */
5232 ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */
5233 ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */
5234 ma_channel channelMap[MA_MAX_CHANNELS];
5235 ma_channel_mix_mode channelMixMode;
5236 ma_dither_mode ditherMode;
5237 struct
5238 {
5239 ma_resample_algorithm algorithm;
5240 struct
5241 {
5242 ma_uint32 lpfOrder;
5243 } linear;
5244 struct
5245 {
5246 int quality;
5247 } speex;
5248 } resampling;
5249 ma_allocation_callbacks allocationCallbacks;
5250 } ma_decoder_config;
5251
5252 struct ma_decoder
5253 {
5254 ma_decoder_read_proc onRead;
5255 ma_decoder_seek_proc onSeek;
5256 void* pUserData;
5257 ma_uint64 readPointer; /* Used for returning back to a previous position after analysing the stream or whatnot. */
5258 ma_format internalFormat;
5259 ma_uint32 internalChannels;
5260 ma_uint32 internalSampleRate;
5261 ma_channel internalChannelMap[MA_MAX_CHANNELS];
5262 ma_format outputFormat;
5263 ma_uint32 outputChannels;
5264 ma_uint32 outputSampleRate;
5265 ma_channel outputChannelMap[MA_MAX_CHANNELS];
5266 ma_data_converter converter; /* <-- Data conversion is achieved by running frames through this. */
5267 ma_allocation_callbacks allocationCallbacks;
5268 ma_decoder_read_pcm_frames_proc onReadPCMFrames;
5269 ma_decoder_seek_to_pcm_frame_proc onSeekToPCMFrame;
5270 ma_decoder_uninit_proc onUninit;
5271 ma_decoder_get_length_in_pcm_frames_proc onGetLengthInPCMFrames;
5272 void* pInternalDecoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
5273 struct
5274 {
5275 const ma_uint8* pData;
5276 size_t dataSize;
5277 size_t currentReadPos;
5278 } memory; /* Only used for decoders that were opened against a block of memory. */
5279 };
5280
5281 MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
5282
5283 MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5284 MA_API ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5285 MA_API ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5286 MA_API ma_result ma_decoder_init_vorbis(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5287 MA_API ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5288 MA_API ma_result ma_decoder_init_raw(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder);
5289
5290 MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5291 MA_API ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5292 MA_API ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5293 MA_API ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5294 MA_API ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5295 MA_API ma_result ma_decoder_init_memory_raw(const void* pData, size_t dataSize, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder);
5296
5297 MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5298 MA_API ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5299 MA_API ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5300 MA_API ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5301 MA_API ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5302
5303 MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5304 MA_API ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5305 MA_API ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5306 MA_API ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5307 MA_API ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
5308
5309 MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder);
5310
5311 /*
5312 Retrieves the length of the decoder in PCM frames.
5313
5314 Do not call this on streams of an undefined length, such as internet radio.
5315
5316 If the length is unknown or an error occurs, 0 will be returned.
5317
5318 This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio
5319 uses internally.
5320
5321 For MP3's, this will decode the entire file. Do not call this in time critical scenarios.
5322
5323 This function is not thread safe without your own synchronization.
5324 */
5325 MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder);
5326
5327 /*
5328 Reads PCM frames from the given decoder.
5329
5330 This is not thread safe without your own synchronization.
5331 */
5332 MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount);
5333
5334 /*
5335 Seeks to a PCM frame based on it's absolute index.
5336
5337 This is not thread safe without your own synchronization.
5338 */
5339 MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);
5340
5341 /*
5342 Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
5343 pConfig should be set to what you want. On output it will be set to what you got.
5344 */
5345 MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppDataOut);
5346 MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppDataOut);
5347
5348 #endif /* MA_NO_DECODING */
5349
5350
5351 /************************************************************************************************************************************************************
5352
5353 Encoding
5354 ========
5355
5356 Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned.
5357
5358 ************************************************************************************************************************************************************/
5359 #ifndef MA_NO_ENCODING
5360 typedef struct ma_encoder ma_encoder;
5361
5362 typedef size_t (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite); /* Returns the number of bytes written. */
5363 typedef ma_bool32 (* ma_encoder_seek_proc) (ma_encoder* pEncoder, int byteOffset, ma_seek_origin origin);
5364 typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder);
5365 typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder);
5366 typedef ma_uint64 (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount);
5367
5368 typedef struct
5369 {
5370 ma_resource_format resourceFormat;
5371 ma_format format;
5372 ma_uint32 channels;
5373 ma_uint32 sampleRate;
5374 ma_allocation_callbacks allocationCallbacks;
5375 } ma_encoder_config;
5376
5377 MA_API ma_encoder_config ma_encoder_config_init(ma_resource_format resourceFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
5378
5379 struct ma_encoder
5380 {
5381 ma_encoder_config config;
5382 ma_encoder_write_proc onWrite;
5383 ma_encoder_seek_proc onSeek;
5384 ma_encoder_init_proc onInit;
5385 ma_encoder_uninit_proc onUninit;
5386 ma_encoder_write_pcm_frames_proc onWritePCMFrames;
5387 void* pUserData;
5388 void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
5389 void* pFile; /* FILE*. Only used when initialized with ma_encoder_init_file(). */
5390 };
5391
5392 MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
5393 MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
5394 MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
5395 MA_API void ma_encoder_uninit(ma_encoder* pEncoder);
5396 MA_API ma_uint64 ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount);
5397
5398 #endif /* MA_NO_ENCODING */
5399
5400
5401 /************************************************************************************************************************************************************
5402
5403 Generation
5404
5405 ************************************************************************************************************************************************************/
5406 typedef enum
5407 {
5408 ma_waveform_type_sine,
5409 ma_waveform_type_square,
5410 ma_waveform_type_triangle,
5411 ma_waveform_type_sawtooth
5412 } ma_waveform_type;
5413
5414 typedef struct
5415 {
5416 ma_format format;
5417 ma_uint32 channels;
5418 ma_uint32 sampleRate;
5419 ma_waveform_type type;
5420 double amplitude;
5421 double frequency;
5422 } ma_waveform_config;
5423
5424 MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency);
5425
5426 typedef struct
5427 {
5428 ma_waveform_config config;
5429 double advance;
5430 double time;
5431 } ma_waveform;
5432
5433 MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform);
5434 MA_API ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount);
5435 MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude);
5436 MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency);
5437 MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate);
5438
5439
5440
5441 typedef struct
5442 {
5443 ma_int32 state;
5444 } ma_lcg;
5445
5446 typedef enum
5447 {
5448 ma_noise_type_white,
5449 ma_noise_type_pink,
5450 ma_noise_type_brownian
5451 } ma_noise_type;
5452
5453 typedef struct
5454 {
5455 ma_format format;
5456 ma_uint32 channels;
5457 ma_noise_type type;
5458 ma_int32 seed;
5459 double amplitude;
5460 ma_bool32 duplicateChannels;
5461 } ma_noise_config;
5462
5463 MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude);
5464
5465 typedef struct
5466 {
5467 ma_noise_config config;
5468 ma_lcg lcg;
5469 union
5470 {
5471 struct
5472 {
5473 double bin[MA_MAX_CHANNELS][16];
5474 double accumulation[MA_MAX_CHANNELS];
5475 ma_uint32 counter[MA_MAX_CHANNELS];
5476 } pink;
5477 struct
5478 {
5479 double accumulation[MA_MAX_CHANNELS];
5480 } brownian;
5481 } state;
5482 } ma_noise;
5483
5484 MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise);
5485 MA_API ma_uint64 ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount);
5486
5487
5488 #ifdef __cplusplus
5489 }
5490 #endif
5491 #endif /* miniaudio_h */
5492
5493
5494
5495 /************************************************************************************************************************************************************
5496 *************************************************************************************************************************************************************
5497
5498 IMPLEMENTATION
5499
5500 *************************************************************************************************************************************************************
5501 ************************************************************************************************************************************************************/
5502 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
5503 #include <assert.h>
5504 #include <limits.h> /* For INT_MAX */
5505 #include <math.h> /* sin(), etc. */
5506
5507 #include <stdarg.h>
5508 #include <stdio.h>
5509 #if !defined(_MSC_VER) && !defined(__DMC__)
5510 #include <strings.h> /* For strcasecmp(). */
5511 #include <wchar.h> /* For wcslen(), wcsrtombs() */
5512 #endif
5513
5514 #ifdef MA_WIN32
5515 #include <windows.h>
5516 #else
5517 #include <stdlib.h> /* For malloc(), free(), wcstombs(). */
5518 #include <string.h> /* For memset() */
5519 #endif
5520
5521 #ifdef MA_EMSCRIPTEN
5522 #include <emscripten/emscripten.h>
5523 #endif
5524
5525 #if !defined(MA_64BIT) && !defined(MA_32BIT)
5526 #ifdef _WIN32
5527 #ifdef _WIN64
5528 #define MA_64BIT
5529 #else
5530 #define MA_32BIT
5531 #endif
5532 #endif
5533 #endif
5534
5535 #if !defined(MA_64BIT) && !defined(MA_32BIT)
5536 #ifdef __GNUC__
5537 #ifdef __LP64__
5538 #define MA_64BIT
5539 #else
5540 #define MA_32BIT
5541 #endif
5542 #endif
5543 #endif
5544
5545 #if !defined(MA_64BIT) && !defined(MA_32BIT)
5546 #include <stdint.h>
5547 #if INTPTR_MAX == INT64_MAX
5548 #define MA_64BIT
5549 #else
5550 #define MA_32BIT
5551 #endif
5552 #endif
5553
5554 /* Architecture Detection */
5555 #if defined(__x86_64__) || defined(_M_X64)
5556 #define MA_X64
5557 #elif defined(__i386) || defined(_M_IX86)
5558 #define MA_X86
5559 #elif defined(__arm__) || defined(_M_ARM)
5560 #define MA_ARM
5561 #endif
5562
5563 /* Cannot currently support AVX-512 if AVX is disabled. */
5564 #if !defined(MA_NO_AVX512) && defined(MA_NO_AVX2)
5565 #define MA_NO_AVX512
5566 #endif
5567
5568 /* Intrinsics Support */
5569 #if defined(MA_X64) || defined(MA_X86)
5570 #if defined(_MSC_VER) && !defined(__clang__)
5571 /* MSVC. */
5572 #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */
5573 #define MA_SUPPORT_SSE2
5574 #endif
5575 /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */
5576 /* #define MA_SUPPORT_AVX*/
5577 /*#endif*/
5578 #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */
5579 #define MA_SUPPORT_AVX2
5580 #endif
5581 #if _MSC_VER >= 1910 && !defined(MA_NO_AVX512) /* 2017 */
5582 #define MA_SUPPORT_AVX512
5583 #endif
5584 #else
5585 /* Assume GNUC-style. */
5586 #if defined(__SSE2__) && !defined(MA_NO_SSE2)
5587 #define MA_SUPPORT_SSE2
5588 #endif
5589 /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/
5590 /* #define MA_SUPPORT_AVX*/
5591 /*#endif*/
5592 #if defined(__AVX2__) && !defined(MA_NO_AVX2)
5593 #define MA_SUPPORT_AVX2
5594 #endif
5595 #if defined(__AVX512F__) && !defined(MA_NO_AVX512)
5596 #define MA_SUPPORT_AVX512
5597 #endif
5598 #endif
5599
5600 /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
5601 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
5602 #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>)
5603 #define MA_SUPPORT_SSE2
5604 #endif
5605 /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include(<immintrin.h>)*/
5606 /* #define MA_SUPPORT_AVX*/
5607 /*#endif*/
5608 #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>)
5609 #define MA_SUPPORT_AVX2
5610 #endif
5611 #if !defined(MA_SUPPORT_AVX512) && !defined(MA_NO_AVX512) && __has_include(<zmmintrin.h>)
5612 #define MA_SUPPORT_AVX512
5613 #endif
5614 #endif
5615
5616 #if defined(MA_SUPPORT_AVX512)
5617 #include <immintrin.h> /* Not a mistake. Intentionally including <immintrin.h> instead of <zmmintrin.h> because otherwise the compiler will complain. */
5618 #elif defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
5619 #include <immintrin.h>
5620 #elif defined(MA_SUPPORT_SSE2)
5621 #include <emmintrin.h>
5622 #endif
5623 #endif
5624
5625 #if defined(MA_ARM)
5626 #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
5627 #define MA_SUPPORT_NEON
5628 #endif
5629
5630 /* Fall back to looking for the #include file. */
5631 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
5632 #if !defined(MA_SUPPORT_NEON) && !defined(MA_NO_NEON) && __has_include(<arm_neon.h>)
5633 #define MA_SUPPORT_NEON
5634 #endif
5635 #endif
5636
5637 #if defined(MA_SUPPORT_NEON)
5638 #include <arm_neon.h>
5639 #endif
5640 #endif
5641
5642 /* Begin globally disabled warnings. */
5643 #if defined(_MSC_VER)
5644 #pragma warning(push)
5645 #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */
5646 #endif
5647
5648 #if defined(MA_X64) || defined(MA_X86)
5649 #if defined(_MSC_VER) && !defined(__clang__)
5650 #if _MSC_VER >= 1400
5651 #include <intrin.h>
5652 static MA_INLINE void ma_cpuid(int info[4], int fid)
5653 {
5654 __cpuid(info, fid);
5655 }
5656 #else
5657 #define MA_NO_CPUID
5658 #endif
5659
5660 #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)
5661 static MA_INLINE unsigned __int64 ma_xgetbv(int reg)
5662 {
5663 return _xgetbv(reg);
5664 }
5665 #else
5666 #define MA_NO_XGETBV
5667 #endif
5668 #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
5669 static MA_INLINE void ma_cpuid(int info[4], int fid)
5670 {
5671 /*
5672 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
5673 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
5674 supporting different assembly dialects.
5675
5676 What's basically happening is that we're saving and restoring the ebx register manually.
5677 */
5678 #if defined(DRFLAC_X86) && defined(__PIC__)
5679 __asm__ __volatile__ (
5680 "xchg{l} {%%}ebx, %k1;"
5681 "cpuid;"
5682 "xchg{l} {%%}ebx, %k1;"
5683 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
5684 );
5685 #else
5686 __asm__ __volatile__ (
5687 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
5688 );
5689 #endif
5690 }
5691
5692 static MA_INLINE ma_uint64 ma_xgetbv(int reg)
5693 {
5694 unsigned int hi;
5695 unsigned int lo;
5696
5697 __asm__ __volatile__ (
5698 "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
5699 );
5700
5701 return ((ma_uint64)hi << 32) | (ma_uint64)lo;
5702 }
5703 #else
5704 #define MA_NO_CPUID
5705 #define MA_NO_XGETBV
5706 #endif
5707 #else
5708 #define MA_NO_CPUID
5709 #define MA_NO_XGETBV
5710 #endif
5711
5712 static MA_INLINE ma_bool32 ma_has_sse2()
5713 {
5714 #if defined(MA_SUPPORT_SSE2)
5715 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
5716 #if defined(MA_X64)
5717 return MA_TRUE; /* 64-bit targets always support SSE2. */
5718 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
5719 return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
5720 #else
5721 #if defined(MA_NO_CPUID)
5722 return MA_FALSE;
5723 #else
5724 int info[4];
5725 ma_cpuid(info, 1);
5726 return (info[3] & (1 << 26)) != 0;
5727 #endif
5728 #endif
5729 #else
5730 return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
5731 #endif
5732 #else
5733 return MA_FALSE; /* No compiler support. */
5734 #endif
5735 }
5736
5737 #if 0
5738 static MA_INLINE ma_bool32 ma_has_avx()
5739 {
5740 #if defined(MA_SUPPORT_AVX)
5741 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)
5742 #if defined(_AVX_) || defined(__AVX__)
5743 return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */
5744 #else
5745 /* AVX requires both CPU and OS support. */
5746 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
5747 return MA_FALSE;
5748 #else
5749 int info[4];
5750 ma_cpuid(info, 1);
5751 if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
5752 ma_uint64 xrc = ma_xgetbv(0);
5753 if ((xrc & 0x06) == 0x06) {
5754 return MA_TRUE;
5755 } else {
5756 return MA_FALSE;
5757 }
5758 } else {
5759 return MA_FALSE;
5760 }
5761 #endif
5762 #endif
5763 #else
5764 return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */
5765 #endif
5766 #else
5767 return MA_FALSE; /* No compiler support. */
5768 #endif
5769 }
5770 #endif
5771
5772 static MA_INLINE ma_bool32 ma_has_avx2()
5773 {
5774 #if defined(MA_SUPPORT_AVX2)
5775 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)
5776 #if defined(_AVX2_) || defined(__AVX2__)
5777 return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */
5778 #else
5779 /* AVX2 requires both CPU and OS support. */
5780 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
5781 return MA_FALSE;
5782 #else
5783 int info1[4];
5784 int info7[4];
5785 ma_cpuid(info1, 1);
5786 ma_cpuid(info7, 7);
5787 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {
5788 ma_uint64 xrc = ma_xgetbv(0);
5789 if ((xrc & 0x06) == 0x06) {
5790 return MA_TRUE;
5791 } else {
5792 return MA_FALSE;
5793 }
5794 } else {
5795 return MA_FALSE;
5796 }
5797 #endif
5798 #endif
5799 #else
5800 return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */
5801 #endif
5802 #else
5803 return MA_FALSE; /* No compiler support. */
5804 #endif
5805 }
5806
5807 static MA_INLINE ma_bool32 ma_has_avx512f()
5808 {
5809 #if defined(MA_SUPPORT_AVX512)
5810 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX512)
5811 #if defined(__AVX512F__)
5812 return MA_TRUE; /* If the compiler is allowed to freely generate AVX-512F code we can assume support. */
5813 #else
5814 /* AVX-512 requires both CPU and OS support. */
5815 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
5816 return MA_FALSE;
5817 #else
5818 int info1[4];
5819 int info7[4];
5820 ma_cpuid(info1, 1);
5821 ma_cpuid(info7, 7);
5822 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 16)) != 0)) {
5823 ma_uint64 xrc = ma_xgetbv(0);
5824 if ((xrc & 0xE6) == 0xE6) {
5825 return MA_TRUE;
5826 } else {
5827 return MA_FALSE;
5828 }
5829 } else {
5830 return MA_FALSE;
5831 }
5832 #endif
5833 #endif
5834 #else
5835 return MA_FALSE; /* AVX-512F is only supported on x86 and x64 architectures. */
5836 #endif
5837 #else
5838 return MA_FALSE; /* No compiler support. */
5839 #endif
5840 }
5841
5842 static MA_INLINE ma_bool32 ma_has_neon()
5843 {
5844 #if defined(MA_SUPPORT_NEON)
5845 #if defined(MA_ARM) && !defined(MA_NO_NEON)
5846 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
5847 return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */
5848 #else
5849 /* TODO: Runtime check. */
5850 return MA_FALSE;
5851 #endif
5852 #else
5853 return MA_FALSE; /* NEON is only supported on ARM architectures. */
5854 #endif
5855 #else
5856 return MA_FALSE; /* No compiler support. */
5857 #endif
5858 }
5859
5860 #define MA_SIMD_NONE 0
5861 #define MA_SIMD_SSE2 1
5862 #define MA_SIMD_AVX2 2
5863 #define MA_SIMD_NEON 3
5864
5865 #ifndef MA_PREFERRED_SIMD
5866 # if defined(MA_SUPPORT_SSE2) && defined(MA_PREFER_SSE2)
5867 #define MA_PREFERRED_SIMD MA_SIMD_SSE2
5868 #elif defined(MA_SUPPORT_AVX2) && defined(MA_PREFER_AVX2)
5869 #define MA_PREFERRED_SIMD MA_SIMD_AVX2
5870 #elif defined(MA_SUPPORT_NEON) && defined(MA_PREFER_NEON)
5871 #define MA_PREFERRED_SIMD MA_SIMD_NEON
5872 #else
5873 #define MA_PREFERRED_SIMD MA_SIMD_NONE
5874 #endif
5875 #endif
5876
5877
5878 static MA_INLINE ma_bool32 ma_is_little_endian()
5879 {
5880 #if defined(MA_X86) || defined(MA_X64)
5881 return MA_TRUE;
5882 #else
5883 int n = 1;
5884 return (*(char*)&n) == 1;
5885 #endif
5886 }
5887
5888 static MA_INLINE ma_bool32 ma_is_big_endian()
5889 {
5890 return !ma_is_little_endian();
5891 }
5892
5893
5894 #ifndef MA_COINIT_VALUE
5895 #define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */
5896 #endif
5897
5898
5899
5900 #ifndef MA_PI
5901 #define MA_PI 3.14159265358979323846264f
5902 #endif
5903 #ifndef MA_PI_D
5904 #define MA_PI_D 3.14159265358979323846264
5905 #endif
5906 #ifndef MA_TAU
5907 #define MA_TAU 6.28318530717958647693f
5908 #endif
5909 #ifndef MA_TAU_D
5910 #define MA_TAU_D 6.28318530717958647693
5911 #endif
5912
5913
5914 /* The default format when ma_format_unknown (0) is requested when initializing a device. */
5915 #ifndef MA_DEFAULT_FORMAT
5916 #define MA_DEFAULT_FORMAT ma_format_f32
5917 #endif
5918
5919 /* The default channel count to use when 0 is used when initializing a device. */
5920 #ifndef MA_DEFAULT_CHANNELS
5921 #define MA_DEFAULT_CHANNELS 2
5922 #endif
5923
5924 /* The default sample rate to use when 0 is used when initializing a device. */
5925 #ifndef MA_DEFAULT_SAMPLE_RATE
5926 #define MA_DEFAULT_SAMPLE_RATE 48000
5927 #endif
5928
5929 /* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */
5930 #ifndef MA_DEFAULT_PERIODS
5931 #define MA_DEFAULT_PERIODS 3
5932 #endif
5933
5934 /* The default period size in milliseconds for low latency mode. */
5935 #ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY
5936 #define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10
5937 #endif
5938
5939 /* The default buffer size in milliseconds for conservative mode. */
5940 #ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE
5941 #define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100
5942 #endif
5943
5944 /* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */
5945 #ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER
5946 #if MA_MAX_FILTER_ORDER >= 4
5947 #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4
5948 #else
5949 #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER
5950 #endif
5951 #endif
5952
5953
5954 #if defined(__GNUC__)
5955 #pragma GCC diagnostic push
5956 #pragma GCC diagnostic ignored "-Wunused-variable"
5957 #endif
5958
5959 #ifndef MA_LINUX
5960 /* Standard sample rates, in order of priority. */
5961 static ma_uint32 g_maStandardSampleRatePriorities[] = {
5962 MA_SAMPLE_RATE_48000, /* Most common */
5963 MA_SAMPLE_RATE_44100,
5964
5965 MA_SAMPLE_RATE_32000, /* Lows */
5966 MA_SAMPLE_RATE_24000,
5967 MA_SAMPLE_RATE_22050,
5968
5969 MA_SAMPLE_RATE_88200, /* Highs */
5970 MA_SAMPLE_RATE_96000,
5971 MA_SAMPLE_RATE_176400,
5972 MA_SAMPLE_RATE_192000,
5973
5974 MA_SAMPLE_RATE_16000, /* Extreme lows */
5975 MA_SAMPLE_RATE_11025,
5976 MA_SAMPLE_RATE_8000,
5977
5978 MA_SAMPLE_RATE_352800, /* Extreme highs */
5979 MA_SAMPLE_RATE_384000
5980 };
5981 #endif
5982
5983 static ma_format g_maFormatPriorities[] = {
5984 ma_format_s16, /* Most common */
5985 ma_format_f32,
5986
5987 /*ma_format_s24_32,*/ /* Clean alignment */
5988 ma_format_s32,
5989
5990 ma_format_s24, /* Unclean alignment */
5991
5992 ma_format_u8 /* Low quality */
5993 };
5994 #if defined(__GNUC__)
5995 #pragma GCC diagnostic pop
5996 #endif
5997
5998
5999 /******************************************************************************
6000
6001 Standard Library Stuff
6002
6003 ******************************************************************************/
6004 #ifndef MA_MALLOC
6005 #ifdef MA_WIN32
6006 #define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz))
6007 #else
6008 #define MA_MALLOC(sz) malloc((sz))
6009 #endif
6010 #endif
6011
6012 #ifndef MA_REALLOC
6013 #ifdef MA_WIN32
6014 #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)))
6015 #else
6016 #define MA_REALLOC(p, sz) realloc((p), (sz))
6017 #endif
6018 #endif
6019
6020 #ifndef MA_FREE
6021 #ifdef MA_WIN32
6022 #define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p))
6023 #else
6024 #define MA_FREE(p) free((p))
6025 #endif
6026 #endif
6027
6028 #ifndef MA_ZERO_MEMORY
6029 #ifdef MA_WIN32
6030 #define MA_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz))
6031 #else
6032 #define MA_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
6033 #endif
6034 #endif
6035
6036 #ifndef MA_COPY_MEMORY
6037 #ifdef MA_WIN32
6038 #define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz))
6039 #else
6040 #define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
6041 #endif
6042 #endif
6043
6044 #ifndef MA_ASSERT
6045 #ifdef MA_WIN32
6046 #define MA_ASSERT(condition) assert(condition)
6047 #else
6048 #define MA_ASSERT(condition) assert(condition)
6049 #endif
6050 #endif
6051
6052 #define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p)))
6053
6054 #define ma_countof(x) (sizeof(x) / sizeof(x[0]))
6055 #define ma_max(x, y) (((x) > (y)) ? (x) : (y))
6056 #define ma_min(x, y) (((x) < (y)) ? (x) : (y))
6057 #define ma_abs(x) (((x) > 0) ? (x) : -(x))
6058 #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi)))
6059 #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
6060
6061 #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))
6062
6063 static MA_INLINE double ma_sin(double x)
6064 {
6065 /* TODO: Implement custom sin(x). */
6066 return sin(x);
6067 }
6068
6069 static MA_INLINE double ma_exp(double x)
6070 {
6071 /* TODO: Implement custom exp(x). */
6072 return exp(x);
6073 }
6074
6075 static MA_INLINE double ma_log(double x)
6076 {
6077 /* TODO: Implement custom log(x). */
6078 return log(x);
6079 }
6080
6081 static MA_INLINE double ma_pow(double x, double y)
6082 {
6083 /* TODO: Implement custom pow(x, y). */
6084 return pow(x, y);
6085 }
6086
6087 static MA_INLINE double ma_sqrt(double x)
6088 {
6089 /* TODO: Implement custom sqrt(x). */
6090 return sqrt(x);
6091 }
6092
6093
6094 static MA_INLINE double ma_cos(double x)
6095 {
6096 return ma_sin((MA_PI_D*0.5) - x);
6097 }
6098
6099 static MA_INLINE double ma_log10(double x)
6100 {
6101 return ma_log(x) * 0.43429448190325182765;
6102 }
6103
6104 static MA_INLINE float ma_powf(float x, float y)
6105 {
6106 return (float)ma_pow((double)x, (double)y);
6107 }
6108
6109 static MA_INLINE float ma_log10f(float x)
6110 {
6111 return (float)ma_log10((double)x);
6112 }
6113
6114
6115 /*
6116 Return Values:
6117 0: Success
6118 22: EINVAL
6119 34: ERANGE
6120
6121 Not using symbolic constants for errors because I want to avoid #including errno.h
6122 */
6123 MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
6124 {
6125 size_t i;
6126
6127 if (dst == 0) {
6128 return 22;
6129 }
6130 if (dstSizeInBytes == 0) {
6131 return 34;
6132 }
6133 if (src == 0) {
6134 dst[0] = '\0';
6135 return 22;
6136 }
6137
6138 for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
6139 dst[i] = src[i];
6140 }
6141
6142 if (i < dstSizeInBytes) {
6143 dst[i] = '\0';
6144 return 0;
6145 }
6146
6147 dst[0] = '\0';
6148 return 34;
6149 }
6150
6151 MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
6152 {
6153 size_t maxcount;
6154 size_t i;
6155
6156 if (dst == 0) {
6157 return 22;
6158 }
6159 if (dstSizeInBytes == 0) {
6160 return 34;
6161 }
6162 if (src == 0) {
6163 dst[0] = '\0';
6164 return 22;
6165 }
6166
6167 maxcount = count;
6168 if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
6169 maxcount = dstSizeInBytes - 1;
6170 }
6171
6172 for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
6173 dst[i] = src[i];
6174 }
6175
6176 if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
6177 dst[i] = '\0';
6178 return 0;
6179 }
6180
6181 dst[0] = '\0';
6182 return 34;
6183 }
6184
6185 MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
6186 {
6187 char* dstorig;
6188
6189 if (dst == 0) {
6190 return 22;
6191 }
6192 if (dstSizeInBytes == 0) {
6193 return 34;
6194 }
6195 if (src == 0) {
6196 dst[0] = '\0';
6197 return 22;
6198 }
6199
6200 dstorig = dst;
6201
6202 while (dstSizeInBytes > 0 && dst[0] != '\0') {
6203 dst += 1;
6204 dstSizeInBytes -= 1;
6205 }
6206
6207 if (dstSizeInBytes == 0) {
6208 return 22; /* Unterminated. */
6209 }
6210
6211
6212 while (dstSizeInBytes > 0 && src[0] != '\0') {
6213 *dst++ = *src++;
6214 dstSizeInBytes -= 1;
6215 }
6216
6217 if (dstSizeInBytes > 0) {
6218 dst[0] = '\0';
6219 } else {
6220 dstorig[0] = '\0';
6221 return 34;
6222 }
6223
6224 return 0;
6225 }
6226
6227 MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
6228 {
6229 char* dstorig;
6230
6231 if (dst == 0) {
6232 return 22;
6233 }
6234 if (dstSizeInBytes == 0) {
6235 return 34;
6236 }
6237 if (src == 0) {
6238 return 22;
6239 }
6240
6241 dstorig = dst;
6242
6243 while (dstSizeInBytes > 0 && dst[0] != '\0') {
6244 dst += 1;
6245 dstSizeInBytes -= 1;
6246 }
6247
6248 if (dstSizeInBytes == 0) {
6249 return 22; /* Unterminated. */
6250 }
6251
6252
6253 if (count == ((size_t)-1)) { /* _TRUNCATE */
6254 count = dstSizeInBytes - 1;
6255 }
6256
6257 while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
6258 *dst++ = *src++;
6259 dstSizeInBytes -= 1;
6260 count -= 1;
6261 }
6262
6263 if (dstSizeInBytes > 0) {
6264 dst[0] = '\0';
6265 } else {
6266 dstorig[0] = '\0';
6267 return 34;
6268 }
6269
6270 return 0;
6271 }
6272
6273 MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
6274 {
6275 int sign;
6276 unsigned int valueU;
6277 char* dstEnd;
6278
6279 if (dst == NULL || dstSizeInBytes == 0) {
6280 return 22;
6281 }
6282 if (radix < 2 || radix > 36) {
6283 dst[0] = '\0';
6284 return 22;
6285 }
6286
6287 sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */
6288
6289 if (value < 0) {
6290 valueU = -value;
6291 } else {
6292 valueU = value;
6293 }
6294
6295 dstEnd = dst;
6296 do
6297 {
6298 int remainder = valueU % radix;
6299 if (remainder > 9) {
6300 *dstEnd = (char)((remainder - 10) + 'a');
6301 } else {
6302 *dstEnd = (char)(remainder + '0');
6303 }
6304
6305 dstEnd += 1;
6306 dstSizeInBytes -= 1;
6307 valueU /= radix;
6308 } while (dstSizeInBytes > 0 && valueU > 0);
6309
6310 if (dstSizeInBytes == 0) {
6311 dst[0] = '\0';
6312 return 22; /* Ran out of room in the output buffer. */
6313 }
6314
6315 if (sign < 0) {
6316 *dstEnd++ = '-';
6317 dstSizeInBytes -= 1;
6318 }
6319
6320 if (dstSizeInBytes == 0) {
6321 dst[0] = '\0';
6322 return 22; /* Ran out of room in the output buffer. */
6323 }
6324
6325 *dstEnd = '\0';
6326
6327
6328 /* At this point the string will be reversed. */
6329 dstEnd -= 1;
6330 while (dst < dstEnd) {
6331 char temp = *dst;
6332 *dst = *dstEnd;
6333 *dstEnd = temp;
6334
6335 dst += 1;
6336 dstEnd -= 1;
6337 }
6338
6339 return 0;
6340 }
6341
6342 MA_API int ma_strcmp(const char* str1, const char* str2)
6343 {
6344 if (str1 == str2) return 0;
6345
6346 /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */
6347 if (str1 == NULL) return -1;
6348 if (str2 == NULL) return 1;
6349
6350 for (;;) {
6351 if (str1[0] == '\0') {
6352 break;
6353 }
6354 if (str1[0] != str2[0]) {
6355 break;
6356 }
6357
6358 str1 += 1;
6359 str2 += 1;
6360 }
6361
6362 return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
6363 }
6364
6365 MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)
6366 {
6367 int result;
6368
6369 result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);
6370 if (result != 0) {
6371 return result;
6372 }
6373
6374 result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);
6375 if (result != 0) {
6376 return result;
6377 }
6378
6379 return result;
6380 }
6381
6382 MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks)
6383 {
6384 size_t sz = strlen(src)+1;
6385 char* dst = (char*)ma_malloc(sz, pAllocationCallbacks);
6386 if (dst == NULL) {
6387 return NULL;
6388 }
6389
6390 ma_strcpy_s(dst, sz, src);
6391
6392 return dst;
6393 }
6394
6395
6396 #include <errno.h>
6397 static ma_result ma_result_from_errno(int e)
6398 {
6399 switch (e)
6400 {
6401 case 0: return MA_SUCCESS;
6402 #ifdef EPERM
6403 case EPERM: return MA_INVALID_OPERATION;
6404 #endif
6405 #ifdef ENOENT
6406 case ENOENT: return MA_DOES_NOT_EXIST;
6407 #endif
6408 #ifdef ESRCH
6409 case ESRCH: return MA_DOES_NOT_EXIST;
6410 #endif
6411 #ifdef EINTR
6412 case EINTR: return MA_INTERRUPT;
6413 #endif
6414 #ifdef EIO
6415 case EIO: return MA_IO_ERROR;
6416 #endif
6417 #ifdef ENXIO
6418 case ENXIO: return MA_DOES_NOT_EXIST;
6419 #endif
6420 #ifdef E2BIG
6421 case E2BIG: return MA_INVALID_ARGS;
6422 #endif
6423 #ifdef ENOEXEC
6424 case ENOEXEC: return MA_INVALID_FILE;
6425 #endif
6426 #ifdef EBADF
6427 case EBADF: return MA_INVALID_FILE;
6428 #endif
6429 #ifdef ECHILD
6430 case ECHILD: return MA_ERROR;
6431 #endif
6432 #ifdef EAGAIN
6433 case EAGAIN: return MA_UNAVAILABLE;
6434 #endif
6435 #ifdef ENOMEM
6436 case ENOMEM: return MA_OUT_OF_MEMORY;
6437 #endif
6438 #ifdef EACCES
6439 case EACCES: return MA_ACCESS_DENIED;
6440 #endif
6441 #ifdef EFAULT
6442 case EFAULT: return MA_BAD_ADDRESS;
6443 #endif
6444 #ifdef ENOTBLK
6445 case ENOTBLK: return MA_ERROR;
6446 #endif
6447 #ifdef EBUSY
6448 case EBUSY: return MA_BUSY;
6449 #endif
6450 #ifdef EEXIST
6451 case EEXIST: return MA_ALREADY_EXISTS;
6452 #endif
6453 #ifdef EXDEV
6454 case EXDEV: return MA_ERROR;
6455 #endif
6456 #ifdef ENODEV
6457 case ENODEV: return MA_DOES_NOT_EXIST;
6458 #endif
6459 #ifdef ENOTDIR
6460 case ENOTDIR: return MA_NOT_DIRECTORY;
6461 #endif
6462 #ifdef EISDIR
6463 case EISDIR: return MA_IS_DIRECTORY;
6464 #endif
6465 #ifdef EINVAL
6466 case EINVAL: return MA_INVALID_ARGS;
6467 #endif
6468 #ifdef ENFILE
6469 case ENFILE: return MA_TOO_MANY_OPEN_FILES;
6470 #endif
6471 #ifdef EMFILE
6472 case EMFILE: return MA_TOO_MANY_OPEN_FILES;
6473 #endif
6474 #ifdef ENOTTY
6475 case ENOTTY: return MA_INVALID_OPERATION;
6476 #endif
6477 #ifdef ETXTBSY
6478 case ETXTBSY: return MA_BUSY;
6479 #endif
6480 #ifdef EFBIG
6481 case EFBIG: return MA_TOO_BIG;
6482 #endif
6483 #ifdef ENOSPC
6484 case ENOSPC: return MA_NO_SPACE;
6485 #endif
6486 #ifdef ESPIPE
6487 case ESPIPE: return MA_BAD_SEEK;
6488 #endif
6489 #ifdef EROFS
6490 case EROFS: return MA_ACCESS_DENIED;
6491 #endif
6492 #ifdef EMLINK
6493 case EMLINK: return MA_TOO_MANY_LINKS;
6494 #endif
6495 #ifdef EPIPE
6496 case EPIPE: return MA_BAD_PIPE;
6497 #endif
6498 #ifdef EDOM
6499 case EDOM: return MA_OUT_OF_RANGE;
6500 #endif
6501 #ifdef ERANGE
6502 case ERANGE: return MA_OUT_OF_RANGE;
6503 #endif
6504 #ifdef EDEADLK
6505 case EDEADLK: return MA_DEADLOCK;
6506 #endif
6507 #ifdef ENAMETOOLONG
6508 case ENAMETOOLONG: return MA_PATH_TOO_LONG;
6509 #endif
6510 #ifdef ENOLCK
6511 case ENOLCK: return MA_ERROR;
6512 #endif
6513 #ifdef ENOSYS
6514 case ENOSYS: return MA_NOT_IMPLEMENTED;
6515 #endif
6516 #ifdef ENOTEMPTY
6517 case ENOTEMPTY: return MA_DIRECTORY_NOT_EMPTY;
6518 #endif
6519 #ifdef ELOOP
6520 case ELOOP: return MA_TOO_MANY_LINKS;
6521 #endif
6522 #ifdef ENOMSG
6523 case ENOMSG: return MA_NO_MESSAGE;
6524 #endif
6525 #ifdef EIDRM
6526 case EIDRM: return MA_ERROR;
6527 #endif
6528 #ifdef ECHRNG
6529 case ECHRNG: return MA_ERROR;
6530 #endif
6531 #ifdef EL2NSYNC
6532 case EL2NSYNC: return MA_ERROR;
6533 #endif
6534 #ifdef EL3HLT
6535 case EL3HLT: return MA_ERROR;
6536 #endif
6537 #ifdef EL3RST
6538 case EL3RST: return MA_ERROR;
6539 #endif
6540 #ifdef ELNRNG
6541 case ELNRNG: return MA_OUT_OF_RANGE;
6542 #endif
6543 #ifdef EUNATCH
6544 case EUNATCH: return MA_ERROR;
6545 #endif
6546 #ifdef ENOCSI
6547 case ENOCSI: return MA_ERROR;
6548 #endif
6549 #ifdef EL2HLT
6550 case EL2HLT: return MA_ERROR;
6551 #endif
6552 #ifdef EBADE
6553 case EBADE: return MA_ERROR;
6554 #endif
6555 #ifdef EBADR
6556 case EBADR: return MA_ERROR;
6557 #endif
6558 #ifdef EXFULL
6559 case EXFULL: return MA_ERROR;
6560 #endif
6561 #ifdef ENOANO
6562 case ENOANO: return MA_ERROR;
6563 #endif
6564 #ifdef EBADRQC
6565 case EBADRQC: return MA_ERROR;
6566 #endif
6567 #ifdef EBADSLT
6568 case EBADSLT: return MA_ERROR;
6569 #endif
6570 #ifdef EBFONT
6571 case EBFONT: return MA_INVALID_FILE;
6572 #endif
6573 #ifdef ENOSTR
6574 case ENOSTR: return MA_ERROR;
6575 #endif
6576 #ifdef ENODATA
6577 case ENODATA: return MA_NO_DATA_AVAILABLE;
6578 #endif
6579 #ifdef ETIME
6580 case ETIME: return MA_TIMEOUT;
6581 #endif
6582 #ifdef ENOSR
6583 case ENOSR: return MA_NO_DATA_AVAILABLE;
6584 #endif
6585 #ifdef ENONET
6586 case ENONET: return MA_NO_NETWORK;
6587 #endif
6588 #ifdef ENOPKG
6589 case ENOPKG: return MA_ERROR;
6590 #endif
6591 #ifdef EREMOTE
6592 case EREMOTE: return MA_ERROR;
6593 #endif
6594 #ifdef ENOLINK
6595 case ENOLINK: return MA_ERROR;
6596 #endif
6597 #ifdef EADV
6598 case EADV: return MA_ERROR;
6599 #endif
6600 #ifdef ESRMNT
6601 case ESRMNT: return MA_ERROR;
6602 #endif
6603 #ifdef ECOMM
6604 case ECOMM: return MA_ERROR;
6605 #endif
6606 #ifdef EPROTO
6607 case EPROTO: return MA_ERROR;
6608 #endif
6609 #ifdef EMULTIHOP
6610 case EMULTIHOP: return MA_ERROR;
6611 #endif
6612 #ifdef EDOTDOT
6613 case EDOTDOT: return MA_ERROR;
6614 #endif
6615 #ifdef EBADMSG
6616 case EBADMSG: return MA_BAD_MESSAGE;
6617 #endif
6618 #ifdef EOVERFLOW
6619 case EOVERFLOW: return MA_TOO_BIG;
6620 #endif
6621 #ifdef ENOTUNIQ
6622 case ENOTUNIQ: return MA_NOT_UNIQUE;
6623 #endif
6624 #ifdef EBADFD
6625 case EBADFD: return MA_ERROR;
6626 #endif
6627 #ifdef EREMCHG
6628 case EREMCHG: return MA_ERROR;
6629 #endif
6630 #ifdef ELIBACC
6631 case ELIBACC: return MA_ACCESS_DENIED;
6632 #endif
6633 #ifdef ELIBBAD
6634 case ELIBBAD: return MA_INVALID_FILE;
6635 #endif
6636 #ifdef ELIBSCN
6637 case ELIBSCN: return MA_INVALID_FILE;
6638 #endif
6639 #ifdef ELIBMAX
6640 case ELIBMAX: return MA_ERROR;
6641 #endif
6642 #ifdef ELIBEXEC
6643 case ELIBEXEC: return MA_ERROR;
6644 #endif
6645 #ifdef EILSEQ
6646 case EILSEQ: return MA_INVALID_DATA;
6647 #endif
6648 #ifdef ERESTART
6649 case ERESTART: return MA_ERROR;
6650 #endif
6651 #ifdef ESTRPIPE
6652 case ESTRPIPE: return MA_ERROR;
6653 #endif
6654 #ifdef EUSERS
6655 case EUSERS: return MA_ERROR;
6656 #endif
6657 #ifdef ENOTSOCK
6658 case ENOTSOCK: return MA_NOT_SOCKET;
6659 #endif
6660 #ifdef EDESTADDRREQ
6661 case EDESTADDRREQ: return MA_NO_ADDRESS;
6662 #endif
6663 #ifdef EMSGSIZE
6664 case EMSGSIZE: return MA_TOO_BIG;
6665 #endif
6666 #ifdef EPROTOTYPE
6667 case EPROTOTYPE: return MA_BAD_PROTOCOL;
6668 #endif
6669 #ifdef ENOPROTOOPT
6670 case ENOPROTOOPT: return MA_PROTOCOL_UNAVAILABLE;
6671 #endif
6672 #ifdef EPROTONOSUPPORT
6673 case EPROTONOSUPPORT: return MA_PROTOCOL_NOT_SUPPORTED;
6674 #endif
6675 #ifdef ESOCKTNOSUPPORT
6676 case ESOCKTNOSUPPORT: return MA_SOCKET_NOT_SUPPORTED;
6677 #endif
6678 #ifdef EOPNOTSUPP
6679 case EOPNOTSUPP: return MA_INVALID_OPERATION;
6680 #endif
6681 #ifdef EPFNOSUPPORT
6682 case EPFNOSUPPORT: return MA_PROTOCOL_FAMILY_NOT_SUPPORTED;
6683 #endif
6684 #ifdef EAFNOSUPPORT
6685 case EAFNOSUPPORT: return MA_ADDRESS_FAMILY_NOT_SUPPORTED;
6686 #endif
6687 #ifdef EADDRINUSE
6688 case EADDRINUSE: return MA_ALREADY_IN_USE;
6689 #endif
6690 #ifdef EADDRNOTAVAIL
6691 case EADDRNOTAVAIL: return MA_ERROR;
6692 #endif
6693 #ifdef ENETDOWN
6694 case ENETDOWN: return MA_NO_NETWORK;
6695 #endif
6696 #ifdef ENETUNREACH
6697 case ENETUNREACH: return MA_NO_NETWORK;
6698 #endif
6699 #ifdef ENETRESET
6700 case ENETRESET: return MA_NO_NETWORK;
6701 #endif
6702 #ifdef ECONNABORTED
6703 case ECONNABORTED: return MA_NO_NETWORK;
6704 #endif
6705 #ifdef ECONNRESET
6706 case ECONNRESET: return MA_CONNECTION_RESET;
6707 #endif
6708 #ifdef ENOBUFS
6709 case ENOBUFS: return MA_NO_SPACE;
6710 #endif
6711 #ifdef EISCONN
6712 case EISCONN: return MA_ALREADY_CONNECTED;
6713 #endif
6714 #ifdef ENOTCONN
6715 case ENOTCONN: return MA_NOT_CONNECTED;
6716 #endif
6717 #ifdef ESHUTDOWN
6718 case ESHUTDOWN: return MA_ERROR;
6719 #endif
6720 #ifdef ETOOMANYREFS
6721 case ETOOMANYREFS: return MA_ERROR;
6722 #endif
6723 #ifdef ETIMEDOUT
6724 case ETIMEDOUT: return MA_TIMEOUT;
6725 #endif
6726 #ifdef ECONNREFUSED
6727 case ECONNREFUSED: return MA_CONNECTION_REFUSED;
6728 #endif
6729 #ifdef EHOSTDOWN
6730 case EHOSTDOWN: return MA_NO_HOST;
6731 #endif
6732 #ifdef EHOSTUNREACH
6733 case EHOSTUNREACH: return MA_NO_HOST;
6734 #endif
6735 #ifdef EALREADY
6736 case EALREADY: return MA_IN_PROGRESS;
6737 #endif
6738 #ifdef EINPROGRESS
6739 case EINPROGRESS: return MA_IN_PROGRESS;
6740 #endif
6741 #ifdef ESTALE
6742 case ESTALE: return MA_INVALID_FILE;
6743 #endif
6744 #ifdef EUCLEAN
6745 case EUCLEAN: return MA_ERROR;
6746 #endif
6747 #ifdef ENOTNAM
6748 case ENOTNAM: return MA_ERROR;
6749 #endif
6750 #ifdef ENAVAIL
6751 case ENAVAIL: return MA_ERROR;
6752 #endif
6753 #ifdef EISNAM
6754 case EISNAM: return MA_ERROR;
6755 #endif
6756 #ifdef EREMOTEIO
6757 case EREMOTEIO: return MA_IO_ERROR;
6758 #endif
6759 #ifdef EDQUOT
6760 case EDQUOT: return MA_NO_SPACE;
6761 #endif
6762 #ifdef ENOMEDIUM
6763 case ENOMEDIUM: return MA_DOES_NOT_EXIST;
6764 #endif
6765 #ifdef EMEDIUMTYPE
6766 case EMEDIUMTYPE: return MA_ERROR;
6767 #endif
6768 #ifdef ECANCELED
6769 case ECANCELED: return MA_CANCELLED;
6770 #endif
6771 #ifdef ENOKEY
6772 case ENOKEY: return MA_ERROR;
6773 #endif
6774 #ifdef EKEYEXPIRED
6775 case EKEYEXPIRED: return MA_ERROR;
6776 #endif
6777 #ifdef EKEYREVOKED
6778 case EKEYREVOKED: return MA_ERROR;
6779 #endif
6780 #ifdef EKEYREJECTED
6781 case EKEYREJECTED: return MA_ERROR;
6782 #endif
6783 #ifdef EOWNERDEAD
6784 case EOWNERDEAD: return MA_ERROR;
6785 #endif
6786 #ifdef ENOTRECOVERABLE
6787 case ENOTRECOVERABLE: return MA_ERROR;
6788 #endif
6789 #ifdef ERFKILL
6790 case ERFKILL: return MA_ERROR;
6791 #endif
6792 #ifdef EHWPOISON
6793 case EHWPOISON: return MA_ERROR;
6794 #endif
6795 default: return MA_ERROR;
6796 }
6797 }
6798
6799 MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
6800 {
6801 #if _MSC_VER && _MSC_VER >= 1400
6802 errno_t err;
6803 #endif
6804
6805 if (ppFile != NULL) {
6806 *ppFile = NULL; /* Safety. */
6807 }
6808
6809 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
6810 return MA_INVALID_ARGS;
6811 }
6812
6813 #if _MSC_VER && _MSC_VER >= 1400
6814 err = fopen_s(ppFile, pFilePath, pOpenMode);
6815 if (err != 0) {
6816 return ma_result_from_errno(err);
6817 }
6818 #else
6819 #if defined(_WIN32) || defined(__APPLE__)
6820 *ppFile = fopen(pFilePath, pOpenMode);
6821 #else
6822 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
6823 *ppFile = fopen64(pFilePath, pOpenMode);
6824 #else
6825 *ppFile = fopen(pFilePath, pOpenMode);
6826 #endif
6827 #endif
6828 if (*ppFile == NULL) {
6829 ma_result result = ma_result_from_errno(errno);
6830 if (result == MA_SUCCESS) {
6831 result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
6832 }
6833
6834 return result;
6835 }
6836 #endif
6837
6838 return MA_SUCCESS;
6839 }
6840
6841
6842
6843 /*
6844 _wfopen() isn't always available in all compilation environments.
6845
6846 * Windows only.
6847 * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
6848 * MinGW-64 (both 32- and 64-bit) seems to support it.
6849 * MinGW wraps it in !defined(__STRICT_ANSI__).
6850
6851 This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
6852 fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
6853 */
6854 #if defined(_WIN32)
6855 #if defined(_MSC_VER) || defined(__MINGW64__) || !defined(__STRICT_ANSI__)
6856 #define MA_HAS_WFOPEN
6857 #endif
6858 #endif
6859
6860 MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks)
6861 {
6862 if (ppFile != NULL) {
6863 *ppFile = NULL; /* Safety. */
6864 }
6865
6866 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
6867 return MA_INVALID_ARGS;
6868 }
6869
6870 #if defined(MA_HAS_WFOPEN)
6871 {
6872 /* Use _wfopen() on Windows. */
6873 #if defined(_MSC_VER) && _MSC_VER >= 1400
6874 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
6875 if (err != 0) {
6876 return ma_result_from_errno(err);
6877 }
6878 #else
6879 *ppFile = _wfopen(pFilePath, pOpenMode);
6880 if (*ppFile == NULL) {
6881 return ma_result_from_errno(errno);
6882 }
6883 #endif
6884 (void)pAllocationCallbacks;
6885 }
6886 #else
6887 /*
6888 Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
6889 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
6890 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.
6891 */
6892 {
6893 mbstate_t mbs;
6894 size_t lenMB;
6895 const wchar_t* pFilePathTemp = pFilePath;
6896 char* pFilePathMB = NULL;
6897 char pOpenModeMB[32] = {0};
6898
6899 /* Get the length first. */
6900 MA_ZERO_OBJECT(&mbs);
6901 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
6902 if (lenMB == (size_t)-1) {
6903 return ma_result_from_errno(errno);
6904 }
6905
6906 pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks);
6907 if (pFilePathMB == NULL) {
6908 return MA_OUT_OF_MEMORY;
6909 }
6910
6911 pFilePathTemp = pFilePath;
6912 MA_ZERO_OBJECT(&mbs);
6913 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
6914
6915 /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
6916 {
6917 size_t i = 0;
6918 for (;;) {
6919 if (pOpenMode[i] == 0) {
6920 pOpenModeMB[i] = '\0';
6921 break;
6922 }
6923
6924 pOpenModeMB[i] = (char)pOpenMode[i];
6925 i += 1;
6926 }
6927 }
6928
6929 *ppFile = fopen(pFilePathMB, pOpenModeMB);
6930
6931 ma_free(pFilePathMB, pAllocationCallbacks);
6932 }
6933
6934 if (*ppFile == NULL) {
6935 return MA_ERROR;
6936 }
6937 #endif
6938
6939 return MA_SUCCESS;
6940 }
6941
6942
6943
6944 static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
6945 {
6946 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
6947 MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes);
6948 #else
6949 while (sizeInBytes > 0) {
6950 ma_uint64 bytesToCopyNow = sizeInBytes;
6951 if (bytesToCopyNow > MA_SIZE_MAX) {
6952 bytesToCopyNow = MA_SIZE_MAX;
6953 }
6954
6955 MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */
6956
6957 sizeInBytes -= bytesToCopyNow;
6958 dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow);
6959 src = (const void*)((const ma_uint8*)src + bytesToCopyNow);
6960 }
6961 #endif
6962 }
6963
6964 static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)
6965 {
6966 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
6967 MA_ZERO_MEMORY(dst, (size_t)sizeInBytes);
6968 #else
6969 while (sizeInBytes > 0) {
6970 ma_uint64 bytesToZeroNow = sizeInBytes;
6971 if (bytesToZeroNow > MA_SIZE_MAX) {
6972 bytesToZeroNow = MA_SIZE_MAX;
6973 }
6974
6975 MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */
6976
6977 sizeInBytes -= bytesToZeroNow;
6978 dst = (void*)((ma_uint8*)dst + bytesToZeroNow);
6979 }
6980 #endif
6981 }
6982
6983
6984 /* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
6985 static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)
6986 {
6987 x--;
6988 x |= x >> 1;
6989 x |= x >> 2;
6990 x |= x >> 4;
6991 x |= x >> 8;
6992 x |= x >> 16;
6993 x++;
6994
6995 return x;
6996 }
6997
6998 static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)
6999 {
7000 return ma_next_power_of_2(x) >> 1;
7001 }
7002
7003 static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)
7004 {
7005 unsigned int prev = ma_prev_power_of_2(x);
7006 unsigned int next = ma_next_power_of_2(x);
7007 if ((next - x) > (x - prev)) {
7008 return prev;
7009 } else {
7010 return next;
7011 }
7012 }
7013
7014 static MA_INLINE unsigned int ma_count_set_bits(unsigned int x)
7015 {
7016 unsigned int count = 0;
7017 while (x != 0) {
7018 if (x & 1) {
7019 count += 1;
7020 }
7021
7022 x = x >> 1;
7023 }
7024
7025 return count;
7026 }
7027
7028
7029
7030 /* Clamps an f32 sample to -1..1 */
7031 static MA_INLINE float ma_clip_f32(float x)
7032 {
7033 if (x < -1) return -1;
7034 if (x > +1) return +1;
7035 return x;
7036 }
7037
7038 static MA_INLINE float ma_mix_f32(float x, float y, float a)
7039 {
7040 return x*(1-a) + y*a;
7041 }
7042 static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
7043 {
7044 float r0 = (y - x);
7045 float r1 = r0*a;
7046 return x + r1;
7047 /*return x + (y - x)*a;*/
7048 }
7049
7050
7051 #if defined(MA_SUPPORT_SSE2)
7052 static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)
7053 {
7054 return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));
7055 }
7056 #endif
7057 #if defined(MA_SUPPORT_AVX2)
7058 static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)
7059 {
7060 return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));
7061 }
7062 #endif
7063 #if defined(MA_SUPPORT_AVX512)
7064 static MA_INLINE __m512 ma_mix_f32_fast__avx512(__m512 x, __m512 y, __m512 a)
7065 {
7066 return _mm512_add_ps(x, _mm512_mul_ps(_mm512_sub_ps(y, x), a));
7067 }
7068 #endif
7069 #if defined(MA_SUPPORT_NEON)
7070 static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)
7071 {
7072 return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));
7073 }
7074 #endif
7075
7076
7077 static MA_INLINE double ma_mix_f64(double x, double y, double a)
7078 {
7079 return x*(1-a) + y*a;
7080 }
7081 static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
7082 {
7083 return x + (y - x)*a;
7084 }
7085
7086 static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
7087 {
7088 return lo + x*(hi-lo);
7089 }
7090
7091
7092 /*
7093 Greatest common factor using Euclid's algorithm iteratively.
7094 */
7095 static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b)
7096 {
7097 for (;;) {
7098 if (b == 0) {
7099 break;
7100 } else {
7101 ma_uint32 t = a;
7102 a = b;
7103 b = t % a;
7104 }
7105 }
7106
7107 return a;
7108 }
7109
7110
7111 /*
7112 Random Number Generation
7113
7114 miniaudio uses the LCG random number generation algorithm. This is good enough for audio.
7115
7116 Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across
7117 multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for
7118 miniaudio's purposes.
7119 */
7120 #ifndef MA_DEFAULT_LCG_SEED
7121 #define MA_DEFAULT_LCG_SEED 4321
7122 #endif
7123
7124 #define MA_LCG_M 2147483647
7125 #define MA_LCG_A 48271
7126 #define MA_LCG_C 0
7127
7128 static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */
7129
7130 static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed)
7131 {
7132 MA_ASSERT(pLCG != NULL);
7133 pLCG->state = seed;
7134 }
7135
7136 static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG)
7137 {
7138 pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M;
7139 return pLCG->state;
7140 }
7141
7142 static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG)
7143 {
7144 return (ma_uint32)ma_lcg_rand_s32(pLCG);
7145 }
7146
7147 static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG)
7148 {
7149 return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF);
7150 }
7151
7152 static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG)
7153 {
7154 return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF;
7155 }
7156
7157 static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG)
7158 {
7159 return (float)ma_lcg_rand_f64(pLCG);
7160 }
7161
7162 static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi)
7163 {
7164 return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi);
7165 }
7166
7167 static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi)
7168 {
7169 if (lo == hi) {
7170 return lo;
7171 }
7172
7173 return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1);
7174 }
7175
7176
7177
7178 static MA_INLINE void ma_seed(ma_int32 seed)
7179 {
7180 ma_lcg_seed(&g_maLCG, seed);
7181 }
7182
7183 static MA_INLINE ma_int32 ma_rand_s32()
7184 {
7185 return ma_lcg_rand_s32(&g_maLCG);
7186 }
7187
7188 static MA_INLINE ma_uint32 ma_rand_u32()
7189 {
7190 return ma_lcg_rand_u32(&g_maLCG);
7191 }
7192
7193 static MA_INLINE double ma_rand_f64()
7194 {
7195 return ma_lcg_rand_f64(&g_maLCG);
7196 }
7197
7198 static MA_INLINE float ma_rand_f32()
7199 {
7200 return ma_lcg_rand_f32(&g_maLCG);
7201 }
7202
7203 static MA_INLINE float ma_rand_range_f32(float lo, float hi)
7204 {
7205 return ma_lcg_rand_range_f32(&g_maLCG, lo, hi);
7206 }
7207
7208 static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)
7209 {
7210 return ma_lcg_rand_range_s32(&g_maLCG, lo, hi);
7211 }
7212
7213
7214 static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)
7215 {
7216 return ma_rand_range_f32(ditherMin, ditherMax);
7217 }
7218
7219 static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)
7220 {
7221 float a = ma_rand_range_f32(ditherMin, 0);
7222 float b = ma_rand_range_f32(0, ditherMax);
7223 return a + b;
7224 }
7225
7226 static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)
7227 {
7228 if (ditherMode == ma_dither_mode_rectangle) {
7229 return ma_dither_f32_rectangle(ditherMin, ditherMax);
7230 }
7231 if (ditherMode == ma_dither_mode_triangle) {
7232 return ma_dither_f32_triangle(ditherMin, ditherMax);
7233 }
7234
7235 return 0;
7236 }
7237
7238 static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)
7239 {
7240 if (ditherMode == ma_dither_mode_rectangle) {
7241 ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);
7242 return a;
7243 }
7244 if (ditherMode == ma_dither_mode_triangle) {
7245 ma_int32 a = ma_rand_range_s32(ditherMin, 0);
7246 ma_int32 b = ma_rand_range_s32(0, ditherMax);
7247 return a + b;
7248 }
7249
7250 return 0;
7251 }
7252
7253
7254 /******************************************************************************
7255
7256 Atomics
7257
7258 ******************************************************************************/
7259 #if defined(__clang__)
7260 #if defined(__has_builtin)
7261 #if __has_builtin(__sync_swap)
7262 #define MA_HAS_SYNC_SWAP
7263 #endif
7264 #endif
7265 #elif defined(__GNUC__)
7266 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC__ >= 7)
7267 #define MA_HAS_GNUC_ATOMICS
7268 #endif
7269 #endif
7270
7271 #if defined(_WIN32) && !defined(__GNUC__) && !defined(__clang__)
7272 #define ma_memory_barrier() MemoryBarrier()
7273 #define ma_atomic_exchange_32(a, b) InterlockedExchange((LONG*)a, (LONG)b)
7274 #define ma_atomic_exchange_64(a, b) InterlockedExchange64((LONGLONG*)a, (LONGLONG)b)
7275 #define ma_atomic_increment_32(a) InterlockedIncrement((LONG*)a)
7276 #define ma_atomic_decrement_32(a) InterlockedDecrement((LONG*)a)
7277 #else
7278 #define ma_memory_barrier() __sync_synchronize()
7279 #if defined(MA_HAS_SYNC_SWAP)
7280 #define ma_atomic_exchange_32(a, b) __sync_swap(a, b)
7281 #define ma_atomic_exchange_64(a, b) __sync_swap(a, b)
7282 #elif defined(MA_HAS_GNUC_ATOMICS)
7283 #define ma_atomic_exchange_32(a, b) (void)__atomic_exchange_n(a, b, __ATOMIC_ACQ_REL)
7284 #define ma_atomic_exchange_64(a, b) (void)__atomic_exchange_n(a, b, __ATOMIC_ACQ_REL)
7285 #else
7286 #define ma_atomic_exchange_32(a, b) __sync_synchronize(); (void)__sync_lock_test_and_set(a, b)
7287 #define ma_atomic_exchange_64(a, b) __sync_synchronize(); (void)__sync_lock_test_and_set(a, b)
7288 #endif
7289 #define ma_atomic_increment_32(a) __sync_add_and_fetch(a, 1)
7290 #define ma_atomic_decrement_32(a) __sync_sub_and_fetch(a, 1)
7291 #endif
7292
7293 #ifdef MA_64BIT
7294 #define ma_atomic_exchange_ptr ma_atomic_exchange_64
7295 #endif
7296 #ifdef MA_32BIT
7297 #define ma_atomic_exchange_ptr ma_atomic_exchange_32
7298 #endif
7299
7300
7301 static void* ma__malloc_default(size_t sz, void* pUserData)
7302 {
7303 (void)pUserData;
7304 return MA_MALLOC(sz);
7305 }
7306
7307 static void* ma__realloc_default(void* p, size_t sz, void* pUserData)
7308 {
7309 (void)pUserData;
7310 return MA_REALLOC(p, sz);
7311 }
7312
7313 static void ma__free_default(void* p, void* pUserData)
7314 {
7315 (void)pUserData;
7316 MA_FREE(p);
7317 }
7318
7319
7320 static void* ma__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
7321 {
7322 if (pAllocationCallbacks == NULL) {
7323 return NULL;
7324 }
7325
7326 if (pAllocationCallbacks->onMalloc != NULL) {
7327 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
7328 }
7329
7330 /* Try using realloc(). */
7331 if (pAllocationCallbacks->onRealloc != NULL) {
7332 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
7333 }
7334
7335 return NULL;
7336 }
7337
7338 static void* ma__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
7339 {
7340 if (pAllocationCallbacks == NULL) {
7341 return NULL;
7342 }
7343
7344 if (pAllocationCallbacks->onRealloc != NULL) {
7345 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
7346 }
7347
7348 /* Try emulating realloc() in terms of malloc()/free(). */
7349 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
7350 void* p2;
7351
7352 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
7353 if (p2 == NULL) {
7354 return NULL;
7355 }
7356
7357 if (p != NULL) {
7358 MA_COPY_MEMORY(p2, p, szOld);
7359 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
7360 }
7361
7362 return p2;
7363 }
7364
7365 return NULL;
7366 }
7367
7368 static MA_INLINE void* ma__calloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
7369 {
7370 void* p = ma__malloc_from_callbacks(sz, pAllocationCallbacks);
7371 if (p != NULL) {
7372 MA_ZERO_MEMORY(p, sz);
7373 }
7374
7375 return p;
7376 }
7377
7378 static void ma__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
7379 {
7380 if (p == NULL || pAllocationCallbacks == NULL) {
7381 return;
7382 }
7383
7384 if (pAllocationCallbacks->onFree != NULL) {
7385 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
7386 }
7387 }
7388
7389 static ma_allocation_callbacks ma_allocation_callbacks_init_default()
7390 {
7391 ma_allocation_callbacks callbacks;
7392 callbacks.pUserData = NULL;
7393 callbacks.onMalloc = ma__malloc_default;
7394 callbacks.onRealloc = ma__realloc_default;
7395 callbacks.onFree = ma__free_default;
7396
7397 return callbacks;
7398 }
7399
7400 static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc)
7401 {
7402 if (pDst == NULL) {
7403 return MA_INVALID_ARGS;
7404 }
7405
7406 if (pSrc == NULL) {
7407 *pDst = ma_allocation_callbacks_init_default();
7408 } else {
7409 if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) {
7410 *pDst = ma_allocation_callbacks_init_default();
7411 } else {
7412 if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) {
7413 return MA_INVALID_ARGS; /* Invalid allocation callbacks. */
7414 } else {
7415 *pDst = *pSrc;
7416 }
7417 }
7418 }
7419
7420 return MA_SUCCESS;
7421 }
7422
7423
7424 MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)
7425 {
7426 /* For robustness we're going to use a resampler object to calculate this since that already has a way of calculating this. */
7427 ma_result result;
7428 ma_uint64 frameCountOut;
7429 ma_resampler_config config;
7430 ma_resampler resampler;
7431
7432 config = ma_resampler_config_init(ma_format_s16, 1, sampleRateIn, sampleRateOut, ma_resample_algorithm_linear);
7433 result = ma_resampler_init(&config, &resampler);
7434 if (result != MA_SUCCESS) {
7435 return 0;
7436 }
7437
7438 frameCountOut = ma_resampler_get_expected_output_frame_count(&resampler, frameCountIn);
7439
7440 ma_resampler_uninit(&resampler);
7441 return frameCountOut;
7442 }
7443
7444 #ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE
7445 #define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096
7446 #endif
7447
7448 /************************************************************************************************************************************************************
7449 *************************************************************************************************************************************************************
7450
7451 DEVICE I/O
7452 ==========
7453
7454 *************************************************************************************************************************************************************
7455 ************************************************************************************************************************************************************/
7456 #ifndef MA_NO_DEVICE_IO
7457 #ifdef MA_WIN32
7458 #include <objbase.h>
7459 #include <mmreg.h>
7460 #include <mmsystem.h>
7461 #endif
7462
7463 #if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
7464 #include <mach/mach_time.h> /* For mach_absolute_time() */
7465 #endif
7466
7467 #ifdef MA_POSIX
7468 #include <sys/time.h>
7469 #include <sys/types.h>
7470 #include <unistd.h>
7471 #include <dlfcn.h>
7472 #endif
7473
7474 /*
7475 Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
7476 using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
7477 compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply
7478 disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
7479 not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
7480 */
7481 /*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/
7482
7483 /* Disable run-time linking on certain backends. */
7484 #ifndef MA_NO_RUNTIME_LINKING
7485 #if defined(MA_ANDROID) || defined(MA_EMSCRIPTEN)
7486 #define MA_NO_RUNTIME_LINKING
7487 #endif
7488 #endif
7489
7490 /*
7491 Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not
7492 certain unused functions and variables can be excluded from the build to avoid warnings.
7493 */
7494 #ifdef MA_ENABLE_WASAPI
7495 #define MA_HAS_WASAPI /* Every compiler should support WASAPI */
7496 #endif
7497 #ifdef MA_ENABLE_DSOUND
7498 #define MA_HAS_DSOUND /* Every compiler should support DirectSound. */
7499 #endif
7500 #ifdef MA_ENABLE_WINMM
7501 #define MA_HAS_WINMM /* Every compiler I'm aware of supports WinMM. */
7502 #endif
7503 #ifdef MA_ENABLE_ALSA
7504 #define MA_HAS_ALSA
7505 #ifdef MA_NO_RUNTIME_LINKING
7506 #ifdef __has_include
7507 #if !__has_include(<alsa/asoundlib.h>)
7508 #undef MA_HAS_ALSA
7509 #endif
7510 #endif
7511 #endif
7512 #endif
7513 #ifdef MA_ENABLE_PULSEAUDIO
7514 #define MA_HAS_PULSEAUDIO
7515 #ifdef MA_NO_RUNTIME_LINKING
7516 #ifdef __has_include
7517 #if !__has_include(<pulse/pulseaudio.h>)
7518 #undef MA_HAS_PULSEAUDIO
7519 #endif
7520 #endif
7521 #endif
7522 #endif
7523 #ifdef MA_ENABLE_JACK
7524 #define MA_HAS_JACK
7525 #ifdef MA_NO_RUNTIME_LINKING
7526 #ifdef __has_include
7527 #if !__has_include(<jack/jack.h>)
7528 #undef MA_HAS_JACK
7529 #endif
7530 #endif
7531 #endif
7532 #endif
7533 #ifdef MA_ENABLE_COREAUDIO
7534 #define MA_HAS_COREAUDIO
7535 #endif
7536 #ifdef MA_ENABLE_SNDIO
7537 #define MA_HAS_SNDIO
7538 #endif
7539 #ifdef MA_ENABLE_AUDIO4
7540 #define MA_HAS_AUDIO4
7541 #endif
7542 #ifdef MA_ENABLE_OSS
7543 #define MA_HAS_OSS
7544 #endif
7545 #ifdef MA_ENABLE_AAUDIO
7546 #define MA_HAS_AAUDIO
7547 #endif
7548 #ifdef MA_ENABLE_OPENSL
7549 #define MA_HAS_OPENSL
7550 #endif
7551 #ifdef MA_ENABLE_WEBAUDIO
7552 #define MA_HAS_WEBAUDIO
7553 #endif
7554 #ifdef MA_ENABLE_NULL
7555 #define MA_HAS_NULL /* Everything supports the null backend. */
7556 #endif
7557
7558 MA_API const char* ma_get_backend_name(ma_backend backend)
7559 {
7560 switch (backend)
7561 {
7562 case ma_backend_wasapi: return "WASAPI";
7563 case ma_backend_dsound: return "DirectSound";
7564 case ma_backend_winmm: return "WinMM";
7565 case ma_backend_coreaudio: return "Core Audio";
7566 case ma_backend_sndio: return "sndio";
7567 case ma_backend_audio4: return "audio(4)";
7568 case ma_backend_oss: return "OSS";
7569 case ma_backend_pulseaudio: return "PulseAudio";
7570 case ma_backend_alsa: return "ALSA";
7571 case ma_backend_jack: return "JACK";
7572 case ma_backend_aaudio: return "AAudio";
7573 case ma_backend_opensl: return "OpenSL|ES";
7574 case ma_backend_webaudio: return "Web Audio";
7575 case ma_backend_null: return "Null";
7576 default: return "Unknown";
7577 }
7578 }
7579
7580 MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
7581 {
7582 switch (backend)
7583 {
7584 case ma_backend_wasapi: return MA_TRUE;
7585 case ma_backend_dsound: return MA_FALSE;
7586 case ma_backend_winmm: return MA_FALSE;
7587 case ma_backend_coreaudio: return MA_FALSE;
7588 case ma_backend_sndio: return MA_FALSE;
7589 case ma_backend_audio4: return MA_FALSE;
7590 case ma_backend_oss: return MA_FALSE;
7591 case ma_backend_pulseaudio: return MA_FALSE;
7592 case ma_backend_alsa: return MA_FALSE;
7593 case ma_backend_jack: return MA_FALSE;
7594 case ma_backend_aaudio: return MA_FALSE;
7595 case ma_backend_opensl: return MA_FALSE;
7596 case ma_backend_webaudio: return MA_FALSE;
7597 case ma_backend_null: return MA_FALSE;
7598 default: return MA_FALSE;
7599 }
7600 }
7601
7602
7603
7604 #ifdef MA_WIN32
7605 #define MA_THREADCALL WINAPI
7606 typedef unsigned long ma_thread_result;
7607 #else
7608 #define MA_THREADCALL
7609 typedef void* ma_thread_result;
7610 #endif
7611 typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
7612
7613 #ifdef MA_WIN32
7614 static ma_result ma_result_from_GetLastError(DWORD error)
7615 {
7616 switch (error)
7617 {
7618 case ERROR_SUCCESS: return MA_SUCCESS;
7619 case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST;
7620 case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES;
7621 case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY;
7622 case ERROR_DISK_FULL: return MA_NO_SPACE;
7623 case ERROR_HANDLE_EOF: return MA_END_OF_FILE;
7624 case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK;
7625 case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS;
7626 case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED;
7627 case ERROR_SEM_TIMEOUT: return MA_TIMEOUT;
7628 case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST;
7629 default: break;
7630 }
7631
7632 return MA_ERROR;
7633 }
7634
7635 /* WASAPI error codes. */
7636 #define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001)
7637 #define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002)
7638 #define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003)
7639 #define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004)
7640 #define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005)
7641 #define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006)
7642 #define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007)
7643 #define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008)
7644 #define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009)
7645 #define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A)
7646 #define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B)
7647 #define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C)
7648 #define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D)
7649 #define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E)
7650 #define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F)
7651 #define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010)
7652 #define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011)
7653 #define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012)
7654 #define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013)
7655 #define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014)
7656 #define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015)
7657 #define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016)
7658 #define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017)
7659 #define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018)
7660 #define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019)
7661 #define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020)
7662 #define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021)
7663 #define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022)
7664 #define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023)
7665 #define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024)
7666 #define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025)
7667 #define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026)
7668 #define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027)
7669 #define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028)
7670 #define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029)
7671 #define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030)
7672 #define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040)
7673 #define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001)
7674 #define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002)
7675 #define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003)
7676
7677 #define MA_DS_OK ((HRESULT)0)
7678 #define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A)
7679 #define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A)
7680 #define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E)
7681 #define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/
7682 #define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032)
7683 #define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/
7684 #define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046)
7685 #define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/
7686 #define MA_DSERR_BADFORMAT ((HRESULT)0x88780064)
7687 #define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/
7688 #define MA_DSERR_NODRIVER ((HRESULT)0x88780078)
7689 #define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082)
7690 #define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/
7691 #define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096)
7692 #define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0)
7693 #define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA)
7694 #define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/
7695 #define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/
7696 #define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4)
7697 #define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE)
7698 #define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8)
7699 #define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2)
7700 #define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161)
7701 #define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC)
7702
7703 static ma_result ma_result_from_HRESULT(HRESULT hr)
7704 {
7705 switch (hr)
7706 {
7707 case NOERROR: return MA_SUCCESS;
7708 /*case S_OK: return MA_SUCCESS;*/
7709
7710 case E_POINTER: return MA_INVALID_ARGS;
7711 case E_UNEXPECTED: return MA_ERROR;
7712 case E_NOTIMPL: return MA_NOT_IMPLEMENTED;
7713 case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY;
7714 case E_INVALIDARG: return MA_INVALID_ARGS;
7715 case E_NOINTERFACE: return MA_API_NOT_FOUND;
7716 case E_HANDLE: return MA_INVALID_ARGS;
7717 case E_ABORT: return MA_ERROR;
7718 case E_FAIL: return MA_ERROR;
7719 case E_ACCESSDENIED: return MA_ACCESS_DENIED;
7720
7721 /* WASAPI */
7722 case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
7723 case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
7724 case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS;
7725 case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE;
7726 case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED;
7727 case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG;
7728 case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION;
7729 case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED;
7730 case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS;
7731 case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY;
7732 case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION;
7733 case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST;
7734 case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION;
7735 case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED;
7736 case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
7737 case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED;
7738 case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS;
7739 case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED;
7740 case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS;
7741 case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS;
7742 case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS;
7743 case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS;
7744 case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR;
7745 case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR;
7746 case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS;
7747 case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS;
7748 case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS;
7749 case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION;
7750 case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY;
7751 case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
7752 case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
7753 case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA;
7754 case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION;
7755 case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION;
7756 case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION;
7757 case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION;
7758 case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION;
7759 case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE;
7760 case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS;
7761 case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR;
7762
7763 /* DirectSound */
7764 /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */
7765 case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS;
7766 case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE;
7767 case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION;
7768 /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */
7769 case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION;
7770 /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */
7771 case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION;
7772 /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */
7773 case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED;
7774 /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */
7775 case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND;
7776 case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
7777 case MA_DSERR_NOAGGREGATION: return MA_ERROR;
7778 case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE;
7779 case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED;
7780 case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
7781 /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */
7782 /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */
7783 case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE;
7784 case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION;
7785 case MA_DSERR_SENDLOOP: return MA_DEADLOCK;
7786 case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS;
7787 case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE;
7788 case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE;
7789
7790 default: return MA_ERROR;
7791 }
7792 }
7793
7794 typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
7795 typedef void (WINAPI * MA_PFN_CoUninitialize)(void);
7796 typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
7797 typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv);
7798 typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar);
7799 typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
7800
7801 typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void);
7802 typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void);
7803
7804 /* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
7805 typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
7806 typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
7807 typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
7808 #endif
7809
7810
7811 #define MA_STATE_UNINITIALIZED 0
7812 #define MA_STATE_STOPPED 1 /* The device's default state after initialization. */
7813 #define MA_STATE_STARTED 2 /* The worker thread is in it's main loop waiting for the driver to request or deliver audio data. */
7814 #define MA_STATE_STARTING 3 /* Transitioning from a stopped state to started. */
7815 #define MA_STATE_STOPPING 4 /* Transitioning from a started state to stopped. */
7816
7817 #define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
7818 #define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
7819
7820
7821 MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
7822 {
7823 switch (logLevel)
7824 {
7825 case MA_LOG_LEVEL_VERBOSE: return "";
7826 case MA_LOG_LEVEL_INFO: return "INFO";
7827 case MA_LOG_LEVEL_WARNING: return "WARNING";
7828 case MA_LOG_LEVEL_ERROR: return "ERROR";
7829 default: return "ERROR";
7830 }
7831 }
7832
7833 /* Posts a log message. */
7834 static void ma_post_log_message(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
7835 {
7836 if (pContext == NULL) {
7837 if (pDevice != NULL) {
7838 pContext = pDevice->pContext;
7839 }
7840 }
7841
7842 if (pContext == NULL) {
7843 return;
7844 }
7845
7846 #if defined(MA_LOG_LEVEL)
7847 if (logLevel <= MA_LOG_LEVEL) {
7848 ma_log_proc onLog;
7849
7850 #if defined(MA_DEBUG_OUTPUT)
7851 if (logLevel <= MA_LOG_LEVEL) {
7852 printf("%s: %s\n", ma_log_level_to_string(logLevel), message);
7853 }
7854 #endif
7855
7856 onLog = pContext->logCallback;
7857 if (onLog) {
7858 onLog(pContext, pDevice, logLevel, message);
7859 }
7860 }
7861 #endif
7862 }
7863
7864 /* Posts a formatted log message. */
7865 static void ma_post_log_messagev(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* pFormat, va_list args)
7866 {
7867 #if (!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__)
7868 {
7869 char pFormattedMessage[1024];
7870 vsnprintf(pFormattedMessage, sizeof(pFormattedMessage), pFormat, args);
7871 ma_post_log_message(pContext, pDevice, logLevel, pFormattedMessage);
7872 }
7873 #else
7874 {
7875 /*
7876 Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll
7877 need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing
7878 a fixed sized stack allocated buffer.
7879 */
7880 #if defined (_MSC_VER)
7881 int formattedLen;
7882 va_list args2;
7883
7884 #if _MSC_VER >= 1800
7885 va_copy(args2, args);
7886 #else
7887 args2 = args;
7888 #endif
7889 formattedLen = _vscprintf(pFormat, args2);
7890 va_end(args2);
7891
7892 if (formattedLen > 0) {
7893 char* pFormattedMessage = NULL;
7894 ma_allocation_callbacks* pAllocationCallbacks = NULL;
7895
7896 /* Make sure we have a context so we can allocate memory. */
7897 if (pContext == NULL) {
7898 if (pDevice != NULL) {
7899 pContext = pDevice->pContext;
7900 }
7901 }
7902
7903 if (pContext != NULL) {
7904 pAllocationCallbacks = &pContext->allocationCallbacks;
7905 }
7906
7907 pFormattedMessage = (char*)ma_malloc(formattedLen + 1, pAllocationCallbacks);
7908 if (pFormattedMessage != NULL) {
7909 vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args);
7910 ma_post_log_message(pContext, pDevice, logLevel, pFormattedMessage);
7911 ma_free(pFormattedMessage, pAllocationCallbacks);
7912 }
7913 }
7914 #else
7915 /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */
7916 (void)pContext;
7917 (void)pDevice;
7918 (void)logLevel;
7919 (void)pFormat;
7920 (void)args;
7921 #endif
7922 }
7923 #endif
7924 }
7925
7926 static MA_INLINE void ma_post_log_messagef(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* pFormat, ...)
7927 {
7928 va_list args;
7929 va_start(args, pFormat);
7930 {
7931 ma_post_log_messagev(pContext, pDevice, logLevel, pFormat, args);
7932 }
7933 va_end(args);
7934 }
7935
7936 /* Posts an log message. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */
7937 static ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
7938 {
7939 ma_post_log_message(pContext, pDevice, logLevel, message);
7940 return resultCode;
7941 }
7942
7943 static ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
7944 {
7945 return ma_context_post_error(NULL, pDevice, logLevel, message, resultCode);
7946 }
7947
7948
7949 /*******************************************************************************
7950
7951 Timing
7952
7953 *******************************************************************************/
7954 #ifdef MA_WIN32
7955 static LARGE_INTEGER g_ma_TimerFrequency = {{0}};
7956 static void ma_timer_init(ma_timer* pTimer)
7957 {
7958 LARGE_INTEGER counter;
7959
7960 if (g_ma_TimerFrequency.QuadPart == 0) {
7961 QueryPerformanceFrequency(&g_ma_TimerFrequency);
7962 }
7963
7964 QueryPerformanceCounter(&counter);
7965 pTimer->counter = counter.QuadPart;
7966 }
7967
7968 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
7969 {
7970 LARGE_INTEGER counter;
7971 if (!QueryPerformanceCounter(&counter)) {
7972 return 0;
7973 }
7974
7975 return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
7976 }
7977 #elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
7978 static ma_uint64 g_ma_TimerFrequency = 0;
7979 static void ma_timer_init(ma_timer* pTimer)
7980 {
7981 mach_timebase_info_data_t baseTime;
7982 mach_timebase_info(&baseTime);
7983 g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
7984
7985 pTimer->counter = mach_absolute_time();
7986 }
7987
7988 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
7989 {
7990 ma_uint64 newTimeCounter = mach_absolute_time();
7991 ma_uint64 oldTimeCounter = pTimer->counter;
7992
7993 return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
7994 }
7995 #elif defined(MA_EMSCRIPTEN)
7996 static MA_INLINE void ma_timer_init(ma_timer* pTimer)
7997 {
7998 pTimer->counterD = emscripten_get_now();
7999 }
8000
8001 static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
8002 {
8003 return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
8004 }
8005 #else
8006 #if _POSIX_C_SOURCE >= 199309L
8007 #if defined(CLOCK_MONOTONIC)
8008 #define MA_CLOCK_ID CLOCK_MONOTONIC
8009 #else
8010 #define MA_CLOCK_ID CLOCK_REALTIME
8011 #endif
8012
8013 static void ma_timer_init(ma_timer* pTimer)
8014 {
8015 struct timespec newTime;
8016 clock_gettime(MA_CLOCK_ID, &newTime);
8017
8018 pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
8019 }
8020
8021 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
8022 {
8023 ma_uint64 newTimeCounter;
8024 ma_uint64 oldTimeCounter;
8025
8026 struct timespec newTime;
8027 clock_gettime(MA_CLOCK_ID, &newTime);
8028
8029 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
8030 oldTimeCounter = pTimer->counter;
8031
8032 return (newTimeCounter - oldTimeCounter) / 1000000000.0;
8033 }
8034 #else
8035 static void ma_timer_init(ma_timer* pTimer)
8036 {
8037 struct timeval newTime;
8038 gettimeofday(&newTime, NULL);
8039
8040 pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
8041 }
8042
8043 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
8044 {
8045 ma_uint64 newTimeCounter;
8046 ma_uint64 oldTimeCounter;
8047
8048 struct timeval newTime;
8049 gettimeofday(&newTime, NULL);
8050
8051 newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
8052 oldTimeCounter = pTimer->counter;
8053
8054 return (newTimeCounter - oldTimeCounter) / 1000000.0;
8055 }
8056 #endif
8057 #endif
8058
8059
8060 /*******************************************************************************
8061
8062 Dynamic Linking
8063
8064 *******************************************************************************/
8065 MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename)
8066 {
8067 ma_handle handle;
8068
8069 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
8070 if (pContext != NULL) {
8071 char message[256];
8072 ma_strappend(message, sizeof(message), "Loading library: ", filename);
8073 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
8074 }
8075 #endif
8076
8077 #ifdef _WIN32
8078 #ifdef MA_WIN32_DESKTOP
8079 handle = (ma_handle)LoadLibraryA(filename);
8080 #else
8081 /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
8082 WCHAR filenameW[4096];
8083 if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
8084 handle = NULL;
8085 } else {
8086 handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
8087 }
8088 #endif
8089 #else
8090 handle = (ma_handle)dlopen(filename, RTLD_NOW);
8091 #endif
8092
8093 /*
8094 I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority
8095 backend is a deliberate design choice. Instead I'm logging it as an informational message.
8096 */
8097 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_INFO
8098 if (handle == NULL) {
8099 char message[256];
8100 ma_strappend(message, sizeof(message), "Failed to load library: ", filename);
8101 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, message);
8102 }
8103 #endif
8104
8105 (void)pContext; /* It's possible for pContext to be unused. */
8106 return handle;
8107 }
8108
8109 MA_API void ma_dlclose(ma_context* pContext, ma_handle handle)
8110 {
8111 #ifdef _WIN32
8112 FreeLibrary((HMODULE)handle);
8113 #else
8114 dlclose((void*)handle);
8115 #endif
8116
8117 (void)pContext;
8118 }
8119
8120 MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol)
8121 {
8122 ma_proc proc;
8123
8124 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
8125 if (pContext != NULL) {
8126 char message[256];
8127 ma_strappend(message, sizeof(message), "Loading symbol: ", symbol);
8128 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
8129 }
8130 #endif
8131
8132 #ifdef _WIN32
8133 proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
8134 #else
8135 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
8136 #pragma GCC diagnostic push
8137 #pragma GCC diagnostic ignored "-Wpedantic"
8138 #endif
8139 proc = (ma_proc)dlsym((void*)handle, symbol);
8140 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
8141 #pragma GCC diagnostic pop
8142 #endif
8143 #endif
8144
8145 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_WARNING
8146 if (handle == NULL) {
8147 char message[256];
8148 ma_strappend(message, sizeof(message), "Failed to load symbol: ", symbol);
8149 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_WARNING, message);
8150 }
8151 #endif
8152
8153 (void)pContext; /* It's possible for pContext to be unused. */
8154 return proc;
8155 }
8156
8157
8158 /*******************************************************************************
8159
8160 Threading
8161
8162 *******************************************************************************/
8163 #ifdef MA_WIN32
8164 static int ma_thread_priority_to_win32(ma_thread_priority priority)
8165 {
8166 switch (priority) {
8167 case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
8168 case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
8169 case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
8170 case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
8171 case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
8172 case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
8173 case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
8174 default: return THREAD_PRIORITY_NORMAL;
8175 }
8176 }
8177
8178 static ma_result ma_thread_create__win32(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
8179 {
8180 pThread->win32.hThread = CreateThread(NULL, 0, entryProc, pData, 0, NULL);
8181 if (pThread->win32.hThread == NULL) {
8182 return ma_result_from_GetLastError(GetLastError());
8183 }
8184
8185 SetThreadPriority((HANDLE)pThread->win32.hThread, ma_thread_priority_to_win32(pContext->threadPriority));
8186
8187 return MA_SUCCESS;
8188 }
8189
8190 static void ma_thread_wait__win32(ma_thread* pThread)
8191 {
8192 WaitForSingleObject(pThread->win32.hThread, INFINITE);
8193 }
8194
8195 static void ma_sleep__win32(ma_uint32 milliseconds)
8196 {
8197 Sleep((DWORD)milliseconds);
8198 }
8199
8200
8201 static ma_result ma_mutex_init__win32(ma_context* pContext, ma_mutex* pMutex)
8202 {
8203 (void)pContext;
8204
8205 pMutex->win32.hMutex = CreateEventW(NULL, FALSE, TRUE, NULL);
8206 if (pMutex->win32.hMutex == NULL) {
8207 return ma_result_from_GetLastError(GetLastError());
8208 }
8209
8210 return MA_SUCCESS;
8211 }
8212
8213 static void ma_mutex_uninit__win32(ma_mutex* pMutex)
8214 {
8215 CloseHandle(pMutex->win32.hMutex);
8216 }
8217
8218 static void ma_mutex_lock__win32(ma_mutex* pMutex)
8219 {
8220 WaitForSingleObject(pMutex->win32.hMutex, INFINITE);
8221 }
8222
8223 static void ma_mutex_unlock__win32(ma_mutex* pMutex)
8224 {
8225 SetEvent(pMutex->win32.hMutex);
8226 }
8227
8228
8229 static ma_result ma_event_init__win32(ma_context* pContext, ma_event* pEvent)
8230 {
8231 (void)pContext;
8232
8233 pEvent->win32.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
8234 if (pEvent->win32.hEvent == NULL) {
8235 return ma_result_from_GetLastError(GetLastError());
8236 }
8237
8238 return MA_SUCCESS;
8239 }
8240
8241 static void ma_event_uninit__win32(ma_event* pEvent)
8242 {
8243 CloseHandle(pEvent->win32.hEvent);
8244 }
8245
8246 static ma_bool32 ma_event_wait__win32(ma_event* pEvent)
8247 {
8248 return WaitForSingleObject(pEvent->win32.hEvent, INFINITE) == WAIT_OBJECT_0;
8249 }
8250
8251 static ma_bool32 ma_event_signal__win32(ma_event* pEvent)
8252 {
8253 return SetEvent(pEvent->win32.hEvent);
8254 }
8255
8256
8257 static ma_result ma_semaphore_init__win32(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore)
8258 {
8259 (void)pContext;
8260
8261 pSemaphore->win32.hSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL);
8262 if (pSemaphore->win32.hSemaphore == NULL) {
8263 return ma_result_from_GetLastError(GetLastError());
8264 }
8265
8266 return MA_SUCCESS;
8267 }
8268
8269 static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)
8270 {
8271 CloseHandle((HANDLE)pSemaphore->win32.hSemaphore);
8272 }
8273
8274 static ma_bool32 ma_semaphore_wait__win32(ma_semaphore* pSemaphore)
8275 {
8276 return WaitForSingleObject((HANDLE)pSemaphore->win32.hSemaphore, INFINITE) == WAIT_OBJECT_0;
8277 }
8278
8279 static ma_bool32 ma_semaphore_release__win32(ma_semaphore* pSemaphore)
8280 {
8281 return ReleaseSemaphore((HANDLE)pSemaphore->win32.hSemaphore, 1, NULL) != 0;
8282 }
8283 #endif
8284
8285
8286 #ifdef MA_POSIX
8287 #include <sched.h>
8288
8289 typedef int (* ma_pthread_create_proc)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
8290 typedef int (* ma_pthread_join_proc)(pthread_t thread, void **retval);
8291 typedef int (* ma_pthread_mutex_init_proc)(pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr);
8292 typedef int (* ma_pthread_mutex_destroy_proc)(pthread_mutex_t *__mutex);
8293 typedef int (* ma_pthread_mutex_lock_proc)(pthread_mutex_t *__mutex);
8294 typedef int (* ma_pthread_mutex_unlock_proc)(pthread_mutex_t *__mutex);
8295 typedef int (* ma_pthread_cond_init_proc)(pthread_cond_t *__restrict __cond, const pthread_condattr_t *__restrict __cond_attr);
8296 typedef int (* ma_pthread_cond_destroy_proc)(pthread_cond_t *__cond);
8297 typedef int (* ma_pthread_cond_signal_proc)(pthread_cond_t *__cond);
8298 typedef int (* ma_pthread_cond_wait_proc)(pthread_cond_t *__restrict __cond, pthread_mutex_t *__restrict __mutex);
8299 typedef int (* ma_pthread_attr_init_proc)(pthread_attr_t *attr);
8300 typedef int (* ma_pthread_attr_destroy_proc)(pthread_attr_t *attr);
8301 typedef int (* ma_pthread_attr_setschedpolicy_proc)(pthread_attr_t *attr, int policy);
8302 typedef int (* ma_pthread_attr_getschedparam_proc)(const pthread_attr_t *attr, struct sched_param *param);
8303 typedef int (* ma_pthread_attr_setschedparam_proc)(pthread_attr_t *attr, const struct sched_param *param);
8304
8305 static ma_result ma_thread_create__posix(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
8306 {
8307 int result;
8308 pthread_attr_t* pAttr = NULL;
8309
8310 #if !defined(__EMSCRIPTEN__)
8311 /* Try setting the thread priority. It's not critical if anything fails here. */
8312 pthread_attr_t attr;
8313 if (((ma_pthread_attr_init_proc)pContext->posix.pthread_attr_init)(&attr) == 0) {
8314 int scheduler = -1;
8315 if (pContext->threadPriority == ma_thread_priority_idle) {
8316 #ifdef SCHED_IDLE
8317 if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_IDLE) == 0) {
8318 scheduler = SCHED_IDLE;
8319 }
8320 #endif
8321 } else if (pContext->threadPriority == ma_thread_priority_realtime) {
8322 #ifdef SCHED_FIFO
8323 if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_FIFO) == 0) {
8324 scheduler = SCHED_FIFO;
8325 }
8326 #endif
8327 #ifdef MA_LINUX
8328 } else {
8329 scheduler = sched_getscheduler(0);
8330 #endif
8331 }
8332
8333 if (scheduler != -1) {
8334 int priorityMin = sched_get_priority_min(scheduler);
8335 int priorityMax = sched_get_priority_max(scheduler);
8336 int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
8337
8338 struct sched_param sched;
8339 if (((ma_pthread_attr_getschedparam_proc)pContext->posix.pthread_attr_getschedparam)(&attr, &sched) == 0) {
8340 if (pContext->threadPriority == ma_thread_priority_idle) {
8341 sched.sched_priority = priorityMin;
8342 } else if (pContext->threadPriority == ma_thread_priority_realtime) {
8343 sched.sched_priority = priorityMax;
8344 } else {
8345 sched.sched_priority += ((int)pContext->threadPriority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
8346 if (sched.sched_priority < priorityMin) {
8347 sched.sched_priority = priorityMin;
8348 }
8349 if (sched.sched_priority > priorityMax) {
8350 sched.sched_priority = priorityMax;
8351 }
8352 }
8353
8354 if (((ma_pthread_attr_setschedparam_proc)pContext->posix.pthread_attr_setschedparam)(&attr, &sched) == 0) {
8355 pAttr = &attr;
8356 }
8357 }
8358 }
8359
8360 ((ma_pthread_attr_destroy_proc)pContext->posix.pthread_attr_destroy)(&attr);
8361 }
8362 #endif
8363
8364 result = ((ma_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, pAttr, entryProc, pData);
8365 if (result != 0) {
8366 return ma_result_from_errno(result);
8367 }
8368
8369 return MA_SUCCESS;
8370 }
8371
8372 static void ma_thread_wait__posix(ma_thread* pThread)
8373 {
8374 ((ma_pthread_join_proc)pThread->pContext->posix.pthread_join)(pThread->posix.thread, NULL);
8375 }
8376
8377 #if !defined(MA_EMSCRIPTEN)
8378 static void ma_sleep__posix(ma_uint32 milliseconds)
8379 {
8380 #ifdef MA_EMSCRIPTEN
8381 (void)milliseconds;
8382 MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */
8383 #else
8384 #if _POSIX_C_SOURCE >= 199309L
8385 struct timespec ts;
8386 ts.tv_sec = milliseconds / 1000000;
8387 ts.tv_nsec = milliseconds % 1000000 * 1000000;
8388 nanosleep(&ts, NULL);
8389 #else
8390 struct timeval tv;
8391 tv.tv_sec = milliseconds / 1000;
8392 tv.tv_usec = milliseconds % 1000 * 1000;
8393 select(0, NULL, NULL, NULL, &tv);
8394 #endif
8395 #endif
8396 }
8397 #endif /* MA_EMSCRIPTEN */
8398
8399
8400 static ma_result ma_mutex_init__posix(ma_context* pContext, ma_mutex* pMutex)
8401 {
8402 int result = ((ma_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pMutex->posix.mutex, NULL);
8403 if (result != 0) {
8404 return ma_result_from_errno(result);
8405 }
8406
8407 return MA_SUCCESS;
8408 }
8409
8410 static void ma_mutex_uninit__posix(ma_mutex* pMutex)
8411 {
8412 ((ma_pthread_mutex_destroy_proc)pMutex->pContext->posix.pthread_mutex_destroy)(&pMutex->posix.mutex);
8413 }
8414
8415 static void ma_mutex_lock__posix(ma_mutex* pMutex)
8416 {
8417 ((ma_pthread_mutex_lock_proc)pMutex->pContext->posix.pthread_mutex_lock)(&pMutex->posix.mutex);
8418 }
8419
8420 static void ma_mutex_unlock__posix(ma_mutex* pMutex)
8421 {
8422 ((ma_pthread_mutex_unlock_proc)pMutex->pContext->posix.pthread_mutex_unlock)(&pMutex->posix.mutex);
8423 }
8424
8425
8426 static ma_result ma_event_init__posix(ma_context* pContext, ma_event* pEvent)
8427 {
8428 int result;
8429
8430 result = ((ma_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pEvent->posix.mutex, NULL);
8431 if (result != 0) {
8432 return ma_result_from_errno(result);
8433 }
8434
8435 result = ((ma_pthread_cond_init_proc)pContext->posix.pthread_cond_init)(&pEvent->posix.condition, NULL);
8436 if (result != 0) {
8437 ((ma_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex);
8438 return ma_result_from_errno(result);
8439 }
8440
8441 pEvent->posix.value = 0;
8442 return MA_SUCCESS;
8443 }
8444
8445 static void ma_event_uninit__posix(ma_event* pEvent)
8446 {
8447 ((ma_pthread_cond_destroy_proc)pEvent->pContext->posix.pthread_cond_destroy)(&pEvent->posix.condition);
8448 ((ma_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex);
8449 }
8450
8451 static ma_bool32 ma_event_wait__posix(ma_event* pEvent)
8452 {
8453 ((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
8454 {
8455 while (pEvent->posix.value == 0) {
8456 ((ma_pthread_cond_wait_proc)pEvent->pContext->posix.pthread_cond_wait)(&pEvent->posix.condition, &pEvent->posix.mutex);
8457 }
8458 pEvent->posix.value = 0; /* Auto-reset. */
8459 }
8460 ((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
8461
8462 return MA_TRUE;
8463 }
8464
8465 static ma_bool32 ma_event_signal__posix(ma_event* pEvent)
8466 {
8467 ((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
8468 {
8469 pEvent->posix.value = 1;
8470 ((ma_pthread_cond_signal_proc)pEvent->pContext->posix.pthread_cond_signal)(&pEvent->posix.condition);
8471 }
8472 ((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
8473
8474 return MA_TRUE;
8475 }
8476
8477
8478 static ma_result ma_semaphore_init__posix(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore)
8479 {
8480 (void)pContext;
8481
8482 #if defined(MA_APPLE)
8483 /* Not yet implemented for Apple platforms since sem_init() is deprecated. Need to use a named semaphore via sem_open() instead. */
8484 (void)initialValue;
8485 (void)pSemaphore;
8486 return MA_INVALID_OPERATION;
8487 #else
8488 if (sem_init(&pSemaphore->posix.semaphore, 0, (unsigned int)initialValue) == 0) {
8489 return ma_result_from_errno(errno);
8490 }
8491 #endif
8492
8493 return MA_SUCCESS;
8494 }
8495
8496 static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)
8497 {
8498 sem_close(&pSemaphore->posix.semaphore);
8499 }
8500
8501 static ma_bool32 ma_semaphore_wait__posix(ma_semaphore* pSemaphore)
8502 {
8503 return sem_wait(&pSemaphore->posix.semaphore) != -1;
8504 }
8505
8506 static ma_bool32 ma_semaphore_release__posix(ma_semaphore* pSemaphore)
8507 {
8508 return sem_post(&pSemaphore->posix.semaphore) != -1;
8509 }
8510 #endif
8511
8512 static ma_result ma_thread_create(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
8513 {
8514 if (pContext == NULL || pThread == NULL || entryProc == NULL) {
8515 return MA_FALSE;
8516 }
8517
8518 pThread->pContext = pContext;
8519
8520 #ifdef MA_WIN32
8521 return ma_thread_create__win32(pContext, pThread, entryProc, pData);
8522 #endif
8523 #ifdef MA_POSIX
8524 return ma_thread_create__posix(pContext, pThread, entryProc, pData);
8525 #endif
8526 }
8527
8528 static void ma_thread_wait(ma_thread* pThread)
8529 {
8530 if (pThread == NULL) {
8531 return;
8532 }
8533
8534 #ifdef MA_WIN32
8535 ma_thread_wait__win32(pThread);
8536 #endif
8537 #ifdef MA_POSIX
8538 ma_thread_wait__posix(pThread);
8539 #endif
8540 }
8541
8542 #if !defined(MA_EMSCRIPTEN)
8543 static void ma_sleep(ma_uint32 milliseconds)
8544 {
8545 #ifdef MA_WIN32
8546 ma_sleep__win32(milliseconds);
8547 #endif
8548 #ifdef MA_POSIX
8549 ma_sleep__posix(milliseconds);
8550 #endif
8551 }
8552 #endif
8553
8554
8555 MA_API ma_result ma_mutex_init(ma_context* pContext, ma_mutex* pMutex)
8556 {
8557 if (pContext == NULL || pMutex == NULL) {
8558 return MA_INVALID_ARGS;
8559 }
8560
8561 pMutex->pContext = pContext;
8562
8563 #ifdef MA_WIN32
8564 return ma_mutex_init__win32(pContext, pMutex);
8565 #endif
8566 #ifdef MA_POSIX
8567 return ma_mutex_init__posix(pContext, pMutex);
8568 #endif
8569 }
8570
8571 MA_API void ma_mutex_uninit(ma_mutex* pMutex)
8572 {
8573 if (pMutex == NULL || pMutex->pContext == NULL) {
8574 return;
8575 }
8576
8577 #ifdef MA_WIN32
8578 ma_mutex_uninit__win32(pMutex);
8579 #endif
8580 #ifdef MA_POSIX
8581 ma_mutex_uninit__posix(pMutex);
8582 #endif
8583 }
8584
8585 MA_API void ma_mutex_lock(ma_mutex* pMutex)
8586 {
8587 if (pMutex == NULL || pMutex->pContext == NULL) {
8588 return;
8589 }
8590
8591 #ifdef MA_WIN32
8592 ma_mutex_lock__win32(pMutex);
8593 #endif
8594 #ifdef MA_POSIX
8595 ma_mutex_lock__posix(pMutex);
8596 #endif
8597 }
8598
8599 MA_API void ma_mutex_unlock(ma_mutex* pMutex)
8600 {
8601 if (pMutex == NULL || pMutex->pContext == NULL) {
8602 return;
8603 }
8604
8605 #ifdef MA_WIN32
8606 ma_mutex_unlock__win32(pMutex);
8607 #endif
8608 #ifdef MA_POSIX
8609 ma_mutex_unlock__posix(pMutex);
8610 #endif
8611 }
8612
8613
8614 MA_API ma_result ma_event_init(ma_context* pContext, ma_event* pEvent)
8615 {
8616 if (pContext == NULL || pEvent == NULL) {
8617 return MA_FALSE;
8618 }
8619
8620 pEvent->pContext = pContext;
8621
8622 #ifdef MA_WIN32
8623 return ma_event_init__win32(pContext, pEvent);
8624 #endif
8625 #ifdef MA_POSIX
8626 return ma_event_init__posix(pContext, pEvent);
8627 #endif
8628 }
8629
8630 MA_API void ma_event_uninit(ma_event* pEvent)
8631 {
8632 if (pEvent == NULL || pEvent->pContext == NULL) {
8633 return;
8634 }
8635
8636 #ifdef MA_WIN32
8637 ma_event_uninit__win32(pEvent);
8638 #endif
8639 #ifdef MA_POSIX
8640 ma_event_uninit__posix(pEvent);
8641 #endif
8642 }
8643
8644 MA_API ma_bool32 ma_event_wait(ma_event* pEvent)
8645 {
8646 if (pEvent == NULL || pEvent->pContext == NULL) {
8647 return MA_FALSE;
8648 }
8649
8650 #ifdef MA_WIN32
8651 return ma_event_wait__win32(pEvent);
8652 #endif
8653 #ifdef MA_POSIX
8654 return ma_event_wait__posix(pEvent);
8655 #endif
8656 }
8657
8658 MA_API ma_bool32 ma_event_signal(ma_event* pEvent)
8659 {
8660 if (pEvent == NULL || pEvent->pContext == NULL) {
8661 return MA_FALSE;
8662 }
8663
8664 #ifdef MA_WIN32
8665 return ma_event_signal__win32(pEvent);
8666 #endif
8667 #ifdef MA_POSIX
8668 return ma_event_signal__posix(pEvent);
8669 #endif
8670 }
8671
8672
8673 MA_API ma_result ma_semaphore_init(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore)
8674 {
8675 if (pContext == NULL || pSemaphore == NULL) {
8676 return MA_INVALID_ARGS;
8677 }
8678
8679 #ifdef MA_WIN32
8680 return ma_semaphore_init__win32(pContext, initialValue, pSemaphore);
8681 #endif
8682 #ifdef MA_POSIX
8683 return ma_semaphore_init__posix(pContext, initialValue, pSemaphore);
8684 #endif
8685 }
8686
8687 MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)
8688 {
8689 if (pSemaphore == NULL) {
8690 return;
8691 }
8692
8693 #ifdef MA_WIN32
8694 ma_semaphore_uninit__win32(pSemaphore);
8695 #endif
8696 #ifdef MA_POSIX
8697 ma_semaphore_uninit__posix(pSemaphore);
8698 #endif
8699 }
8700
8701 MA_API ma_bool32 ma_semaphore_wait(ma_semaphore* pSemaphore)
8702 {
8703 if (pSemaphore == NULL) {
8704 return MA_FALSE;
8705 }
8706
8707 #ifdef MA_WIN32
8708 return ma_semaphore_wait__win32(pSemaphore);
8709 #endif
8710 #ifdef MA_POSIX
8711 return ma_semaphore_wait__posix(pSemaphore);
8712 #endif
8713 }
8714
8715 MA_API ma_bool32 ma_semaphore_release(ma_semaphore* pSemaphore)
8716 {
8717 if (pSemaphore == NULL) {
8718 return MA_FALSE;
8719 }
8720
8721 #ifdef MA_WIN32
8722 return ma_semaphore_release__win32(pSemaphore);
8723 #endif
8724 #ifdef MA_POSIX
8725 return ma_semaphore_release__posix(pSemaphore);
8726 #endif
8727 }
8728
8729
8730 #if 0
8731 static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
8732 {
8733 ma_uint32 closestRate = 0;
8734 ma_uint32 closestDiff = 0xFFFFFFFF;
8735 size_t iStandardRate;
8736
8737 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
8738 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
8739 ma_uint32 diff;
8740
8741 if (sampleRateIn > standardRate) {
8742 diff = sampleRateIn - standardRate;
8743 } else {
8744 diff = standardRate - sampleRateIn;
8745 }
8746
8747 if (diff == 0) {
8748 return standardRate; /* The input sample rate is a standard rate. */
8749 }
8750
8751 if (closestDiff > diff) {
8752 closestDiff = diff;
8753 closestRate = standardRate;
8754 }
8755 }
8756
8757 return closestRate;
8758 }
8759 #endif
8760
8761 MA_API ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale)
8762 {
8763 return ma_max(1, (ma_uint32)(baseBufferSize*scale));
8764 }
8765
8766 MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
8767 {
8768 return bufferSizeInFrames / (sampleRate/1000);
8769 }
8770
8771 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
8772 {
8773 return bufferSizeInMilliseconds * (sampleRate/1000);
8774 }
8775
8776 MA_API void ma_zero_pcm_frames(void* p, ma_uint32 frameCount, ma_format format, ma_uint32 channels)
8777 {
8778 MA_ZERO_MEMORY(p, frameCount * ma_get_bytes_per_frame(format, channels));
8779 }
8780
8781 MA_API void ma_clip_samples_f32(float* p, ma_uint32 sampleCount)
8782 {
8783 ma_uint32 iSample;
8784
8785 /* TODO: Research a branchless SSE implementation. */
8786 for (iSample = 0; iSample < sampleCount; iSample += 1) {
8787 p[iSample] = ma_clip_f32(p[iSample]);
8788 }
8789 }
8790
8791
8792 MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint32 sampleCount, float factor)
8793 {
8794 ma_uint32 iSample;
8795
8796 if (pSamplesOut == NULL || pSamplesIn == NULL) {
8797 return;
8798 }
8799
8800 for (iSample = 0; iSample < sampleCount; iSample += 1) {
8801 pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);
8802 }
8803 }
8804
8805 MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint32 sampleCount, float factor)
8806 {
8807 ma_uint32 iSample;
8808
8809 if (pSamplesOut == NULL || pSamplesIn == NULL) {
8810 return;
8811 }
8812
8813 for (iSample = 0; iSample < sampleCount; iSample += 1) {
8814 pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);
8815 }
8816 }
8817
8818 MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint32 sampleCount, float factor)
8819 {
8820 ma_uint32 iSample;
8821 ma_uint8* pSamplesOut8;
8822 ma_uint8* pSamplesIn8;
8823
8824 if (pSamplesOut == NULL || pSamplesIn == NULL) {
8825 return;
8826 }
8827
8828 pSamplesOut8 = (ma_uint8*)pSamplesOut;
8829 pSamplesIn8 = (ma_uint8*)pSamplesIn;
8830
8831 for (iSample = 0; iSample < sampleCount; iSample += 1) {
8832 ma_int32 sampleS32;
8833
8834 sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);
8835 sampleS32 = (ma_int32)(sampleS32 * factor);
8836
8837 pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8);
8838 pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);
8839 pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);
8840 }
8841 }
8842
8843 MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint32 sampleCount, float factor)
8844 {
8845 ma_uint32 iSample;
8846
8847 if (pSamplesOut == NULL || pSamplesIn == NULL) {
8848 return;
8849 }
8850
8851 for (iSample = 0; iSample < sampleCount; iSample += 1) {
8852 pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);
8853 }
8854 }
8855
8856 MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint32 sampleCount, float factor)
8857 {
8858 ma_uint32 iSample;
8859
8860 if (pSamplesOut == NULL || pSamplesIn == NULL) {
8861 return;
8862 }
8863
8864 for (iSample = 0; iSample < sampleCount; iSample += 1) {
8865 pSamplesOut[iSample] = pSamplesIn[iSample] * factor;
8866 }
8867 }
8868
8869 MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint32 sampleCount, float factor)
8870 {
8871 ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
8872 }
8873
8874 MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint32 sampleCount, float factor)
8875 {
8876 ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
8877 }
8878
8879 MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint32 sampleCount, float factor)
8880 {
8881 ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
8882 }
8883
8884 MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint32 sampleCount, float factor)
8885 {
8886 ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
8887 }
8888
8889 MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint32 sampleCount, float factor)
8890 {
8891 ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
8892 }
8893
8894 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
8895 {
8896 ma_copy_and_apply_volume_factor_u8(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
8897 }
8898
8899 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
8900 {
8901 ma_copy_and_apply_volume_factor_s16(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
8902 }
8903
8904 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
8905 {
8906 ma_copy_and_apply_volume_factor_s24(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
8907 }
8908
8909 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
8910 {
8911 ma_copy_and_apply_volume_factor_s32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
8912 }
8913
8914 MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
8915 {
8916 ma_copy_and_apply_volume_factor_f32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
8917 }
8918
8919 MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor)
8920 {
8921 switch (format)
8922 {
8923 case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pPCMFramesOut, (const ma_uint8*)pPCMFramesIn, frameCount, channels, factor); return;
8924 case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pPCMFramesOut, (const ma_int16*)pPCMFramesIn, frameCount, channels, factor); return;
8925 case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pPCMFramesOut, pPCMFramesIn, frameCount, channels, factor); return;
8926 case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pPCMFramesOut, (const ma_int32*)pPCMFramesIn, frameCount, channels, factor); return;
8927 case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pPCMFramesOut, (const float*)pPCMFramesIn, frameCount, channels, factor); return;
8928 default: return; /* Do nothing. */
8929 }
8930 }
8931
8932 MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
8933 {
8934 ma_copy_and_apply_volume_factor_pcm_frames_u8(pPCMFrames, pPCMFrames, frameCount, channels, factor);
8935 }
8936
8937 MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
8938 {
8939 ma_copy_and_apply_volume_factor_pcm_frames_s16(pPCMFrames, pPCMFrames, frameCount, channels, factor);
8940 }
8941
8942 MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
8943 {
8944 ma_copy_and_apply_volume_factor_pcm_frames_s24(pPCMFrames, pPCMFrames, frameCount, channels, factor);
8945 }
8946
8947 MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
8948 {
8949 ma_copy_and_apply_volume_factor_pcm_frames_s32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
8950 }
8951
8952 MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
8953 {
8954 ma_copy_and_apply_volume_factor_pcm_frames_f32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
8955 }
8956
8957 MA_API void ma_apply_volume_factor_pcm_frames(void* pPCMFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor)
8958 {
8959 ma_copy_and_apply_volume_factor_pcm_frames(pPCMFrames, pPCMFrames, frameCount, format, channels, factor);
8960 }
8961
8962
8963 MA_API float ma_factor_to_gain_db(float factor)
8964 {
8965 return (float)(20*ma_log10f(factor));
8966 }
8967
8968 MA_API float ma_gain_db_to_factor(float gain)
8969 {
8970 return (float)ma_powf(10, gain/20.0f);
8971 }
8972
8973
8974 static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
8975 {
8976 float masterVolumeFactor;
8977
8978 masterVolumeFactor = pDevice->masterVolumeFactor;
8979
8980 if (pDevice->onData) {
8981 if (!pDevice->noPreZeroedOutputBuffer && pFramesOut != NULL) {
8982 ma_zero_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
8983 }
8984
8985 /* 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. */
8986 if (pFramesIn != NULL && masterVolumeFactor < 1) {
8987 ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
8988 ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
8989 ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
8990 ma_uint32 totalFramesProcessed = 0;
8991 while (totalFramesProcessed < frameCount) {
8992 ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed;
8993 if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) {
8994 framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture;
8995 }
8996
8997 ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor);
8998
8999 pDevice->onData(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration);
9000
9001 totalFramesProcessed += framesToProcessThisIteration;
9002 }
9003 } else {
9004 pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount);
9005 }
9006
9007 /* Volume control and clipping for playback devices. */
9008 if (pFramesOut != NULL) {
9009 if (masterVolumeFactor < 1) {
9010 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. */
9011 ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor);
9012 }
9013 }
9014
9015 if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) {
9016 ma_clip_pcm_frames_f32((float*)pFramesOut, frameCount, pDevice->playback.channels);
9017 }
9018 }
9019 }
9020 }
9021
9022
9023
9024 /* A helper function for reading sample data from the client. */
9025 static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)
9026 {
9027 MA_ASSERT(pDevice != NULL);
9028 MA_ASSERT(frameCount > 0);
9029 MA_ASSERT(pFramesOut != NULL);
9030
9031 if (pDevice->playback.converter.isPassthrough) {
9032 ma_device__on_data(pDevice, pFramesOut, NULL, frameCount);
9033 } else {
9034 ma_result result;
9035 ma_uint64 totalFramesReadOut;
9036 ma_uint64 totalFramesReadIn;
9037 void* pRunningFramesOut;
9038
9039 totalFramesReadOut = 0;
9040 totalFramesReadIn = 0;
9041 pRunningFramesOut = pFramesOut;
9042
9043 while (totalFramesReadOut < frameCount) {
9044 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */
9045 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
9046 ma_uint64 framesToReadThisIterationIn;
9047 ma_uint64 framesReadThisIterationIn;
9048 ma_uint64 framesToReadThisIterationOut;
9049 ma_uint64 framesReadThisIterationOut;
9050 ma_uint64 requiredInputFrameCount;
9051
9052 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
9053 framesToReadThisIterationIn = framesToReadThisIterationOut;
9054 if (framesToReadThisIterationIn > intermediaryBufferCap) {
9055 framesToReadThisIterationIn = intermediaryBufferCap;
9056 }
9057
9058 requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut);
9059 if (framesToReadThisIterationIn > requiredInputFrameCount) {
9060 framesToReadThisIterationIn = requiredInputFrameCount;
9061 }
9062
9063 if (framesToReadThisIterationIn > 0) {
9064 ma_device__on_data(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
9065 totalFramesReadIn += framesToReadThisIterationIn;
9066 }
9067
9068 /*
9069 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
9070 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
9071 */
9072 framesReadThisIterationIn = framesToReadThisIterationIn;
9073 framesReadThisIterationOut = framesToReadThisIterationOut;
9074 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
9075 if (result != MA_SUCCESS) {
9076 break;
9077 }
9078
9079 totalFramesReadOut += framesReadThisIterationOut;
9080 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
9081
9082 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
9083 break; /* We're done. */
9084 }
9085 }
9086 }
9087 }
9088
9089 /* A helper for sending sample data to the client. */
9090 static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat)
9091 {
9092 MA_ASSERT(pDevice != NULL);
9093 MA_ASSERT(frameCountInDeviceFormat > 0);
9094 MA_ASSERT(pFramesInDeviceFormat != NULL);
9095
9096 if (pDevice->capture.converter.isPassthrough) {
9097 ma_device__on_data(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat);
9098 } else {
9099 ma_result result;
9100 ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
9101 ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
9102 ma_uint64 totalDeviceFramesProcessed = 0;
9103 ma_uint64 totalClientFramesProcessed = 0;
9104 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
9105
9106 /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */
9107 for (;;) {
9108 ma_uint64 deviceFramesProcessedThisIteration;
9109 ma_uint64 clientFramesProcessedThisIteration;
9110
9111 deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
9112 clientFramesProcessedThisIteration = framesInClientFormatCap;
9113
9114 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration);
9115 if (result != MA_SUCCESS) {
9116 break;
9117 }
9118
9119 if (clientFramesProcessedThisIteration > 0) {
9120 ma_device__on_data(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */
9121 }
9122
9123 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
9124 totalDeviceFramesProcessed += deviceFramesProcessedThisIteration;
9125 totalClientFramesProcessed += clientFramesProcessedThisIteration;
9126
9127 if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) {
9128 break; /* We're done. */
9129 }
9130 }
9131 }
9132 }
9133
9134
9135 /* 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. */
9136 #if defined(MA_HAS_JACK) || \
9137 defined(MA_HAS_COREAUDIO) || \
9138 defined(MA_HAS_AAUDIO) || \
9139 defined(MA_HAS_OPENSL) || \
9140 defined(MA_HAS_WEBAUDIO)
9141 static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB)
9142 {
9143 ma_result result;
9144 ma_uint32 totalDeviceFramesProcessed = 0;
9145 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
9146
9147 MA_ASSERT(pDevice != NULL);
9148 MA_ASSERT(frameCountInDeviceFormat > 0);
9149 MA_ASSERT(pFramesInDeviceFormat != NULL);
9150 MA_ASSERT(pRB != NULL);
9151
9152 /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */
9153 for (;;) {
9154 ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
9155 ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
9156 ma_uint64 framesProcessedInDeviceFormat;
9157 ma_uint64 framesProcessedInClientFormat;
9158 void* pFramesInClientFormat;
9159
9160 result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat);
9161 if (result != MA_SUCCESS) {
9162 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.", result);
9163 break;
9164 }
9165
9166 if (framesToProcessInClientFormat == 0) {
9167 if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) {
9168 break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
9169 }
9170 }
9171
9172 /* Convert. */
9173 framesProcessedInDeviceFormat = framesToProcessInDeviceFormat;
9174 framesProcessedInClientFormat = framesToProcessInClientFormat;
9175 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat);
9176 if (result != MA_SUCCESS) {
9177 break;
9178 }
9179
9180 result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInDeviceFormat, pFramesInClientFormat); /* Safe cast. */
9181 if (result != MA_SUCCESS) {
9182 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.", result);
9183 break;
9184 }
9185
9186 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
9187 totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */
9188
9189 /* We're done when we're unable to process any client nor device frames. */
9190 if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) {
9191 break; /* Done. */
9192 }
9193 }
9194
9195 return MA_SUCCESS;
9196 }
9197
9198 static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
9199 {
9200 ma_result result;
9201 ma_uint8 playbackFramesInExternalFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
9202 ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
9203 ma_uint32 totalFramesToReadFromClient;
9204 ma_uint32 totalFramesReadFromClient;
9205 ma_uint32 totalFramesReadOut = 0;
9206
9207 MA_ASSERT(pDevice != NULL);
9208 MA_ASSERT(frameCount > 0);
9209 MA_ASSERT(pFramesInInternalFormat != NULL);
9210 MA_ASSERT(pRB != NULL);
9211
9212 /*
9213 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
9214 the whole frameCount frames we just use silence instead for the input data.
9215 */
9216 MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames));
9217
9218 /* We need to calculate how many output frames are required to be read from the client to completely fill frameCount internal frames. */
9219 totalFramesToReadFromClient = (ma_uint32)ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, frameCount);
9220 totalFramesReadFromClient = 0;
9221 while (totalFramesReadFromClient < totalFramesToReadFromClient && ma_device_is_started(pDevice)) {
9222 ma_uint32 framesRemainingFromClient;
9223 ma_uint32 framesToProcessFromClient;
9224 ma_uint32 inputFrameCount;
9225 void* pInputFrames;
9226
9227 framesRemainingFromClient = (totalFramesToReadFromClient - totalFramesReadFromClient);
9228 framesToProcessFromClient = sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
9229 if (framesToProcessFromClient > framesRemainingFromClient) {
9230 framesToProcessFromClient = framesRemainingFromClient;
9231 }
9232
9233 /* We need to grab captured samples before firing the callback. If there's not enough input samples we just pass silence. */
9234 inputFrameCount = framesToProcessFromClient;
9235 result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
9236 if (result == MA_SUCCESS) {
9237 if (inputFrameCount > 0) {
9238 /* Use actual input frames. */
9239 ma_device__on_data(pDevice, playbackFramesInExternalFormat, pInputFrames, inputFrameCount);
9240 } else {
9241 if (ma_pcm_rb_pointer_distance(pRB) == 0) {
9242 break; /* Underrun. */
9243 }
9244 }
9245
9246 /* We're done with the captured samples. */
9247 result = ma_pcm_rb_commit_read(pRB, inputFrameCount, pInputFrames);
9248 if (result != MA_SUCCESS) {
9249 break; /* Don't know what to do here... Just abandon ship. */
9250 }
9251 } else {
9252 /* Use silent input frames. */
9253 inputFrameCount = ma_min(
9254 sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels),
9255 sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)
9256 );
9257
9258 ma_device__on_data(pDevice, playbackFramesInExternalFormat, silentInputFrames, inputFrameCount);
9259 }
9260
9261 /* We have samples in external format so now we need to convert to internal format and output to the device. */
9262 {
9263 ma_uint64 framesConvertedIn = inputFrameCount;
9264 ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut);
9265 ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackFramesInExternalFormat, &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut);
9266
9267 totalFramesReadFromClient += (ma_uint32)framesConvertedIn; /* Safe cast. */
9268 totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */
9269 pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
9270 }
9271 }
9272
9273 return MA_SUCCESS;
9274 }
9275 #endif /* Asynchronous backends. */
9276
9277 /* A helper for changing the state of the device. */
9278 static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_uint32 newState)
9279 {
9280 ma_atomic_exchange_32(&pDevice->state, newState);
9281 }
9282
9283 /* A helper for getting the state of the device. */
9284 static MA_INLINE ma_uint32 ma_device__get_state(ma_device* pDevice)
9285 {
9286 return pDevice->state;
9287 }
9288
9289
9290 #ifdef MA_WIN32
9291 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
9292 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
9293 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
9294 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
9295 #endif
9296
9297
9298 typedef struct
9299 {
9300 ma_device_type deviceType;
9301 const ma_device_id* pDeviceID;
9302 char* pName;
9303 size_t nameBufferSize;
9304 ma_bool32 foundDevice;
9305 } ma_context__try_get_device_name_by_id__enum_callback_data;
9306
9307 static 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)
9308 {
9309 ma_context__try_get_device_name_by_id__enum_callback_data* pData = (ma_context__try_get_device_name_by_id__enum_callback_data*)pUserData;
9310 MA_ASSERT(pData != NULL);
9311
9312 if (pData->deviceType == deviceType) {
9313 if (pContext->onDeviceIDEqual(pContext, pData->pDeviceID, &pDeviceInfo->id)) {
9314 ma_strncpy_s(pData->pName, pData->nameBufferSize, pDeviceInfo->name, (size_t)-1);
9315 pData->foundDevice = MA_TRUE;
9316 }
9317 }
9318
9319 return !pData->foundDevice;
9320 }
9321
9322 /*
9323 Generic function for retrieving the name of a device by it's ID.
9324
9325 This function simply enumerates every device and then retrieves the name of the first device that has the same ID.
9326 */
9327 static 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)
9328 {
9329 ma_result result;
9330 ma_context__try_get_device_name_by_id__enum_callback_data data;
9331
9332 MA_ASSERT(pContext != NULL);
9333 MA_ASSERT(pName != NULL);
9334
9335 if (pDeviceID == NULL) {
9336 return MA_NO_DEVICE;
9337 }
9338
9339 data.deviceType = deviceType;
9340 data.pDeviceID = pDeviceID;
9341 data.pName = pName;
9342 data.nameBufferSize = nameBufferSize;
9343 data.foundDevice = MA_FALSE;
9344 result = ma_context_enumerate_devices(pContext, ma_context__try_get_device_name_by_id__enum_callback, &data);
9345 if (result != MA_SUCCESS) {
9346 return result;
9347 }
9348
9349 if (!data.foundDevice) {
9350 return MA_NO_DEVICE;
9351 } else {
9352 return MA_SUCCESS;
9353 }
9354 }
9355
9356
9357 MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */
9358 {
9359 ma_uint32 i;
9360 for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
9361 if (g_maFormatPriorities[i] == format) {
9362 return i;
9363 }
9364 }
9365
9366 /* Getting here means the format could not be found or is equal to ma_format_unknown. */
9367 return (ma_uint32)-1;
9368 }
9369
9370 static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
9371
9372
9373 /*******************************************************************************
9374
9375 Null Backend
9376
9377 *******************************************************************************/
9378 #ifdef MA_HAS_NULL
9379
9380 #define MA_DEVICE_OP_NONE__NULL 0
9381 #define MA_DEVICE_OP_START__NULL 1
9382 #define MA_DEVICE_OP_SUSPEND__NULL 2
9383 #define MA_DEVICE_OP_KILL__NULL 3
9384
9385 static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
9386 {
9387 ma_device* pDevice = (ma_device*)pData;
9388 MA_ASSERT(pDevice != NULL);
9389
9390 for (;;) { /* Keep the thread alive until the device is uninitialized. */
9391 /* Wait for an operation to be requested. */
9392 ma_event_wait(&pDevice->null_device.operationEvent);
9393
9394 /* At this point an event should have been triggered. */
9395
9396 /* Starting the device needs to put the thread into a loop. */
9397 if (pDevice->null_device.operation == MA_DEVICE_OP_START__NULL) {
9398 ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
9399
9400 /* Reset the timer just in case. */
9401 ma_timer_init(&pDevice->null_device.timer);
9402
9403 /* Keep looping until an operation has been requested. */
9404 while (pDevice->null_device.operation != MA_DEVICE_OP_NONE__NULL && pDevice->null_device.operation != MA_DEVICE_OP_START__NULL) {
9405 ma_sleep(10); /* Don't hog the CPU. */
9406 }
9407
9408 /* Getting here means a suspend or kill operation has been requested. */
9409 ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
9410 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
9411 continue;
9412 }
9413
9414 /* Suspending the device means we need to stop the timer and just continue the loop. */
9415 if (pDevice->null_device.operation == MA_DEVICE_OP_SUSPEND__NULL) {
9416 ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
9417
9418 /* We need to add the current run time to the prior run time, then reset the timer. */
9419 pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
9420 ma_timer_init(&pDevice->null_device.timer);
9421
9422 /* We're done. */
9423 ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
9424 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
9425 continue;
9426 }
9427
9428 /* Killing the device means we need to get out of this loop so that this thread can terminate. */
9429 if (pDevice->null_device.operation == MA_DEVICE_OP_KILL__NULL) {
9430 ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
9431 ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
9432 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
9433 break;
9434 }
9435
9436 /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
9437 if (pDevice->null_device.operation == MA_DEVICE_OP_NONE__NULL) {
9438 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). */
9439 ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_INVALID_OPERATION);
9440 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
9441 continue; /* Continue the loop. Don't terminate. */
9442 }
9443 }
9444
9445 return (ma_thread_result)0;
9446 }
9447
9448 static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
9449 {
9450 ma_atomic_exchange_32(&pDevice->null_device.operation, operation);
9451 if (!ma_event_signal(&pDevice->null_device.operationEvent)) {
9452 return MA_ERROR;
9453 }
9454
9455 if (!ma_event_wait(&pDevice->null_device.operationCompletionEvent)) {
9456 return MA_ERROR;
9457 }
9458
9459 return pDevice->null_device.operationResult;
9460 }
9461
9462 static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
9463 {
9464 ma_uint32 internalSampleRate;
9465 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
9466 internalSampleRate = pDevice->capture.internalSampleRate;
9467 } else {
9468 internalSampleRate = pDevice->playback.internalSampleRate;
9469 }
9470
9471
9472 return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
9473 }
9474
9475 static ma_bool32 ma_context_is_device_id_equal__null(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
9476 {
9477 MA_ASSERT(pContext != NULL);
9478 MA_ASSERT(pID0 != NULL);
9479 MA_ASSERT(pID1 != NULL);
9480 (void)pContext;
9481
9482 return pID0->nullbackend == pID1->nullbackend;
9483 }
9484
9485 static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
9486 {
9487 ma_bool32 cbResult = MA_TRUE;
9488
9489 MA_ASSERT(pContext != NULL);
9490 MA_ASSERT(callback != NULL);
9491
9492 /* Playback. */
9493 if (cbResult) {
9494 ma_device_info deviceInfo;
9495 MA_ZERO_OBJECT(&deviceInfo);
9496 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
9497 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
9498 }
9499
9500 /* Capture. */
9501 if (cbResult) {
9502 ma_device_info deviceInfo;
9503 MA_ZERO_OBJECT(&deviceInfo);
9504 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
9505 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
9506 }
9507
9508 return MA_SUCCESS;
9509 }
9510
9511 static 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)
9512 {
9513 ma_uint32 iFormat;
9514
9515 MA_ASSERT(pContext != NULL);
9516
9517 if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
9518 return MA_NO_DEVICE; /* Don't know the device. */
9519 }
9520
9521 /* Name / Description */
9522 if (deviceType == ma_device_type_playback) {
9523 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
9524 } else {
9525 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
9526 }
9527
9528 /* Support everything on the null backend. */
9529 pDeviceInfo->formatCount = ma_format_count - 1; /* Minus one because we don't want to include ma_format_unknown. */
9530 for (iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
9531 pDeviceInfo->formats[iFormat] = (ma_format)(iFormat + 1); /* +1 to skip over ma_format_unknown. */
9532 }
9533
9534 pDeviceInfo->minChannels = 1;
9535 pDeviceInfo->maxChannels = MA_MAX_CHANNELS;
9536 pDeviceInfo->minSampleRate = MA_SAMPLE_RATE_8000;
9537 pDeviceInfo->maxSampleRate = MA_SAMPLE_RATE_384000;
9538
9539 (void)pContext;
9540 (void)shareMode;
9541 return MA_SUCCESS;
9542 }
9543
9544
9545 static void ma_device_uninit__null(ma_device* pDevice)
9546 {
9547 MA_ASSERT(pDevice != NULL);
9548
9549 /* Keep it clean and wait for the device thread to finish before returning. */
9550 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
9551
9552 /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */
9553 ma_event_uninit(&pDevice->null_device.operationCompletionEvent);
9554 ma_event_uninit(&pDevice->null_device.operationEvent);
9555 }
9556
9557 static ma_result ma_device_init__null(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
9558 {
9559 ma_result result;
9560 ma_uint32 periodSizeInFrames;
9561
9562 MA_ASSERT(pDevice != NULL);
9563
9564 MA_ZERO_OBJECT(&pDevice->null_device);
9565
9566 if (pConfig->deviceType == ma_device_type_loopback) {
9567 return MA_DEVICE_TYPE_NOT_SUPPORTED;
9568 }
9569
9570 periodSizeInFrames = pConfig->periodSizeInFrames;
9571 if (periodSizeInFrames == 0) {
9572 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pConfig->sampleRate);
9573 }
9574
9575 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
9576 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "NULL Capture Device", (size_t)-1);
9577 pDevice->capture.internalFormat = pConfig->capture.format;
9578 pDevice->capture.internalChannels = pConfig->capture.channels;
9579 ma_channel_map_copy(pDevice->capture.internalChannelMap, pConfig->capture.channelMap, pConfig->capture.channels);
9580 pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames;
9581 pDevice->capture.internalPeriods = pConfig->periods;
9582 }
9583 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
9584 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "NULL Playback Device", (size_t)-1);
9585 pDevice->playback.internalFormat = pConfig->playback.format;
9586 pDevice->playback.internalChannels = pConfig->playback.channels;
9587 ma_channel_map_copy(pDevice->playback.internalChannelMap, pConfig->playback.channelMap, pConfig->playback.channels);
9588 pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
9589 pDevice->playback.internalPeriods = pConfig->periods;
9590 }
9591
9592 /*
9593 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
9594 first period is "written" to it, and then stopped in ma_device_stop__null().
9595 */
9596 result = ma_event_init(pContext, &pDevice->null_device.operationEvent);
9597 if (result != MA_SUCCESS) {
9598 return result;
9599 }
9600
9601 result = ma_event_init(pContext, &pDevice->null_device.operationCompletionEvent);
9602 if (result != MA_SUCCESS) {
9603 return result;
9604 }
9605
9606 result = ma_thread_create(pContext, &pDevice->thread, ma_device_thread__null, pDevice);
9607 if (result != MA_SUCCESS) {
9608 return result;
9609 }
9610
9611 return MA_SUCCESS;
9612 }
9613
9614 static ma_result ma_device_start__null(ma_device* pDevice)
9615 {
9616 MA_ASSERT(pDevice != NULL);
9617
9618 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
9619
9620 ma_atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE);
9621 return MA_SUCCESS;
9622 }
9623
9624 static ma_result ma_device_stop__null(ma_device* pDevice)
9625 {
9626 MA_ASSERT(pDevice != NULL);
9627
9628 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
9629
9630 ma_atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE);
9631 return MA_SUCCESS;
9632 }
9633
9634 static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
9635 {
9636 ma_result result = MA_SUCCESS;
9637 ma_uint32 totalPCMFramesProcessed;
9638 ma_bool32 wasStartedOnEntry;
9639
9640 if (pFramesWritten != NULL) {
9641 *pFramesWritten = 0;
9642 }
9643
9644 wasStartedOnEntry = pDevice->null_device.isStarted;
9645
9646 /* Keep going until everything has been read. */
9647 totalPCMFramesProcessed = 0;
9648 while (totalPCMFramesProcessed < frameCount) {
9649 ma_uint64 targetFrame;
9650
9651 /* If there are any frames remaining in the current period, consume those first. */
9652 if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) {
9653 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
9654 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback;
9655 if (framesToProcess > framesRemaining) {
9656 framesToProcess = framesRemaining;
9657 }
9658
9659 /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */
9660 (void)pPCMFrames;
9661
9662 pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
9663 totalPCMFramesProcessed += framesToProcess;
9664 }
9665
9666 /* 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. */
9667 if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) {
9668 pDevice->null_device.currentPeriodFramesRemainingPlayback = 0;
9669
9670 if (!pDevice->null_device.isStarted && !wasStartedOnEntry) {
9671 result = ma_device_start__null(pDevice);
9672 if (result != MA_SUCCESS) {
9673 break;
9674 }
9675 }
9676 }
9677
9678 /* If we've consumed the whole buffer we can return now. */
9679 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
9680 if (totalPCMFramesProcessed == frameCount) {
9681 break;
9682 }
9683
9684 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
9685 targetFrame = pDevice->null_device.lastProcessedFramePlayback;
9686 for (;;) {
9687 ma_uint64 currentFrame;
9688
9689 /* Stop waiting if the device has been stopped. */
9690 if (!pDevice->null_device.isStarted) {
9691 break;
9692 }
9693
9694 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
9695 if (currentFrame >= targetFrame) {
9696 break;
9697 }
9698
9699 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
9700 ma_sleep(10);
9701 }
9702
9703 pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames;
9704 pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames;
9705 }
9706
9707 if (pFramesWritten != NULL) {
9708 *pFramesWritten = totalPCMFramesProcessed;
9709 }
9710
9711 return result;
9712 }
9713
9714 static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
9715 {
9716 ma_result result = MA_SUCCESS;
9717 ma_uint32 totalPCMFramesProcessed;
9718
9719 if (pFramesRead != NULL) {
9720 *pFramesRead = 0;
9721 }
9722
9723 /* Keep going until everything has been read. */
9724 totalPCMFramesProcessed = 0;
9725 while (totalPCMFramesProcessed < frameCount) {
9726 ma_uint64 targetFrame;
9727
9728 /* If there are any frames remaining in the current period, consume those first. */
9729 if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) {
9730 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
9731 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
9732 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture;
9733 if (framesToProcess > framesRemaining) {
9734 framesToProcess = framesRemaining;
9735 }
9736
9737 /* We need to ensured the output buffer is zeroed. */
9738 MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);
9739
9740 pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;
9741 totalPCMFramesProcessed += framesToProcess;
9742 }
9743
9744 /* 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. */
9745 if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) {
9746 pDevice->null_device.currentPeriodFramesRemainingCapture = 0;
9747 }
9748
9749 /* If we've consumed the whole buffer we can return now. */
9750 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
9751 if (totalPCMFramesProcessed == frameCount) {
9752 break;
9753 }
9754
9755 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
9756 targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames;
9757 for (;;) {
9758 ma_uint64 currentFrame;
9759
9760 /* Stop waiting if the device has been stopped. */
9761 if (!pDevice->null_device.isStarted) {
9762 break;
9763 }
9764
9765 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
9766 if (currentFrame >= targetFrame) {
9767 break;
9768 }
9769
9770 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
9771 ma_sleep(10);
9772 }
9773
9774 pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames;
9775 pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames;
9776 }
9777
9778 if (pFramesRead != NULL) {
9779 *pFramesRead = totalPCMFramesProcessed;
9780 }
9781
9782 return result;
9783 }
9784
9785 static ma_result ma_device_main_loop__null(ma_device* pDevice)
9786 {
9787 ma_result result = MA_SUCCESS;
9788 ma_bool32 exitLoop = MA_FALSE;
9789
9790 MA_ASSERT(pDevice != NULL);
9791
9792 /* The capture device needs to be started immediately. */
9793 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
9794 result = ma_device_start__null(pDevice);
9795 if (result != MA_SUCCESS) {
9796 return result;
9797 }
9798 }
9799
9800 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
9801 switch (pDevice->type)
9802 {
9803 case ma_device_type_duplex:
9804 {
9805 /* The process is: device_read -> convert -> callback -> convert -> device_write */
9806 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
9807 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
9808
9809 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
9810 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
9811 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
9812 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
9813 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
9814 ma_uint32 capturedDeviceFramesRemaining;
9815 ma_uint32 capturedDeviceFramesProcessed;
9816 ma_uint32 capturedDeviceFramesToProcess;
9817 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
9818 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
9819 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
9820 }
9821
9822 result = ma_device_read__null(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
9823 if (result != MA_SUCCESS) {
9824 exitLoop = MA_TRUE;
9825 break;
9826 }
9827
9828 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
9829 capturedDeviceFramesProcessed = 0;
9830
9831 /* At this point we have our captured data in device format and we now need to convert it to client format. */
9832 for (;;) {
9833 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
9834 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
9835 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
9836 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
9837 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
9838 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
9839 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
9840
9841 /* Convert capture data from device format to client format. */
9842 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
9843 if (result != MA_SUCCESS) {
9844 break;
9845 }
9846
9847 /*
9848 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
9849 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
9850 */
9851 if (capturedClientFramesToProcessThisIteration == 0) {
9852 break;
9853 }
9854
9855 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
9856
9857 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
9858 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
9859
9860 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
9861 for (;;) {
9862 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
9863 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
9864 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
9865 if (result != MA_SUCCESS) {
9866 break;
9867 }
9868
9869 result = ma_device_write__null(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
9870 if (result != MA_SUCCESS) {
9871 exitLoop = MA_TRUE;
9872 break;
9873 }
9874
9875 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
9876 if (capturedClientFramesToProcessThisIteration == 0) {
9877 break;
9878 }
9879 }
9880
9881 /* In case an error happened from ma_device_write__null()... */
9882 if (result != MA_SUCCESS) {
9883 exitLoop = MA_TRUE;
9884 break;
9885 }
9886 }
9887
9888 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
9889 }
9890 } break;
9891
9892 case ma_device_type_capture:
9893 {
9894 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
9895 ma_uint8 intermediaryBuffer[8192];
9896 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
9897 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
9898 ma_uint32 framesReadThisPeriod = 0;
9899 while (framesReadThisPeriod < periodSizeInFrames) {
9900 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
9901 ma_uint32 framesProcessed;
9902 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
9903 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
9904 framesToReadThisIteration = intermediaryBufferSizeInFrames;
9905 }
9906
9907 result = ma_device_read__null(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
9908 if (result != MA_SUCCESS) {
9909 exitLoop = MA_TRUE;
9910 break;
9911 }
9912
9913 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
9914
9915 framesReadThisPeriod += framesProcessed;
9916 }
9917 } break;
9918
9919 case ma_device_type_playback:
9920 {
9921 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
9922 ma_uint8 intermediaryBuffer[8192];
9923 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
9924 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
9925 ma_uint32 framesWrittenThisPeriod = 0;
9926 while (framesWrittenThisPeriod < periodSizeInFrames) {
9927 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
9928 ma_uint32 framesProcessed;
9929 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
9930 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
9931 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
9932 }
9933
9934 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
9935
9936 result = ma_device_write__null(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
9937 if (result != MA_SUCCESS) {
9938 exitLoop = MA_TRUE;
9939 break;
9940 }
9941
9942 framesWrittenThisPeriod += framesProcessed;
9943 }
9944 } break;
9945
9946 /* To silence a warning. Will never hit this. */
9947 case ma_device_type_loopback:
9948 default: break;
9949 }
9950 }
9951
9952
9953 /* Here is where the device is started. */
9954 ma_device_stop__null(pDevice);
9955
9956 return result;
9957 }
9958
9959 static ma_result ma_context_uninit__null(ma_context* pContext)
9960 {
9961 MA_ASSERT(pContext != NULL);
9962 MA_ASSERT(pContext->backend == ma_backend_null);
9963
9964 (void)pContext;
9965 return MA_SUCCESS;
9966 }
9967
9968 static ma_result ma_context_init__null(const ma_context_config* pConfig, ma_context* pContext)
9969 {
9970 MA_ASSERT(pContext != NULL);
9971
9972 (void)pConfig;
9973
9974 pContext->onUninit = ma_context_uninit__null;
9975 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__null;
9976 pContext->onEnumDevices = ma_context_enumerate_devices__null;
9977 pContext->onGetDeviceInfo = ma_context_get_device_info__null;
9978 pContext->onDeviceInit = ma_device_init__null;
9979 pContext->onDeviceUninit = ma_device_uninit__null;
9980 pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
9981 pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
9982 pContext->onDeviceMainLoop = ma_device_main_loop__null;
9983
9984 /* The null backend always works. */
9985 return MA_SUCCESS;
9986 }
9987 #endif
9988
9989
9990 /*******************************************************************************
9991
9992 WIN32 COMMON
9993
9994 *******************************************************************************/
9995 #if defined(MA_WIN32)
9996 #if defined(MA_WIN32_DESKTOP)
9997 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit)
9998 #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
9999 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
10000 #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
10001 #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
10002 #else
10003 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
10004 #define ma_CoUninitialize(pContext) CoUninitialize()
10005 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
10006 #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
10007 #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
10008 #endif
10009
10010 #if !defined(MAXULONG_PTR)
10011 typedef size_t DWORD_PTR;
10012 #endif
10013
10014 #if !defined(WAVE_FORMAT_44M08)
10015 #define WAVE_FORMAT_44M08 0x00000100
10016 #define WAVE_FORMAT_44S08 0x00000200
10017 #define WAVE_FORMAT_44M16 0x00000400
10018 #define WAVE_FORMAT_44S16 0x00000800
10019 #define WAVE_FORMAT_48M08 0x00001000
10020 #define WAVE_FORMAT_48S08 0x00002000
10021 #define WAVE_FORMAT_48M16 0x00004000
10022 #define WAVE_FORMAT_48S16 0x00008000
10023 #define WAVE_FORMAT_96M08 0x00010000
10024 #define WAVE_FORMAT_96S08 0x00020000
10025 #define WAVE_FORMAT_96M16 0x00040000
10026 #define WAVE_FORMAT_96S16 0x00080000
10027 #endif
10028
10029 #ifndef SPEAKER_FRONT_LEFT
10030 #define SPEAKER_FRONT_LEFT 0x1
10031 #define SPEAKER_FRONT_RIGHT 0x2
10032 #define SPEAKER_FRONT_CENTER 0x4
10033 #define SPEAKER_LOW_FREQUENCY 0x8
10034 #define SPEAKER_BACK_LEFT 0x10
10035 #define SPEAKER_BACK_RIGHT 0x20
10036 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
10037 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
10038 #define SPEAKER_BACK_CENTER 0x100
10039 #define SPEAKER_SIDE_LEFT 0x200
10040 #define SPEAKER_SIDE_RIGHT 0x400
10041 #define SPEAKER_TOP_CENTER 0x800
10042 #define SPEAKER_TOP_FRONT_LEFT 0x1000
10043 #define SPEAKER_TOP_FRONT_CENTER 0x2000
10044 #define SPEAKER_TOP_FRONT_RIGHT 0x4000
10045 #define SPEAKER_TOP_BACK_LEFT 0x8000
10046 #define SPEAKER_TOP_BACK_CENTER 0x10000
10047 #define SPEAKER_TOP_BACK_RIGHT 0x20000
10048 #endif
10049
10050 /*
10051 The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We
10052 define our own implementation in this case.
10053 */
10054 #if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__)
10055 typedef struct
10056 {
10057 WAVEFORMATEX Format;
10058 union
10059 {
10060 WORD wValidBitsPerSample;
10061 WORD wSamplesPerBlock;
10062 WORD wReserved;
10063 } Samples;
10064 DWORD dwChannelMask;
10065 GUID SubFormat;
10066 } WAVEFORMATEXTENSIBLE;
10067 #endif
10068
10069 #ifndef WAVE_FORMAT_EXTENSIBLE
10070 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
10071 #endif
10072
10073 #ifndef WAVE_FORMAT_IEEE_FLOAT
10074 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
10075 #endif
10076
10077 static GUID MA_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
10078
10079 /* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
10080 static ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
10081 {
10082 switch (id)
10083 {
10084 case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
10085 case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
10086 case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
10087 case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
10088 case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
10089 case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
10090 case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
10091 case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
10092 case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
10093 case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
10094 case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
10095 case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
10096 case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
10097 case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
10098 case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
10099 case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
10100 case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
10101 case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
10102 default: return 0;
10103 }
10104 }
10105
10106 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */
10107 static DWORD ma_channel_id_to_win32(DWORD id)
10108 {
10109 switch (id)
10110 {
10111 case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER;
10112 case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT;
10113 case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT;
10114 case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER;
10115 case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY;
10116 case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT;
10117 case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT;
10118 case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER;
10119 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;
10120 case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER;
10121 case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT;
10122 case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT;
10123 case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER;
10124 case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT;
10125 case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER;
10126 case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT;
10127 case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT;
10128 case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER;
10129 case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT;
10130 default: return 0;
10131 }
10132 }
10133
10134 /* Converts a channel mapping to a Win32-style channel mask. */
10135 static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel channelMap[MA_MAX_CHANNELS], ma_uint32 channels)
10136 {
10137 DWORD dwChannelMask = 0;
10138 ma_uint32 iChannel;
10139
10140 for (iChannel = 0; iChannel < channels; ++iChannel) {
10141 dwChannelMask |= ma_channel_id_to_win32(channelMap[iChannel]);
10142 }
10143
10144 return dwChannelMask;
10145 }
10146
10147 /* Converts a Win32-style channel mask to a miniaudio channel map. */
10148 static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
10149 {
10150 if (channels == 1 && dwChannelMask == 0) {
10151 channelMap[0] = MA_CHANNEL_MONO;
10152 } else if (channels == 2 && dwChannelMask == 0) {
10153 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
10154 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
10155 } else {
10156 if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
10157 channelMap[0] = MA_CHANNEL_MONO;
10158 } else {
10159 /* Just iterate over each bit. */
10160 ma_uint32 iChannel = 0;
10161 ma_uint32 iBit;
10162
10163 for (iBit = 0; iBit < 32; ++iBit) {
10164 DWORD bitValue = (dwChannelMask & (1UL << iBit));
10165 if (bitValue != 0) {
10166 /* The bit is set. */
10167 channelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
10168 iChannel += 1;
10169 }
10170 }
10171 }
10172 }
10173 }
10174
10175 #ifdef __cplusplus
10176 static ma_bool32 ma_is_guid_equal(const void* a, const void* b)
10177 {
10178 return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);
10179 }
10180 #else
10181 #define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
10182 #endif
10183
10184 static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF)
10185 {
10186 MA_ASSERT(pWF != NULL);
10187
10188 if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
10189 const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF;
10190 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
10191 if (pWFEX->Samples.wValidBitsPerSample == 32) {
10192 return ma_format_s32;
10193 }
10194 if (pWFEX->Samples.wValidBitsPerSample == 24) {
10195 if (pWFEX->Format.wBitsPerSample == 32) {
10196 /*return ma_format_s24_32;*/
10197 }
10198 if (pWFEX->Format.wBitsPerSample == 24) {
10199 return ma_format_s24;
10200 }
10201 }
10202 if (pWFEX->Samples.wValidBitsPerSample == 16) {
10203 return ma_format_s16;
10204 }
10205 if (pWFEX->Samples.wValidBitsPerSample == 8) {
10206 return ma_format_u8;
10207 }
10208 }
10209 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
10210 if (pWFEX->Samples.wValidBitsPerSample == 32) {
10211 return ma_format_f32;
10212 }
10213 /*
10214 if (pWFEX->Samples.wValidBitsPerSample == 64) {
10215 return ma_format_f64;
10216 }
10217 */
10218 }
10219 } else {
10220 if (pWF->wFormatTag == WAVE_FORMAT_PCM) {
10221 if (pWF->wBitsPerSample == 32) {
10222 return ma_format_s32;
10223 }
10224 if (pWF->wBitsPerSample == 24) {
10225 return ma_format_s24;
10226 }
10227 if (pWF->wBitsPerSample == 16) {
10228 return ma_format_s16;
10229 }
10230 if (pWF->wBitsPerSample == 8) {
10231 return ma_format_u8;
10232 }
10233 }
10234 if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
10235 if (pWF->wBitsPerSample == 32) {
10236 return ma_format_f32;
10237 }
10238 if (pWF->wBitsPerSample == 64) {
10239 /*return ma_format_f64;*/
10240 }
10241 }
10242 }
10243
10244 return ma_format_unknown;
10245 }
10246 #endif
10247
10248
10249 /*******************************************************************************
10250
10251 WASAPI Backend
10252
10253 *******************************************************************************/
10254 #ifdef MA_HAS_WASAPI
10255 #if 0
10256 #if defined(_MSC_VER)
10257 #pragma warning(push)
10258 #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */
10259 #endif
10260 #include <audioclient.h>
10261 #include <mmdeviceapi.h>
10262 #if defined(_MSC_VER)
10263 #pragma warning(pop)
10264 #endif
10265 #endif /* 0 */
10266
10267 /* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */
10268 #define MA_WIN32_WINNT_VISTA 0x0600
10269 #define MA_VER_MINORVERSION 0x01
10270 #define MA_VER_MAJORVERSION 0x02
10271 #define MA_VER_SERVICEPACKMAJOR 0x20
10272 #define MA_VER_GREATER_EQUAL 0x03
10273
10274 typedef struct {
10275 DWORD dwOSVersionInfoSize;
10276 DWORD dwMajorVersion;
10277 DWORD dwMinorVersion;
10278 DWORD dwBuildNumber;
10279 DWORD dwPlatformId;
10280 WCHAR szCSDVersion[128];
10281 WORD wServicePackMajor;
10282 WORD wServicePackMinor;
10283 WORD wSuiteMask;
10284 BYTE wProductType;
10285 BYTE wReserved;
10286 } ma_OSVERSIONINFOEXW;
10287
10288 typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
10289 typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
10290
10291
10292 #ifndef PROPERTYKEY_DEFINED
10293 #define PROPERTYKEY_DEFINED
10294 typedef struct
10295 {
10296 GUID fmtid;
10297 DWORD pid;
10298 } PROPERTYKEY;
10299 #endif
10300
10301 /* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */
10302 static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp)
10303 {
10304 MA_ZERO_OBJECT(pProp);
10305 }
10306
10307
10308 static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};
10309 static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0};
10310
10311 static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */
10312 static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */
10313
10314 static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */
10315 static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */
10316 static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */
10317 static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */
10318 static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */
10319 static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */
10320 #ifndef MA_WIN32_DESKTOP
10321 static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */
10322 static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */
10323 static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */
10324 #endif
10325
10326 static const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */
10327 static const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */
10328 #ifdef __cplusplus
10329 #define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance
10330 #define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance
10331 #else
10332 #define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance
10333 #define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance
10334 #endif
10335
10336 typedef struct ma_IUnknown ma_IUnknown;
10337 #ifdef MA_WIN32_DESKTOP
10338 #define MA_MM_DEVICE_STATE_ACTIVE 1
10339 #define MA_MM_DEVICE_STATE_DISABLED 2
10340 #define MA_MM_DEVICE_STATE_NOTPRESENT 4
10341 #define MA_MM_DEVICE_STATE_UNPLUGGED 8
10342
10343 typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator;
10344 typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection;
10345 typedef struct ma_IMMDevice ma_IMMDevice;
10346 #else
10347 typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;
10348 typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation;
10349 #endif
10350 typedef struct ma_IPropertyStore ma_IPropertyStore;
10351 typedef struct ma_IAudioClient ma_IAudioClient;
10352 typedef struct ma_IAudioClient2 ma_IAudioClient2;
10353 typedef struct ma_IAudioClient3 ma_IAudioClient3;
10354 typedef struct ma_IAudioRenderClient ma_IAudioRenderClient;
10355 typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient;
10356
10357 typedef ma_int64 MA_REFERENCE_TIME;
10358
10359 #define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000
10360 #define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000
10361 #define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
10362 #define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
10363 #define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
10364 #define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
10365 #define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
10366 #define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000
10367 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000
10368 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000
10369
10370 /* Buffer flags. */
10371 #define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1
10372 #define MA_AUDCLNT_BUFFERFLAGS_SILENT 2
10373 #define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4
10374
10375 typedef enum
10376 {
10377 ma_eRender = 0,
10378 ma_eCapture = 1,
10379 ma_eAll = 2
10380 } ma_EDataFlow;
10381
10382 typedef enum
10383 {
10384 ma_eConsole = 0,
10385 ma_eMultimedia = 1,
10386 ma_eCommunications = 2
10387 } ma_ERole;
10388
10389 typedef enum
10390 {
10391 MA_AUDCLNT_SHAREMODE_SHARED,
10392 MA_AUDCLNT_SHAREMODE_EXCLUSIVE
10393 } MA_AUDCLNT_SHAREMODE;
10394
10395 typedef enum
10396 {
10397 MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */
10398 } MA_AUDIO_STREAM_CATEGORY;
10399
10400 typedef struct
10401 {
10402 UINT32 cbSize;
10403 BOOL bIsOffload;
10404 MA_AUDIO_STREAM_CATEGORY eCategory;
10405 } ma_AudioClientProperties;
10406
10407 /* IUnknown */
10408 typedef struct
10409 {
10410 /* IUnknown */
10411 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);
10412 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis);
10413 ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis);
10414 } ma_IUnknownVtbl;
10415 struct ma_IUnknown
10416 {
10417 ma_IUnknownVtbl* lpVtbl;
10418 };
10419 static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10420 static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10421 static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); }
10422
10423 #ifdef MA_WIN32_DESKTOP
10424 /* IMMNotificationClient */
10425 typedef struct
10426 {
10427 /* IUnknown */
10428 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);
10429 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis);
10430 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis);
10431
10432 /* IMMNotificationClient */
10433 HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState);
10434 HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
10435 HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
10436 HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID);
10437 HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key);
10438 } ma_IMMNotificationClientVtbl;
10439
10440 /* IMMDeviceEnumerator */
10441 typedef struct
10442 {
10443 /* IUnknown */
10444 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);
10445 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis);
10446 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis);
10447
10448 /* IMMDeviceEnumerator */
10449 HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);
10450 HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);
10451 HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice);
10452 HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
10453 HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
10454 } ma_IMMDeviceEnumeratorVtbl;
10455 struct ma_IMMDeviceEnumerator
10456 {
10457 ma_IMMDeviceEnumeratorVtbl* lpVtbl;
10458 };
10459 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10460 static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10461 static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); }
10462 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); }
10463 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); }
10464 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }
10465 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }
10466 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }
10467
10468
10469 /* IMMDeviceCollection */
10470 typedef struct
10471 {
10472 /* IUnknown */
10473 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);
10474 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis);
10475 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis);
10476
10477 /* IMMDeviceCollection */
10478 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);
10479 HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);
10480 } ma_IMMDeviceCollectionVtbl;
10481 struct ma_IMMDeviceCollection
10482 {
10483 ma_IMMDeviceCollectionVtbl* lpVtbl;
10484 };
10485 static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10486 static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10487 static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); }
10488 static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); }
10489 static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }
10490
10491
10492 /* IMMDevice */
10493 typedef struct
10494 {
10495 /* IUnknown */
10496 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);
10497 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis);
10498 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis);
10499
10500 /* IMMDevice */
10501 HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface);
10502 HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);
10503 HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID);
10504 HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState);
10505 } ma_IMMDeviceVtbl;
10506 struct ma_IMMDevice
10507 {
10508 ma_IMMDeviceVtbl* lpVtbl;
10509 };
10510 static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10511 static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10512 static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); }
10513 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); }
10514 static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }
10515 static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); }
10516 static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); }
10517 #else
10518 /* IActivateAudioInterfaceAsyncOperation */
10519 typedef struct
10520 {
10521 /* IUnknown */
10522 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);
10523 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
10524 ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
10525
10526 /* IActivateAudioInterfaceAsyncOperation */
10527 HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);
10528 } ma_IActivateAudioInterfaceAsyncOperationVtbl;
10529 struct ma_IActivateAudioInterfaceAsyncOperation
10530 {
10531 ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;
10532 };
10533 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10534 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10535 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); }
10536 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }
10537 #endif
10538
10539 /* IPropertyStore */
10540 typedef struct
10541 {
10542 /* IUnknown */
10543 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);
10544 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis);
10545 ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis);
10546
10547 /* IPropertyStore */
10548 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);
10549 HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);
10550 HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar);
10551 HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar);
10552 HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis);
10553 } ma_IPropertyStoreVtbl;
10554 struct ma_IPropertyStore
10555 {
10556 ma_IPropertyStoreVtbl* lpVtbl;
10557 };
10558 static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10559 static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10560 static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); }
10561 static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); }
10562 static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }
10563 static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }
10564 static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); }
10565 static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); }
10566
10567
10568 /* IAudioClient */
10569 typedef struct
10570 {
10571 /* IUnknown */
10572 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);
10573 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis);
10574 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis);
10575
10576 /* IAudioClient */
10577 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);
10578 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);
10579 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);
10580 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);
10581 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
10582 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat);
10583 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
10584 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis);
10585 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis);
10586 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis);
10587 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle);
10588 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp);
10589 } ma_IAudioClientVtbl;
10590 struct ma_IAudioClient
10591 {
10592 ma_IAudioClientVtbl* lpVtbl;
10593 };
10594 static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10595 static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10596 static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); }
10597 static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
10598 static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
10599 static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
10600 static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
10601 static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
10602 static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
10603 static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
10604 static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); }
10605 static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); }
10606 static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); }
10607 static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
10608 static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
10609
10610 /* IAudioClient2 */
10611 typedef struct
10612 {
10613 /* IUnknown */
10614 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);
10615 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis);
10616 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis);
10617
10618 /* IAudioClient */
10619 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);
10620 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);
10621 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);
10622 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);
10623 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
10624 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat);
10625 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
10626 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis);
10627 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis);
10628 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis);
10629 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle);
10630 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp);
10631
10632 /* IAudioClient2 */
10633 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
10634 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);
10635 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
10636 } ma_IAudioClient2Vtbl;
10637 struct ma_IAudioClient2
10638 {
10639 ma_IAudioClient2Vtbl* lpVtbl;
10640 };
10641 static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10642 static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10643 static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); }
10644 static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
10645 static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
10646 static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
10647 static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
10648 static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
10649 static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
10650 static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
10651 static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); }
10652 static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); }
10653 static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); }
10654 static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
10655 static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
10656 static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
10657 static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
10658 static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); }
10659
10660
10661 /* IAudioClient3 */
10662 typedef struct
10663 {
10664 /* IUnknown */
10665 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);
10666 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis);
10667 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis);
10668
10669 /* IAudioClient */
10670 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);
10671 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);
10672 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);
10673 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);
10674 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
10675 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat);
10676 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
10677 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis);
10678 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis);
10679 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis);
10680 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle);
10681 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp);
10682
10683 /* IAudioClient2 */
10684 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
10685 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);
10686 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
10687
10688 /* IAudioClient3 */
10689 HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames);
10690 HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames);
10691 HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
10692 } ma_IAudioClient3Vtbl;
10693 struct ma_IAudioClient3
10694 {
10695 ma_IAudioClient3Vtbl* lpVtbl;
10696 };
10697 static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10698 static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10699 static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); }
10700 static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
10701 static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
10702 static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
10703 static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
10704 static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
10705 static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
10706 static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
10707 static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); }
10708 static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); }
10709 static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); }
10710 static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
10711 static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
10712 static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
10713 static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
10714 static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); }
10715 static 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); }
10716 static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }
10717 static 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); }
10718
10719
10720 /* IAudioRenderClient */
10721 typedef struct
10722 {
10723 /* IUnknown */
10724 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);
10725 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis);
10726 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis);
10727
10728 /* IAudioRenderClient */
10729 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);
10730 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);
10731 } ma_IAudioRenderClientVtbl;
10732 struct ma_IAudioRenderClient
10733 {
10734 ma_IAudioRenderClientVtbl* lpVtbl;
10735 };
10736 static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10737 static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10738 static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
10739 static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
10740 static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
10741
10742
10743 /* IAudioCaptureClient */
10744 typedef struct
10745 {
10746 /* IUnknown */
10747 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
10748 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
10749 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
10750
10751 /* IAudioRenderClient */
10752 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
10753 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
10754 HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
10755 } ma_IAudioCaptureClientVtbl;
10756 struct ma_IAudioCaptureClient
10757 {
10758 ma_IAudioCaptureClientVtbl* lpVtbl;
10759 };
10760 static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10761 static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10762 static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
10763 static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); }
10764 static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
10765 static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
10766
10767 #ifndef MA_WIN32_DESKTOP
10768 #include <mmdeviceapi.h>
10769 typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
10770
10771 typedef struct
10772 {
10773 /* IUnknown */
10774 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
10775 ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
10776 ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
10777
10778 /* IActivateAudioInterfaceCompletionHandler */
10779 HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
10780 } ma_completion_handler_uwp_vtbl;
10781 struct ma_completion_handler_uwp
10782 {
10783 ma_completion_handler_uwp_vtbl* lpVtbl;
10784 ma_uint32 counter;
10785 HANDLE hEvent;
10786 };
10787
10788 static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
10789 {
10790 /*
10791 We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To
10792 "implement" this, we just make sure we return pThis when the IAgileObject is requested.
10793 */
10794 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)) {
10795 *ppObject = NULL;
10796 return E_NOINTERFACE;
10797 }
10798
10799 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
10800 *ppObject = (void*)pThis;
10801 ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
10802 return S_OK;
10803 }
10804
10805 static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
10806 {
10807 return (ULONG)ma_atomic_increment_32(&pThis->counter);
10808 }
10809
10810 static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
10811 {
10812 ma_uint32 newRefCount = ma_atomic_decrement_32(&pThis->counter);
10813 if (newRefCount == 0) {
10814 return 0; /* We don't free anything here because we never allocate the object on the heap. */
10815 }
10816
10817 return (ULONG)newRefCount;
10818 }
10819
10820 static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
10821 {
10822 (void)pActivateOperation;
10823 SetEvent(pThis->hEvent);
10824 return S_OK;
10825 }
10826
10827
10828 static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
10829 ma_completion_handler_uwp_QueryInterface,
10830 ma_completion_handler_uwp_AddRef,
10831 ma_completion_handler_uwp_Release,
10832 ma_completion_handler_uwp_ActivateCompleted
10833 };
10834
10835 static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
10836 {
10837 MA_ASSERT(pHandler != NULL);
10838 MA_ZERO_OBJECT(pHandler);
10839
10840 pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
10841 pHandler->counter = 1;
10842 pHandler->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
10843 if (pHandler->hEvent == NULL) {
10844 return ma_result_from_GetLastError(GetLastError());
10845 }
10846
10847 return MA_SUCCESS;
10848 }
10849
10850 static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
10851 {
10852 if (pHandler->hEvent != NULL) {
10853 CloseHandle(pHandler->hEvent);
10854 }
10855 }
10856
10857 static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
10858 {
10859 WaitForSingleObject(pHandler->hEvent, INFINITE);
10860 }
10861 #endif /* !MA_WIN32_DESKTOP */
10862
10863 /* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
10864 #ifdef MA_WIN32_DESKTOP
10865 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
10866 {
10867 /*
10868 We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
10869 we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
10870 */
10871 if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
10872 *ppObject = NULL;
10873 return E_NOINTERFACE;
10874 }
10875
10876 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
10877 *ppObject = (void*)pThis;
10878 ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
10879 return S_OK;
10880 }
10881
10882 static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
10883 {
10884 return (ULONG)ma_atomic_increment_32(&pThis->counter);
10885 }
10886
10887 static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
10888 {
10889 ma_uint32 newRefCount = ma_atomic_decrement_32(&pThis->counter);
10890 if (newRefCount == 0) {
10891 return 0; /* We don't free anything here because we never allocate the object on the heap. */
10892 }
10893
10894 return (ULONG)newRefCount;
10895 }
10896
10897
10898 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState)
10899 {
10900 #ifdef MA_DEBUG_OUTPUT
10901 printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);
10902 #endif
10903
10904 (void)pThis;
10905 (void)pDeviceID;
10906 (void)dwNewState;
10907 return S_OK;
10908 }
10909
10910 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
10911 {
10912 #ifdef MA_DEBUG_OUTPUT
10913 printf("IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
10914 #endif
10915
10916 /* We don't need to worry about this event for our purposes. */
10917 (void)pThis;
10918 (void)pDeviceID;
10919 return S_OK;
10920 }
10921
10922 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
10923 {
10924 #ifdef MA_DEBUG_OUTPUT
10925 printf("IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
10926 #endif
10927
10928 /* We don't need to worry about this event for our purposes. */
10929 (void)pThis;
10930 (void)pDeviceID;
10931 return S_OK;
10932 }
10933
10934 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID)
10935 {
10936 #ifdef MA_DEBUG_OUTPUT
10937 printf("IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");
10938 #endif
10939
10940 /* We only ever use the eConsole role in miniaudio. */
10941 if (role != ma_eConsole) {
10942 return S_OK;
10943 }
10944
10945 /* We only care about devices with the same data flow and role as the current device. */
10946 if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
10947 (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) {
10948 return S_OK;
10949 }
10950
10951 /* Don't do automatic stream routing if we're not allowed. */
10952 if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||
10953 (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) {
10954 return S_OK;
10955 }
10956
10957 /*
10958 Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
10959 AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
10960 it's fixed.
10961 */
10962 if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
10963 (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
10964 return S_OK;
10965 }
10966
10967 /*
10968 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
10969 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
10970 that properly.
10971 */
10972 if (dataFlow == ma_eRender && pThis->pDevice->type != ma_device_type_loopback) {
10973 ma_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_TRUE);
10974 }
10975 if (dataFlow == ma_eCapture || pThis->pDevice->type == ma_device_type_loopback) {
10976 ma_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_TRUE);
10977 }
10978
10979 (void)pDefaultDeviceID;
10980 return S_OK;
10981 }
10982
10983 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key)
10984 {
10985 #ifdef MA_DEBUG_OUTPUT
10986 printf("IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
10987 #endif
10988
10989 (void)pThis;
10990 (void)pDeviceID;
10991 (void)key;
10992 return S_OK;
10993 }
10994
10995 static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
10996 ma_IMMNotificationClient_QueryInterface,
10997 ma_IMMNotificationClient_AddRef,
10998 ma_IMMNotificationClient_Release,
10999 ma_IMMNotificationClient_OnDeviceStateChanged,
11000 ma_IMMNotificationClient_OnDeviceAdded,
11001 ma_IMMNotificationClient_OnDeviceRemoved,
11002 ma_IMMNotificationClient_OnDefaultDeviceChanged,
11003 ma_IMMNotificationClient_OnPropertyValueChanged
11004 };
11005 #endif /* MA_WIN32_DESKTOP */
11006
11007 #ifdef MA_WIN32_DESKTOP
11008 typedef ma_IMMDevice ma_WASAPIDeviceInterface;
11009 #else
11010 typedef ma_IUnknown ma_WASAPIDeviceInterface;
11011 #endif
11012
11013
11014
11015 static ma_bool32 ma_context_is_device_id_equal__wasapi(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
11016 {
11017 MA_ASSERT(pContext != NULL);
11018 MA_ASSERT(pID0 != NULL);
11019 MA_ASSERT(pID1 != NULL);
11020 (void)pContext;
11021
11022 return memcmp(pID0->wasapi, pID1->wasapi, sizeof(pID0->wasapi)) == 0;
11023 }
11024
11025 static void ma_set_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_device_info* pInfo)
11026 {
11027 MA_ASSERT(pWF != NULL);
11028 MA_ASSERT(pInfo != NULL);
11029
11030 pInfo->formatCount = 1;
11031 pInfo->formats[0] = ma_format_from_WAVEFORMATEX(pWF);
11032 pInfo->minChannels = pWF->nChannels;
11033 pInfo->maxChannels = pWF->nChannels;
11034 pInfo->minSampleRate = pWF->nSamplesPerSec;
11035 pInfo->maxSampleRate = pWF->nSamplesPerSec;
11036 }
11037
11038 static 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)
11039 {
11040 MA_ASSERT(pAudioClient != NULL);
11041 MA_ASSERT(pInfo != NULL);
11042
11043 /* We use a different technique to retrieve the device information depending on whether or not we are using shared or exclusive mode. */
11044 if (shareMode == ma_share_mode_shared) {
11045 /* Shared Mode. We use GetMixFormat() here. */
11046 WAVEFORMATEX* pWF = NULL;
11047 HRESULT hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF);
11048 if (SUCCEEDED(hr)) {
11049 ma_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
11050 return MA_SUCCESS;
11051 } else {
11052 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.", ma_result_from_HRESULT(hr));
11053 }
11054 } else {
11055 /* Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently support on UWP. */
11056 #ifdef MA_WIN32_DESKTOP
11057 /*
11058 The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
11059 correct which will simplify our searching.
11060 */
11061 ma_IPropertyStore *pProperties;
11062 HRESULT hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
11063 if (SUCCEEDED(hr)) {
11064 PROPVARIANT var;
11065 ma_PropVariantInit(&var);
11066
11067 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);
11068 if (SUCCEEDED(hr)) {
11069 WAVEFORMATEX* pWF = (WAVEFORMATEX*)var.blob.pBlobData;
11070 ma_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
11071
11072 /*
11073 In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
11074 first. If this fails, fall back to a search.
11075 */
11076 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
11077 ma_PropVariantClear(pContext, &var);
11078
11079 if (FAILED(hr)) {
11080 /*
11081 The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
11082 count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
11083 */
11084 ma_uint32 channels = pInfo->minChannels;
11085 ma_format formatsToSearch[] = {
11086 ma_format_s16,
11087 ma_format_s24,
11088 /*ma_format_s24_32,*/
11089 ma_format_f32,
11090 ma_format_s32,
11091 ma_format_u8
11092 };
11093 ma_channel defaultChannelMap[MA_MAX_CHANNELS];
11094 WAVEFORMATEXTENSIBLE wf;
11095 ma_bool32 found;
11096 ma_uint32 iFormat;
11097
11098 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, channels, defaultChannelMap);
11099
11100 MA_ZERO_OBJECT(&wf);
11101 wf.Format.cbSize = sizeof(wf);
11102 wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
11103 wf.Format.nChannels = (WORD)channels;
11104 wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
11105
11106 found = MA_FALSE;
11107 for (iFormat = 0; iFormat < ma_countof(formatsToSearch); ++iFormat) {
11108 ma_format format = formatsToSearch[iFormat];
11109 ma_uint32 iSampleRate;
11110
11111 wf.Format.wBitsPerSample = (WORD)ma_get_bytes_per_sample(format)*8;
11112 wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
11113 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
11114 wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample;
11115 if (format == ma_format_f32) {
11116 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
11117 } else {
11118 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
11119 }
11120
11121 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
11122 wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
11123
11124 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL);
11125 if (SUCCEEDED(hr)) {
11126 ma_set_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, pInfo);
11127 found = MA_TRUE;
11128 break;
11129 }
11130 }
11131
11132 if (found) {
11133 break;
11134 }
11135 }
11136
11137 if (!found) {
11138 ma_IPropertyStore_Release(pProperties);
11139 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to find suitable device format for device info retrieval.", MA_FORMAT_NOT_SUPPORTED);
11140 }
11141 }
11142 } else {
11143 ma_IPropertyStore_Release(pProperties);
11144 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve device format for device info retrieval.", ma_result_from_HRESULT(hr));
11145 }
11146
11147 ma_IPropertyStore_Release(pProperties);
11148 } else {
11149 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to open property store for device info retrieval.", ma_result_from_HRESULT(hr));
11150 }
11151
11152 return MA_SUCCESS;
11153 #else
11154 /* Exclusive mode not fully supported in UWP right now. */
11155 return MA_ERROR;
11156 #endif
11157 }
11158 }
11159
11160 #ifdef MA_WIN32_DESKTOP
11161 static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType)
11162 {
11163 if (deviceType == ma_device_type_playback) {
11164 return ma_eRender;
11165 } else if (deviceType == ma_device_type_capture) {
11166 return ma_eCapture;
11167 } else {
11168 MA_ASSERT(MA_FALSE);
11169 return ma_eRender; /* Should never hit this. */
11170 }
11171 }
11172
11173 static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator)
11174 {
11175 HRESULT hr;
11176 ma_IMMDeviceEnumerator* pDeviceEnumerator;
11177
11178 MA_ASSERT(pContext != NULL);
11179 MA_ASSERT(ppDeviceEnumerator != NULL);
11180
11181 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
11182 if (FAILED(hr)) {
11183 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
11184 }
11185
11186 *ppDeviceEnumerator = pDeviceEnumerator;
11187
11188 return MA_SUCCESS;
11189 }
11190
11191 static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType)
11192 {
11193 HRESULT hr;
11194 ma_IMMDevice* pMMDefaultDevice = NULL;
11195 LPWSTR pDefaultDeviceID = NULL;
11196 ma_EDataFlow dataFlow;
11197 ma_ERole role;
11198
11199 MA_ASSERT(pContext != NULL);
11200 MA_ASSERT(pDeviceEnumerator != NULL);
11201
11202 /* Grab the EDataFlow type from the device type. */
11203 dataFlow = ma_device_type_to_EDataFlow(deviceType);
11204
11205 /* The role is always eConsole, but we may make this configurable later. */
11206 role = ma_eConsole;
11207
11208 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice);
11209 if (FAILED(hr)) {
11210 return NULL;
11211 }
11212
11213 hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID);
11214
11215 ma_IMMDevice_Release(pMMDefaultDevice);
11216 pMMDefaultDevice = NULL;
11217
11218 if (FAILED(hr)) {
11219 return NULL;
11220 }
11221
11222 return pDefaultDeviceID;
11223 }
11224
11225 static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */
11226 {
11227 ma_result result;
11228 ma_IMMDeviceEnumerator* pDeviceEnumerator = NULL;
11229 LPWSTR pDefaultDeviceID = NULL;
11230
11231 MA_ASSERT(pContext != NULL);
11232
11233 result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator);
11234 if (result != MA_SUCCESS) {
11235 return NULL;
11236 }
11237
11238 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
11239
11240 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
11241 return pDefaultDeviceID;
11242 }
11243
11244 static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
11245 {
11246 ma_IMMDeviceEnumerator* pDeviceEnumerator;
11247 HRESULT hr;
11248
11249 MA_ASSERT(pContext != NULL);
11250 MA_ASSERT(ppMMDevice != NULL);
11251
11252 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
11253 if (FAILED(hr)) {
11254 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.", ma_result_from_HRESULT(hr));
11255 }
11256
11257 if (pDeviceID == NULL) {
11258 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);
11259 } else {
11260 hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
11261 }
11262
11263 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
11264 if (FAILED(hr)) {
11265 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.", ma_result_from_HRESULT(hr));
11266 }
11267
11268 return MA_SUCCESS;
11269 }
11270
11271 static 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)
11272 {
11273 LPWSTR pDeviceID;
11274 HRESULT hr;
11275
11276 MA_ASSERT(pContext != NULL);
11277 MA_ASSERT(pMMDevice != NULL);
11278 MA_ASSERT(pInfo != NULL);
11279
11280 /* ID. */
11281 hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceID);
11282 if (SUCCEEDED(hr)) {
11283 size_t idlen = wcslen(pDeviceID);
11284 if (idlen+1 > ma_countof(pInfo->id.wasapi)) {
11285 ma_CoTaskMemFree(pContext, pDeviceID);
11286 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. */
11287 return MA_ERROR;
11288 }
11289
11290 MA_COPY_MEMORY(pInfo->id.wasapi, pDeviceID, idlen * sizeof(wchar_t));
11291 pInfo->id.wasapi[idlen] = '\0';
11292
11293 if (pDefaultDeviceID != NULL) {
11294 if (wcscmp(pDeviceID, pDefaultDeviceID) == 0) {
11295 /* It's a default device. */
11296 pInfo->_private.isDefault = MA_TRUE;
11297 }
11298 }
11299
11300 ma_CoTaskMemFree(pContext, pDeviceID);
11301 }
11302
11303 {
11304 ma_IPropertyStore *pProperties;
11305 hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
11306 if (SUCCEEDED(hr)) {
11307 PROPVARIANT var;
11308
11309 /* Description / Friendly Name */
11310 ma_PropVariantInit(&var);
11311 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
11312 if (SUCCEEDED(hr)) {
11313 WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
11314 ma_PropVariantClear(pContext, &var);
11315 }
11316
11317 ma_IPropertyStore_Release(pProperties);
11318 }
11319 }
11320
11321 /* Format */
11322 if (!onlySimpleInfo) {
11323 ma_IAudioClient* pAudioClient;
11324 hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
11325 if (SUCCEEDED(hr)) {
11326 ma_result result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, shareMode, pInfo);
11327
11328 ma_IAudioClient_Release(pAudioClient);
11329 return result;
11330 } else {
11331 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.", ma_result_from_HRESULT(hr));
11332 }
11333 }
11334
11335 return MA_SUCCESS;
11336 }
11337
11338 static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData)
11339 {
11340 ma_result result = MA_SUCCESS;
11341 UINT deviceCount;
11342 HRESULT hr;
11343 ma_uint32 iDevice;
11344 LPWSTR pDefaultDeviceID = NULL;
11345 ma_IMMDeviceCollection* pDeviceCollection = NULL;
11346
11347 MA_ASSERT(pContext != NULL);
11348 MA_ASSERT(callback != NULL);
11349
11350 /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */
11351 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
11352
11353 /* We need to enumerate the devices which returns a device collection. */
11354 hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
11355 if (SUCCEEDED(hr)) {
11356 hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
11357 if (FAILED(hr)) {
11358 result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.", ma_result_from_HRESULT(hr));
11359 goto done;
11360 }
11361
11362 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
11363 ma_device_info deviceInfo;
11364 ma_IMMDevice* pMMDevice;
11365
11366 MA_ZERO_OBJECT(&deviceInfo);
11367
11368 hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
11369 if (SUCCEEDED(hr)) {
11370 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, ma_share_mode_shared, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
11371
11372 ma_IMMDevice_Release(pMMDevice);
11373 if (result == MA_SUCCESS) {
11374 ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
11375 if (cbResult == MA_FALSE) {
11376 break;
11377 }
11378 }
11379 }
11380 }
11381 }
11382
11383 done:
11384 if (pDefaultDeviceID != NULL) {
11385 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
11386 pDefaultDeviceID = NULL;
11387 }
11388
11389 if (pDeviceCollection != NULL) {
11390 ma_IMMDeviceCollection_Release(pDeviceCollection);
11391 pDeviceCollection = NULL;
11392 }
11393
11394 return result;
11395 }
11396
11397 static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice)
11398 {
11399 ma_result result;
11400 HRESULT hr;
11401
11402 MA_ASSERT(pContext != NULL);
11403 MA_ASSERT(ppAudioClient != NULL);
11404 MA_ASSERT(ppMMDevice != NULL);
11405
11406 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
11407 if (result != MA_SUCCESS) {
11408 return result;
11409 }
11410
11411 hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient);
11412 if (FAILED(hr)) {
11413 return ma_result_from_HRESULT(hr);
11414 }
11415
11416 return MA_SUCCESS;
11417 }
11418 #else
11419 static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface)
11420 {
11421 ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
11422 ma_completion_handler_uwp completionHandler;
11423 IID iid;
11424 LPOLESTR iidStr;
11425 HRESULT hr;
11426 ma_result result;
11427 HRESULT activateResult;
11428 ma_IUnknown* pActivatedInterface;
11429
11430 MA_ASSERT(pContext != NULL);
11431 MA_ASSERT(ppAudioClient != NULL);
11432
11433 if (pDeviceID != NULL) {
11434 MA_COPY_MEMORY(&iid, pDeviceID->wasapi, sizeof(iid));
11435 } else {
11436 if (deviceType == ma_device_type_playback) {
11437 iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
11438 } else {
11439 iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
11440 }
11441 }
11442
11443 #if defined(__cplusplus)
11444 hr = StringFromIID(iid, &iidStr);
11445 #else
11446 hr = StringFromIID(&iid, &iidStr);
11447 #endif
11448 if (FAILED(hr)) {
11449 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", ma_result_from_HRESULT(hr));
11450 }
11451
11452 result = ma_completion_handler_uwp_init(&completionHandler);
11453 if (result != MA_SUCCESS) {
11454 ma_CoTaskMemFree(pContext, iidStr);
11455 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", result);
11456 }
11457
11458 #if defined(__cplusplus)
11459 hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
11460 #else
11461 hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
11462 #endif
11463 if (FAILED(hr)) {
11464 ma_completion_handler_uwp_uninit(&completionHandler);
11465 ma_CoTaskMemFree(pContext, iidStr);
11466 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", ma_result_from_HRESULT(hr));
11467 }
11468
11469 ma_CoTaskMemFree(pContext, iidStr);
11470
11471 /* Wait for the async operation for finish. */
11472 ma_completion_handler_uwp_wait(&completionHandler);
11473 ma_completion_handler_uwp_uninit(&completionHandler);
11474
11475 hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
11476 ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
11477
11478 if (FAILED(hr) || FAILED(activateResult)) {
11479 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.", FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult));
11480 }
11481
11482 /* Here is where we grab the IAudioClient interface. */
11483 hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
11484 if (FAILED(hr)) {
11485 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", ma_result_from_HRESULT(hr));
11486 }
11487
11488 if (ppActivatedInterface) {
11489 *ppActivatedInterface = pActivatedInterface;
11490 } else {
11491 ma_IUnknown_Release(pActivatedInterface);
11492 }
11493
11494 return MA_SUCCESS;
11495 }
11496 #endif
11497
11498 static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface)
11499 {
11500 #ifdef MA_WIN32_DESKTOP
11501 return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
11502 #else
11503 return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
11504 #endif
11505 }
11506
11507
11508 static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
11509 {
11510 /* Different enumeration for desktop and UWP. */
11511 #ifdef MA_WIN32_DESKTOP
11512 /* Desktop */
11513 HRESULT hr;
11514 ma_IMMDeviceEnumerator* pDeviceEnumerator;
11515
11516 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
11517 if (FAILED(hr)) {
11518 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
11519 }
11520
11521 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
11522 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
11523
11524 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
11525 #else
11526 /*
11527 UWP
11528
11529 The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
11530 over devices without using MMDevice, I'm restricting devices to defaults.
11531
11532 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/
11533 */
11534 if (callback) {
11535 ma_bool32 cbResult = MA_TRUE;
11536
11537 /* Playback. */
11538 if (cbResult) {
11539 ma_device_info deviceInfo;
11540 MA_ZERO_OBJECT(&deviceInfo);
11541 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
11542 deviceInfo._private.isDefault = MA_TRUE;
11543 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
11544 }
11545
11546 /* Capture. */
11547 if (cbResult) {
11548 ma_device_info deviceInfo;
11549 MA_ZERO_OBJECT(&deviceInfo);
11550 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
11551 deviceInfo._private.isDefault = MA_TRUE;
11552 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
11553 }
11554 }
11555 #endif
11556
11557 return MA_SUCCESS;
11558 }
11559
11560 static 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)
11561 {
11562 #ifdef MA_WIN32_DESKTOP
11563 ma_result result;
11564 ma_IMMDevice* pMMDevice = NULL;
11565 LPWSTR pDefaultDeviceID = NULL;
11566
11567 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
11568 if (result != MA_SUCCESS) {
11569 return result;
11570 }
11571
11572 /* We need the default device ID so we can set the isDefault flag in the device info. */
11573 pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
11574
11575 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
11576
11577 if (pDefaultDeviceID != NULL) {
11578 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
11579 pDefaultDeviceID = NULL;
11580 }
11581
11582 ma_IMMDevice_Release(pMMDevice);
11583
11584 return result;
11585 #else
11586 ma_IAudioClient* pAudioClient;
11587 ma_result result;
11588
11589 /* UWP currently only uses default devices. */
11590 if (deviceType == ma_device_type_playback) {
11591 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
11592 } else {
11593 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
11594 }
11595
11596 /* Not currently supporting exclusive mode on UWP. */
11597 if (shareMode == ma_share_mode_exclusive) {
11598 return MA_ERROR;
11599 }
11600
11601 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL);
11602 if (result != MA_SUCCESS) {
11603 return result;
11604 }
11605
11606 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, shareMode, pDeviceInfo);
11607
11608 pDeviceInfo->_private.isDefault = MA_TRUE; /* UWP only supports default devices. */
11609
11610 ma_IAudioClient_Release(pAudioClient);
11611 return result;
11612 #endif
11613 }
11614
11615 static void ma_device_uninit__wasapi(ma_device* pDevice)
11616 {
11617 MA_ASSERT(pDevice != NULL);
11618
11619 #ifdef MA_WIN32_DESKTOP
11620 if (pDevice->wasapi.pDeviceEnumerator) {
11621 ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
11622 ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
11623 }
11624 #endif
11625
11626 if (pDevice->wasapi.pRenderClient) {
11627 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
11628 }
11629 if (pDevice->wasapi.pCaptureClient) {
11630 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
11631 }
11632
11633 if (pDevice->wasapi.pAudioClientPlayback) {
11634 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
11635 }
11636 if (pDevice->wasapi.pAudioClientCapture) {
11637 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
11638 }
11639
11640 if (pDevice->wasapi.hEventPlayback) {
11641 CloseHandle(pDevice->wasapi.hEventPlayback);
11642 }
11643 if (pDevice->wasapi.hEventCapture) {
11644 CloseHandle(pDevice->wasapi.hEventCapture);
11645 }
11646 }
11647
11648
11649 typedef struct
11650 {
11651 /* Input. */
11652 ma_format formatIn;
11653 ma_uint32 channelsIn;
11654 ma_uint32 sampleRateIn;
11655 ma_channel channelMapIn[MA_MAX_CHANNELS];
11656 ma_uint32 periodSizeInFramesIn;
11657 ma_uint32 periodSizeInMillisecondsIn;
11658 ma_uint32 periodsIn;
11659 ma_bool32 usingDefaultFormat;
11660 ma_bool32 usingDefaultChannels;
11661 ma_bool32 usingDefaultSampleRate;
11662 ma_bool32 usingDefaultChannelMap;
11663 ma_share_mode shareMode;
11664 ma_bool32 noAutoConvertSRC;
11665 ma_bool32 noDefaultQualitySRC;
11666 ma_bool32 noHardwareOffloading;
11667
11668 /* Output. */
11669 ma_IAudioClient* pAudioClient;
11670 ma_IAudioRenderClient* pRenderClient;
11671 ma_IAudioCaptureClient* pCaptureClient;
11672 ma_format formatOut;
11673 ma_uint32 channelsOut;
11674 ma_uint32 sampleRateOut;
11675 ma_channel channelMapOut[MA_MAX_CHANNELS];
11676 ma_uint32 periodSizeInFramesOut;
11677 ma_uint32 periodsOut;
11678 ma_bool32 usingAudioClient3;
11679 char deviceName[256];
11680 } ma_device_init_internal_data__wasapi;
11681
11682 static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData)
11683 {
11684 HRESULT hr;
11685 ma_result result = MA_SUCCESS;
11686 const char* errorMsg = "";
11687 MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
11688 DWORD streamFlags = 0;
11689 MA_REFERENCE_TIME periodDurationInMicroseconds;
11690 ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
11691 WAVEFORMATEXTENSIBLE wf;
11692 ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
11693 ma_IAudioClient2* pAudioClient2;
11694 ma_uint32 nativeSampleRate;
11695
11696 MA_ASSERT(pContext != NULL);
11697 MA_ASSERT(pData != NULL);
11698
11699 /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */
11700 if (deviceType == ma_device_type_duplex) {
11701 return MA_INVALID_ARGS;
11702 }
11703
11704 pData->pAudioClient = NULL;
11705 pData->pRenderClient = NULL;
11706 pData->pCaptureClient = NULL;
11707
11708 streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
11709 if (!pData->noAutoConvertSRC && !pData->usingDefaultSampleRate && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */
11710 streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
11711 }
11712 if (!pData->noDefaultQualitySRC && !pData->usingDefaultSampleRate && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
11713 streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
11714 }
11715 if (deviceType == ma_device_type_loopback) {
11716 streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
11717 }
11718
11719 result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface);
11720 if (result != MA_SUCCESS) {
11721 goto done;
11722 }
11723
11724 MA_ZERO_OBJECT(&wf);
11725
11726 /* Try enabling hardware offloading. */
11727 if (!pData->noHardwareOffloading) {
11728 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
11729 if (SUCCEEDED(hr)) {
11730 BOOL isHardwareOffloadingSupported = 0;
11731 hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
11732 if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
11733 ma_AudioClientProperties clientProperties;
11734 MA_ZERO_OBJECT(&clientProperties);
11735 clientProperties.cbSize = sizeof(clientProperties);
11736 clientProperties.bIsOffload = 1;
11737 clientProperties.eCategory = MA_AudioCategory_Other;
11738 ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
11739 }
11740
11741 pAudioClient2->lpVtbl->Release(pAudioClient2);
11742 }
11743 }
11744
11745 /* 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. */
11746 result = MA_FORMAT_NOT_SUPPORTED;
11747 if (pData->shareMode == ma_share_mode_exclusive) {
11748 #ifdef MA_WIN32_DESKTOP
11749 /* In exclusive mode on desktop we always use the backend's native format. */
11750 ma_IPropertyStore* pStore = NULL;
11751 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
11752 if (SUCCEEDED(hr)) {
11753 PROPVARIANT prop;
11754 ma_PropVariantInit(&prop);
11755 hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
11756 if (SUCCEEDED(hr)) {
11757 WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData;
11758 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
11759 if (SUCCEEDED(hr)) {
11760 MA_COPY_MEMORY(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE));
11761 }
11762
11763 ma_PropVariantClear(pContext, &prop);
11764 }
11765
11766 ma_IPropertyStore_Release(pStore);
11767 }
11768 #else
11769 /*
11770 I do not know how to query the device's native format on UWP so for now I'm just disabling support for
11771 exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()
11772 until you find one that works.
11773
11774 TODO: Add support for exclusive mode to UWP.
11775 */
11776 hr = S_FALSE;
11777 #endif
11778
11779 if (hr == S_OK) {
11780 shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
11781 result = MA_SUCCESS;
11782 } else {
11783 result = MA_SHARE_MODE_NOT_SUPPORTED;
11784 }
11785 } else {
11786 /* In shared mode we are always using the format reported by the operating system. */
11787 WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
11788 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat);
11789 if (hr != S_OK) {
11790 result = MA_FORMAT_NOT_SUPPORTED;
11791 } else {
11792 MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(wf));
11793 result = MA_SUCCESS;
11794 }
11795
11796 ma_CoTaskMemFree(pContext, pNativeFormat);
11797
11798 shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
11799 }
11800
11801 /* Return an error if we still haven't found a format. */
11802 if (result != MA_SUCCESS) {
11803 errorMsg = "[WASAPI] Failed to find best device mix format.";
11804 goto done;
11805 }
11806
11807 /*
11808 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
11809 WASAPI to perform the sample rate conversion.
11810 */
11811 nativeSampleRate = wf.Format.nSamplesPerSec;
11812 if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
11813 wf.Format.nSamplesPerSec = pData->sampleRateIn;
11814 wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign;
11815 }
11816
11817 pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf);
11818 pData->channelsOut = wf.Format.nChannels;
11819 pData->sampleRateOut = wf.Format.nSamplesPerSec;
11820
11821 /* Get the internal channel map based on the channel mask. */
11822 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
11823
11824 /* Period size. */
11825 pData->periodsOut = pData->periodsIn;
11826 pData->periodSizeInFramesOut = pData->periodSizeInFramesIn;
11827 if (pData->periodSizeInFramesOut == 0) {
11828 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec);
11829 }
11830
11831 periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec;
11832
11833
11834 /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
11835 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
11836 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * 10;
11837
11838 /*
11839 If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
11840 it and trying it again.
11841 */
11842 hr = E_FAIL;
11843 for (;;) {
11844 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
11845 if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
11846 if (bufferDuration > 500*10000) {
11847 break;
11848 } else {
11849 if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */
11850 break;
11851 }
11852
11853 bufferDuration = bufferDuration * 2;
11854 continue;
11855 }
11856 } else {
11857 break;
11858 }
11859 }
11860
11861 if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
11862 ma_uint32 bufferSizeInFrames;
11863 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
11864 if (SUCCEEDED(hr)) {
11865 bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5);
11866
11867 /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
11868 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
11869
11870 #ifdef MA_WIN32_DESKTOP
11871 hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
11872 #else
11873 hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
11874 #endif
11875
11876 if (SUCCEEDED(hr)) {
11877 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
11878 }
11879 }
11880 }
11881
11882 if (FAILED(hr)) {
11883 /* 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. */
11884 if (hr == E_ACCESSDENIED) {
11885 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
11886 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
11887 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY;
11888 } else {
11889 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr);
11890 }
11891 goto done;
11892 }
11893 }
11894
11895 if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
11896 /*
11897 Low latency shared mode via IAudioClient3.
11898
11899 NOTE
11900 ====
11901 Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
11902 use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
11903 any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
11904 that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
11905 */
11906 #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
11907 if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) {
11908 ma_IAudioClient3* pAudioClient3 = NULL;
11909 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
11910 if (SUCCEEDED(hr)) {
11911 UINT32 defaultPeriodInFrames;
11912 UINT32 fundamentalPeriodInFrames;
11913 UINT32 minPeriodInFrames;
11914 UINT32 maxPeriodInFrames;
11915 hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
11916 if (SUCCEEDED(hr)) {
11917 UINT32 desiredPeriodInFrames = pData->periodSizeInFramesOut;
11918 UINT32 actualPeriodInFrames = desiredPeriodInFrames;
11919
11920 /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
11921 actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
11922 actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
11923
11924 /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
11925 actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
11926
11927 #if defined(MA_DEBUG_OUTPUT)
11928 printf("[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
11929 printf(" defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
11930 printf(" fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
11931 printf(" minPeriodInFrames=%d\n", minPeriodInFrames);
11932 printf(" maxPeriodInFrames=%d\n", maxPeriodInFrames);
11933 #endif
11934
11935 /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
11936 if (actualPeriodInFrames >= desiredPeriodInFrames) {
11937 /*
11938 MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,
11939 IAudioClient3_InitializeSharedAudioStream() will fail.
11940 */
11941 hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL);
11942 if (SUCCEEDED(hr)) {
11943 wasInitializedUsingIAudioClient3 = MA_TRUE;
11944 pData->periodSizeInFramesOut = actualPeriodInFrames;
11945 #if defined(MA_DEBUG_OUTPUT)
11946 printf("[WASAPI] Using IAudioClient3\n");
11947 printf(" periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
11948 #endif
11949 } else {
11950 #if defined(MA_DEBUG_OUTPUT)
11951 printf("[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
11952 #endif
11953 }
11954 } else {
11955 #if defined(MA_DEBUG_OUTPUT)
11956 printf("[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n");
11957 #endif
11958 }
11959 } else {
11960 #if defined(MA_DEBUG_OUTPUT)
11961 printf("[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
11962 #endif
11963 }
11964
11965 ma_IAudioClient3_Release(pAudioClient3);
11966 pAudioClient3 = NULL;
11967 }
11968 }
11969 #else
11970 #if defined(MA_DEBUG_OUTPUT)
11971 printf("[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n");
11972 #endif
11973 #endif
11974
11975 /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
11976 if (!wasInitializedUsingIAudioClient3) {
11977 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */
11978 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL);
11979 if (FAILED(hr)) {
11980 if (hr == E_ACCESSDENIED) {
11981 errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
11982 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
11983 errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY;
11984 } else {
11985 errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr);
11986 }
11987
11988 goto done;
11989 }
11990 }
11991 }
11992
11993 if (!wasInitializedUsingIAudioClient3) {
11994 ma_uint32 bufferSizeInFrames;
11995 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
11996 if (FAILED(hr)) {
11997 errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr);
11998 goto done;
11999 }
12000
12001 pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;
12002 }
12003
12004 pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
12005
12006 if (deviceType == ma_device_type_playback) {
12007 hr = ma_IAudioClient_GetService((ma_IAudioClient*)pData->pAudioClient, &MA_IID_IAudioRenderClient, (void**)&pData->pRenderClient);
12008 } else {
12009 hr = ma_IAudioClient_GetService((ma_IAudioClient*)pData->pAudioClient, &MA_IID_IAudioCaptureClient, (void**)&pData->pCaptureClient);
12010 }
12011
12012 if (FAILED(hr)) {
12013 errorMsg = "[WASAPI] Failed to get audio client service.", result = ma_result_from_HRESULT(hr);
12014 goto done;
12015 }
12016
12017
12018 /* Grab the name of the device. */
12019 #ifdef MA_WIN32_DESKTOP
12020 {
12021 ma_IPropertyStore *pProperties;
12022 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
12023 if (SUCCEEDED(hr)) {
12024 PROPVARIANT varName;
12025 ma_PropVariantInit(&varName);
12026 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
12027 if (SUCCEEDED(hr)) {
12028 WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
12029 ma_PropVariantClear(pContext, &varName);
12030 }
12031
12032 ma_IPropertyStore_Release(pProperties);
12033 }
12034 }
12035 #endif
12036
12037 done:
12038 /* Clean up. */
12039 #ifdef MA_WIN32_DESKTOP
12040 if (pDeviceInterface != NULL) {
12041 ma_IMMDevice_Release(pDeviceInterface);
12042 }
12043 #else
12044 if (pDeviceInterface != NULL) {
12045 ma_IUnknown_Release(pDeviceInterface);
12046 }
12047 #endif
12048
12049 if (result != MA_SUCCESS) {
12050 if (pData->pRenderClient) {
12051 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
12052 pData->pRenderClient = NULL;
12053 }
12054 if (pData->pCaptureClient) {
12055 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
12056 pData->pCaptureClient = NULL;
12057 }
12058 if (pData->pAudioClient) {
12059 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
12060 pData->pAudioClient = NULL;
12061 }
12062
12063 if (errorMsg != NULL && errorMsg[0] != '\0') {
12064 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, errorMsg, result);
12065 }
12066
12067 return result;
12068 } else {
12069 return MA_SUCCESS;
12070 }
12071 }
12072
12073 static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
12074 {
12075 ma_device_init_internal_data__wasapi data;
12076 ma_result result;
12077
12078 MA_ASSERT(pDevice != NULL);
12079
12080 /* We only re-initialize the playback or capture device. Never a full-duplex device. */
12081 if (deviceType == ma_device_type_duplex) {
12082 return MA_INVALID_ARGS;
12083 }
12084
12085 if (deviceType == ma_device_type_playback) {
12086 data.formatIn = pDevice->playback.format;
12087 data.channelsIn = pDevice->playback.channels;
12088 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
12089 data.shareMode = pDevice->playback.shareMode;
12090 data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
12091 data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
12092 data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
12093 } else {
12094 data.formatIn = pDevice->capture.format;
12095 data.channelsIn = pDevice->capture.channels;
12096 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
12097 data.shareMode = pDevice->capture.shareMode;
12098 data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
12099 data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
12100 data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
12101 }
12102
12103 data.sampleRateIn = pDevice->sampleRate;
12104 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
12105 data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames;
12106 data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;
12107 data.periodsIn = pDevice->wasapi.originalPeriods;
12108 data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
12109 data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
12110 data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
12111 result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
12112 if (result != MA_SUCCESS) {
12113 return result;
12114 }
12115
12116 /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
12117 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
12118 if (pDevice->wasapi.pCaptureClient) {
12119 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
12120 pDevice->wasapi.pCaptureClient = NULL;
12121 }
12122
12123 if (pDevice->wasapi.pAudioClientCapture) {
12124 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
12125 pDevice->wasapi.pAudioClientCapture = NULL;
12126 }
12127
12128 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
12129 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
12130
12131 pDevice->capture.internalFormat = data.formatOut;
12132 pDevice->capture.internalChannels = data.channelsOut;
12133 pDevice->capture.internalSampleRate = data.sampleRateOut;
12134 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
12135 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
12136 pDevice->capture.internalPeriods = data.periodsOut;
12137 ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
12138
12139 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
12140
12141 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
12142 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture);
12143
12144 /* The device may be in a started state. If so we need to immediately restart it. */
12145 if (pDevice->wasapi.isStartedCapture) {
12146 HRESULT hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
12147 if (FAILED(hr)) {
12148 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device after reinitialization.", ma_result_from_HRESULT(hr));
12149 }
12150 }
12151 }
12152
12153 if (deviceType == ma_device_type_playback) {
12154 if (pDevice->wasapi.pRenderClient) {
12155 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
12156 pDevice->wasapi.pRenderClient = NULL;
12157 }
12158
12159 if (pDevice->wasapi.pAudioClientPlayback) {
12160 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
12161 pDevice->wasapi.pAudioClientPlayback = NULL;
12162 }
12163
12164 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
12165 pDevice->wasapi.pRenderClient = data.pRenderClient;
12166
12167 pDevice->playback.internalFormat = data.formatOut;
12168 pDevice->playback.internalChannels = data.channelsOut;
12169 pDevice->playback.internalSampleRate = data.sampleRateOut;
12170 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
12171 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
12172 pDevice->playback.internalPeriods = data.periodsOut;
12173 ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
12174
12175 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
12176
12177 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
12178 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback);
12179
12180 /* The device may be in a started state. If so we need to immediately restart it. */
12181 if (pDevice->wasapi.isStartedPlayback) {
12182 HRESULT hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
12183 if (FAILED(hr)) {
12184 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device after reinitialization.", ma_result_from_HRESULT(hr));
12185 }
12186 }
12187 }
12188
12189 return MA_SUCCESS;
12190 }
12191
12192 static ma_result ma_device_init__wasapi(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
12193 {
12194 ma_result result = MA_SUCCESS;
12195
12196 (void)pContext;
12197
12198 MA_ASSERT(pContext != NULL);
12199 MA_ASSERT(pDevice != NULL);
12200
12201 MA_ZERO_OBJECT(&pDevice->wasapi);
12202 pDevice->wasapi.originalPeriodSizeInFrames = pConfig->periodSizeInFrames;
12203 pDevice->wasapi.originalPeriodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
12204 pDevice->wasapi.originalPeriods = pConfig->periods;
12205 pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
12206 pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
12207 pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
12208
12209 /* Exclusive mode is not allowed with loopback. */
12210 if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) {
12211 return MA_INVALID_DEVICE_CONFIG;
12212 }
12213
12214 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
12215 ma_device_init_internal_data__wasapi data;
12216 data.formatIn = pConfig->capture.format;
12217 data.channelsIn = pConfig->capture.channels;
12218 data.sampleRateIn = pConfig->sampleRate;
12219 MA_COPY_MEMORY(data.channelMapIn, pConfig->capture.channelMap, sizeof(pConfig->capture.channelMap));
12220 data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
12221 data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
12222 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
12223 data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
12224 data.shareMode = pConfig->capture.shareMode;
12225 data.periodSizeInFramesIn = pConfig->periodSizeInFrames;
12226 data.periodSizeInMillisecondsIn = pConfig->periodSizeInMilliseconds;
12227 data.periodsIn = pConfig->periods;
12228 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
12229 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
12230 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
12231
12232 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);
12233 if (result != MA_SUCCESS) {
12234 return result;
12235 }
12236
12237 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
12238 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
12239
12240 pDevice->capture.internalFormat = data.formatOut;
12241 pDevice->capture.internalChannels = data.channelsOut;
12242 pDevice->capture.internalSampleRate = data.sampleRateOut;
12243 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
12244 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
12245 pDevice->capture.internalPeriods = data.periodsOut;
12246 ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
12247
12248 /*
12249 The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
12250 however, because we want to block until we actually have something for the first call to ma_device_read().
12251 */
12252 pDevice->wasapi.hEventCapture = CreateEventW(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
12253 if (pDevice->wasapi.hEventCapture == NULL) {
12254 result = ma_result_from_GetLastError(GetLastError());
12255
12256 if (pDevice->wasapi.pCaptureClient != NULL) {
12257 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
12258 pDevice->wasapi.pCaptureClient = NULL;
12259 }
12260 if (pDevice->wasapi.pAudioClientCapture != NULL) {
12261 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
12262 pDevice->wasapi.pAudioClientCapture = NULL;
12263 }
12264
12265 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.", result);
12266 }
12267 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
12268
12269 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
12270 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture);
12271 }
12272
12273 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
12274 ma_device_init_internal_data__wasapi data;
12275 data.formatIn = pConfig->playback.format;
12276 data.channelsIn = pConfig->playback.channels;
12277 data.sampleRateIn = pConfig->sampleRate;
12278 MA_COPY_MEMORY(data.channelMapIn, pConfig->playback.channelMap, sizeof(pConfig->playback.channelMap));
12279 data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
12280 data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
12281 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
12282 data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
12283 data.shareMode = pConfig->playback.shareMode;
12284 data.periodSizeInFramesIn = pConfig->periodSizeInFrames;
12285 data.periodSizeInMillisecondsIn = pConfig->periodSizeInMilliseconds;
12286 data.periodsIn = pConfig->periods;
12287 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
12288 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
12289 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
12290
12291 result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pConfig->playback.pDeviceID, &data);
12292 if (result != MA_SUCCESS) {
12293 if (pConfig->deviceType == ma_device_type_duplex) {
12294 if (pDevice->wasapi.pCaptureClient != NULL) {
12295 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
12296 pDevice->wasapi.pCaptureClient = NULL;
12297 }
12298 if (pDevice->wasapi.pAudioClientCapture != NULL) {
12299 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
12300 pDevice->wasapi.pAudioClientCapture = NULL;
12301 }
12302
12303 CloseHandle(pDevice->wasapi.hEventCapture);
12304 pDevice->wasapi.hEventCapture = NULL;
12305 }
12306 return result;
12307 }
12308
12309 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
12310 pDevice->wasapi.pRenderClient = data.pRenderClient;
12311
12312 pDevice->playback.internalFormat = data.formatOut;
12313 pDevice->playback.internalChannels = data.channelsOut;
12314 pDevice->playback.internalSampleRate = data.sampleRateOut;
12315 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
12316 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
12317 pDevice->playback.internalPeriods = data.periodsOut;
12318 ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
12319
12320 /*
12321 The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
12322 only after the whole available space has been filled, never before.
12323
12324 The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
12325 to get passed WaitForMultipleObjects().
12326 */
12327 pDevice->wasapi.hEventPlayback = CreateEventW(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
12328 if (pDevice->wasapi.hEventPlayback == NULL) {
12329 result = ma_result_from_GetLastError(GetLastError());
12330
12331 if (pConfig->deviceType == ma_device_type_duplex) {
12332 if (pDevice->wasapi.pCaptureClient != NULL) {
12333 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
12334 pDevice->wasapi.pCaptureClient = NULL;
12335 }
12336 if (pDevice->wasapi.pAudioClientCapture != NULL) {
12337 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
12338 pDevice->wasapi.pAudioClientCapture = NULL;
12339 }
12340
12341 CloseHandle(pDevice->wasapi.hEventCapture);
12342 pDevice->wasapi.hEventCapture = NULL;
12343 }
12344
12345 if (pDevice->wasapi.pRenderClient != NULL) {
12346 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
12347 pDevice->wasapi.pRenderClient = NULL;
12348 }
12349 if (pDevice->wasapi.pAudioClientPlayback != NULL) {
12350 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
12351 pDevice->wasapi.pAudioClientPlayback = NULL;
12352 }
12353
12354 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.", result);
12355 }
12356 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
12357
12358 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
12359 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback);
12360 }
12361
12362 /*
12363 We need to get notifications of when the default device changes. We do this through a device enumerator by
12364 registering a IMMNotificationClient with it. We only care about this if it's the default device.
12365 */
12366 #ifdef MA_WIN32_DESKTOP
12367 if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
12368 if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID == NULL) {
12369 pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;
12370 }
12371 if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
12372 pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
12373 }
12374
12375 if (pDevice->wasapi.allowCaptureAutoStreamRouting || pDevice->wasapi.allowPlaybackAutoStreamRouting) {
12376 ma_IMMDeviceEnumerator* pDeviceEnumerator;
12377 HRESULT hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
12378 if (FAILED(hr)) {
12379 ma_device_uninit__wasapi(pDevice);
12380 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
12381 }
12382
12383 pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
12384 pDevice->wasapi.notificationClient.counter = 1;
12385 pDevice->wasapi.notificationClient.pDevice = pDevice;
12386
12387 hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
12388 if (SUCCEEDED(hr)) {
12389 pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
12390 } else {
12391 /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
12392 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
12393 }
12394 }
12395 }
12396 #endif
12397
12398 ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
12399 ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
12400
12401 return MA_SUCCESS;
12402 }
12403
12404 static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
12405 {
12406 ma_uint32 paddingFramesCount;
12407 HRESULT hr;
12408 ma_share_mode shareMode;
12409
12410 MA_ASSERT(pDevice != NULL);
12411 MA_ASSERT(pFrameCount != NULL);
12412
12413 *pFrameCount = 0;
12414
12415 if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
12416 return MA_INVALID_OPERATION;
12417 }
12418
12419 hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
12420 if (FAILED(hr)) {
12421 return ma_result_from_HRESULT(hr);
12422 }
12423
12424 /* Slightly different rules for exclusive and shared modes. */
12425 shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
12426 if (shareMode == ma_share_mode_exclusive) {
12427 *pFrameCount = paddingFramesCount;
12428 } else {
12429 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
12430 *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesPlayback - paddingFramesCount;
12431 } else {
12432 *pFrameCount = paddingFramesCount;
12433 }
12434 }
12435
12436 return MA_SUCCESS;
12437 }
12438
12439 static ma_bool32 ma_device_is_reroute_required__wasapi(ma_device* pDevice, ma_device_type deviceType)
12440 {
12441 MA_ASSERT(pDevice != NULL);
12442
12443 if (deviceType == ma_device_type_playback) {
12444 return pDevice->wasapi.hasDefaultPlaybackDeviceChanged;
12445 }
12446
12447 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
12448 return pDevice->wasapi.hasDefaultCaptureDeviceChanged;
12449 }
12450
12451 return MA_FALSE;
12452 }
12453
12454 static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
12455 {
12456 ma_result result;
12457
12458 if (deviceType == ma_device_type_duplex) {
12459 return MA_INVALID_ARGS;
12460 }
12461
12462 if (deviceType == ma_device_type_playback) {
12463 ma_atomic_exchange_32(&pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_FALSE);
12464 }
12465 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
12466 ma_atomic_exchange_32(&pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_FALSE);
12467 }
12468
12469
12470 #ifdef MA_DEBUG_OUTPUT
12471 printf("=== CHANGING DEVICE ===\n");
12472 #endif
12473
12474 result = ma_device_reinit__wasapi(pDevice, deviceType);
12475 if (result != MA_SUCCESS) {
12476 return result;
12477 }
12478
12479 ma_device__post_init_setup(pDevice, deviceType);
12480
12481 return MA_SUCCESS;
12482 }
12483
12484
12485 static ma_result ma_device_stop__wasapi(ma_device* pDevice)
12486 {
12487 MA_ASSERT(pDevice != NULL);
12488
12489 /*
12490 We need to explicitly signal the capture event in loopback mode to ensure we return from WaitForSingleObject() when nothing is being played. When nothing
12491 is being played, the event is never signalled internally by WASAPI which means we will deadlock when stopping the device.
12492 */
12493 if (pDevice->type == ma_device_type_loopback) {
12494 SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
12495 }
12496
12497 return MA_SUCCESS;
12498 }
12499
12500
12501 static ma_result ma_device_main_loop__wasapi(ma_device* pDevice)
12502 {
12503 ma_result result;
12504 HRESULT hr;
12505 ma_bool32 exitLoop = MA_FALSE;
12506 ma_uint32 framesWrittenToPlaybackDevice = 0;
12507 ma_uint32 mappedDeviceBufferSizeInFramesCapture = 0;
12508 ma_uint32 mappedDeviceBufferSizeInFramesPlayback = 0;
12509 ma_uint32 mappedDeviceBufferFramesRemainingCapture = 0;
12510 ma_uint32 mappedDeviceBufferFramesRemainingPlayback = 0;
12511 BYTE* pMappedDeviceBufferCapture = NULL;
12512 BYTE* pMappedDeviceBufferPlayback = NULL;
12513 ma_uint32 bpfCaptureDevice = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
12514 ma_uint32 bpfPlaybackDevice = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
12515 ma_uint32 bpfCaptureClient = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
12516 ma_uint32 bpfPlaybackClient = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
12517 ma_uint8 inputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12518 ma_uint32 inputDataInClientFormatCap = sizeof(inputDataInClientFormat) / bpfCaptureClient;
12519 ma_uint8 outputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12520 ma_uint32 outputDataInClientFormatCap = sizeof(outputDataInClientFormat) / bpfPlaybackClient;
12521 ma_uint32 outputDataInClientFormatCount = 0;
12522 ma_uint32 outputDataInClientFormatConsumed = 0;
12523 ma_uint32 periodSizeInFramesCapture = 0;
12524
12525 MA_ASSERT(pDevice != NULL);
12526
12527 /* The capture device needs to be started immediately. */
12528 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
12529 periodSizeInFramesCapture = pDevice->capture.internalPeriodSizeInFrames;
12530
12531 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
12532 if (FAILED(hr)) {
12533 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", ma_result_from_HRESULT(hr));
12534 }
12535 ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE);
12536 }
12537
12538 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
12539 /* We may need to reroute the device. */
12540 if (ma_device_is_reroute_required__wasapi(pDevice, ma_device_type_playback)) {
12541 result = ma_device_reroute__wasapi(pDevice, ma_device_type_playback);
12542 if (result != MA_SUCCESS) {
12543 exitLoop = MA_TRUE;
12544 break;
12545 }
12546 }
12547 if (ma_device_is_reroute_required__wasapi(pDevice, ma_device_type_capture)) {
12548 result = ma_device_reroute__wasapi(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
12549 if (result != MA_SUCCESS) {
12550 exitLoop = MA_TRUE;
12551 break;
12552 }
12553 }
12554
12555 switch (pDevice->type)
12556 {
12557 case ma_device_type_duplex:
12558 {
12559 ma_uint32 framesAvailableCapture;
12560 ma_uint32 framesAvailablePlayback;
12561 DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
12562
12563 /* The process is to map the playback buffer and fill it as quickly as possible from input data. */
12564 if (pMappedDeviceBufferPlayback == NULL) {
12565 /* WASAPI is weird with exclusive mode. You need to wait on the event _before_ querying the available frames. */
12566 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
12567 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
12568 return MA_ERROR; /* Wait failed. */
12569 }
12570 }
12571
12572 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
12573 if (result != MA_SUCCESS) {
12574 return result;
12575 }
12576
12577 /*printf("TRACE 1: framesAvailablePlayback=%d\n", framesAvailablePlayback);*/
12578
12579
12580 /* In exclusive mode, the frame count needs to exactly match the value returned by GetCurrentPadding(). */
12581 if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
12582 if (framesAvailablePlayback > pDevice->wasapi.periodSizeInFramesPlayback) {
12583 framesAvailablePlayback = pDevice->wasapi.periodSizeInFramesPlayback;
12584 }
12585 }
12586
12587 /* If there's no frames available in the playback device we need to wait for more. */
12588 if (framesAvailablePlayback == 0) {
12589 /* In exclusive mode we waited at the top. */
12590 if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
12591 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
12592 return MA_ERROR; /* Wait failed. */
12593 }
12594 }
12595
12596 continue;
12597 }
12598
12599 /* We're ready to map the playback device's buffer. We don't release this until it's been entirely filled. */
12600 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback);
12601 if (FAILED(hr)) {
12602 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
12603 exitLoop = MA_TRUE;
12604 break;
12605 }
12606
12607 mappedDeviceBufferSizeInFramesPlayback = framesAvailablePlayback;
12608 mappedDeviceBufferFramesRemainingPlayback = framesAvailablePlayback;
12609 }
12610
12611 /* At this point we should have a buffer available for output. We need to keep writing input samples to it. */
12612 for (;;) {
12613 /* Try grabbing some captured data if we haven't already got a mapped buffer. */
12614 if (pMappedDeviceBufferCapture == NULL) {
12615 if (pDevice->capture.shareMode == ma_share_mode_shared) {
12616 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
12617 return MA_ERROR; /* Wait failed. */
12618 }
12619 }
12620
12621 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
12622 if (result != MA_SUCCESS) {
12623 exitLoop = MA_TRUE;
12624 break;
12625 }
12626
12627 /*printf("TRACE 2: framesAvailableCapture=%d\n", framesAvailableCapture);*/
12628
12629 /* Wait for more if nothing is available. */
12630 if (framesAvailableCapture == 0) {
12631 /* In exclusive mode we waited at the top. */
12632 if (pDevice->capture.shareMode != ma_share_mode_shared) {
12633 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
12634 return MA_ERROR; /* Wait failed. */
12635 }
12636 }
12637
12638 continue;
12639 }
12640
12641 /* Getting here means there's data available for writing to the output device. */
12642 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
12643 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
12644 if (FAILED(hr)) {
12645 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
12646 exitLoop = MA_TRUE;
12647 break;
12648 }
12649
12650
12651 /* Overrun detection. */
12652 if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
12653 /* Glitched. Probably due to an overrun. */
12654 #ifdef MA_DEBUG_OUTPUT
12655 printf("[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
12656 #endif
12657
12658 /*
12659 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
12660 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
12661 last period.
12662 */
12663 if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) {
12664 #ifdef MA_DEBUG_OUTPUT
12665 printf("[WASAPI] Synchronizing capture stream. ");
12666 #endif
12667 do
12668 {
12669 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
12670 if (FAILED(hr)) {
12671 break;
12672 }
12673
12674 framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture;
12675
12676 if (framesAvailableCapture > 0) {
12677 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
12678 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
12679 if (FAILED(hr)) {
12680 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
12681 exitLoop = MA_TRUE;
12682 break;
12683 }
12684 } else {
12685 pMappedDeviceBufferCapture = NULL;
12686 mappedDeviceBufferSizeInFramesCapture = 0;
12687 }
12688 } while (framesAvailableCapture > periodSizeInFramesCapture);
12689 #ifdef MA_DEBUG_OUTPUT
12690 printf("framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
12691 #endif
12692 }
12693 } else {
12694 #ifdef MA_DEBUG_OUTPUT
12695 if (flagsCapture != 0) {
12696 printf("[WASAPI] Capture Flags: %d\n", flagsCapture);
12697 }
12698 #endif
12699 }
12700
12701 mappedDeviceBufferFramesRemainingCapture = mappedDeviceBufferSizeInFramesCapture;
12702 }
12703
12704
12705 /* 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. */
12706 for (;;) {
12707 BYTE* pRunningDeviceBufferCapture;
12708 BYTE* pRunningDeviceBufferPlayback;
12709 ma_uint32 framesToProcess;
12710 ma_uint32 framesProcessed;
12711
12712 pRunningDeviceBufferCapture = pMappedDeviceBufferCapture + ((mappedDeviceBufferSizeInFramesCapture - mappedDeviceBufferFramesRemainingCapture ) * bpfCaptureDevice);
12713 pRunningDeviceBufferPlayback = pMappedDeviceBufferPlayback + ((mappedDeviceBufferSizeInFramesPlayback - mappedDeviceBufferFramesRemainingPlayback) * bpfPlaybackDevice);
12714
12715 /* There may be some data sitting in the converter that needs to be processed first. Once this is exhaused, run the data callback again. */
12716 if (!pDevice->playback.converter.isPassthrough && outputDataInClientFormatConsumed < outputDataInClientFormatCount) {
12717 ma_uint64 convertedFrameCountClient = (outputDataInClientFormatCount - outputDataInClientFormatConsumed);
12718 ma_uint64 convertedFrameCountDevice = mappedDeviceBufferFramesRemainingPlayback;
12719 void* pConvertedFramesClient = outputDataInClientFormat + (outputDataInClientFormatConsumed * bpfPlaybackClient);
12720 void* pConvertedFramesDevice = pRunningDeviceBufferPlayback;
12721 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesClient, &convertedFrameCountClient, pConvertedFramesDevice, &convertedFrameCountDevice);
12722 if (result != MA_SUCCESS) {
12723 break;
12724 }
12725
12726 outputDataInClientFormatConsumed += (ma_uint32)convertedFrameCountClient; /* Safe cast. */
12727 mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)convertedFrameCountDevice; /* Safe cast. */
12728
12729 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
12730 break;
12731 }
12732 }
12733
12734 /*
12735 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
12736 buffers directly to the callback.
12737 */
12738 if (pDevice->capture.converter.isPassthrough && pDevice->playback.converter.isPassthrough) {
12739 /* Optimal path. We can pass mapped pointers directly to the callback. */
12740 framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, mappedDeviceBufferFramesRemainingPlayback);
12741 framesProcessed = framesToProcess;
12742
12743 ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, pRunningDeviceBufferCapture, framesToProcess);
12744
12745 mappedDeviceBufferFramesRemainingCapture -= framesProcessed;
12746 mappedDeviceBufferFramesRemainingPlayback -= framesProcessed;
12747
12748 if (mappedDeviceBufferFramesRemainingCapture == 0) {
12749 break; /* Exhausted input data. */
12750 }
12751 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
12752 break; /* Exhausted output data. */
12753 }
12754 } else if (pDevice->capture.converter.isPassthrough) {
12755 /* The input buffer is a passthrough, but the playback buffer requires a conversion. */
12756 framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, outputDataInClientFormatCap);
12757 framesProcessed = framesToProcess;
12758
12759 ma_device__on_data(pDevice, outputDataInClientFormat, pRunningDeviceBufferCapture, framesToProcess);
12760 outputDataInClientFormatCount = framesProcessed;
12761 outputDataInClientFormatConsumed = 0;
12762
12763 mappedDeviceBufferFramesRemainingCapture -= framesProcessed;
12764 if (mappedDeviceBufferFramesRemainingCapture == 0) {
12765 break; /* Exhausted input data. */
12766 }
12767 } else if (pDevice->playback.converter.isPassthrough) {
12768 /* The input buffer requires conversion, the playback buffer is passthrough. */
12769 ma_uint64 capturedDeviceFramesToProcess = mappedDeviceBufferFramesRemainingCapture;
12770 ma_uint64 capturedClientFramesToProcess = ma_min(inputDataInClientFormatCap, mappedDeviceBufferFramesRemainingPlayback);
12771
12772 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningDeviceBufferCapture, &capturedDeviceFramesToProcess, inputDataInClientFormat, &capturedClientFramesToProcess);
12773 if (result != MA_SUCCESS) {
12774 break;
12775 }
12776
12777 if (capturedClientFramesToProcess == 0) {
12778 break;
12779 }
12780
12781 ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess); /* Safe cast. */
12782
12783 mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess;
12784 mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)capturedClientFramesToProcess;
12785 } else {
12786 ma_uint64 capturedDeviceFramesToProcess = mappedDeviceBufferFramesRemainingCapture;
12787 ma_uint64 capturedClientFramesToProcess = ma_min(inputDataInClientFormatCap, outputDataInClientFormatCap);
12788
12789 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningDeviceBufferCapture, &capturedDeviceFramesToProcess, inputDataInClientFormat, &capturedClientFramesToProcess);
12790 if (result != MA_SUCCESS) {
12791 break;
12792 }
12793
12794 if (capturedClientFramesToProcess == 0) {
12795 break;
12796 }
12797
12798 ma_device__on_data(pDevice, outputDataInClientFormat, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess);
12799
12800 mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess;
12801 outputDataInClientFormatCount = (ma_uint32)capturedClientFramesToProcess;
12802 outputDataInClientFormatConsumed = 0;
12803 }
12804 }
12805
12806
12807 /* If at this point we've run out of capture data we need to release the buffer. */
12808 if (mappedDeviceBufferFramesRemainingCapture == 0 && pMappedDeviceBufferCapture != NULL) {
12809 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
12810 if (FAILED(hr)) {
12811 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr));
12812 exitLoop = MA_TRUE;
12813 break;
12814 }
12815
12816 /*printf("TRACE: Released capture buffer\n");*/
12817
12818 pMappedDeviceBufferCapture = NULL;
12819 mappedDeviceBufferFramesRemainingCapture = 0;
12820 mappedDeviceBufferSizeInFramesCapture = 0;
12821 }
12822
12823 /* Get out of this loop if we're run out of room in the playback buffer. */
12824 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
12825 break;
12826 }
12827 }
12828
12829
12830 /* If at this point we've run out of data we need to release the buffer. */
12831 if (mappedDeviceBufferFramesRemainingPlayback == 0 && pMappedDeviceBufferPlayback != NULL) {
12832 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedDeviceBufferSizeInFramesPlayback, 0);
12833 if (FAILED(hr)) {
12834 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr));
12835 exitLoop = MA_TRUE;
12836 break;
12837 }
12838
12839 /*printf("TRACE: Released playback buffer\n");*/
12840 framesWrittenToPlaybackDevice += mappedDeviceBufferSizeInFramesPlayback;
12841
12842 pMappedDeviceBufferPlayback = NULL;
12843 mappedDeviceBufferFramesRemainingPlayback = 0;
12844 mappedDeviceBufferSizeInFramesPlayback = 0;
12845 }
12846
12847 if (!pDevice->wasapi.isStartedPlayback) {
12848 ma_uint32 startThreshold = pDevice->playback.internalPeriodSizeInFrames * 1;
12849
12850 /* 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. */
12851 if (startThreshold > pDevice->wasapi.actualPeriodSizeInFramesPlayback) {
12852 startThreshold = pDevice->wasapi.actualPeriodSizeInFramesPlayback;
12853 }
12854
12855 if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= startThreshold) {
12856 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
12857 if (FAILED(hr)) {
12858 ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
12859 ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
12860 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", ma_result_from_HRESULT(hr));
12861 }
12862 ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
12863 }
12864 }
12865 } break;
12866
12867
12868
12869 case ma_device_type_capture:
12870 case ma_device_type_loopback:
12871 {
12872 ma_uint32 framesAvailableCapture;
12873 DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
12874
12875 /* Wait for data to become available first. */
12876 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
12877 exitLoop = MA_TRUE;
12878 break; /* Wait failed. */
12879 }
12880
12881 /* 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. */
12882 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
12883 if (result != MA_SUCCESS) {
12884 exitLoop = MA_TRUE;
12885 break;
12886 }
12887
12888 if (framesAvailableCapture < pDevice->wasapi.periodSizeInFramesCapture) {
12889 continue; /* Nothing available. Keep waiting. */
12890 }
12891
12892 /* Map the data buffer in preparation for sending to the client. */
12893 mappedDeviceBufferSizeInFramesCapture = framesAvailableCapture;
12894 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
12895 if (FAILED(hr)) {
12896 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
12897 exitLoop = MA_TRUE;
12898 break;
12899 }
12900
12901 /* Overrun detection. */
12902 if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
12903 /* Glitched. Probably due to an overrun. */
12904 #ifdef MA_DEBUG_OUTPUT
12905 printf("[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
12906 #endif
12907
12908 /*
12909 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
12910 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
12911 last period.
12912 */
12913 if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) {
12914 #ifdef MA_DEBUG_OUTPUT
12915 printf("[WASAPI] Synchronizing capture stream. ");
12916 #endif
12917 do
12918 {
12919 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
12920 if (FAILED(hr)) {
12921 break;
12922 }
12923
12924 framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture;
12925
12926 if (framesAvailableCapture > 0) {
12927 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
12928 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
12929 if (FAILED(hr)) {
12930 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
12931 exitLoop = MA_TRUE;
12932 break;
12933 }
12934 } else {
12935 pMappedDeviceBufferCapture = NULL;
12936 mappedDeviceBufferSizeInFramesCapture = 0;
12937 }
12938 } while (framesAvailableCapture > periodSizeInFramesCapture);
12939 #ifdef MA_DEBUG_OUTPUT
12940 printf("framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
12941 #endif
12942 }
12943 } else {
12944 #ifdef MA_DEBUG_OUTPUT
12945 if (flagsCapture != 0) {
12946 printf("[WASAPI] Capture Flags: %d\n", flagsCapture);
12947 }
12948 #endif
12949 }
12950
12951 /* We should have a buffer at this point, but let's just do a sanity check anyway. */
12952 if (mappedDeviceBufferSizeInFramesCapture > 0 && pMappedDeviceBufferCapture != NULL) {
12953 ma_device__send_frames_to_client(pDevice, mappedDeviceBufferSizeInFramesCapture, pMappedDeviceBufferCapture);
12954
12955 /* At this point we're done with the buffer. */
12956 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
12957 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. */
12958 mappedDeviceBufferSizeInFramesCapture = 0;
12959 if (FAILED(hr)) {
12960 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr));
12961 exitLoop = MA_TRUE;
12962 break;
12963 }
12964 }
12965 } break;
12966
12967
12968
12969 case ma_device_type_playback:
12970 {
12971 ma_uint32 framesAvailablePlayback;
12972
12973 /* Wait for space to become available first. */
12974 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
12975 exitLoop = MA_TRUE;
12976 break; /* Wait failed. */
12977 }
12978
12979 /* Check how much space is available. If this returns 0 we just keep waiting. */
12980 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
12981 if (result != MA_SUCCESS) {
12982 exitLoop = MA_TRUE;
12983 break;
12984 }
12985
12986 if (framesAvailablePlayback < pDevice->wasapi.periodSizeInFramesPlayback) {
12987 continue; /* No space available. */
12988 }
12989
12990 /* Map a the data buffer in preparation for the callback. */
12991 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback);
12992 if (FAILED(hr)) {
12993 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
12994 exitLoop = MA_TRUE;
12995 break;
12996 }
12997
12998 /* We should have a buffer at this point. */
12999 ma_device__read_frames_from_client(pDevice, framesAvailablePlayback, pMappedDeviceBufferPlayback);
13000
13001 /* At this point we're done writing to the device and we just need to release the buffer. */
13002 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, 0);
13003 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. */
13004 mappedDeviceBufferSizeInFramesPlayback = 0;
13005
13006 if (FAILED(hr)) {
13007 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr));
13008 exitLoop = MA_TRUE;
13009 break;
13010 }
13011
13012 framesWrittenToPlaybackDevice += framesAvailablePlayback;
13013 if (!pDevice->wasapi.isStartedPlayback) {
13014 if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames*1) {
13015 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
13016 if (FAILED(hr)) {
13017 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", ma_result_from_HRESULT(hr));
13018 exitLoop = MA_TRUE;
13019 break;
13020 }
13021 ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
13022 }
13023 }
13024 } break;
13025
13026 default: return MA_INVALID_ARGS;
13027 }
13028 }
13029
13030 /* Here is where the device needs to be stopped. */
13031 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
13032 /* Any mapped buffers need to be released. */
13033 if (pMappedDeviceBufferCapture != NULL) {
13034 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
13035 }
13036
13037 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
13038 if (FAILED(hr)) {
13039 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.", ma_result_from_HRESULT(hr));
13040 }
13041
13042 /* The audio client needs to be reset otherwise restarting will fail. */
13043 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
13044 if (FAILED(hr)) {
13045 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.", ma_result_from_HRESULT(hr));
13046 }
13047
13048 ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
13049 }
13050
13051 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
13052 /* Any mapped buffers need to be released. */
13053 if (pMappedDeviceBufferPlayback != NULL) {
13054 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedDeviceBufferSizeInFramesPlayback, 0);
13055 }
13056
13057 /*
13058 The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
13059 the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
13060 */
13061 if (pDevice->wasapi.isStartedPlayback) {
13062 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
13063 WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
13064 } else {
13065 ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
13066 ma_uint32 framesAvailablePlayback;
13067 for (;;) {
13068 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
13069 if (result != MA_SUCCESS) {
13070 break;
13071 }
13072
13073 if (framesAvailablePlayback >= pDevice->wasapi.actualPeriodSizeInFramesPlayback) {
13074 break;
13075 }
13076
13077 /*
13078 Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
13079 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.
13080 */
13081 if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
13082 break;
13083 }
13084 prevFramesAvaialablePlayback = framesAvailablePlayback;
13085
13086 WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
13087 ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */
13088 }
13089 }
13090 }
13091
13092 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
13093 if (FAILED(hr)) {
13094 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.", ma_result_from_HRESULT(hr));
13095 }
13096
13097 /* The audio client needs to be reset otherwise restarting will fail. */
13098 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
13099 if (FAILED(hr)) {
13100 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.", ma_result_from_HRESULT(hr));
13101 }
13102
13103 ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
13104 }
13105
13106 return MA_SUCCESS;
13107 }
13108
13109 static ma_result ma_context_uninit__wasapi(ma_context* pContext)
13110 {
13111 MA_ASSERT(pContext != NULL);
13112 MA_ASSERT(pContext->backend == ma_backend_wasapi);
13113 (void)pContext;
13114
13115 return MA_SUCCESS;
13116 }
13117
13118 static ma_result ma_context_init__wasapi(const ma_context_config* pConfig, ma_context* pContext)
13119 {
13120 ma_result result = MA_SUCCESS;
13121
13122 MA_ASSERT(pContext != NULL);
13123
13124 (void)pConfig;
13125
13126 #ifdef MA_WIN32_DESKTOP
13127 /*
13128 WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
13129 exclusive mode does not work until SP1.
13130
13131 Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a lin error.
13132 */
13133 {
13134 ma_OSVERSIONINFOEXW osvi;
13135 ma_handle kernel32DLL;
13136 ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
13137 ma_PFNVerSetConditionMask _VerSetConditionMask;
13138
13139 kernel32DLL = ma_dlopen(pContext, "kernel32.dll");
13140 if (kernel32DLL == NULL) {
13141 return MA_NO_BACKEND;
13142 }
13143
13144 _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW)ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW");
13145 _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask");
13146 if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
13147 ma_dlclose(pContext, kernel32DLL);
13148 return MA_NO_BACKEND;
13149 }
13150
13151 MA_ZERO_OBJECT(&osvi);
13152 osvi.dwOSVersionInfoSize = sizeof(osvi);
13153 osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF);
13154 osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF);
13155 osvi.wServicePackMajor = 1;
13156 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))) {
13157 result = MA_SUCCESS;
13158 } else {
13159 result = MA_NO_BACKEND;
13160 }
13161
13162 ma_dlclose(pContext, kernel32DLL);
13163 }
13164 #endif
13165
13166 if (result != MA_SUCCESS) {
13167 return result;
13168 }
13169
13170 pContext->onUninit = ma_context_uninit__wasapi;
13171 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__wasapi;
13172 pContext->onEnumDevices = ma_context_enumerate_devices__wasapi;
13173 pContext->onGetDeviceInfo = ma_context_get_device_info__wasapi;
13174 pContext->onDeviceInit = ma_device_init__wasapi;
13175 pContext->onDeviceUninit = ma_device_uninit__wasapi;
13176 pContext->onDeviceStart = NULL; /* Not used. Started in onDeviceMainLoop. */
13177 pContext->onDeviceStop = ma_device_stop__wasapi; /* Required to ensure the capture event is signalled when stopping a loopback device while nothing is playing. */
13178 pContext->onDeviceMainLoop = ma_device_main_loop__wasapi;
13179
13180 return result;
13181 }
13182 #endif
13183
13184 /******************************************************************************
13185
13186 DirectSound Backend
13187
13188 ******************************************************************************/
13189 #ifdef MA_HAS_DSOUND
13190 /*#include <dsound.h>*/
13191
13192 static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};
13193
13194 /* miniaudio only uses priority or exclusive modes. */
13195 #define MA_DSSCL_NORMAL 1
13196 #define MA_DSSCL_PRIORITY 2
13197 #define MA_DSSCL_EXCLUSIVE 3
13198 #define MA_DSSCL_WRITEPRIMARY 4
13199
13200 #define MA_DSCAPS_PRIMARYMONO 0x00000001
13201 #define MA_DSCAPS_PRIMARYSTEREO 0x00000002
13202 #define MA_DSCAPS_PRIMARY8BIT 0x00000004
13203 #define MA_DSCAPS_PRIMARY16BIT 0x00000008
13204 #define MA_DSCAPS_CONTINUOUSRATE 0x00000010
13205 #define MA_DSCAPS_EMULDRIVER 0x00000020
13206 #define MA_DSCAPS_CERTIFIED 0x00000040
13207 #define MA_DSCAPS_SECONDARYMONO 0x00000100
13208 #define MA_DSCAPS_SECONDARYSTEREO 0x00000200
13209 #define MA_DSCAPS_SECONDARY8BIT 0x00000400
13210 #define MA_DSCAPS_SECONDARY16BIT 0x00000800
13211
13212 #define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
13213 #define MA_DSBCAPS_STATIC 0x00000002
13214 #define MA_DSBCAPS_LOCHARDWARE 0x00000004
13215 #define MA_DSBCAPS_LOCSOFTWARE 0x00000008
13216 #define MA_DSBCAPS_CTRL3D 0x00000010
13217 #define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
13218 #define MA_DSBCAPS_CTRLPAN 0x00000040
13219 #define MA_DSBCAPS_CTRLVOLUME 0x00000080
13220 #define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
13221 #define MA_DSBCAPS_CTRLFX 0x00000200
13222 #define MA_DSBCAPS_STICKYFOCUS 0x00004000
13223 #define MA_DSBCAPS_GLOBALFOCUS 0x00008000
13224 #define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
13225 #define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
13226 #define MA_DSBCAPS_LOCDEFER 0x00040000
13227 #define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
13228
13229 #define MA_DSBPLAY_LOOPING 0x00000001
13230 #define MA_DSBPLAY_LOCHARDWARE 0x00000002
13231 #define MA_DSBPLAY_LOCSOFTWARE 0x00000004
13232 #define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
13233 #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
13234 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
13235
13236 #define MA_DSCBSTART_LOOPING 0x00000001
13237
13238 typedef struct
13239 {
13240 DWORD dwSize;
13241 DWORD dwFlags;
13242 DWORD dwBufferBytes;
13243 DWORD dwReserved;
13244 WAVEFORMATEX* lpwfxFormat;
13245 GUID guid3DAlgorithm;
13246 } MA_DSBUFFERDESC;
13247
13248 typedef struct
13249 {
13250 DWORD dwSize;
13251 DWORD dwFlags;
13252 DWORD dwBufferBytes;
13253 DWORD dwReserved;
13254 WAVEFORMATEX* lpwfxFormat;
13255 DWORD dwFXCount;
13256 void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */
13257 } MA_DSCBUFFERDESC;
13258
13259 typedef struct
13260 {
13261 DWORD dwSize;
13262 DWORD dwFlags;
13263 DWORD dwMinSecondarySampleRate;
13264 DWORD dwMaxSecondarySampleRate;
13265 DWORD dwPrimaryBuffers;
13266 DWORD dwMaxHwMixingAllBuffers;
13267 DWORD dwMaxHwMixingStaticBuffers;
13268 DWORD dwMaxHwMixingStreamingBuffers;
13269 DWORD dwFreeHwMixingAllBuffers;
13270 DWORD dwFreeHwMixingStaticBuffers;
13271 DWORD dwFreeHwMixingStreamingBuffers;
13272 DWORD dwMaxHw3DAllBuffers;
13273 DWORD dwMaxHw3DStaticBuffers;
13274 DWORD dwMaxHw3DStreamingBuffers;
13275 DWORD dwFreeHw3DAllBuffers;
13276 DWORD dwFreeHw3DStaticBuffers;
13277 DWORD dwFreeHw3DStreamingBuffers;
13278 DWORD dwTotalHwMemBytes;
13279 DWORD dwFreeHwMemBytes;
13280 DWORD dwMaxContigFreeHwMemBytes;
13281 DWORD dwUnlockTransferRateHwBuffers;
13282 DWORD dwPlayCpuOverheadSwBuffers;
13283 DWORD dwReserved1;
13284 DWORD dwReserved2;
13285 } MA_DSCAPS;
13286
13287 typedef struct
13288 {
13289 DWORD dwSize;
13290 DWORD dwFlags;
13291 DWORD dwBufferBytes;
13292 DWORD dwUnlockTransferRate;
13293 DWORD dwPlayCpuOverhead;
13294 } MA_DSBCAPS;
13295
13296 typedef struct
13297 {
13298 DWORD dwSize;
13299 DWORD dwFlags;
13300 DWORD dwFormats;
13301 DWORD dwChannels;
13302 } MA_DSCCAPS;
13303
13304 typedef struct
13305 {
13306 DWORD dwSize;
13307 DWORD dwFlags;
13308 DWORD dwBufferBytes;
13309 DWORD dwReserved;
13310 } MA_DSCBCAPS;
13311
13312 typedef struct
13313 {
13314 DWORD dwOffset;
13315 HANDLE hEventNotify;
13316 } MA_DSBPOSITIONNOTIFY;
13317
13318 typedef struct ma_IDirectSound ma_IDirectSound;
13319 typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
13320 typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
13321 typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
13322 typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
13323
13324
13325 /*
13326 COM objects. The way these work is that you have a vtable (a list of function pointers, kind of
13327 like how C++ works internally), and then you have a structure with a single member, which is a
13328 pointer to the vtable. The vtable is where the methods of the object are defined. Methods need
13329 to be in a specific order, and parent classes need to have their methods declared first.
13330 */
13331
13332 /* IDirectSound */
13333 typedef struct
13334 {
13335 /* IUnknown */
13336 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
13337 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
13338 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
13339
13340 /* IDirectSound */
13341 HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
13342 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
13343 HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
13344 HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
13345 HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
13346 HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
13347 HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
13348 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
13349 } ma_IDirectSoundVtbl;
13350 struct ma_IDirectSound
13351 {
13352 ma_IDirectSoundVtbl* lpVtbl;
13353 };
13354 static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13355 static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13356 static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
13357 static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); }
13358 static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
13359 static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
13360 static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
13361 static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
13362 static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
13363 static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
13364 static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
13365
13366
13367 /* IDirectSoundBuffer */
13368 typedef struct
13369 {
13370 /* IUnknown */
13371 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
13372 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
13373 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
13374
13375 /* IDirectSoundBuffer */
13376 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
13377 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
13378 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
13379 HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
13380 HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
13381 HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
13382 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
13383 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
13384 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
13385 HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
13386 HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
13387 HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat);
13388 HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
13389 HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
13390 HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
13391 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
13392 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
13393 HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
13394 } ma_IDirectSoundBufferVtbl;
13395 struct ma_IDirectSoundBuffer
13396 {
13397 ma_IDirectSoundBufferVtbl* lpVtbl;
13398 };
13399 static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13400 static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13401 static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
13402 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
13403 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
13404 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
13405 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
13406 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
13407 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
13408 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
13409 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
13410 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); }
13411 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
13412 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
13413 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
13414 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
13415 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
13416 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
13417 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
13418 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }
13419 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
13420
13421
13422 /* IDirectSoundCapture */
13423 typedef struct
13424 {
13425 /* IUnknown */
13426 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
13427 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
13428 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
13429
13430 /* IDirectSoundCapture */
13431 HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
13432 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
13433 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
13434 } ma_IDirectSoundCaptureVtbl;
13435 struct ma_IDirectSoundCapture
13436 {
13437 ma_IDirectSoundCaptureVtbl* lpVtbl;
13438 };
13439 static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13440 static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13441 static MA_INLINE ULONG ma_IDirectSoundCapture_Release(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
13442 static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); }
13443 static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
13444 static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
13445
13446
13447 /* IDirectSoundCaptureBuffer */
13448 typedef struct
13449 {
13450 /* IUnknown */
13451 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
13452 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
13453 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
13454
13455 /* IDirectSoundCaptureBuffer */
13456 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
13457 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
13458 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
13459 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
13460 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
13461 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
13462 HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
13463 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
13464 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
13465 } ma_IDirectSoundCaptureBufferVtbl;
13466 struct ma_IDirectSoundCaptureBuffer
13467 {
13468 ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
13469 };
13470 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13471 static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13472 static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
13473 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
13474 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
13475 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
13476 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
13477 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
13478 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); }
13479 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
13480 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
13481 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }
13482
13483
13484 /* IDirectSoundNotify */
13485 typedef struct
13486 {
13487 /* IUnknown */
13488 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
13489 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
13490 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
13491
13492 /* IDirectSoundNotify */
13493 HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
13494 } ma_IDirectSoundNotifyVtbl;
13495 struct ma_IDirectSoundNotify
13496 {
13497 ma_IDirectSoundNotifyVtbl* lpVtbl;
13498 };
13499 static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13500 static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13501 static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
13502 static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
13503
13504
13505 typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext);
13506 typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter);
13507 typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
13508 typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter);
13509 typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
13510
13511 static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
13512 {
13513 /* Normalize the range in case we were given something stupid. */
13514 if (sampleRateMin < MA_MIN_SAMPLE_RATE) {
13515 sampleRateMin = MA_MIN_SAMPLE_RATE;
13516 }
13517 if (sampleRateMax > MA_MAX_SAMPLE_RATE) {
13518 sampleRateMax = MA_MAX_SAMPLE_RATE;
13519 }
13520 if (sampleRateMin > sampleRateMax) {
13521 sampleRateMin = sampleRateMax;
13522 }
13523
13524 if (sampleRateMin == sampleRateMax) {
13525 return sampleRateMax;
13526 } else {
13527 size_t iStandardRate;
13528 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
13529 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
13530 if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
13531 return standardRate;
13532 }
13533 }
13534 }
13535
13536 /* Should never get here. */
13537 MA_ASSERT(MA_FALSE);
13538 return 0;
13539 }
13540
13541 /*
13542 Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,
13543 the channel count and channel map will be left unmodified.
13544 */
13545 static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
13546 {
13547 WORD channels;
13548 DWORD channelMap;
13549
13550 channels = 0;
13551 if (pChannelsOut != NULL) {
13552 channels = *pChannelsOut;
13553 }
13554
13555 channelMap = 0;
13556 if (pChannelMapOut != NULL) {
13557 channelMap = *pChannelMapOut;
13558 }
13559
13560 /*
13561 The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper
13562 16 bits is for the geometry.
13563 */
13564 switch ((BYTE)(speakerConfig)) {
13565 case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
13566 case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
13567 case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
13568 case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
13569 case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
13570 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;
13571 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;
13572 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;
13573 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;
13574 default: break;
13575 }
13576
13577 if (pChannelsOut != NULL) {
13578 *pChannelsOut = channels;
13579 }
13580
13581 if (pChannelMapOut != NULL) {
13582 *pChannelMapOut = channelMap;
13583 }
13584 }
13585
13586
13587 static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
13588 {
13589 ma_IDirectSound* pDirectSound;
13590 HWND hWnd;
13591 HRESULT hr;
13592
13593 MA_ASSERT(pContext != NULL);
13594 MA_ASSERT(ppDirectSound != NULL);
13595
13596 *ppDirectSound = NULL;
13597 pDirectSound = NULL;
13598
13599 if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
13600 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
13601 }
13602
13603 /* The cooperative level must be set before doing anything else. */
13604 hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
13605 if (hWnd == NULL) {
13606 hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
13607 }
13608
13609 hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
13610 if (FAILED(hr)) {
13611 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", ma_result_from_HRESULT(hr));
13612 }
13613
13614 *ppDirectSound = pDirectSound;
13615 return MA_SUCCESS;
13616 }
13617
13618 static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
13619 {
13620 ma_IDirectSoundCapture* pDirectSoundCapture;
13621 HRESULT hr;
13622
13623 MA_ASSERT(pContext != NULL);
13624 MA_ASSERT(ppDirectSoundCapture != NULL);
13625
13626 /* DirectSound does not support exclusive mode for capture. */
13627 if (shareMode == ma_share_mode_exclusive) {
13628 return MA_SHARE_MODE_NOT_SUPPORTED;
13629 }
13630
13631 *ppDirectSoundCapture = NULL;
13632 pDirectSoundCapture = NULL;
13633
13634 hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL);
13635 if (FAILED(hr)) {
13636 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", ma_result_from_HRESULT(hr));
13637 }
13638
13639 *ppDirectSoundCapture = pDirectSoundCapture;
13640 return MA_SUCCESS;
13641 }
13642
13643 static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
13644 {
13645 HRESULT hr;
13646 MA_DSCCAPS caps;
13647 WORD bitsPerSample;
13648 DWORD sampleRate;
13649
13650 MA_ASSERT(pContext != NULL);
13651 MA_ASSERT(pDirectSoundCapture != NULL);
13652
13653 if (pChannels) {
13654 *pChannels = 0;
13655 }
13656 if (pBitsPerSample) {
13657 *pBitsPerSample = 0;
13658 }
13659 if (pSampleRate) {
13660 *pSampleRate = 0;
13661 }
13662
13663 MA_ZERO_OBJECT(&caps);
13664 caps.dwSize = sizeof(caps);
13665 hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps);
13666 if (FAILED(hr)) {
13667 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.", ma_result_from_HRESULT(hr));
13668 }
13669
13670 if (pChannels) {
13671 *pChannels = (WORD)caps.dwChannels;
13672 }
13673
13674 /* 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. */
13675 bitsPerSample = 16;
13676 sampleRate = 48000;
13677
13678 if (caps.dwChannels == 1) {
13679 if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
13680 sampleRate = 48000;
13681 } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
13682 sampleRate = 44100;
13683 } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
13684 sampleRate = 22050;
13685 } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
13686 sampleRate = 11025;
13687 } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
13688 sampleRate = 96000;
13689 } else {
13690 bitsPerSample = 8;
13691 if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
13692 sampleRate = 48000;
13693 } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
13694 sampleRate = 44100;
13695 } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
13696 sampleRate = 22050;
13697 } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
13698 sampleRate = 11025;
13699 } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
13700 sampleRate = 96000;
13701 } else {
13702 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
13703 }
13704 }
13705 } else if (caps.dwChannels == 2) {
13706 if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
13707 sampleRate = 48000;
13708 } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
13709 sampleRate = 44100;
13710 } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
13711 sampleRate = 22050;
13712 } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
13713 sampleRate = 11025;
13714 } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
13715 sampleRate = 96000;
13716 } else {
13717 bitsPerSample = 8;
13718 if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
13719 sampleRate = 48000;
13720 } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
13721 sampleRate = 44100;
13722 } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
13723 sampleRate = 22050;
13724 } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
13725 sampleRate = 11025;
13726 } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
13727 sampleRate = 96000;
13728 } else {
13729 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
13730 }
13731 }
13732 }
13733
13734 if (pBitsPerSample) {
13735 *pBitsPerSample = bitsPerSample;
13736 }
13737 if (pSampleRate) {
13738 *pSampleRate = sampleRate;
13739 }
13740
13741 return MA_SUCCESS;
13742 }
13743
13744 static ma_bool32 ma_context_is_device_id_equal__dsound(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
13745 {
13746 MA_ASSERT(pContext != NULL);
13747 MA_ASSERT(pID0 != NULL);
13748 MA_ASSERT(pID1 != NULL);
13749 (void)pContext;
13750
13751 return memcmp(pID0->dsound, pID1->dsound, sizeof(pID0->dsound)) == 0;
13752 }
13753
13754
13755 typedef struct
13756 {
13757 ma_context* pContext;
13758 ma_device_type deviceType;
13759 ma_enum_devices_callback_proc callback;
13760 void* pUserData;
13761 ma_bool32 terminated;
13762 } ma_context_enumerate_devices_callback_data__dsound;
13763
13764 static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
13765 {
13766 ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
13767 ma_device_info deviceInfo;
13768
13769 MA_ZERO_OBJECT(&deviceInfo);
13770
13771 /* ID. */
13772 if (lpGuid != NULL) {
13773 MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16);
13774 } else {
13775 MA_ZERO_MEMORY(deviceInfo.id.dsound, 16);
13776 }
13777
13778 /* Name / Description */
13779 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
13780
13781
13782 /* Call the callback function, but make sure we stop enumerating if the callee requested so. */
13783 MA_ASSERT(pData != NULL);
13784 pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData);
13785 if (pData->terminated) {
13786 return FALSE; /* Stop enumeration. */
13787 } else {
13788 return TRUE; /* Continue enumeration. */
13789 }
13790
13791 (void)lpcstrModule;
13792 }
13793
13794 static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
13795 {
13796 ma_context_enumerate_devices_callback_data__dsound data;
13797
13798 MA_ASSERT(pContext != NULL);
13799 MA_ASSERT(callback != NULL);
13800
13801 data.pContext = pContext;
13802 data.callback = callback;
13803 data.pUserData = pUserData;
13804 data.terminated = MA_FALSE;
13805
13806 /* Playback. */
13807 if (!data.terminated) {
13808 data.deviceType = ma_device_type_playback;
13809 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
13810 }
13811
13812 /* Capture. */
13813 if (!data.terminated) {
13814 data.deviceType = ma_device_type_capture;
13815 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
13816 }
13817
13818 return MA_SUCCESS;
13819 }
13820
13821
13822 typedef struct
13823 {
13824 const ma_device_id* pDeviceID;
13825 ma_device_info* pDeviceInfo;
13826 ma_bool32 found;
13827 } ma_context_get_device_info_callback_data__dsound;
13828
13829 static BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
13830 {
13831 ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
13832 MA_ASSERT(pData != NULL);
13833
13834 if ((pData->pDeviceID == NULL || ma_is_guid_equal(pData->pDeviceID->dsound, &MA_GUID_NULL)) && (lpGuid == NULL || ma_is_guid_equal(lpGuid, &MA_GUID_NULL))) {
13835 /* Default device. */
13836 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
13837 pData->found = MA_TRUE;
13838 return FALSE; /* Stop enumeration. */
13839 } else {
13840 /* Not the default device. */
13841 if (lpGuid != NULL && pData->pDeviceID != NULL) {
13842 if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
13843 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
13844 pData->found = MA_TRUE;
13845 return FALSE; /* Stop enumeration. */
13846 }
13847 }
13848 }
13849
13850 (void)lpcstrModule;
13851 return TRUE;
13852 }
13853
13854 static 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)
13855 {
13856 ma_result result;
13857 HRESULT hr;
13858
13859 /* Exclusive mode and capture not supported with DirectSound. */
13860 if (deviceType == ma_device_type_capture && shareMode == ma_share_mode_exclusive) {
13861 return MA_SHARE_MODE_NOT_SUPPORTED;
13862 }
13863
13864 if (pDeviceID != NULL) {
13865 ma_context_get_device_info_callback_data__dsound data;
13866
13867 /* ID. */
13868 MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
13869
13870 /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */
13871 data.pDeviceID = pDeviceID;
13872 data.pDeviceInfo = pDeviceInfo;
13873 data.found = MA_FALSE;
13874 if (deviceType == ma_device_type_playback) {
13875 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
13876 } else {
13877 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
13878 }
13879
13880 if (!data.found) {
13881 return MA_NO_DEVICE;
13882 }
13883 } else {
13884 /* 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. */
13885
13886 /* ID */
13887 MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16);
13888
13889 /* Name / Description */
13890 if (deviceType == ma_device_type_playback) {
13891 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
13892 } else {
13893 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
13894 }
13895 }
13896
13897 /* Retrieving detailed information is slightly different depending on the device type. */
13898 if (deviceType == ma_device_type_playback) {
13899 /* Playback. */
13900 ma_IDirectSound* pDirectSound;
13901 MA_DSCAPS caps;
13902 ma_uint32 iFormat;
13903
13904 result = ma_context_create_IDirectSound__dsound(pContext, shareMode, pDeviceID, &pDirectSound);
13905 if (result != MA_SUCCESS) {
13906 return result;
13907 }
13908
13909 MA_ZERO_OBJECT(&caps);
13910 caps.dwSize = sizeof(caps);
13911 hr = ma_IDirectSound_GetCaps(pDirectSound, &caps);
13912 if (FAILED(hr)) {
13913 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", ma_result_from_HRESULT(hr));
13914 }
13915
13916 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
13917 /* It supports at least stereo, but could support more. */
13918 WORD channels = 2;
13919
13920 /* Look at the speaker configuration to get a better idea on the channel count. */
13921 DWORD speakerConfig;
13922 hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig);
13923 if (SUCCEEDED(hr)) {
13924 ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
13925 }
13926
13927 pDeviceInfo->minChannels = channels;
13928 pDeviceInfo->maxChannels = channels;
13929 } else {
13930 /* It does not support stereo, which means we are stuck with mono. */
13931 pDeviceInfo->minChannels = 1;
13932 pDeviceInfo->maxChannels = 1;
13933 }
13934
13935 /* Sample rate. */
13936 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
13937 pDeviceInfo->minSampleRate = caps.dwMinSecondarySampleRate;
13938 pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate;
13939
13940 /*
13941 On my machine the min and max sample rates can return 100 and 200000 respectively. I'd rather these be within
13942 the range of our standard sample rates so I'm clamping.
13943 */
13944 if (caps.dwMinSecondarySampleRate < MA_MIN_SAMPLE_RATE && caps.dwMaxSecondarySampleRate >= MA_MIN_SAMPLE_RATE) {
13945 pDeviceInfo->minSampleRate = MA_MIN_SAMPLE_RATE;
13946 }
13947 if (caps.dwMaxSecondarySampleRate > MA_MAX_SAMPLE_RATE && caps.dwMinSecondarySampleRate <= MA_MAX_SAMPLE_RATE) {
13948 pDeviceInfo->maxSampleRate = MA_MAX_SAMPLE_RATE;
13949 }
13950 } else {
13951 /* Only supports a single sample rate. Set both min an max to the same thing. Do not clamp within the standard rates. */
13952 pDeviceInfo->minSampleRate = caps.dwMaxSecondarySampleRate;
13953 pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate;
13954 }
13955
13956 /* DirectSound can support all formats. */
13957 pDeviceInfo->formatCount = ma_format_count - 1; /* Minus one because we don't want to include ma_format_unknown. */
13958 for (iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
13959 pDeviceInfo->formats[iFormat] = (ma_format)(iFormat + 1); /* +1 to skip over ma_format_unknown. */
13960 }
13961
13962 ma_IDirectSound_Release(pDirectSound);
13963 } else {
13964 /*
13965 Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
13966 devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just
13967 reporting the best format.
13968 */
13969 ma_IDirectSoundCapture* pDirectSoundCapture;
13970 WORD channels;
13971 WORD bitsPerSample;
13972 DWORD sampleRate;
13973
13974 result = ma_context_create_IDirectSoundCapture__dsound(pContext, shareMode, pDeviceID, &pDirectSoundCapture);
13975 if (result != MA_SUCCESS) {
13976 return result;
13977 }
13978
13979 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
13980 if (result != MA_SUCCESS) {
13981 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
13982 return result;
13983 }
13984
13985 pDeviceInfo->minChannels = channels;
13986 pDeviceInfo->maxChannels = channels;
13987 pDeviceInfo->minSampleRate = sampleRate;
13988 pDeviceInfo->maxSampleRate = sampleRate;
13989 pDeviceInfo->formatCount = 1;
13990 if (bitsPerSample == 8) {
13991 pDeviceInfo->formats[0] = ma_format_u8;
13992 } else if (bitsPerSample == 16) {
13993 pDeviceInfo->formats[0] = ma_format_s16;
13994 } else if (bitsPerSample == 24) {
13995 pDeviceInfo->formats[0] = ma_format_s24;
13996 } else if (bitsPerSample == 32) {
13997 pDeviceInfo->formats[0] = ma_format_s32;
13998 } else {
13999 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
14000 return MA_FORMAT_NOT_SUPPORTED;
14001 }
14002
14003 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
14004 }
14005
14006 return MA_SUCCESS;
14007 }
14008
14009
14010
14011 static void ma_device_uninit__dsound(ma_device* pDevice)
14012 {
14013 MA_ASSERT(pDevice != NULL);
14014
14015 if (pDevice->dsound.pCaptureBuffer != NULL) {
14016 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
14017 }
14018 if (pDevice->dsound.pCapture != NULL) {
14019 ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
14020 }
14021
14022 if (pDevice->dsound.pPlaybackBuffer != NULL) {
14023 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
14024 }
14025 if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
14026 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
14027 }
14028 if (pDevice->dsound.pPlayback != NULL) {
14029 ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
14030 }
14031 }
14032
14033 static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF)
14034 {
14035 GUID subformat;
14036
14037 switch (format)
14038 {
14039 case ma_format_u8:
14040 case ma_format_s16:
14041 case ma_format_s24:
14042 /*case ma_format_s24_32:*/
14043 case ma_format_s32:
14044 {
14045 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
14046 } break;
14047
14048 case ma_format_f32:
14049 {
14050 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
14051 } break;
14052
14053 default:
14054 return MA_FORMAT_NOT_SUPPORTED;
14055 }
14056
14057 MA_ZERO_OBJECT(pWF);
14058 pWF->Format.cbSize = sizeof(*pWF);
14059 pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
14060 pWF->Format.nChannels = (WORD)channels;
14061 pWF->Format.nSamplesPerSec = (DWORD)sampleRate;
14062 pWF->Format.wBitsPerSample = (WORD)ma_get_bytes_per_sample(format)*8;
14063 pWF->Format.nBlockAlign = (pWF->Format.nChannels * pWF->Format.wBitsPerSample) / 8;
14064 pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec;
14065 pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample;
14066 pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
14067 pWF->SubFormat = subformat;
14068
14069 return MA_SUCCESS;
14070 }
14071
14072 static ma_result ma_device_init__dsound(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
14073 {
14074 ma_result result;
14075 HRESULT hr;
14076 ma_uint32 periodSizeInMilliseconds;
14077
14078 MA_ASSERT(pDevice != NULL);
14079 MA_ZERO_OBJECT(&pDevice->dsound);
14080
14081 if (pConfig->deviceType == ma_device_type_loopback) {
14082 return MA_DEVICE_TYPE_NOT_SUPPORTED;
14083 }
14084
14085 periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
14086 if (periodSizeInMilliseconds == 0) {
14087 periodSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->periodSizeInFrames, pConfig->sampleRate);
14088 }
14089
14090 /* DirectSound should use a latency of about 20ms per period for low latency mode. */
14091 if (pDevice->usingDefaultBufferSize) {
14092 if (pConfig->performanceProfile == ma_performance_profile_low_latency) {
14093 periodSizeInMilliseconds = 20;
14094 } else {
14095 periodSizeInMilliseconds = 200;
14096 }
14097 }
14098
14099 /* 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. */
14100 if (periodSizeInMilliseconds < 20) {
14101 periodSizeInMilliseconds = 20;
14102 }
14103
14104 /*
14105 Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize
14106 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
14107 full-duplex mode.
14108 */
14109 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
14110 WAVEFORMATEXTENSIBLE wf;
14111 MA_DSCBUFFERDESC descDS;
14112 ma_uint32 periodSizeInFrames;
14113 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
14114 WAVEFORMATEXTENSIBLE* pActualFormat;
14115
14116 result = ma_config_to_WAVEFORMATEXTENSIBLE(pConfig->capture.format, pConfig->capture.channels, pConfig->sampleRate, pConfig->capture.channelMap, &wf);
14117 if (result != MA_SUCCESS) {
14118 return result;
14119 }
14120
14121 result = ma_context_create_IDirectSoundCapture__dsound(pContext, pConfig->capture.shareMode, pConfig->capture.pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
14122 if (result != MA_SUCCESS) {
14123 ma_device_uninit__dsound(pDevice);
14124 return result;
14125 }
14126
14127 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec);
14128 if (result != MA_SUCCESS) {
14129 ma_device_uninit__dsound(pDevice);
14130 return result;
14131 }
14132
14133 wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
14134 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
14135 wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
14136 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
14137
14138 /* The size of the buffer must be a clean multiple of the period count. */
14139 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, wf.Format.nSamplesPerSec);
14140
14141 MA_ZERO_OBJECT(&descDS);
14142 descDS.dwSize = sizeof(descDS);
14143 descDS.dwFlags = 0;
14144 descDS.dwBufferBytes = periodSizeInFrames * pConfig->periods * ma_get_bytes_per_frame(pDevice->capture.internalFormat, wf.Format.nChannels);
14145 descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
14146 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
14147 if (FAILED(hr)) {
14148 ma_device_uninit__dsound(pDevice);
14149 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", ma_result_from_HRESULT(hr));
14150 }
14151
14152 /* Get the _actual_ properties of the buffer. */
14153 pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
14154 hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
14155 if (FAILED(hr)) {
14156 ma_device_uninit__dsound(pDevice);
14157 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer.", ma_result_from_HRESULT(hr));
14158 }
14159
14160 pDevice->capture.internalFormat = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
14161 pDevice->capture.internalChannels = pActualFormat->Format.nChannels;
14162 pDevice->capture.internalSampleRate = pActualFormat->Format.nSamplesPerSec;
14163
14164 /* Get the internal channel map based on the channel mask. */
14165 if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
14166 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
14167 } else {
14168 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
14169 }
14170
14171 /*
14172 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
14173 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.
14174 */
14175 if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) / pConfig->periods)) {
14176 descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, wf.Format.nChannels) * pConfig->periods;
14177 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
14178
14179 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
14180 if (FAILED(hr)) {
14181 ma_device_uninit__dsound(pDevice);
14182 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", ma_result_from_HRESULT(hr));
14183 }
14184 }
14185
14186 /* DirectSound should give us a buffer exactly the size we asked for. */
14187 pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames;
14188 pDevice->capture.internalPeriods = pConfig->periods;
14189 }
14190
14191 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
14192 WAVEFORMATEXTENSIBLE wf;
14193 MA_DSBUFFERDESC descDSPrimary;
14194 MA_DSCAPS caps;
14195 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
14196 WAVEFORMATEXTENSIBLE* pActualFormat;
14197 ma_uint32 periodSizeInFrames;
14198 MA_DSBUFFERDESC descDS;
14199
14200 result = ma_config_to_WAVEFORMATEXTENSIBLE(pConfig->playback.format, pConfig->playback.channels, pConfig->sampleRate, pConfig->playback.channelMap, &wf);
14201 if (result != MA_SUCCESS) {
14202 return result;
14203 }
14204
14205 result = ma_context_create_IDirectSound__dsound(pContext, pConfig->playback.shareMode, pConfig->playback.pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
14206 if (result != MA_SUCCESS) {
14207 ma_device_uninit__dsound(pDevice);
14208 return result;
14209 }
14210
14211 MA_ZERO_OBJECT(&descDSPrimary);
14212 descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
14213 descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
14214 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL);
14215 if (FAILED(hr)) {
14216 ma_device_uninit__dsound(pDevice);
14217 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", ma_result_from_HRESULT(hr));
14218 }
14219
14220
14221 /* We may want to make some adjustments to the format if we are using defaults. */
14222 MA_ZERO_OBJECT(&caps);
14223 caps.dwSize = sizeof(caps);
14224 hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps);
14225 if (FAILED(hr)) {
14226 ma_device_uninit__dsound(pDevice);
14227 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", ma_result_from_HRESULT(hr));
14228 }
14229
14230 if (pDevice->playback.usingDefaultChannels) {
14231 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
14232 DWORD speakerConfig;
14233
14234 /* It supports at least stereo, but could support more. */
14235 wf.Format.nChannels = 2;
14236
14237 /* Look at the speaker configuration to get a better idea on the channel count. */
14238 if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
14239 ma_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask);
14240 }
14241 } else {
14242 /* It does not support stereo, which means we are stuck with mono. */
14243 wf.Format.nChannels = 1;
14244 }
14245 }
14246
14247 if (pDevice->usingDefaultSampleRate) {
14248 /* We base the sample rate on the values returned by GetCaps(). */
14249 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
14250 wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
14251 } else {
14252 wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
14253 }
14254 }
14255
14256 wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
14257 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
14258
14259 /*
14260 From MSDN:
14261
14262 The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
14263 supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
14264 and compare the result with the format that was requested with the SetFormat method.
14265 */
14266 hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf);
14267 if (FAILED(hr)) {
14268 ma_device_uninit__dsound(pDevice);
14269 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.", ma_result_from_HRESULT(hr));
14270 }
14271
14272 /* Get the _actual_ properties of the buffer. */
14273 pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
14274 hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
14275 if (FAILED(hr)) {
14276 ma_device_uninit__dsound(pDevice);
14277 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", ma_result_from_HRESULT(hr));
14278 }
14279
14280 pDevice->playback.internalFormat = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
14281 pDevice->playback.internalChannels = pActualFormat->Format.nChannels;
14282 pDevice->playback.internalSampleRate = pActualFormat->Format.nSamplesPerSec;
14283
14284 /* Get the internal channel map based on the channel mask. */
14285 if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
14286 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
14287 } else {
14288 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
14289 }
14290
14291 /* The size of the buffer must be a clean multiple of the period count. */
14292 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pDevice->playback.internalSampleRate);
14293
14294 /*
14295 Meaning of dwFlags (from MSDN):
14296
14297 DSBCAPS_CTRLPOSITIONNOTIFY
14298 The buffer has position notification capability.
14299
14300 DSBCAPS_GLOBALFOCUS
14301 With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
14302 another application, even if the new application uses DirectSound.
14303
14304 DSBCAPS_GETCURRENTPOSITION2
14305 In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
14306 sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
14307 application can get a more accurate play cursor.
14308 */
14309 MA_ZERO_OBJECT(&descDS);
14310 descDS.dwSize = sizeof(descDS);
14311 descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
14312 descDS.dwBufferBytes = periodSizeInFrames * pConfig->periods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
14313 descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
14314 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL);
14315 if (FAILED(hr)) {
14316 ma_device_uninit__dsound(pDevice);
14317 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", ma_result_from_HRESULT(hr));
14318 }
14319
14320 /* DirectSound should give us a buffer exactly the size we asked for. */
14321 pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
14322 pDevice->playback.internalPeriods = pConfig->periods;
14323 }
14324
14325 (void)pContext;
14326 return MA_SUCCESS;
14327 }
14328
14329
14330 static ma_result ma_device_main_loop__dsound(ma_device* pDevice)
14331 {
14332 ma_result result = MA_SUCCESS;
14333 ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
14334 ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
14335 HRESULT hr;
14336 DWORD lockOffsetInBytesCapture;
14337 DWORD lockSizeInBytesCapture;
14338 DWORD mappedSizeInBytesCapture;
14339 DWORD mappedDeviceFramesProcessedCapture;
14340 void* pMappedDeviceBufferCapture;
14341 DWORD lockOffsetInBytesPlayback;
14342 DWORD lockSizeInBytesPlayback;
14343 DWORD mappedSizeInBytesPlayback;
14344 void* pMappedDeviceBufferPlayback;
14345 DWORD prevReadCursorInBytesCapture = 0;
14346 DWORD prevPlayCursorInBytesPlayback = 0;
14347 ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
14348 DWORD virtualWriteCursorInBytesPlayback = 0;
14349 ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
14350 ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
14351 ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */
14352 ma_uint32 waitTimeInMilliseconds = 1;
14353
14354 MA_ASSERT(pDevice != NULL);
14355
14356 /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */
14357 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
14358 if (FAILED(ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING))) {
14359 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
14360 }
14361 }
14362
14363 while (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
14364 switch (pDevice->type)
14365 {
14366 case ma_device_type_duplex:
14367 {
14368 DWORD physicalCaptureCursorInBytes;
14369 DWORD physicalReadCursorInBytes;
14370 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
14371 if (FAILED(hr)) {
14372 return ma_result_from_HRESULT(hr);
14373 }
14374
14375 /* If nothing is available we just sleep for a bit and return from this iteration. */
14376 if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
14377 ma_sleep(waitTimeInMilliseconds);
14378 continue; /* Nothing is available in the capture buffer. */
14379 }
14380
14381 /*
14382 The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure
14383 we don't return until every frame has been copied over.
14384 */
14385 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
14386 /* The capture position has not looped. This is the simple case. */
14387 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
14388 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
14389 } else {
14390 /*
14391 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
14392 do it again from the start.
14393 */
14394 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
14395 /* Lock up to the end of the buffer. */
14396 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
14397 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
14398 } else {
14399 /* Lock starting from the start of the buffer. */
14400 lockOffsetInBytesCapture = 0;
14401 lockSizeInBytesCapture = physicalReadCursorInBytes;
14402 }
14403 }
14404
14405 if (lockSizeInBytesCapture == 0) {
14406 ma_sleep(waitTimeInMilliseconds);
14407 continue; /* Nothing is available in the capture buffer. */
14408 }
14409
14410 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
14411 if (FAILED(hr)) {
14412 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
14413 }
14414
14415
14416 /* 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. */
14417 mappedDeviceFramesProcessedCapture = 0;
14418
14419 for (;;) { /* Keep writing to the playback device. */
14420 ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
14421 ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
14422 ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
14423 ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
14424 ma_uint32 outputFramesInClientFormatCount;
14425 ma_uint32 outputFramesInClientFormatConsumed = 0;
14426 ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);
14427 ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;
14428 void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);
14429
14430 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);
14431 if (result != MA_SUCCESS) {
14432 break;
14433 }
14434
14435 outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess;
14436 mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;
14437
14438 ma_device__on_data(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);
14439
14440 /* 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. */
14441 for (;;) {
14442 ma_uint32 framesWrittenThisIteration;
14443 DWORD physicalPlayCursorInBytes;
14444 DWORD physicalWriteCursorInBytes;
14445 DWORD availableBytesPlayback;
14446 DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */
14447
14448 /* We need the physical play and write cursors. */
14449 if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
14450 break;
14451 }
14452
14453 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
14454 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
14455 }
14456 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
14457
14458 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
14459 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
14460 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
14461 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
14462 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
14463 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
14464 } else {
14465 /* This is an error. */
14466 #ifdef MA_DEBUG_OUTPUT
14467 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);
14468 #endif
14469 availableBytesPlayback = 0;
14470 }
14471 } else {
14472 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
14473 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
14474 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
14475 } else {
14476 /* This is an error. */
14477 #ifdef MA_DEBUG_OUTPUT
14478 printf("[DirectSound] (Duplex/Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
14479 #endif
14480 availableBytesPlayback = 0;
14481 }
14482 }
14483
14484 #ifdef MA_DEBUG_OUTPUT
14485 /*printf("[DirectSound] (Duplex/Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
14486 #endif
14487
14488 /* If there's no room available for writing we need to wait for more. */
14489 if (availableBytesPlayback == 0) {
14490 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
14491 if (!isPlaybackDeviceStarted) {
14492 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
14493 if (FAILED(hr)) {
14494 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
14495 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
14496 }
14497 isPlaybackDeviceStarted = MA_TRUE;
14498 } else {
14499 ma_sleep(waitTimeInMilliseconds);
14500 continue;
14501 }
14502 }
14503
14504
14505 /* 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. */
14506 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
14507 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
14508 /* Same loop iteration. Go up to the end of the buffer. */
14509 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
14510 } else {
14511 /* Different loop iterations. Go up to the physical play cursor. */
14512 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
14513 }
14514
14515 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
14516 if (FAILED(hr)) {
14517 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
14518 break;
14519 }
14520
14521 /*
14522 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
14523 endless glitching due to it constantly running out of data.
14524 */
14525 if (isPlaybackDeviceStarted) {
14526 DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;
14527 if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {
14528 silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;
14529 if (silentPaddingInBytes > lockSizeInBytesPlayback) {
14530 silentPaddingInBytes = lockSizeInBytesPlayback;
14531 }
14532
14533 #ifdef MA_DEBUG_OUTPUT
14534 printf("[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%d, silentPaddingInBytes=%d\n", availableBytesPlayback, silentPaddingInBytes);
14535 #endif
14536 }
14537 }
14538
14539 /* At this point we have a buffer for output. */
14540 if (silentPaddingInBytes > 0) {
14541 MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);
14542 framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;
14543 } else {
14544 ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);
14545 ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;
14546 void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);
14547 void* pConvertedFramesOut = pMappedDeviceBufferPlayback;
14548
14549 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);
14550 if (result != MA_SUCCESS) {
14551 break;
14552 }
14553
14554 outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;
14555 framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut;
14556 }
14557
14558
14559 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);
14560 if (FAILED(hr)) {
14561 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr));
14562 break;
14563 }
14564
14565 virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;
14566 if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {
14567 virtualWriteCursorInBytesPlayback = 0;
14568 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
14569 }
14570
14571 /*
14572 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
14573 a bit of a buffer to prevent the playback buffer from getting starved.
14574 */
14575 framesWrittenToPlaybackDevice += framesWrittenThisIteration;
14576 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {
14577 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
14578 if (FAILED(hr)) {
14579 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
14580 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
14581 }
14582 isPlaybackDeviceStarted = MA_TRUE;
14583 }
14584
14585 if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {
14586 break; /* We're finished with the output data.*/
14587 }
14588 }
14589
14590 if (clientCapturedFramesToProcess == 0) {
14591 break; /* We just consumed every input sample. */
14592 }
14593 }
14594
14595
14596 /* At this point we're done with the mapped portion of the capture buffer. */
14597 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
14598 if (FAILED(hr)) {
14599 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr));
14600 }
14601 prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
14602 } break;
14603
14604
14605
14606 case ma_device_type_capture:
14607 {
14608 DWORD physicalCaptureCursorInBytes;
14609 DWORD physicalReadCursorInBytes;
14610 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
14611 if (FAILED(hr)) {
14612 return MA_ERROR;
14613 }
14614
14615 /* If the previous capture position is the same as the current position we need to wait a bit longer. */
14616 if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
14617 ma_sleep(waitTimeInMilliseconds);
14618 continue;
14619 }
14620
14621 /* Getting here means we have capture data available. */
14622 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
14623 /* The capture position has not looped. This is the simple case. */
14624 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
14625 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
14626 } else {
14627 /*
14628 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
14629 do it again from the start.
14630 */
14631 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
14632 /* Lock up to the end of the buffer. */
14633 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
14634 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
14635 } else {
14636 /* Lock starting from the start of the buffer. */
14637 lockOffsetInBytesCapture = 0;
14638 lockSizeInBytesCapture = physicalReadCursorInBytes;
14639 }
14640 }
14641
14642 #ifdef MA_DEBUG_OUTPUT
14643 /*printf("[DirectSound] (Capture) physicalCaptureCursorInBytes=%d, physicalReadCursorInBytes=%d\n", physicalCaptureCursorInBytes, physicalReadCursorInBytes);*/
14644 /*printf("[DirectSound] (Capture) lockOffsetInBytesCapture=%d, lockSizeInBytesCapture=%d\n", lockOffsetInBytesCapture, lockSizeInBytesCapture);*/
14645 #endif
14646
14647 if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {
14648 ma_sleep(waitTimeInMilliseconds);
14649 continue; /* Nothing is available in the capture buffer. */
14650 }
14651
14652 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
14653 if (FAILED(hr)) {
14654 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
14655 }
14656
14657 #ifdef MA_DEBUG_OUTPUT
14658 if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
14659 printf("[DirectSound] (Capture) lockSizeInBytesCapture=%d != mappedSizeInBytesCapture=%d\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
14660 }
14661 #endif
14662
14663 ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);
14664
14665 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
14666 if (FAILED(hr)) {
14667 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr));
14668 }
14669 prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
14670
14671 if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {
14672 prevReadCursorInBytesCapture = 0;
14673 }
14674 } break;
14675
14676
14677
14678 case ma_device_type_playback:
14679 {
14680 DWORD availableBytesPlayback;
14681 DWORD physicalPlayCursorInBytes;
14682 DWORD physicalWriteCursorInBytes;
14683 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
14684 if (FAILED(hr)) {
14685 break;
14686 }
14687
14688 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
14689 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
14690 }
14691 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
14692
14693 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
14694 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
14695 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
14696 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
14697 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
14698 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
14699 } else {
14700 /* This is an error. */
14701 #ifdef MA_DEBUG_OUTPUT
14702 printf("[DirectSound] (Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
14703 #endif
14704 availableBytesPlayback = 0;
14705 }
14706 } else {
14707 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
14708 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
14709 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
14710 } else {
14711 /* This is an error. */
14712 #ifdef MA_DEBUG_OUTPUT
14713 printf("[DirectSound] (Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
14714 #endif
14715 availableBytesPlayback = 0;
14716 }
14717 }
14718
14719 #ifdef MA_DEBUG_OUTPUT
14720 /*printf("[DirectSound] (Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
14721 #endif
14722
14723 /* If there's no room available for writing we need to wait for more. */
14724 if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {
14725 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
14726 if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
14727 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
14728 if (FAILED(hr)) {
14729 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
14730 }
14731 isPlaybackDeviceStarted = MA_TRUE;
14732 } else {
14733 ma_sleep(waitTimeInMilliseconds);
14734 continue;
14735 }
14736 }
14737
14738 /* 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. */
14739 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
14740 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
14741 /* Same loop iteration. Go up to the end of the buffer. */
14742 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
14743 } else {
14744 /* Different loop iterations. Go up to the physical play cursor. */
14745 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
14746 }
14747
14748 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
14749 if (FAILED(hr)) {
14750 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
14751 break;
14752 }
14753
14754 /* At this point we have a buffer for output. */
14755 ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);
14756
14757 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
14758 if (FAILED(hr)) {
14759 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr));
14760 break;
14761 }
14762
14763 virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
14764 if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {
14765 virtualWriteCursorInBytesPlayback = 0;
14766 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
14767 }
14768
14769 /*
14770 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
14771 a bit of a buffer to prevent the playback buffer from getting starved.
14772 */
14773 framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;
14774 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {
14775 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
14776 if (FAILED(hr)) {
14777 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
14778 }
14779 isPlaybackDeviceStarted = MA_TRUE;
14780 }
14781 } break;
14782
14783
14784 default: return MA_INVALID_ARGS; /* Invalid device type. */
14785 }
14786
14787 if (result != MA_SUCCESS) {
14788 return result;
14789 }
14790 }
14791
14792 /* Getting here means the device is being stopped. */
14793 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
14794 hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
14795 if (FAILED(hr)) {
14796 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", ma_result_from_HRESULT(hr));
14797 }
14798 }
14799
14800 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
14801 /* 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. */
14802 if (isPlaybackDeviceStarted) {
14803 for (;;) {
14804 DWORD availableBytesPlayback = 0;
14805 DWORD physicalPlayCursorInBytes;
14806 DWORD physicalWriteCursorInBytes;
14807 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
14808 if (FAILED(hr)) {
14809 break;
14810 }
14811
14812 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
14813 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
14814 }
14815 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
14816
14817 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
14818 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
14819 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
14820 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
14821 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
14822 } else {
14823 break;
14824 }
14825 } else {
14826 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
14827 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
14828 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
14829 } else {
14830 break;
14831 }
14832 }
14833
14834 if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) {
14835 break;
14836 }
14837
14838 ma_sleep(waitTimeInMilliseconds);
14839 }
14840 }
14841
14842 hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
14843 if (FAILED(hr)) {
14844 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.", ma_result_from_HRESULT(hr));
14845 }
14846
14847 ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
14848 }
14849
14850 return MA_SUCCESS;
14851 }
14852
14853 static ma_result ma_context_uninit__dsound(ma_context* pContext)
14854 {
14855 MA_ASSERT(pContext != NULL);
14856 MA_ASSERT(pContext->backend == ma_backend_dsound);
14857
14858 ma_dlclose(pContext, pContext->dsound.hDSoundDLL);
14859
14860 return MA_SUCCESS;
14861 }
14862
14863 static ma_result ma_context_init__dsound(const ma_context_config* pConfig, ma_context* pContext)
14864 {
14865 MA_ASSERT(pContext != NULL);
14866
14867 (void)pConfig;
14868
14869 pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll");
14870 if (pContext->dsound.hDSoundDLL == NULL) {
14871 return MA_API_NOT_FOUND;
14872 }
14873
14874 pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate");
14875 pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
14876 pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
14877 pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
14878
14879 pContext->onUninit = ma_context_uninit__dsound;
14880 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__dsound;
14881 pContext->onEnumDevices = ma_context_enumerate_devices__dsound;
14882 pContext->onGetDeviceInfo = ma_context_get_device_info__dsound;
14883 pContext->onDeviceInit = ma_device_init__dsound;
14884 pContext->onDeviceUninit = ma_device_uninit__dsound;
14885 pContext->onDeviceStart = NULL; /* Not used. Started in onDeviceMainLoop. */
14886 pContext->onDeviceStop = NULL; /* Not used. Stopped in onDeviceMainLoop. */
14887 pContext->onDeviceMainLoop = ma_device_main_loop__dsound;
14888
14889 return MA_SUCCESS;
14890 }
14891 #endif
14892
14893
14894
14895 /******************************************************************************
14896
14897 WinMM Backend
14898
14899 ******************************************************************************/
14900 #ifdef MA_HAS_WINMM
14901
14902 /*
14903 Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures
14904 are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping
14905 the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version.
14906 */
14907 typedef struct
14908 {
14909 WORD wMid;
14910 WORD wPid;
14911 MMVERSION vDriverVersion;
14912 CHAR szPname[MAXPNAMELEN];
14913 DWORD dwFormats;
14914 WORD wChannels;
14915 WORD wReserved1;
14916 DWORD dwSupport;
14917 GUID ManufacturerGuid;
14918 GUID ProductGuid;
14919 GUID NameGuid;
14920 } MA_WAVEOUTCAPS2A;
14921 typedef struct
14922 {
14923 WORD wMid;
14924 WORD wPid;
14925 MMVERSION vDriverVersion;
14926 CHAR szPname[MAXPNAMELEN];
14927 DWORD dwFormats;
14928 WORD wChannels;
14929 WORD wReserved1;
14930 GUID ManufacturerGuid;
14931 GUID ProductGuid;
14932 GUID NameGuid;
14933 } MA_WAVEINCAPS2A;
14934
14935 typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
14936 typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc);
14937 typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
14938 typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo);
14939 typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
14940 typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
14941 typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
14942 typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo);
14943 typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
14944 typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic);
14945 typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
14946 typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi);
14947 typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
14948 typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
14949 typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
14950 typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi);
14951 typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi);
14952
14953 static ma_result ma_result_from_MMRESULT(MMRESULT resultMM)
14954 {
14955 switch (resultMM) {
14956 case MMSYSERR_NOERROR: return MA_SUCCESS;
14957 case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
14958 case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
14959 case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
14960 case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
14961 case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
14962 case MMSYSERR_HANDLEBUSY: return MA_BUSY;
14963 case MMSYSERR_ERROR: return MA_ERROR;
14964 default: return MA_ERROR;
14965 }
14966 }
14967
14968 static char* ma_find_last_character(char* str, char ch)
14969 {
14970 char* last;
14971
14972 if (str == NULL) {
14973 return NULL;
14974 }
14975
14976 last = NULL;
14977 while (*str != '\0') {
14978 if (*str == ch) {
14979 last = str;
14980 }
14981
14982 str += 1;
14983 }
14984
14985 return last;
14986 }
14987
14988 static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels)
14989 {
14990 return periodSizeInFrames * ma_get_bytes_per_frame(format, channels);
14991 }
14992
14993
14994 /*
14995 Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so
14996 we can do things generically and typesafely. Names are being kept the same for consistency.
14997 */
14998 typedef struct
14999 {
15000 CHAR szPname[MAXPNAMELEN];
15001 DWORD dwFormats;
15002 WORD wChannels;
15003 GUID NameGuid;
15004 } MA_WAVECAPSA;
15005
15006 static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
15007 {
15008 WORD bitsPerSample = 0;
15009 DWORD sampleRate = 0;
15010
15011 if (pBitsPerSample) {
15012 *pBitsPerSample = 0;
15013 }
15014 if (pSampleRate) {
15015 *pSampleRate = 0;
15016 }
15017
15018 if (channels == 1) {
15019 bitsPerSample = 16;
15020 if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
15021 sampleRate = 48000;
15022 } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
15023 sampleRate = 44100;
15024 } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
15025 sampleRate = 22050;
15026 } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
15027 sampleRate = 11025;
15028 } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
15029 sampleRate = 96000;
15030 } else {
15031 bitsPerSample = 8;
15032 if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
15033 sampleRate = 48000;
15034 } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
15035 sampleRate = 44100;
15036 } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
15037 sampleRate = 22050;
15038 } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
15039 sampleRate = 11025;
15040 } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
15041 sampleRate = 96000;
15042 } else {
15043 return MA_FORMAT_NOT_SUPPORTED;
15044 }
15045 }
15046 } else {
15047 bitsPerSample = 16;
15048 if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
15049 sampleRate = 48000;
15050 } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
15051 sampleRate = 44100;
15052 } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
15053 sampleRate = 22050;
15054 } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
15055 sampleRate = 11025;
15056 } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
15057 sampleRate = 96000;
15058 } else {
15059 bitsPerSample = 8;
15060 if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
15061 sampleRate = 48000;
15062 } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
15063 sampleRate = 44100;
15064 } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
15065 sampleRate = 22050;
15066 } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
15067 sampleRate = 11025;
15068 } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
15069 sampleRate = 96000;
15070 } else {
15071 return MA_FORMAT_NOT_SUPPORTED;
15072 }
15073 }
15074 }
15075
15076 if (pBitsPerSample) {
15077 *pBitsPerSample = bitsPerSample;
15078 }
15079 if (pSampleRate) {
15080 *pSampleRate = sampleRate;
15081 }
15082
15083 return MA_SUCCESS;
15084 }
15085
15086 static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF)
15087 {
15088 MA_ASSERT(pWF != NULL);
15089
15090 MA_ZERO_OBJECT(pWF);
15091 pWF->cbSize = sizeof(*pWF);
15092 pWF->wFormatTag = WAVE_FORMAT_PCM;
15093 pWF->nChannels = (WORD)channels;
15094 if (pWF->nChannels > 2) {
15095 pWF->nChannels = 2;
15096 }
15097
15098 if (channels == 1) {
15099 pWF->wBitsPerSample = 16;
15100 if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
15101 pWF->nSamplesPerSec = 48000;
15102 } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
15103 pWF->nSamplesPerSec = 44100;
15104 } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
15105 pWF->nSamplesPerSec = 22050;
15106 } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
15107 pWF->nSamplesPerSec = 11025;
15108 } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
15109 pWF->nSamplesPerSec = 96000;
15110 } else {
15111 pWF->wBitsPerSample = 8;
15112 if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
15113 pWF->nSamplesPerSec = 48000;
15114 } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
15115 pWF->nSamplesPerSec = 44100;
15116 } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
15117 pWF->nSamplesPerSec = 22050;
15118 } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
15119 pWF->nSamplesPerSec = 11025;
15120 } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
15121 pWF->nSamplesPerSec = 96000;
15122 } else {
15123 return MA_FORMAT_NOT_SUPPORTED;
15124 }
15125 }
15126 } else {
15127 pWF->wBitsPerSample = 16;
15128 if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
15129 pWF->nSamplesPerSec = 48000;
15130 } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
15131 pWF->nSamplesPerSec = 44100;
15132 } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
15133 pWF->nSamplesPerSec = 22050;
15134 } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
15135 pWF->nSamplesPerSec = 11025;
15136 } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
15137 pWF->nSamplesPerSec = 96000;
15138 } else {
15139 pWF->wBitsPerSample = 8;
15140 if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
15141 pWF->nSamplesPerSec = 48000;
15142 } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
15143 pWF->nSamplesPerSec = 44100;
15144 } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
15145 pWF->nSamplesPerSec = 22050;
15146 } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
15147 pWF->nSamplesPerSec = 11025;
15148 } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
15149 pWF->nSamplesPerSec = 96000;
15150 } else {
15151 return MA_FORMAT_NOT_SUPPORTED;
15152 }
15153 }
15154 }
15155
15156 pWF->nBlockAlign = (pWF->nChannels * pWF->wBitsPerSample) / 8;
15157 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
15158
15159 return MA_SUCCESS;
15160 }
15161
15162 static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
15163 {
15164 WORD bitsPerSample;
15165 DWORD sampleRate;
15166 ma_result result;
15167
15168 MA_ASSERT(pContext != NULL);
15169 MA_ASSERT(pCaps != NULL);
15170 MA_ASSERT(pDeviceInfo != NULL);
15171
15172 /*
15173 Name / Description
15174
15175 Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking
15176 situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try
15177 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.
15178 */
15179
15180 /* Set the default to begin with. */
15181 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
15182
15183 /*
15184 Now try the registry. There's a few things to consider here:
15185 - The name GUID can be null, in which we case we just need to stick to the original 31 characters.
15186 - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
15187 - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
15188 problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"),
15189 but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to
15190 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
15191 name, and then concatenate the name from the registry.
15192 */
15193 if (!ma_is_guid_equal(&pCaps->NameGuid, &MA_GUID_NULL)) {
15194 wchar_t guidStrW[256];
15195 if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
15196 char guidStr[256];
15197 char keyStr[1024];
15198 HKEY hKey;
15199
15200 WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
15201
15202 ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
15203 ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
15204
15205 if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
15206 BYTE nameFromReg[512];
15207 DWORD nameFromRegSize = sizeof(nameFromReg);
15208 result = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize);
15209 ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
15210
15211 if (result == ERROR_SUCCESS) {
15212 /* We have the value from the registry, so now we need to construct the name string. */
15213 char name[1024];
15214 if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
15215 char* nameBeg = ma_find_last_character(name, '(');
15216 if (nameBeg != NULL) {
15217 size_t leadingLen = (nameBeg - name);
15218 ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
15219
15220 /* The closing ")", if it can fit. */
15221 if (leadingLen + nameFromRegSize < sizeof(name)-1) {
15222 ma_strcat_s(name, sizeof(name), ")");
15223 }
15224
15225 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
15226 }
15227 }
15228 }
15229 }
15230 }
15231 }
15232
15233
15234 result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
15235 if (result != MA_SUCCESS) {
15236 return result;
15237 }
15238
15239 pDeviceInfo->minChannels = pCaps->wChannels;
15240 pDeviceInfo->maxChannels = pCaps->wChannels;
15241 pDeviceInfo->minSampleRate = sampleRate;
15242 pDeviceInfo->maxSampleRate = sampleRate;
15243 pDeviceInfo->formatCount = 1;
15244 if (bitsPerSample == 8) {
15245 pDeviceInfo->formats[0] = ma_format_u8;
15246 } else if (bitsPerSample == 16) {
15247 pDeviceInfo->formats[0] = ma_format_s16;
15248 } else if (bitsPerSample == 24) {
15249 pDeviceInfo->formats[0] = ma_format_s24;
15250 } else if (bitsPerSample == 32) {
15251 pDeviceInfo->formats[0] = ma_format_s32;
15252 } else {
15253 return MA_FORMAT_NOT_SUPPORTED;
15254 }
15255
15256 return MA_SUCCESS;
15257 }
15258
15259 static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
15260 {
15261 MA_WAVECAPSA caps;
15262
15263 MA_ASSERT(pContext != NULL);
15264 MA_ASSERT(pCaps != NULL);
15265 MA_ASSERT(pDeviceInfo != NULL);
15266
15267 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
15268 caps.dwFormats = pCaps->dwFormats;
15269 caps.wChannels = pCaps->wChannels;
15270 caps.NameGuid = pCaps->NameGuid;
15271 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
15272 }
15273
15274 static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
15275 {
15276 MA_WAVECAPSA caps;
15277
15278 MA_ASSERT(pContext != NULL);
15279 MA_ASSERT(pCaps != NULL);
15280 MA_ASSERT(pDeviceInfo != NULL);
15281
15282 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
15283 caps.dwFormats = pCaps->dwFormats;
15284 caps.wChannels = pCaps->wChannels;
15285 caps.NameGuid = pCaps->NameGuid;
15286 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
15287 }
15288
15289
15290 static ma_bool32 ma_context_is_device_id_equal__winmm(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
15291 {
15292 MA_ASSERT(pContext != NULL);
15293 MA_ASSERT(pID0 != NULL);
15294 MA_ASSERT(pID1 != NULL);
15295 (void)pContext;
15296
15297 return pID0->winmm == pID1->winmm;
15298 }
15299
15300 static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
15301 {
15302 UINT playbackDeviceCount;
15303 UINT captureDeviceCount;
15304 UINT iPlaybackDevice;
15305 UINT iCaptureDevice;
15306
15307 MA_ASSERT(pContext != NULL);
15308 MA_ASSERT(callback != NULL);
15309
15310 /* Playback. */
15311 playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
15312 for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
15313 MMRESULT result;
15314 MA_WAVEOUTCAPS2A caps;
15315
15316 MA_ZERO_OBJECT(&caps);
15317
15318 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps));
15319 if (result == MMSYSERR_NOERROR) {
15320 ma_device_info deviceInfo;
15321
15322 MA_ZERO_OBJECT(&deviceInfo);
15323 deviceInfo.id.winmm = iPlaybackDevice;
15324
15325 if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
15326 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
15327 if (cbResult == MA_FALSE) {
15328 return MA_SUCCESS; /* Enumeration was stopped. */
15329 }
15330 }
15331 }
15332 }
15333
15334 /* Capture. */
15335 captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
15336 for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
15337 MMRESULT result;
15338 MA_WAVEINCAPS2A caps;
15339
15340 MA_ZERO_OBJECT(&caps);
15341
15342 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps));
15343 if (result == MMSYSERR_NOERROR) {
15344 ma_device_info deviceInfo;
15345
15346 MA_ZERO_OBJECT(&deviceInfo);
15347 deviceInfo.id.winmm = iCaptureDevice;
15348
15349 if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
15350 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
15351 if (cbResult == MA_FALSE) {
15352 return MA_SUCCESS; /* Enumeration was stopped. */
15353 }
15354 }
15355 }
15356 }
15357
15358 return MA_SUCCESS;
15359 }
15360
15361 static 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)
15362 {
15363 UINT winMMDeviceID;
15364
15365 MA_ASSERT(pContext != NULL);
15366
15367 if (shareMode == ma_share_mode_exclusive) {
15368 return MA_SHARE_MODE_NOT_SUPPORTED;
15369 }
15370
15371 winMMDeviceID = 0;
15372 if (pDeviceID != NULL) {
15373 winMMDeviceID = (UINT)pDeviceID->winmm;
15374 }
15375
15376 pDeviceInfo->id.winmm = winMMDeviceID;
15377
15378 if (deviceType == ma_device_type_playback) {
15379 MMRESULT result;
15380 MA_WAVEOUTCAPS2A caps;
15381
15382 MA_ZERO_OBJECT(&caps);
15383
15384 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps));
15385 if (result == MMSYSERR_NOERROR) {
15386 return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
15387 }
15388 } else {
15389 MMRESULT result;
15390 MA_WAVEINCAPS2A caps;
15391
15392 MA_ZERO_OBJECT(&caps);
15393
15394 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps));
15395 if (result == MMSYSERR_NOERROR) {
15396 return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
15397 }
15398 }
15399
15400 return MA_NO_DEVICE;
15401 }
15402
15403
15404 static void ma_device_uninit__winmm(ma_device* pDevice)
15405 {
15406 MA_ASSERT(pDevice != NULL);
15407
15408 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
15409 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
15410 CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
15411 }
15412
15413 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
15414 ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
15415 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
15416 CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
15417 }
15418
15419 ma__free_from_callbacks(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
15420
15421 MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */
15422 }
15423
15424 static ma_result ma_device_init__winmm(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
15425 {
15426 const char* errorMsg = "";
15427 ma_result errorCode = MA_ERROR;
15428 ma_result result = MA_SUCCESS;
15429 ma_uint32 heapSize;
15430 UINT winMMDeviceIDPlayback = 0;
15431 UINT winMMDeviceIDCapture = 0;
15432 ma_uint32 periodSizeInMilliseconds;
15433
15434 MA_ASSERT(pDevice != NULL);
15435 MA_ZERO_OBJECT(&pDevice->winmm);
15436
15437 if (pConfig->deviceType == ma_device_type_loopback) {
15438 return MA_DEVICE_TYPE_NOT_SUPPORTED;
15439 }
15440
15441 /* No exlusive mode with WinMM. */
15442 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
15443 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
15444 return MA_SHARE_MODE_NOT_SUPPORTED;
15445 }
15446
15447 periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
15448 if (periodSizeInMilliseconds == 0) {
15449 periodSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->periodSizeInFrames, pConfig->sampleRate);
15450 }
15451
15452 /* WinMM has horrible latency. */
15453 if (pDevice->usingDefaultBufferSize) {
15454 if (pConfig->performanceProfile == ma_performance_profile_low_latency) {
15455 periodSizeInMilliseconds = 40;
15456 } else {
15457 periodSizeInMilliseconds = 400;
15458 }
15459 }
15460
15461
15462 if (pConfig->playback.pDeviceID != NULL) {
15463 winMMDeviceIDPlayback = (UINT)pConfig->playback.pDeviceID->winmm;
15464 }
15465 if (pConfig->capture.pDeviceID != NULL) {
15466 winMMDeviceIDCapture = (UINT)pConfig->capture.pDeviceID->winmm;
15467 }
15468
15469 /* The capture device needs to be initialized first. */
15470 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
15471 WAVEINCAPSA caps;
15472 WAVEFORMATEX wf;
15473 MMRESULT resultMM;
15474
15475 /* We use an event to know when a new fragment needs to be enqueued. */
15476 pDevice->winmm.hEventCapture = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL);
15477 if (pDevice->winmm.hEventCapture == NULL) {
15478 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
15479 goto on_error;
15480 }
15481
15482 /* The format should be based on the device's actual format. */
15483 if (((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
15484 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
15485 goto on_error;
15486 }
15487
15488 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
15489 if (result != MA_SUCCESS) {
15490 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
15491 goto on_error;
15492 }
15493
15494 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);
15495 if (resultMM != MMSYSERR_NOERROR) {
15496 errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
15497 goto on_error;
15498 }
15499
15500 pDevice->capture.internalFormat = ma_format_from_WAVEFORMATEX(&wf);
15501 pDevice->capture.internalChannels = wf.nChannels;
15502 pDevice->capture.internalSampleRate = wf.nSamplesPerSec;
15503 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
15504 pDevice->capture.internalPeriods = pConfig->periods;
15505 pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pDevice->capture.internalSampleRate);
15506 }
15507
15508 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
15509 WAVEOUTCAPSA caps;
15510 WAVEFORMATEX wf;
15511 MMRESULT resultMM;
15512
15513 /* We use an event to know when a new fragment needs to be enqueued. */
15514 pDevice->winmm.hEventPlayback = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL);
15515 if (pDevice->winmm.hEventPlayback == NULL) {
15516 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
15517 goto on_error;
15518 }
15519
15520 /* The format should be based on the device's actual format. */
15521 if (((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
15522 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
15523 goto on_error;
15524 }
15525
15526 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
15527 if (result != MA_SUCCESS) {
15528 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
15529 goto on_error;
15530 }
15531
15532 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);
15533 if (resultMM != MMSYSERR_NOERROR) {
15534 errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
15535 goto on_error;
15536 }
15537
15538 pDevice->playback.internalFormat = ma_format_from_WAVEFORMATEX(&wf);
15539 pDevice->playback.internalChannels = wf.nChannels;
15540 pDevice->playback.internalSampleRate = wf.nSamplesPerSec;
15541 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
15542 pDevice->playback.internalPeriods = pConfig->periods;
15543 pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pDevice->playback.internalSampleRate);
15544 }
15545
15546 /*
15547 The heap allocated data is allocated like so:
15548
15549 [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
15550 */
15551 heapSize = 0;
15552 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
15553 heapSize += sizeof(WAVEHDR)*pDevice->capture.internalPeriods + (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
15554 }
15555 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
15556 heapSize += sizeof(WAVEHDR)*pDevice->playback.internalPeriods + (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
15557 }
15558
15559 pDevice->winmm._pHeapData = (ma_uint8*)ma__calloc_from_callbacks(heapSize, &pContext->allocationCallbacks);
15560 if (pDevice->winmm._pHeapData == NULL) {
15561 errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
15562 goto on_error;
15563 }
15564
15565 MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);
15566
15567 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
15568 ma_uint32 iPeriod;
15569
15570 if (pConfig->deviceType == ma_device_type_capture) {
15571 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
15572 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
15573 } else {
15574 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
15575 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods + pDevice->playback.internalPeriods));
15576 }
15577
15578 /* Prepare headers. */
15579 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
15580 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalFormat, pDevice->capture.internalChannels);
15581
15582 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));
15583 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;
15584 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
15585 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
15586 ((MA_PFN_waveInPrepareHeader)pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
15587
15588 /*
15589 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
15590 it's unlocked and available for writing. A value of 1 means it's locked.
15591 */
15592 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
15593 }
15594 }
15595 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
15596 ma_uint32 iPeriod;
15597
15598 if (pConfig->deviceType == ma_device_type_playback) {
15599 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
15600 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDevice->playback.internalPeriods);
15601 } else {
15602 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
15603 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));
15604 }
15605
15606 /* Prepare headers. */
15607 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
15608 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalFormat, pDevice->playback.internalChannels);
15609
15610 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));
15611 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;
15612 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
15613 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
15614 ((MA_PFN_waveOutPrepareHeader)pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
15615
15616 /*
15617 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
15618 it's unlocked and available for writing. A value of 1 means it's locked.
15619 */
15620 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
15621 }
15622 }
15623
15624 return MA_SUCCESS;
15625
15626 on_error:
15627 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
15628 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
15629 ma_uint32 iPeriod;
15630 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
15631 ((MA_PFN_waveInUnprepareHeader)pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
15632 }
15633 }
15634
15635 ((MA_PFN_waveInClose)pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
15636 }
15637
15638 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
15639 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
15640 ma_uint32 iPeriod;
15641 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
15642 ((MA_PFN_waveOutUnprepareHeader)pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
15643 }
15644 }
15645
15646 ((MA_PFN_waveOutClose)pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
15647 }
15648
15649 ma__free_from_callbacks(pDevice->winmm._pHeapData, &pContext->allocationCallbacks);
15650 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, errorMsg, errorCode);
15651 }
15652
15653 static ma_result ma_device_stop__winmm(ma_device* pDevice)
15654 {
15655 MMRESULT resultMM;
15656
15657 MA_ASSERT(pDevice != NULL);
15658
15659 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
15660 if (pDevice->winmm.hDeviceCapture == NULL) {
15661 return MA_INVALID_ARGS;
15662 }
15663
15664 resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture);
15665 if (resultMM != MMSYSERR_NOERROR) {
15666 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset capture device.", ma_result_from_MMRESULT(resultMM));
15667 }
15668 }
15669
15670 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
15671 ma_uint32 iPeriod;
15672 WAVEHDR* pWAVEHDR;
15673
15674 if (pDevice->winmm.hDevicePlayback == NULL) {
15675 return MA_INVALID_ARGS;
15676 }
15677
15678 /* 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. */
15679 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
15680 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {
15681 if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */
15682 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
15683 break; /* An error occurred so just abandon ship and stop the device without draining. */
15684 }
15685
15686 pWAVEHDR[iPeriod].dwUser = 0;
15687 }
15688 }
15689
15690 resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
15691 if (resultMM != MMSYSERR_NOERROR) {
15692 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset playback device.", ma_result_from_MMRESULT(resultMM));
15693 }
15694 }
15695
15696 return MA_SUCCESS;
15697 }
15698
15699 static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
15700 {
15701 ma_result result = MA_SUCCESS;
15702 MMRESULT resultMM;
15703 ma_uint32 totalFramesWritten;
15704 WAVEHDR* pWAVEHDR;
15705
15706 MA_ASSERT(pDevice != NULL);
15707 MA_ASSERT(pPCMFrames != NULL);
15708
15709 if (pFramesWritten != NULL) {
15710 *pFramesWritten = 0;
15711 }
15712
15713 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
15714
15715 /* Keep processing as much data as possible. */
15716 totalFramesWritten = 0;
15717 while (totalFramesWritten < frameCount) {
15718 /* If the current header has some space available we need to write part of it. */
15719 if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
15720 /*
15721 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
15722 write it out and move on to the next iteration.
15723 */
15724 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
15725 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
15726
15727 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
15728 const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
15729 void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
15730 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
15731
15732 pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
15733 totalFramesWritten += framesToCopy;
15734
15735 /* If we've consumed the buffer entirely we need to write it out to the device. */
15736 if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
15737 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */
15738 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
15739
15740 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
15741 ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
15742
15743 /* The device will be started here. */
15744 resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR));
15745 if (resultMM != MMSYSERR_NOERROR) {
15746 result = ma_result_from_MMRESULT(resultMM);
15747 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.", result);
15748 break;
15749 }
15750
15751 /* Make sure we move to the next header. */
15752 pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
15753 pDevice->winmm.headerFramesConsumedPlayback = 0;
15754 }
15755
15756 /* If at this point we have consumed the entire input buffer we can return. */
15757 MA_ASSERT(totalFramesWritten <= frameCount);
15758 if (totalFramesWritten == frameCount) {
15759 break;
15760 }
15761
15762 /* Getting here means there's more to process. */
15763 continue;
15764 }
15765
15766 /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
15767 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
15768 result = MA_ERROR;
15769 break;
15770 }
15771
15772 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
15773 if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) {
15774 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */
15775 pDevice->winmm.headerFramesConsumedPlayback = 0;
15776 }
15777
15778 /* If the device has been stopped we need to break. */
15779 if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
15780 break;
15781 }
15782 }
15783
15784 if (pFramesWritten != NULL) {
15785 *pFramesWritten = totalFramesWritten;
15786 }
15787
15788 return result;
15789 }
15790
15791 static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
15792 {
15793 ma_result result = MA_SUCCESS;
15794 MMRESULT resultMM;
15795 ma_uint32 totalFramesRead;
15796 WAVEHDR* pWAVEHDR;
15797
15798 MA_ASSERT(pDevice != NULL);
15799 MA_ASSERT(pPCMFrames != NULL);
15800
15801 if (pFramesRead != NULL) {
15802 *pFramesRead = 0;
15803 }
15804
15805 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
15806
15807 /* Keep processing as much data as possible. */
15808 totalFramesRead = 0;
15809 while (totalFramesRead < frameCount) {
15810 /* If the current header has some space available we need to write part of it. */
15811 if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
15812 /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
15813 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
15814 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
15815
15816 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
15817 const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
15818 void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
15819 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
15820
15821 pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
15822 totalFramesRead += framesToCopy;
15823
15824 /* If we've consumed the buffer entirely we need to add it back to the device. */
15825 if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
15826 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */
15827 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
15828
15829 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
15830 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
15831
15832 /* The device will be started here. */
15833 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR));
15834 if (resultMM != MMSYSERR_NOERROR) {
15835 result = ma_result_from_MMRESULT(resultMM);
15836 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.", result);
15837 break;
15838 }
15839
15840 /* Make sure we move to the next header. */
15841 pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
15842 pDevice->winmm.headerFramesConsumedCapture = 0;
15843 }
15844
15845 /* If at this point we have filled the entire input buffer we can return. */
15846 MA_ASSERT(totalFramesRead <= frameCount);
15847 if (totalFramesRead == frameCount) {
15848 break;
15849 }
15850
15851 /* Getting here means there's more to process. */
15852 continue;
15853 }
15854
15855 /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
15856 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
15857 result = MA_ERROR;
15858 break;
15859 }
15860
15861 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
15862 if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) {
15863 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */
15864 pDevice->winmm.headerFramesConsumedCapture = 0;
15865 }
15866
15867 /* If the device has been stopped we need to break. */
15868 if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
15869 break;
15870 }
15871 }
15872
15873 if (pFramesRead != NULL) {
15874 *pFramesRead = totalFramesRead;
15875 }
15876
15877 return result;
15878 }
15879
15880 static ma_result ma_device_main_loop__winmm(ma_device* pDevice)
15881 {
15882 ma_result result = MA_SUCCESS;
15883 ma_bool32 exitLoop = MA_FALSE;
15884
15885 MA_ASSERT(pDevice != NULL);
15886
15887 /* The capture device needs to be started immediately. */
15888 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
15889 MMRESULT resultMM;
15890 WAVEHDR* pWAVEHDR;
15891 ma_uint32 iPeriod;
15892
15893 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
15894
15895 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
15896 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
15897
15898 /* 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. */
15899 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
15900 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
15901 if (resultMM != MMSYSERR_NOERROR) {
15902 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));
15903 }
15904
15905 /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
15906 pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */
15907 }
15908
15909 /* Capture devices need to be explicitly started, unlike playback devices. */
15910 resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture);
15911 if (resultMM != MMSYSERR_NOERROR) {
15912 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.", ma_result_from_MMRESULT(resultMM));
15913 }
15914 }
15915
15916
15917 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
15918 switch (pDevice->type)
15919 {
15920 case ma_device_type_duplex:
15921 {
15922 /* The process is: device_read -> convert -> callback -> convert -> device_write */
15923 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
15924 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
15925
15926 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
15927 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
15928 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
15929 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
15930 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
15931 ma_uint32 capturedDeviceFramesRemaining;
15932 ma_uint32 capturedDeviceFramesProcessed;
15933 ma_uint32 capturedDeviceFramesToProcess;
15934 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
15935 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
15936 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
15937 }
15938
15939 result = ma_device_read__winmm(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
15940 if (result != MA_SUCCESS) {
15941 exitLoop = MA_TRUE;
15942 break;
15943 }
15944
15945 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
15946 capturedDeviceFramesProcessed = 0;
15947
15948 for (;;) {
15949 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
15950 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
15951 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
15952 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
15953 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
15954 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
15955 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
15956
15957 /* Convert capture data from device format to client format. */
15958 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
15959 if (result != MA_SUCCESS) {
15960 break;
15961 }
15962
15963 /*
15964 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
15965 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
15966 */
15967 if (capturedClientFramesToProcessThisIteration == 0) {
15968 break;
15969 }
15970
15971 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
15972
15973 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
15974 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
15975
15976 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
15977 for (;;) {
15978 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
15979 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
15980 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
15981 if (result != MA_SUCCESS) {
15982 break;
15983 }
15984
15985 result = ma_device_write__winmm(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
15986 if (result != MA_SUCCESS) {
15987 exitLoop = MA_TRUE;
15988 break;
15989 }
15990
15991 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
15992 if (capturedClientFramesToProcessThisIteration == 0) {
15993 break;
15994 }
15995 }
15996
15997 /* In case an error happened from ma_device_write__winmm()... */
15998 if (result != MA_SUCCESS) {
15999 exitLoop = MA_TRUE;
16000 break;
16001 }
16002 }
16003
16004 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
16005 }
16006 } break;
16007
16008 case ma_device_type_capture:
16009 {
16010 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
16011 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
16012 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
16013 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
16014 ma_uint32 framesReadThisPeriod = 0;
16015 while (framesReadThisPeriod < periodSizeInFrames) {
16016 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
16017 ma_uint32 framesProcessed;
16018 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
16019 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
16020 framesToReadThisIteration = intermediaryBufferSizeInFrames;
16021 }
16022
16023 result = ma_device_read__winmm(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
16024 if (result != MA_SUCCESS) {
16025 exitLoop = MA_TRUE;
16026 break;
16027 }
16028
16029 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
16030
16031 framesReadThisPeriod += framesProcessed;
16032 }
16033 } break;
16034
16035 case ma_device_type_playback:
16036 {
16037 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
16038 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
16039 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
16040 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
16041 ma_uint32 framesWrittenThisPeriod = 0;
16042 while (framesWrittenThisPeriod < periodSizeInFrames) {
16043 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
16044 ma_uint32 framesProcessed;
16045 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
16046 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
16047 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
16048 }
16049
16050 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
16051
16052 result = ma_device_write__winmm(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
16053 if (result != MA_SUCCESS) {
16054 exitLoop = MA_TRUE;
16055 break;
16056 }
16057
16058 framesWrittenThisPeriod += framesProcessed;
16059 }
16060 } break;
16061
16062 /* To silence a warning. Will never hit this. */
16063 case ma_device_type_loopback:
16064 default: break;
16065 }
16066 }
16067
16068
16069 /* Here is where the device is started. */
16070 ma_device_stop__winmm(pDevice);
16071
16072 return result;
16073 }
16074
16075 static ma_result ma_context_uninit__winmm(ma_context* pContext)
16076 {
16077 MA_ASSERT(pContext != NULL);
16078 MA_ASSERT(pContext->backend == ma_backend_winmm);
16079
16080 ma_dlclose(pContext, pContext->winmm.hWinMM);
16081 return MA_SUCCESS;
16082 }
16083
16084 static ma_result ma_context_init__winmm(const ma_context_config* pConfig, ma_context* pContext)
16085 {
16086 MA_ASSERT(pContext != NULL);
16087
16088 (void)pConfig;
16089
16090 pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll");
16091 if (pContext->winmm.hWinMM == NULL) {
16092 return MA_NO_BACKEND;
16093 }
16094
16095 pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs");
16096 pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA");
16097 pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen");
16098 pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose");
16099 pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader");
16100 pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader");
16101 pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite");
16102 pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset");
16103 pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs");
16104 pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA");
16105 pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen");
16106 pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose");
16107 pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader");
16108 pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader");
16109 pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer");
16110 pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart");
16111 pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset");
16112
16113 pContext->onUninit = ma_context_uninit__winmm;
16114 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__winmm;
16115 pContext->onEnumDevices = ma_context_enumerate_devices__winmm;
16116 pContext->onGetDeviceInfo = ma_context_get_device_info__winmm;
16117 pContext->onDeviceInit = ma_device_init__winmm;
16118 pContext->onDeviceUninit = ma_device_uninit__winmm;
16119 pContext->onDeviceStart = NULL; /* Not used with synchronous backends. */
16120 pContext->onDeviceStop = NULL; /* Not used with synchronous backends. */
16121 pContext->onDeviceMainLoop = ma_device_main_loop__winmm;
16122
16123 return MA_SUCCESS;
16124 }
16125 #endif
16126
16127
16128
16129
16130 /******************************************************************************
16131
16132 ALSA Backend
16133
16134 ******************************************************************************/
16135 #ifdef MA_HAS_ALSA
16136
16137 #ifdef MA_NO_RUNTIME_LINKING
16138 #include <alsa/asoundlib.h>
16139 typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
16140 typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
16141 typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
16142 typedef snd_pcm_format_t ma_snd_pcm_format_t;
16143 typedef snd_pcm_access_t ma_snd_pcm_access_t;
16144 typedef snd_pcm_t ma_snd_pcm_t;
16145 typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
16146 typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
16147 typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
16148 typedef snd_pcm_info_t ma_snd_pcm_info_t;
16149 typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
16150 typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
16151
16152 /* snd_pcm_stream_t */
16153 #define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
16154 #define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
16155
16156 /* snd_pcm_format_t */
16157 #define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
16158 #define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
16159 #define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
16160 #define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
16161 #define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
16162 #define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
16163 #define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
16164 #define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
16165 #define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
16166 #define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
16167 #define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
16168 #define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
16169 #define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
16170 #define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
16171 #define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
16172 #define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
16173
16174 /* ma_snd_pcm_access_t */
16175 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
16176 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
16177 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
16178 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
16179 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
16180
16181 /* Channel positions. */
16182 #define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
16183 #define MA_SND_CHMAP_NA SND_CHMAP_NA
16184 #define MA_SND_CHMAP_MONO SND_CHMAP_MONO
16185 #define MA_SND_CHMAP_FL SND_CHMAP_FL
16186 #define MA_SND_CHMAP_FR SND_CHMAP_FR
16187 #define MA_SND_CHMAP_RL SND_CHMAP_RL
16188 #define MA_SND_CHMAP_RR SND_CHMAP_RR
16189 #define MA_SND_CHMAP_FC SND_CHMAP_FC
16190 #define MA_SND_CHMAP_LFE SND_CHMAP_LFE
16191 #define MA_SND_CHMAP_SL SND_CHMAP_SL
16192 #define MA_SND_CHMAP_SR SND_CHMAP_SR
16193 #define MA_SND_CHMAP_RC SND_CHMAP_RC
16194 #define MA_SND_CHMAP_FLC SND_CHMAP_FLC
16195 #define MA_SND_CHMAP_FRC SND_CHMAP_FRC
16196 #define MA_SND_CHMAP_RLC SND_CHMAP_RLC
16197 #define MA_SND_CHMAP_RRC SND_CHMAP_RRC
16198 #define MA_SND_CHMAP_FLW SND_CHMAP_FLW
16199 #define MA_SND_CHMAP_FRW SND_CHMAP_FRW
16200 #define MA_SND_CHMAP_FLH SND_CHMAP_FLH
16201 #define MA_SND_CHMAP_FCH SND_CHMAP_FCH
16202 #define MA_SND_CHMAP_FRH SND_CHMAP_FRH
16203 #define MA_SND_CHMAP_TC SND_CHMAP_TC
16204 #define MA_SND_CHMAP_TFL SND_CHMAP_TFL
16205 #define MA_SND_CHMAP_TFR SND_CHMAP_TFR
16206 #define MA_SND_CHMAP_TFC SND_CHMAP_TFC
16207 #define MA_SND_CHMAP_TRL SND_CHMAP_TRL
16208 #define MA_SND_CHMAP_TRR SND_CHMAP_TRR
16209 #define MA_SND_CHMAP_TRC SND_CHMAP_TRC
16210 #define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
16211 #define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
16212 #define MA_SND_CHMAP_TSL SND_CHMAP_TSL
16213 #define MA_SND_CHMAP_TSR SND_CHMAP_TSR
16214 #define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
16215 #define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
16216 #define MA_SND_CHMAP_BC SND_CHMAP_BC
16217 #define MA_SND_CHMAP_BLC SND_CHMAP_BLC
16218 #define MA_SND_CHMAP_BRC SND_CHMAP_BRC
16219
16220 /* Open mode flags. */
16221 #define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
16222 #define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
16223 #define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
16224 #else
16225 #include <errno.h> /* For EPIPE, etc. */
16226 typedef unsigned long ma_snd_pcm_uframes_t;
16227 typedef long ma_snd_pcm_sframes_t;
16228 typedef int ma_snd_pcm_stream_t;
16229 typedef int ma_snd_pcm_format_t;
16230 typedef int ma_snd_pcm_access_t;
16231 typedef struct ma_snd_pcm_t ma_snd_pcm_t;
16232 typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
16233 typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
16234 typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
16235 typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
16236 typedef struct
16237 {
16238 void* addr;
16239 unsigned int first;
16240 unsigned int step;
16241 } ma_snd_pcm_channel_area_t;
16242 typedef struct
16243 {
16244 unsigned int channels;
16245 unsigned int pos[1];
16246 } ma_snd_pcm_chmap_t;
16247
16248 /* snd_pcm_state_t */
16249 #define MA_SND_PCM_STATE_OPEN 0
16250 #define MA_SND_PCM_STATE_SETUP 1
16251 #define MA_SND_PCM_STATE_PREPARED 2
16252 #define MA_SND_PCM_STATE_RUNNING 3
16253 #define MA_SND_PCM_STATE_XRUN 4
16254 #define MA_SND_PCM_STATE_DRAINING 5
16255 #define MA_SND_PCM_STATE_PAUSED 6
16256 #define MA_SND_PCM_STATE_SUSPENDED 7
16257 #define MA_SND_PCM_STATE_DISCONNECTED 8
16258
16259 /* snd_pcm_stream_t */
16260 #define MA_SND_PCM_STREAM_PLAYBACK 0
16261 #define MA_SND_PCM_STREAM_CAPTURE 1
16262
16263 /* snd_pcm_format_t */
16264 #define MA_SND_PCM_FORMAT_UNKNOWN -1
16265 #define MA_SND_PCM_FORMAT_U8 1
16266 #define MA_SND_PCM_FORMAT_S16_LE 2
16267 #define MA_SND_PCM_FORMAT_S16_BE 3
16268 #define MA_SND_PCM_FORMAT_S24_LE 6
16269 #define MA_SND_PCM_FORMAT_S24_BE 7
16270 #define MA_SND_PCM_FORMAT_S32_LE 10
16271 #define MA_SND_PCM_FORMAT_S32_BE 11
16272 #define MA_SND_PCM_FORMAT_FLOAT_LE 14
16273 #define MA_SND_PCM_FORMAT_FLOAT_BE 15
16274 #define MA_SND_PCM_FORMAT_FLOAT64_LE 16
16275 #define MA_SND_PCM_FORMAT_FLOAT64_BE 17
16276 #define MA_SND_PCM_FORMAT_MU_LAW 20
16277 #define MA_SND_PCM_FORMAT_A_LAW 21
16278 #define MA_SND_PCM_FORMAT_S24_3LE 32
16279 #define MA_SND_PCM_FORMAT_S24_3BE 33
16280
16281 /* snd_pcm_access_t */
16282 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
16283 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
16284 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
16285 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
16286 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
16287
16288 /* Channel positions. */
16289 #define MA_SND_CHMAP_UNKNOWN 0
16290 #define MA_SND_CHMAP_NA 1
16291 #define MA_SND_CHMAP_MONO 2
16292 #define MA_SND_CHMAP_FL 3
16293 #define MA_SND_CHMAP_FR 4
16294 #define MA_SND_CHMAP_RL 5
16295 #define MA_SND_CHMAP_RR 6
16296 #define MA_SND_CHMAP_FC 7
16297 #define MA_SND_CHMAP_LFE 8
16298 #define MA_SND_CHMAP_SL 9
16299 #define MA_SND_CHMAP_SR 10
16300 #define MA_SND_CHMAP_RC 11
16301 #define MA_SND_CHMAP_FLC 12
16302 #define MA_SND_CHMAP_FRC 13
16303 #define MA_SND_CHMAP_RLC 14
16304 #define MA_SND_CHMAP_RRC 15
16305 #define MA_SND_CHMAP_FLW 16
16306 #define MA_SND_CHMAP_FRW 17
16307 #define MA_SND_CHMAP_FLH 18
16308 #define MA_SND_CHMAP_FCH 19
16309 #define MA_SND_CHMAP_FRH 20
16310 #define MA_SND_CHMAP_TC 21
16311 #define MA_SND_CHMAP_TFL 22
16312 #define MA_SND_CHMAP_TFR 23
16313 #define MA_SND_CHMAP_TFC 24
16314 #define MA_SND_CHMAP_TRL 25
16315 #define MA_SND_CHMAP_TRR 26
16316 #define MA_SND_CHMAP_TRC 27
16317 #define MA_SND_CHMAP_TFLC 28
16318 #define MA_SND_CHMAP_TFRC 29
16319 #define MA_SND_CHMAP_TSL 30
16320 #define MA_SND_CHMAP_TSR 31
16321 #define MA_SND_CHMAP_LLFE 32
16322 #define MA_SND_CHMAP_RLFE 33
16323 #define MA_SND_CHMAP_BC 34
16324 #define MA_SND_CHMAP_BLC 35
16325 #define MA_SND_CHMAP_BRC 36
16326
16327 /* Open mode flags. */
16328 #define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
16329 #define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
16330 #define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
16331 #endif
16332
16333 typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
16334 typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
16335 typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
16336 typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
16337 typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val);
16338 typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
16339 typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask);
16340 typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val);
16341 typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
16342 typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
16343 typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
16344 typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
16345 typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access);
16346 typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
16347 typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
16348 typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
16349 typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
16350 typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
16351 typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
16352 typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
16353 typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
16354 typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
16355 typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
16356 typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
16357 typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
16358 typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
16359 typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
16360 typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
16361 typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
16362 typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
16363 typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
16364 typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
16365 typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
16366 typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
16367 typedef int (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
16368 typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
16369 typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
16370 typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
16371 typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
16372 typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
16373 typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
16374 typedef int (* ma_snd_card_get_index_proc) (const char *name);
16375 typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
16376 typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames);
16377 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames);
16378 typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
16379 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
16380 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size);
16381 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
16382 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
16383 typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
16384 typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
16385 typedef size_t (* ma_snd_pcm_info_sizeof_proc) ();
16386 typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
16387 typedef int (* ma_snd_config_update_free_global_proc) ();
16388
16389 /* This array specifies each of the common devices that can be used for both playback and capture. */
16390 static const char* g_maCommonDeviceNamesALSA[] = {
16391 "default",
16392 "null",
16393 "pulse",
16394 "jack"
16395 };
16396
16397 /* This array allows us to blacklist specific playback devices. */
16398 static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
16399 ""
16400 };
16401
16402 /* This array allows us to blacklist specific capture devices. */
16403 static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
16404 ""
16405 };
16406
16407
16408 /*
16409 This array allows miniaudio to control device-specific default buffer sizes. This uses a scaling factor. Order is important. If
16410 any part of the string is present in the device's name, the associated scale will be used.
16411 */
16412 static struct
16413 {
16414 const char* name;
16415 float scale;
16416 } g_maDefaultBufferSizeScalesALSA[] = {
16417 {"bcm2835 IEC958/HDMI", 2.0f},
16418 {"bcm2835 ALSA", 2.0f}
16419 };
16420
16421 static float ma_find_default_buffer_size_scale__alsa(const char* deviceName)
16422 {
16423 size_t i;
16424
16425 if (deviceName == NULL) {
16426 return 1;
16427 }
16428
16429 for (i = 0; i < ma_countof(g_maDefaultBufferSizeScalesALSA); ++i) {
16430 if (strstr(g_maDefaultBufferSizeScalesALSA[i].name, deviceName) != NULL) {
16431 return g_maDefaultBufferSizeScalesALSA[i].scale;
16432 }
16433 }
16434
16435 return 1;
16436 }
16437
16438 static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
16439 {
16440 ma_snd_pcm_format_t ALSAFormats[] = {
16441 MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */
16442 MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */
16443 MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
16444 MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
16445 MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
16446 MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */
16447 };
16448
16449 if (ma_is_big_endian()) {
16450 ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
16451 ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
16452 ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
16453 ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
16454 ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
16455 ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
16456 }
16457
16458 return ALSAFormats[format];
16459 }
16460
16461 static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
16462 {
16463 if (ma_is_little_endian()) {
16464 switch (formatALSA) {
16465 case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
16466 case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
16467 case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
16468 case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
16469 default: break;
16470 }
16471 } else {
16472 switch (formatALSA) {
16473 case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
16474 case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
16475 case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
16476 case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
16477 default: break;
16478 }
16479 }
16480
16481 /* Endian agnostic. */
16482 switch (formatALSA) {
16483 case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
16484 default: return ma_format_unknown;
16485 }
16486 }
16487
16488 static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
16489 {
16490 switch (alsaChannelPos)
16491 {
16492 case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
16493 case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
16494 case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
16495 case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
16496 case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
16497 case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
16498 case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
16499 case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
16500 case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
16501 case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
16502 case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
16503 case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
16504 case MA_SND_CHMAP_RLC: return 0;
16505 case MA_SND_CHMAP_RRC: return 0;
16506 case MA_SND_CHMAP_FLW: return 0;
16507 case MA_SND_CHMAP_FRW: return 0;
16508 case MA_SND_CHMAP_FLH: return 0;
16509 case MA_SND_CHMAP_FCH: return 0;
16510 case MA_SND_CHMAP_FRH: return 0;
16511 case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
16512 case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
16513 case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
16514 case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
16515 case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
16516 case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
16517 case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
16518 default: break;
16519 }
16520
16521 return 0;
16522 }
16523
16524 static ma_bool32 ma_is_common_device_name__alsa(const char* name)
16525 {
16526 size_t iName;
16527 for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
16528 if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
16529 return MA_TRUE;
16530 }
16531 }
16532
16533 return MA_FALSE;
16534 }
16535
16536
16537 static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
16538 {
16539 size_t iName;
16540 for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
16541 if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
16542 return MA_TRUE;
16543 }
16544 }
16545
16546 return MA_FALSE;
16547 }
16548
16549 static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
16550 {
16551 size_t iName;
16552 for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
16553 if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
16554 return MA_TRUE;
16555 }
16556 }
16557
16558 return MA_FALSE;
16559 }
16560
16561 static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
16562 {
16563 if (deviceType == ma_device_type_playback) {
16564 return ma_is_playback_device_blacklisted__alsa(name);
16565 } else {
16566 return ma_is_capture_device_blacklisted__alsa(name);
16567 }
16568 }
16569
16570
16571 static const char* ma_find_char(const char* str, char c, int* index)
16572 {
16573 int i = 0;
16574 for (;;) {
16575 if (str[i] == '\0') {
16576 if (index) *index = -1;
16577 return NULL;
16578 }
16579
16580 if (str[i] == c) {
16581 if (index) *index = i;
16582 return str + i;
16583 }
16584
16585 i += 1;
16586 }
16587
16588 /* Should never get here, but treat it as though the character was not found to make me feel better inside. */
16589 if (index) *index = -1;
16590 return NULL;
16591 }
16592
16593 static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
16594 {
16595 /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */
16596
16597 int commaPos;
16598 const char* dev;
16599 int i;
16600
16601 if (hwid == NULL) {
16602 return MA_FALSE;
16603 }
16604
16605 if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
16606 return MA_FALSE;
16607 }
16608
16609 hwid += 3;
16610
16611 dev = ma_find_char(hwid, ',', &commaPos);
16612 if (dev == NULL) {
16613 return MA_FALSE;
16614 } else {
16615 dev += 1; /* Skip past the ",". */
16616 }
16617
16618 /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */
16619 for (i = 0; i < commaPos; ++i) {
16620 if (hwid[i] < '0' || hwid[i] > '9') {
16621 return MA_FALSE;
16622 }
16623 }
16624
16625 /* Check if everything after the "," is numeric. If not, return false. */
16626 i = 0;
16627 while (dev[i] != '\0') {
16628 if (dev[i] < '0' || dev[i] > '9') {
16629 return MA_FALSE;
16630 }
16631 i += 1;
16632 }
16633
16634 return MA_TRUE;
16635 }
16636
16637 static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */
16638 {
16639 /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */
16640
16641 int colonPos;
16642 int commaPos;
16643 char card[256];
16644 const char* dev;
16645 int cardIndex;
16646
16647 if (dst == NULL) {
16648 return -1;
16649 }
16650 if (dstSize < 7) {
16651 return -1; /* Absolute minimum size of the output buffer is 7 bytes. */
16652 }
16653
16654 *dst = '\0'; /* Safety. */
16655 if (src == NULL) {
16656 return -1;
16657 }
16658
16659 /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */
16660 if (ma_is_device_name_in_hw_format__alsa(src)) {
16661 return ma_strcpy_s(dst, dstSize, src);
16662 }
16663
16664 src = ma_find_char(src, ':', &colonPos);
16665 if (src == NULL) {
16666 return -1; /* Couldn't find a colon */
16667 }
16668
16669 dev = ma_find_char(src, ',', &commaPos);
16670 if (dev == NULL) {
16671 dev = "0";
16672 ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */
16673 } else {
16674 dev = dev + 5; /* +5 = ",DEV=" */
16675 ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */
16676 }
16677
16678 cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
16679 if (cardIndex < 0) {
16680 return -2; /* Failed to retrieve the card index. */
16681 }
16682
16683 /*printf("TESTING: CARD=%s,DEV=%s\n", card, dev); */
16684
16685
16686 /* Construction. */
16687 dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
16688 if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
16689 return -3;
16690 }
16691 if (ma_strcat_s(dst, dstSize, ",") != 0) {
16692 return -3;
16693 }
16694 if (ma_strcat_s(dst, dstSize, dev) != 0) {
16695 return -3;
16696 }
16697
16698 return 0;
16699 }
16700
16701 static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
16702 {
16703 ma_uint32 i;
16704
16705 MA_ASSERT(pHWID != NULL);
16706
16707 for (i = 0; i < count; ++i) {
16708 if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
16709 return MA_TRUE;
16710 }
16711 }
16712
16713 return MA_FALSE;
16714 }
16715
16716
16717 static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM)
16718 {
16719 ma_snd_pcm_t* pPCM;
16720 ma_snd_pcm_stream_t stream;
16721
16722 MA_ASSERT(pContext != NULL);
16723 MA_ASSERT(ppPCM != NULL);
16724
16725 *ppPCM = NULL;
16726 pPCM = NULL;
16727
16728 stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
16729
16730 if (pDeviceID == NULL) {
16731 ma_bool32 isDeviceOpen;
16732 size_t i;
16733
16734 /*
16735 We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
16736 me feel better to try as hard as we can get to get _something_ working.
16737 */
16738 const char* defaultDeviceNames[] = {
16739 "default",
16740 NULL,
16741 NULL,
16742 NULL,
16743 NULL,
16744 NULL,
16745 NULL
16746 };
16747
16748 if (shareMode == ma_share_mode_exclusive) {
16749 defaultDeviceNames[1] = "hw";
16750 defaultDeviceNames[2] = "hw:0";
16751 defaultDeviceNames[3] = "hw:0,0";
16752 } else {
16753 if (deviceType == ma_device_type_playback) {
16754 defaultDeviceNames[1] = "dmix";
16755 defaultDeviceNames[2] = "dmix:0";
16756 defaultDeviceNames[3] = "dmix:0,0";
16757 } else {
16758 defaultDeviceNames[1] = "dsnoop";
16759 defaultDeviceNames[2] = "dsnoop:0";
16760 defaultDeviceNames[3] = "dsnoop:0,0";
16761 }
16762 defaultDeviceNames[4] = "hw";
16763 defaultDeviceNames[5] = "hw:0";
16764 defaultDeviceNames[6] = "hw:0,0";
16765 }
16766
16767 isDeviceOpen = MA_FALSE;
16768 for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
16769 if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
16770 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
16771 isDeviceOpen = MA_TRUE;
16772 break;
16773 }
16774 }
16775 }
16776
16777 if (!isDeviceOpen) {
16778 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);
16779 }
16780 } else {
16781 /*
16782 We're trying to open a specific device. There's a few things to consider here:
16783
16784 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
16785 an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
16786 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").
16787 */
16788
16789 /* May end up needing to make small adjustments to the ID, so make a copy. */
16790 ma_device_id deviceID = *pDeviceID;
16791 int resultALSA = -ENODEV;
16792
16793 if (deviceID.alsa[0] != ':') {
16794 /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
16795 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode);
16796 } else {
16797 char hwid[256];
16798
16799 /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
16800 if (deviceID.alsa[1] == '\0') {
16801 deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */
16802 }
16803
16804 if (shareMode == ma_share_mode_shared) {
16805 if (deviceType == ma_device_type_playback) {
16806 ma_strcpy_s(hwid, sizeof(hwid), "dmix");
16807 } else {
16808 ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
16809 }
16810
16811 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
16812 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
16813 }
16814 }
16815
16816 /* 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. */
16817 if (resultALSA != 0) {
16818 ma_strcpy_s(hwid, sizeof(hwid), "hw");
16819 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
16820 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
16821 }
16822 }
16823 }
16824
16825 if (resultALSA < 0) {
16826 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.", ma_result_from_errno(-resultALSA));
16827 }
16828 }
16829
16830 *ppPCM = pPCM;
16831 return MA_SUCCESS;
16832 }
16833
16834
16835 static ma_bool32 ma_context_is_device_id_equal__alsa(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
16836 {
16837 MA_ASSERT(pContext != NULL);
16838 MA_ASSERT(pID0 != NULL);
16839 MA_ASSERT(pID1 != NULL);
16840 (void)pContext;
16841
16842 return ma_strcmp(pID0->alsa, pID1->alsa) == 0;
16843 }
16844
16845 static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
16846 {
16847 int resultALSA;
16848 ma_bool32 cbResult = MA_TRUE;
16849 char** ppDeviceHints;
16850 ma_device_id* pUniqueIDs = NULL;
16851 ma_uint32 uniqueIDCount = 0;
16852 char** ppNextDeviceHint;
16853
16854 MA_ASSERT(pContext != NULL);
16855 MA_ASSERT(callback != NULL);
16856
16857 ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
16858
16859 resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints);
16860 if (resultALSA < 0) {
16861 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
16862 return ma_result_from_errno(-resultALSA);
16863 }
16864
16865 ppNextDeviceHint = ppDeviceHints;
16866 while (*ppNextDeviceHint != NULL) {
16867 char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
16868 char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
16869 char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
16870 ma_device_type deviceType = ma_device_type_playback;
16871 ma_bool32 stopEnumeration = MA_FALSE;
16872 char hwid[sizeof(pUniqueIDs->alsa)];
16873 ma_device_info deviceInfo;
16874
16875 if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
16876 deviceType = ma_device_type_playback;
16877 }
16878 if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
16879 deviceType = ma_device_type_capture;
16880 }
16881
16882 if (NAME != NULL) {
16883 if (pContext->alsa.useVerboseDeviceEnumeration) {
16884 /* Verbose mode. Use the name exactly as-is. */
16885 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
16886 } else {
16887 /* Simplified mode. Use ":%d,%d" format. */
16888 if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
16889 /*
16890 At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
16891 plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
16892 initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
16893 device type and sharing mode.
16894 */
16895 char* dst = hwid;
16896 char* src = hwid+2;
16897 while ((*dst++ = *src++));
16898 } else {
16899 /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
16900 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
16901 }
16902
16903 if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
16904 goto next_device; /* The device has already been enumerated. Move on to the next one. */
16905 } else {
16906 /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
16907 size_t oldCapacity = sizeof(*pUniqueIDs) * uniqueIDCount;
16908 size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);
16909 ma_device_id* pNewUniqueIDs = (ma_device_id*)ma__realloc_from_callbacks(pUniqueIDs, newCapacity, oldCapacity, &pContext->allocationCallbacks);
16910 if (pNewUniqueIDs == NULL) {
16911 goto next_device; /* Failed to allocate memory. */
16912 }
16913
16914 pUniqueIDs = pNewUniqueIDs;
16915 MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
16916 uniqueIDCount += 1;
16917 }
16918 }
16919 } else {
16920 MA_ZERO_MEMORY(hwid, sizeof(hwid));
16921 }
16922
16923 MA_ZERO_OBJECT(&deviceInfo);
16924 ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
16925
16926 /*
16927 DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
16928 device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
16929 between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
16930 description.
16931
16932 The value in DESC seems to be split into two lines, with the first line being the name of the device and the
16933 second line being a description of the device. I don't like having the description be across two lines because
16934 it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
16935 being put into parentheses. In simplified mode I'm just stripping the second line entirely.
16936 */
16937 if (DESC != NULL) {
16938 int lfPos;
16939 const char* line2 = ma_find_char(DESC, '\n', &lfPos);
16940 if (line2 != NULL) {
16941 line2 += 1; /* Skip past the new-line character. */
16942
16943 if (pContext->alsa.useVerboseDeviceEnumeration) {
16944 /* Verbose mode. Put the second line in brackets. */
16945 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
16946 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
16947 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
16948 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
16949 } else {
16950 /* Simplified mode. Strip the second line entirely. */
16951 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
16952 }
16953 } else {
16954 /* There's no second line. Just copy the whole description. */
16955 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
16956 }
16957 }
16958
16959 if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
16960 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
16961 }
16962
16963 /*
16964 Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback
16965 again for the other device type in this case. We do this for known devices.
16966 */
16967 if (cbResult) {
16968 if (ma_is_common_device_name__alsa(NAME)) {
16969 if (deviceType == ma_device_type_playback) {
16970 if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
16971 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
16972 }
16973 } else {
16974 if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
16975 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
16976 }
16977 }
16978 }
16979 }
16980
16981 if (cbResult == MA_FALSE) {
16982 stopEnumeration = MA_TRUE;
16983 }
16984
16985 next_device:
16986 free(NAME);
16987 free(DESC);
16988 free(IOID);
16989 ppNextDeviceHint += 1;
16990
16991 /* We need to stop enumeration if the callback returned false. */
16992 if (stopEnumeration) {
16993 break;
16994 }
16995 }
16996
16997 ma__free_from_callbacks(pUniqueIDs, &pContext->allocationCallbacks);
16998 ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
16999
17000 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
17001
17002 return MA_SUCCESS;
17003 }
17004
17005
17006 typedef struct
17007 {
17008 ma_device_type deviceType;
17009 const ma_device_id* pDeviceID;
17010 ma_share_mode shareMode;
17011 ma_device_info* pDeviceInfo;
17012 ma_bool32 foundDevice;
17013 } ma_context_get_device_info_enum_callback_data__alsa;
17014
17015 static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
17016 {
17017 ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
17018 MA_ASSERT(pData != NULL);
17019
17020 if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
17021 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
17022 pData->foundDevice = MA_TRUE;
17023 } else {
17024 if (pData->deviceType == deviceType && ma_context_is_device_id_equal__alsa(pContext, pData->pDeviceID, &pDeviceInfo->id)) {
17025 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
17026 pData->foundDevice = MA_TRUE;
17027 }
17028 }
17029
17030 /* Keep enumerating until we have found the device. */
17031 return !pData->foundDevice;
17032 }
17033
17034 static 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)
17035 {
17036 ma_context_get_device_info_enum_callback_data__alsa data;
17037 ma_result result;
17038 int resultALSA;
17039 ma_snd_pcm_t* pPCM;
17040 ma_snd_pcm_hw_params_t* pHWParams;
17041 ma_snd_pcm_format_mask_t* pFormatMask;
17042 int sampleRateDir = 0;
17043
17044 MA_ASSERT(pContext != NULL);
17045
17046 /* We just enumerate to find basic information about the device. */
17047 data.deviceType = deviceType;
17048 data.pDeviceID = pDeviceID;
17049 data.shareMode = shareMode;
17050 data.pDeviceInfo = pDeviceInfo;
17051 data.foundDevice = MA_FALSE;
17052 result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
17053 if (result != MA_SUCCESS) {
17054 return result;
17055 }
17056
17057 if (!data.foundDevice) {
17058 return MA_NO_DEVICE;
17059 }
17060
17061 /* For detailed info we need to open the device. */
17062 result = ma_context_open_pcm__alsa(pContext, shareMode, deviceType, pDeviceID, 0, &pPCM);
17063 if (result != MA_SUCCESS) {
17064 return result;
17065 }
17066
17067 /* We need to initialize a HW parameters object in order to know what formats are supported. */
17068 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);
17069 if (pHWParams == NULL) {
17070 return MA_OUT_OF_MEMORY;
17071 }
17072
17073 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
17074 if (resultALSA < 0) {
17075 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17076 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", ma_result_from_errno(-resultALSA));
17077 }
17078
17079 ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &pDeviceInfo->minChannels);
17080 ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &pDeviceInfo->maxChannels);
17081 ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &pDeviceInfo->minSampleRate, &sampleRateDir);
17082 ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &pDeviceInfo->maxSampleRate, &sampleRateDir);
17083
17084 /* Formats. */
17085 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);
17086 if (pFormatMask == NULL) {
17087 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17088 return MA_OUT_OF_MEMORY;
17089 }
17090
17091 ((ma_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask);
17092
17093 pDeviceInfo->formatCount = 0;
17094 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_U8)) {
17095 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_u8;
17096 }
17097 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S16_LE)) {
17098 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s16;
17099 }
17100 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S24_3LE)) {
17101 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s24;
17102 }
17103 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S32_LE)) {
17104 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s32;
17105 }
17106 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_FLOAT_LE)) {
17107 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_f32;
17108 }
17109
17110 ma__free_from_callbacks(pFormatMask, &pContext->allocationCallbacks);
17111 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17112
17113 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
17114 return MA_SUCCESS;
17115 }
17116
17117
17118 #if 0
17119 /*
17120 Waits for a number of frames to become available for either capture or playback. The return
17121 value is the number of frames available.
17122
17123 This will return early if the main loop is broken with ma_device__break_main_loop().
17124 */
17125 static ma_uint32 ma_device__wait_for_frames__alsa(ma_device* pDevice, ma_bool32* pRequiresRestart)
17126 {
17127 MA_ASSERT(pDevice != NULL);
17128
17129 if (pRequiresRestart) *pRequiresRestart = MA_FALSE;
17130
17131 /* 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. */
17132 ma_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods;
17133 while (!pDevice->alsa.breakFromMainLoop) {
17134 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);
17135 if (framesAvailable < 0) {
17136 if (framesAvailable == -EPIPE) {
17137 if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesAvailable, MA_TRUE) < 0) {
17138 return 0;
17139 }
17140
17141 /* A device recovery means a restart for mmap mode. */
17142 if (pRequiresRestart) {
17143 *pRequiresRestart = MA_TRUE;
17144 }
17145
17146 /* Try again, but if it fails this time just return an error. */
17147 framesAvailable = ((ma_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((ma_snd_pcm_t*)pDevice->alsa.pPCM);
17148 if (framesAvailable < 0) {
17149 return 0;
17150 }
17151 }
17152 }
17153
17154 if (framesAvailable >= periodSizeInFrames) {
17155 return periodSizeInFrames;
17156 }
17157
17158 if (framesAvailable < periodSizeInFrames) {
17159 /* Less than a whole period is available so keep waiting. */
17160 int waitResult = ((ma_snd_pcm_wait_proc)pDevice->pContext->alsa.snd_pcm_wait)((ma_snd_pcm_t*)pDevice->alsa.pPCM, -1);
17161 if (waitResult < 0) {
17162 if (waitResult == -EPIPE) {
17163 if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, waitResult, MA_TRUE) < 0) {
17164 return 0;
17165 }
17166
17167 /* A device recovery means a restart for mmap mode. */
17168 if (pRequiresRestart) {
17169 *pRequiresRestart = MA_TRUE;
17170 }
17171 }
17172 }
17173 }
17174 }
17175
17176 /* We'll get here if the loop was terminated. Just return whatever's available. */
17177 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);
17178 if (framesAvailable < 0) {
17179 return 0;
17180 }
17181
17182 return framesAvailable;
17183 }
17184
17185 static ma_bool32 ma_device_read_from_client_and_write__alsa(ma_device* pDevice)
17186 {
17187 MA_ASSERT(pDevice != NULL);
17188 if (!ma_device_is_started(pDevice) && ma_device__get_state(pDevice) != MA_STATE_STARTING) {
17189 return MA_FALSE;
17190 }
17191 if (pDevice->alsa.breakFromMainLoop) {
17192 return MA_FALSE;
17193 }
17194
17195 if (pDevice->alsa.isUsingMMap) {
17196 /* mmap. */
17197 ma_bool32 requiresRestart;
17198 ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, &requiresRestart);
17199 if (framesAvailable == 0) {
17200 return MA_FALSE;
17201 }
17202
17203 /* Don't bother asking the client for more audio data if we're just stopping the device anyway. */
17204 if (pDevice->alsa.breakFromMainLoop) {
17205 return MA_FALSE;
17206 }
17207
17208 const ma_snd_pcm_channel_area_t* pAreas;
17209 ma_snd_pcm_uframes_t mappedOffset;
17210 ma_snd_pcm_uframes_t mappedFrames = framesAvailable;
17211 while (framesAvailable > 0) {
17212 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);
17213 if (result < 0) {
17214 return MA_FALSE;
17215 }
17216
17217 if (mappedFrames > 0) {
17218 void* pBuffer = (ma_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8);
17219 ma_device__read_frames_from_client(pDevice, mappedFrames, pBuffer);
17220 }
17221
17222 result = ((ma_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((ma_snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames);
17223 if (result < 0 || (ma_snd_pcm_uframes_t)result != mappedFrames) {
17224 ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, result, MA_TRUE);
17225 return MA_FALSE;
17226 }
17227
17228 if (requiresRestart) {
17229 if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
17230 return MA_FALSE;
17231 }
17232 }
17233
17234 if (framesAvailable >= mappedFrames) {
17235 framesAvailable -= mappedFrames;
17236 } else {
17237 framesAvailable = 0;
17238 }
17239 }
17240 } else {
17241 /* readi/writei. */
17242 while (!pDevice->alsa.breakFromMainLoop) {
17243 ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, NULL);
17244 if (framesAvailable == 0) {
17245 continue;
17246 }
17247
17248 /* Don't bother asking the client for more audio data if we're just stopping the device anyway. */
17249 if (pDevice->alsa.breakFromMainLoop) {
17250 return MA_FALSE;
17251 }
17252
17253 ma_device__read_frames_from_client(pDevice, framesAvailable, pDevice->alsa.pIntermediaryBuffer);
17254
17255 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);
17256 if (framesWritten < 0) {
17257 if (framesWritten == -EAGAIN) {
17258 continue; /* Just keep trying... */
17259 } else if (framesWritten == -EPIPE) {
17260 /* Underrun. Just recover and try writing again. */
17261 if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesWritten, MA_TRUE) < 0) {
17262 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
17263 return MA_FALSE;
17264 }
17265
17266 framesWritten = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
17267 if (framesWritten < 0) {
17268 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to the internal device.", ma_result_from_errno((int)-framesWritten));
17269 return MA_FALSE;
17270 }
17271
17272 break; /* Success. */
17273 } else {
17274 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_writei() failed when writing initial data.", ma_result_from_errno((int)-framesWritten));
17275 return MA_FALSE;
17276 }
17277 } else {
17278 break; /* Success. */
17279 }
17280 }
17281 }
17282
17283 return MA_TRUE;
17284 }
17285
17286 static ma_bool32 ma_device_read_and_send_to_client__alsa(ma_device* pDevice)
17287 {
17288 MA_ASSERT(pDevice != NULL);
17289 if (!ma_device_is_started(pDevice)) {
17290 return MA_FALSE;
17291 }
17292 if (pDevice->alsa.breakFromMainLoop) {
17293 return MA_FALSE;
17294 }
17295
17296 ma_uint32 framesToSend = 0;
17297 void* pBuffer = NULL;
17298 if (pDevice->alsa.pIntermediaryBuffer == NULL) {
17299 /* mmap. */
17300 ma_bool32 requiresRestart;
17301 ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, &requiresRestart);
17302 if (framesAvailable == 0) {
17303 return MA_FALSE;
17304 }
17305
17306 const ma_snd_pcm_channel_area_t* pAreas;
17307 ma_snd_pcm_uframes_t mappedOffset;
17308 ma_snd_pcm_uframes_t mappedFrames = framesAvailable;
17309 while (framesAvailable > 0) {
17310 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);
17311 if (result < 0) {
17312 return MA_FALSE;
17313 }
17314
17315 if (mappedFrames > 0) {
17316 void* pBuffer = (ma_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8);
17317 ma_device__send_frames_to_client(pDevice, mappedFrames, pBuffer);
17318 }
17319
17320 result = ((ma_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((ma_snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames);
17321 if (result < 0 || (ma_snd_pcm_uframes_t)result != mappedFrames) {
17322 ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, result, MA_TRUE);
17323 return MA_FALSE;
17324 }
17325
17326 if (requiresRestart) {
17327 if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
17328 return MA_FALSE;
17329 }
17330 }
17331
17332 if (framesAvailable >= mappedFrames) {
17333 framesAvailable -= mappedFrames;
17334 } else {
17335 framesAvailable = 0;
17336 }
17337 }
17338 } else {
17339 /* readi/writei. */
17340 ma_snd_pcm_sframes_t framesRead = 0;
17341 while (!pDevice->alsa.breakFromMainLoop) {
17342 ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, NULL);
17343 if (framesAvailable == 0) {
17344 continue;
17345 }
17346
17347 framesRead = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
17348 if (framesRead < 0) {
17349 if (framesRead == -EAGAIN) {
17350 continue; /* Just keep trying... */
17351 } else if (framesRead == -EPIPE) {
17352 /* Overrun. Just recover and try reading again. */
17353 if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesRead, MA_TRUE) < 0) {
17354 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
17355 return MA_FALSE;
17356 }
17357
17358 framesRead = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
17359 if (framesRead < 0) {
17360 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", ma_result_from_errno((int)-framesRead));
17361 return MA_FALSE;
17362 }
17363
17364 break; /* Success. */
17365 } else {
17366 return MA_FALSE;
17367 }
17368 } else {
17369 break; /* Success. */
17370 }
17371 }
17372
17373 framesToSend = framesRead;
17374 pBuffer = pDevice->alsa.pIntermediaryBuffer;
17375 }
17376
17377 if (framesToSend > 0) {
17378 ma_device__send_frames_to_client(pDevice, framesToSend, pBuffer);
17379 }
17380
17381 return MA_TRUE;
17382 }
17383 #endif /* 0 */
17384
17385 static void ma_device_uninit__alsa(ma_device* pDevice)
17386 {
17387 MA_ASSERT(pDevice != NULL);
17388
17389 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
17390 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
17391 }
17392
17393 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
17394 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
17395 }
17396 }
17397
17398 static ma_result ma_device_init_by_type__alsa(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
17399 {
17400 ma_result result;
17401 int resultALSA;
17402 ma_snd_pcm_t* pPCM;
17403 ma_bool32 isUsingMMap;
17404 ma_snd_pcm_format_t formatALSA;
17405 ma_share_mode shareMode;
17406 ma_device_id* pDeviceID;
17407 ma_format internalFormat;
17408 ma_uint32 internalChannels;
17409 ma_uint32 internalSampleRate;
17410 ma_channel internalChannelMap[MA_MAX_CHANNELS];
17411 ma_uint32 internalPeriodSizeInFrames;
17412 ma_uint32 internalPeriods;
17413 int openMode;
17414 ma_snd_pcm_hw_params_t* pHWParams;
17415 ma_snd_pcm_sw_params_t* pSWParams;
17416 ma_snd_pcm_uframes_t bufferBoundary;
17417 float bufferSizeScaleFactor;
17418
17419 MA_ASSERT(pContext != NULL);
17420 MA_ASSERT(pConfig != NULL);
17421 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
17422 MA_ASSERT(pDevice != NULL);
17423
17424 formatALSA = ma_convert_ma_format_to_alsa_format((deviceType == ma_device_type_capture) ? pConfig->capture.format : pConfig->playback.format);
17425 shareMode = (deviceType == ma_device_type_capture) ? pConfig->capture.shareMode : pConfig->playback.shareMode;
17426 pDeviceID = (deviceType == ma_device_type_capture) ? pConfig->capture.pDeviceID : pConfig->playback.pDeviceID;
17427
17428 openMode = 0;
17429 if (pConfig->alsa.noAutoResample) {
17430 openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE;
17431 }
17432 if (pConfig->alsa.noAutoChannels) {
17433 openMode |= MA_SND_PCM_NO_AUTO_CHANNELS;
17434 }
17435 if (pConfig->alsa.noAutoFormat) {
17436 openMode |= MA_SND_PCM_NO_AUTO_FORMAT;
17437 }
17438
17439 result = ma_context_open_pcm__alsa(pContext, shareMode, deviceType, pDeviceID, openMode, &pPCM);
17440 if (result != MA_SUCCESS) {
17441 return result;
17442 }
17443
17444 /* If using the default buffer size we may want to apply some device-specific scaling for known devices that have peculiar latency characteristics */
17445 bufferSizeScaleFactor = 1;
17446 if (pDevice->usingDefaultBufferSize) {
17447 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);
17448 if (pInfo == NULL) {
17449 return MA_OUT_OF_MEMORY;
17450 }
17451
17452 /* We may need to scale the size of the buffer depending on the device. */
17453 if (((ma_snd_pcm_info_proc)pContext->alsa.snd_pcm_info)(pPCM, pInfo) == 0) {
17454 const char* deviceName = ((ma_snd_pcm_info_get_name_proc)pContext->alsa.snd_pcm_info_get_name)(pInfo);
17455 if (deviceName != NULL) {
17456 if (ma_strcmp(deviceName, "default") == 0) {
17457 char** ppDeviceHints;
17458 char** ppNextDeviceHint;
17459
17460 /* It's the default device. We need to use DESC from snd_device_name_hint(). */
17461 if (((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) {
17462 ma__free_from_callbacks(pInfo, &pContext->allocationCallbacks);
17463 return MA_NO_BACKEND;
17464 }
17465
17466 ppNextDeviceHint = ppDeviceHints;
17467 while (*ppNextDeviceHint != NULL) {
17468 char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
17469 char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
17470 char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
17471
17472 ma_bool32 foundDevice = MA_FALSE;
17473 if ((deviceType == ma_device_type_playback && (IOID == NULL || ma_strcmp(IOID, "Output") == 0)) ||
17474 (deviceType == ma_device_type_capture && (IOID != NULL && ma_strcmp(IOID, "Input" ) == 0))) {
17475 if (ma_strcmp(NAME, deviceName) == 0) {
17476 bufferSizeScaleFactor = ma_find_default_buffer_size_scale__alsa(DESC);
17477 foundDevice = MA_TRUE;
17478 }
17479 }
17480
17481 free(NAME);
17482 free(DESC);
17483 free(IOID);
17484 ppNextDeviceHint += 1;
17485
17486 if (foundDevice) {
17487 break;
17488 }
17489 }
17490
17491 ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
17492 } else {
17493 bufferSizeScaleFactor = ma_find_default_buffer_size_scale__alsa(deviceName);
17494 }
17495 }
17496 }
17497
17498 ma__free_from_callbacks(pInfo, &pContext->allocationCallbacks);
17499 }
17500
17501
17502 /* Hardware parameters. */
17503 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);
17504 if (pHWParams == NULL) {
17505 return MA_OUT_OF_MEMORY;
17506 }
17507
17508 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
17509 if (resultALSA < 0) {
17510 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17511 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17512 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", ma_result_from_errno(-resultALSA));
17513 }
17514
17515 /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */
17516 isUsingMMap = MA_FALSE;
17517 #if 0 /* NOTE: MMAP mode temporarily disabled. */
17518 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. */
17519 if (!pConfig->alsa.noMMap && ma_device__is_async(pDevice)) {
17520 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) {
17521 pDevice->alsa.isUsingMMap = MA_TRUE;
17522 }
17523 }
17524 }
17525 #endif
17526
17527 if (!isUsingMMap) {
17528 resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED);
17529 if (resultALSA < 0) {
17530 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17531 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17532 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.", ma_result_from_errno(-resultALSA));
17533 }
17534 }
17535
17536 /*
17537 Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
17538 find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
17539 */
17540
17541 /* Format. */
17542 {
17543 ma_snd_pcm_format_mask_t* pFormatMask;
17544
17545 /* Try getting every supported format first. */
17546 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);
17547 if (pFormatMask == NULL) {
17548 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17549 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17550 return MA_OUT_OF_MEMORY;
17551 }
17552
17553 ((ma_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask);
17554
17555 /*
17556 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
17557 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.
17558 */
17559 if (!((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, formatALSA)) {
17560 size_t i;
17561
17562 /* The requested format is not supported so now try running through the list of formats and return the best one. */
17563 ma_snd_pcm_format_t preferredFormatsALSA[] = {
17564 MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
17565 MA_SND_PCM_FORMAT_FLOAT_LE, /* ma_format_f32 */
17566 MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
17567 MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
17568 MA_SND_PCM_FORMAT_U8 /* ma_format_u8 */
17569 };
17570
17571 if (ma_is_big_endian()) {
17572 preferredFormatsALSA[0] = MA_SND_PCM_FORMAT_S16_BE;
17573 preferredFormatsALSA[1] = MA_SND_PCM_FORMAT_FLOAT_BE;
17574 preferredFormatsALSA[2] = MA_SND_PCM_FORMAT_S32_BE;
17575 preferredFormatsALSA[3] = MA_SND_PCM_FORMAT_S24_3BE;
17576 preferredFormatsALSA[4] = MA_SND_PCM_FORMAT_U8;
17577 }
17578
17579 formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
17580 for (i = 0; i < (sizeof(preferredFormatsALSA) / sizeof(preferredFormatsALSA[0])); ++i) {
17581 if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, preferredFormatsALSA[i])) {
17582 formatALSA = preferredFormatsALSA[i];
17583 break;
17584 }
17585 }
17586
17587 if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
17588 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17589 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17590 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);
17591 }
17592 }
17593
17594 ma__free_from_callbacks(pFormatMask, &pContext->allocationCallbacks);
17595 pFormatMask = NULL;
17596
17597 resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA);
17598 if (resultALSA < 0) {
17599 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17600 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17601 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", ma_result_from_errno(-resultALSA));
17602 }
17603
17604 internalFormat = ma_format_from_alsa(formatALSA);
17605 if (internalFormat == ma_format_unknown) {
17606 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17607 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17608 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
17609 }
17610 }
17611
17612 /* Channels. */
17613 {
17614 unsigned int channels = (deviceType == ma_device_type_capture) ? pConfig->capture.channels : pConfig->playback.channels;
17615 resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels);
17616 if (resultALSA < 0) {
17617 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17618 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17619 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.", ma_result_from_errno(-resultALSA));
17620 }
17621 internalChannels = (ma_uint32)channels;
17622 }
17623
17624 /* Sample Rate */
17625 {
17626 unsigned int sampleRate;
17627
17628 /*
17629 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
17630 problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
17631 resampling.
17632
17633 To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
17634 sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
17635 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
17636 faster rate.
17637
17638 miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
17639 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
17640 good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
17641
17642 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
17643 this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
17644 */
17645 ((ma_snd_pcm_hw_params_set_rate_resample_proc)pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
17646
17647 sampleRate = pConfig->sampleRate;
17648 resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0);
17649 if (resultALSA < 0) {
17650 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17651 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17652 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", ma_result_from_errno(-resultALSA));
17653 }
17654 internalSampleRate = (ma_uint32)sampleRate;
17655 }
17656
17657 /* Periods. */
17658 {
17659 ma_uint32 periods = pConfig->periods;
17660 resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL);
17661 if (resultALSA < 0) {
17662 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17663 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17664 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", ma_result_from_errno(-resultALSA));
17665 }
17666 internalPeriods = periods;
17667 }
17668
17669 /* Buffer Size */
17670 {
17671 ma_snd_pcm_uframes_t actualBufferSizeInFrames = pConfig->periodSizeInFrames * internalPeriods;
17672 if (actualBufferSizeInFrames == 0) {
17673 actualBufferSizeInFrames = ma_scale_buffer_size(ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, internalSampleRate), bufferSizeScaleFactor) * internalPeriods;
17674 }
17675
17676 resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames);
17677 if (resultALSA < 0) {
17678 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17679 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17680 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", ma_result_from_errno(-resultALSA));
17681 }
17682 internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;
17683 }
17684
17685 /* Apply hardware parameters. */
17686 resultALSA = ((ma_snd_pcm_hw_params_proc)pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams);
17687 if (resultALSA < 0) {
17688 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17689 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17690 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", ma_result_from_errno(-resultALSA));
17691 }
17692
17693 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
17694 pHWParams = NULL;
17695
17696
17697 /* Software parameters. */
17698 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);
17699 if (pSWParams == NULL) {
17700 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17701 return MA_OUT_OF_MEMORY;
17702 }
17703
17704 resultALSA = ((ma_snd_pcm_sw_params_current_proc)pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams);
17705 if (resultALSA < 0) {
17706 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
17707 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17708 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.", ma_result_from_errno(-resultALSA));
17709 }
17710
17711 resultALSA = ((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));
17712 if (resultALSA < 0) {
17713 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
17714 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17715 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", ma_result_from_errno(-resultALSA));
17716 }
17717
17718 resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);
17719 if (resultALSA < 0) {
17720 bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
17721 }
17722
17723 /*printf("TRACE: bufferBoundary=%ld\n", bufferBoundary);*/
17724
17725 if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
17726 /*
17727 Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
17728 the size of a period. But for full-duplex we need to set it such that it is at least two periods.
17729 */
17730 resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);
17731 if (resultALSA < 0) {
17732 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
17733 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17734 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.", ma_result_from_errno(-resultALSA));
17735 }
17736
17737 resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);
17738 if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
17739 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
17740 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17741 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed.", ma_result_from_errno(-resultALSA));
17742 }
17743 }
17744
17745 resultALSA = ((ma_snd_pcm_sw_params_proc)pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);
17746 if (resultALSA < 0) {
17747 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
17748 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17749 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", ma_result_from_errno(-resultALSA));
17750 }
17751
17752 ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
17753 pSWParams = NULL;
17754
17755
17756 /* 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. */
17757 {
17758 ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pContext->alsa.snd_pcm_get_chmap)(pPCM);
17759 if (pChmap != NULL) {
17760 ma_uint32 iChannel;
17761
17762 /* 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(). */
17763 if (pChmap->channels >= internalChannels) {
17764 /* Drop excess channels. */
17765 for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
17766 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
17767 }
17768 } else {
17769 ma_uint32 i;
17770
17771 /*
17772 Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
17773 channels. If validation fails, fall back to defaults.
17774 */
17775 ma_bool32 isValid = MA_TRUE;
17776
17777 /* Fill with defaults. */
17778 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
17779
17780 /* Overwrite first pChmap->channels channels. */
17781 for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
17782 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
17783 }
17784
17785 /* Validate. */
17786 for (i = 0; i < internalChannels && isValid; ++i) {
17787 ma_uint32 j;
17788 for (j = i+1; j < internalChannels; ++j) {
17789 if (internalChannelMap[i] == internalChannelMap[j]) {
17790 isValid = MA_FALSE;
17791 break;
17792 }
17793 }
17794 }
17795
17796 /* If our channel map is invalid, fall back to defaults. */
17797 if (!isValid) {
17798 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
17799 }
17800 }
17801
17802 free(pChmap);
17803 pChmap = NULL;
17804 } else {
17805 /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
17806 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
17807 }
17808 }
17809
17810
17811 /* We're done. Prepare the device. */
17812 resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
17813 if (resultALSA < 0) {
17814 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
17815 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", ma_result_from_errno(-resultALSA));
17816 }
17817
17818
17819 if (deviceType == ma_device_type_capture) {
17820 pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
17821 pDevice->alsa.isUsingMMapCapture = isUsingMMap;
17822 pDevice->capture.internalFormat = internalFormat;
17823 pDevice->capture.internalChannels = internalChannels;
17824 pDevice->capture.internalSampleRate = internalSampleRate;
17825 ma_channel_map_copy(pDevice->capture.internalChannelMap, internalChannelMap, internalChannels);
17826 pDevice->capture.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
17827 pDevice->capture.internalPeriods = internalPeriods;
17828 } else {
17829 pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
17830 pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
17831 pDevice->playback.internalFormat = internalFormat;
17832 pDevice->playback.internalChannels = internalChannels;
17833 pDevice->playback.internalSampleRate = internalSampleRate;
17834 ma_channel_map_copy(pDevice->playback.internalChannelMap, internalChannelMap, internalChannels);
17835 pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
17836 pDevice->playback.internalPeriods = internalPeriods;
17837 }
17838
17839 return MA_SUCCESS;
17840 }
17841
17842 static ma_result ma_device_init__alsa(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
17843 {
17844 MA_ASSERT(pDevice != NULL);
17845
17846 MA_ZERO_OBJECT(&pDevice->alsa);
17847
17848 if (pConfig->deviceType == ma_device_type_loopback) {
17849 return MA_DEVICE_TYPE_NOT_SUPPORTED;
17850 }
17851
17852 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
17853 ma_result result = ma_device_init_by_type__alsa(pContext, pConfig, ma_device_type_capture, pDevice);
17854 if (result != MA_SUCCESS) {
17855 return result;
17856 }
17857 }
17858
17859 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
17860 ma_result result = ma_device_init_by_type__alsa(pContext, pConfig, ma_device_type_playback, pDevice);
17861 if (result != MA_SUCCESS) {
17862 return result;
17863 }
17864 }
17865
17866 return MA_SUCCESS;
17867 }
17868
17869 static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
17870 {
17871 ma_snd_pcm_sframes_t resultALSA;
17872
17873 MA_ASSERT(pDevice != NULL);
17874 MA_ASSERT(pFramesOut != NULL);
17875
17876 if (pFramesRead != NULL) {
17877 *pFramesRead = 0;
17878 }
17879
17880 for (;;) {
17881 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
17882 if (resultALSA >= 0) {
17883 break; /* Success. */
17884 } else {
17885 if (resultALSA == -EAGAIN) {
17886 /*printf("TRACE: EGAIN (read)\n");*/
17887 continue; /* Try again. */
17888 } else if (resultALSA == -EPIPE) {
17889 #if defined(MA_DEBUG_OUTPUT)
17890 printf("TRACE: EPIPE (read)\n");
17891 #endif
17892
17893 /* Overrun. Recover and try again. If this fails we need to return an error. */
17894 resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE);
17895 if (resultALSA < 0) {
17896 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", ma_result_from_errno((int)-resultALSA));
17897 }
17898
17899 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
17900 if (resultALSA < 0) {
17901 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA));
17902 }
17903
17904 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
17905 if (resultALSA < 0) {
17906 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", ma_result_from_errno((int)-resultALSA));
17907 }
17908 }
17909 }
17910 }
17911
17912 if (pFramesRead != NULL) {
17913 *pFramesRead = resultALSA;
17914 }
17915
17916 return MA_SUCCESS;
17917 }
17918
17919 static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
17920 {
17921 ma_snd_pcm_sframes_t resultALSA;
17922
17923 MA_ASSERT(pDevice != NULL);
17924 MA_ASSERT(pFrames != NULL);
17925
17926 if (pFramesWritten != NULL) {
17927 *pFramesWritten = 0;
17928 }
17929
17930 for (;;) {
17931 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
17932 if (resultALSA >= 0) {
17933 break; /* Success. */
17934 } else {
17935 if (resultALSA == -EAGAIN) {
17936 /*printf("TRACE: EGAIN (write)\n");*/
17937 continue; /* Try again. */
17938 } else if (resultALSA == -EPIPE) {
17939 #if defined(MA_DEBUG_OUTPUT)
17940 printf("TRACE: EPIPE (write)\n");
17941 #endif
17942
17943 /* Underrun. Recover and try again. If this fails we need to return an error. */
17944 resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE);
17945 if (resultALSA < 0) { /* MA_TRUE=silent (don't print anything on error). */
17946 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", ma_result_from_errno((int)-resultALSA));
17947 }
17948
17949 /*
17950 In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
17951 up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
17952 frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
17953 if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
17954 quite right here.
17955 */
17956 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
17957 if (resultALSA < 0) {
17958 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA));
17959 }
17960
17961 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
17962 if (resultALSA < 0) {
17963 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to device after underrun.", ma_result_from_errno((int)-resultALSA));
17964 }
17965 }
17966 }
17967 }
17968
17969 if (pFramesWritten != NULL) {
17970 *pFramesWritten = resultALSA;
17971 }
17972
17973 return MA_SUCCESS;
17974 }
17975
17976 static ma_result ma_device_main_loop__alsa(ma_device* pDevice)
17977 {
17978 ma_result result = MA_SUCCESS;
17979 int resultALSA;
17980 ma_bool32 exitLoop = MA_FALSE;
17981
17982 MA_ASSERT(pDevice != NULL);
17983
17984 /* Capture devices need to be started immediately. */
17985 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
17986 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
17987 if (resultALSA < 0) {
17988 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device in preparation for reading.", ma_result_from_errno(-resultALSA));
17989 }
17990 }
17991
17992 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
17993 switch (pDevice->type)
17994 {
17995 case ma_device_type_duplex:
17996 {
17997 if (pDevice->alsa.isUsingMMapCapture || pDevice->alsa.isUsingMMapPlayback) {
17998 /* MMAP */
17999 return MA_INVALID_OPERATION; /* Not yet implemented. */
18000 } else {
18001 /* readi() and writei() */
18002
18003 /* The process is: device_read -> convert -> callback -> convert -> device_write */
18004 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
18005 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
18006
18007 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
18008 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18009 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18010 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
18011 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
18012 ma_uint32 capturedDeviceFramesRemaining;
18013 ma_uint32 capturedDeviceFramesProcessed;
18014 ma_uint32 capturedDeviceFramesToProcess;
18015 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
18016 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
18017 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
18018 }
18019
18020 result = ma_device_read__alsa(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
18021 if (result != MA_SUCCESS) {
18022 exitLoop = MA_TRUE;
18023 break;
18024 }
18025
18026 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
18027 capturedDeviceFramesProcessed = 0;
18028
18029 for (;;) {
18030 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18031 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18032 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
18033 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
18034 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
18035 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
18036 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
18037
18038 /* Convert capture data from device format to client format. */
18039 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
18040 if (result != MA_SUCCESS) {
18041 break;
18042 }
18043
18044 /*
18045 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
18046 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
18047 */
18048 if (capturedClientFramesToProcessThisIteration == 0) {
18049 break;
18050 }
18051
18052 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
18053
18054 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
18055 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
18056
18057 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
18058 for (;;) {
18059 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
18060 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
18061 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
18062 if (result != MA_SUCCESS) {
18063 break;
18064 }
18065
18066 result = ma_device_write__alsa(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
18067 if (result != MA_SUCCESS) {
18068 exitLoop = MA_TRUE;
18069 break;
18070 }
18071
18072 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
18073 if (capturedClientFramesToProcessThisIteration == 0) {
18074 break;
18075 }
18076 }
18077
18078 /* In case an error happened from ma_device_write__alsa()... */
18079 if (result != MA_SUCCESS) {
18080 exitLoop = MA_TRUE;
18081 break;
18082 }
18083 }
18084
18085 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
18086 }
18087 }
18088 } break;
18089
18090 case ma_device_type_capture:
18091 {
18092 if (pDevice->alsa.isUsingMMapCapture) {
18093 /* MMAP */
18094 return MA_INVALID_OPERATION; /* Not yet implemented. */
18095 } else {
18096 /* readi() */
18097
18098 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
18099 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18100 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
18101 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
18102 ma_uint32 framesReadThisPeriod = 0;
18103 while (framesReadThisPeriod < periodSizeInFrames) {
18104 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
18105 ma_uint32 framesProcessed;
18106 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
18107 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
18108 framesToReadThisIteration = intermediaryBufferSizeInFrames;
18109 }
18110
18111 result = ma_device_read__alsa(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
18112 if (result != MA_SUCCESS) {
18113 exitLoop = MA_TRUE;
18114 break;
18115 }
18116
18117 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
18118
18119 framesReadThisPeriod += framesProcessed;
18120 }
18121 }
18122 } break;
18123
18124 case ma_device_type_playback:
18125 {
18126 if (pDevice->alsa.isUsingMMapPlayback) {
18127 /* MMAP */
18128 return MA_INVALID_OPERATION; /* Not yet implemented. */
18129 } else {
18130 /* writei() */
18131
18132 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
18133 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18134 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
18135 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
18136 ma_uint32 framesWrittenThisPeriod = 0;
18137 while (framesWrittenThisPeriod < periodSizeInFrames) {
18138 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
18139 ma_uint32 framesProcessed;
18140 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
18141 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
18142 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
18143 }
18144
18145 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
18146
18147 result = ma_device_write__alsa(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
18148 if (result != MA_SUCCESS) {
18149 exitLoop = MA_TRUE;
18150 break;
18151 }
18152
18153 framesWrittenThisPeriod += framesProcessed;
18154 }
18155 }
18156 } break;
18157
18158 /* To silence a warning. Will never hit this. */
18159 case ma_device_type_loopback:
18160 default: break;
18161 }
18162 }
18163
18164 /* Here is where the device needs to be stopped. */
18165 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18166 ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
18167
18168 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
18169 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
18170 #ifdef MA_DEBUG_OUTPUT
18171 printf("[ALSA] Failed to prepare capture device after stopping.\n");
18172 #endif
18173 }
18174 }
18175
18176 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18177 ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
18178
18179 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
18180 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
18181 #ifdef MA_DEBUG_OUTPUT
18182 printf("[ALSA] Failed to prepare playback device after stopping.\n");
18183 #endif
18184 }
18185 }
18186
18187 return result;
18188 }
18189
18190 static ma_result ma_context_uninit__alsa(ma_context* pContext)
18191 {
18192 MA_ASSERT(pContext != NULL);
18193 MA_ASSERT(pContext->backend == ma_backend_alsa);
18194
18195 /* Clean up memory for memory leak checkers. */
18196 ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
18197
18198 #ifndef MA_NO_RUNTIME_LINKING
18199 ma_dlclose(pContext, pContext->alsa.asoundSO);
18200 #endif
18201
18202 ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
18203
18204 return MA_SUCCESS;
18205 }
18206
18207 static ma_result ma_context_init__alsa(const ma_context_config* pConfig, ma_context* pContext)
18208 {
18209 #ifndef MA_NO_RUNTIME_LINKING
18210 const char* libasoundNames[] = {
18211 "libasound.so.2",
18212 "libasound.so"
18213 };
18214 size_t i;
18215
18216 for (i = 0; i < ma_countof(libasoundNames); ++i) {
18217 pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]);
18218 if (pContext->alsa.asoundSO != NULL) {
18219 break;
18220 }
18221 }
18222
18223 if (pContext->alsa.asoundSO == NULL) {
18224 #ifdef MA_DEBUG_OUTPUT
18225 printf("[ALSA] Failed to open shared object.\n");
18226 #endif
18227 return MA_NO_BACKEND;
18228 }
18229
18230 pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open");
18231 pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close");
18232 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
18233 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
18234 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
18235 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
18236 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
18237 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
18238 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
18239 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
18240 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");
18241 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
18242 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
18243 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
18244 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
18245 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
18246 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
18247 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
18248 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
18249 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
18250 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
18251 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
18252 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
18253 pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params");
18254 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
18255 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
18256 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
18257 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
18258 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
18259 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
18260 pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params");
18261 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
18262 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
18263 pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap");
18264 pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state");
18265 pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare");
18266 pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start");
18267 pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop");
18268 pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain");
18269 pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint");
18270 pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint");
18271 pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index");
18272 pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint");
18273 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
18274 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
18275 pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover");
18276 pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi");
18277 pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei");
18278 pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail");
18279 pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update");
18280 pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait");
18281 pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info");
18282 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
18283 pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name");
18284 pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global");
18285 #else
18286 /* The system below is just for type safety. */
18287 ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
18288 ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
18289 ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
18290 ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
18291 ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
18292 ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
18293 ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
18294 ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
18295 ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
18296 ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
18297 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;
18298 ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
18299 ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
18300 ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
18301 ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
18302 ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
18303 ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
18304 ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
18305 ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
18306 ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
18307 ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
18308 ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
18309 ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
18310 ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
18311 ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
18312 ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
18313 ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
18314 ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
18315 ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
18316 ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
18317 ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
18318 ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
18319 ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
18320 ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
18321 ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
18322 ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
18323 ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
18324 ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
18325 ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
18326 ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
18327 ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
18328 ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
18329 ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
18330 ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
18331 ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
18332 ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
18333 ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
18334 ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
18335 ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
18336 ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
18337 ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
18338 ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
18339 ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
18340 ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
18341 ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
18342
18343 pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
18344 pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
18345 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
18346 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
18347 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
18348 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
18349 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
18350 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
18351 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
18352 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
18353 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
18354 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
18355 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
18356 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
18357 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
18358 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
18359 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
18360 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
18361 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
18362 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
18363 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
18364 pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
18365 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
18366 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
18367 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
18368 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
18369 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
18370 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
18371 pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
18372 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
18373 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
18374 pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
18375 pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
18376 pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
18377 pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
18378 pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
18379 pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
18380 pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
18381 pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
18382 pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
18383 pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
18384 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
18385 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
18386 pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
18387 pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
18388 pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
18389 pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
18390 pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
18391 pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
18392 pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
18393 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
18394 pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
18395 pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
18396 #endif
18397
18398 pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
18399
18400 if (ma_mutex_init(pContext, &pContext->alsa.internalDeviceEnumLock) != MA_SUCCESS) {
18401 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.", MA_ERROR);
18402 }
18403
18404 pContext->onUninit = ma_context_uninit__alsa;
18405 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__alsa;
18406 pContext->onEnumDevices = ma_context_enumerate_devices__alsa;
18407 pContext->onGetDeviceInfo = ma_context_get_device_info__alsa;
18408 pContext->onDeviceInit = ma_device_init__alsa;
18409 pContext->onDeviceUninit = ma_device_uninit__alsa;
18410 pContext->onDeviceStart = NULL; /* Not used. Started in the main loop. */
18411 pContext->onDeviceStop = NULL; /* Not used. Started in the main loop. */
18412 pContext->onDeviceMainLoop = ma_device_main_loop__alsa;
18413
18414 return MA_SUCCESS;
18415 }
18416 #endif /* ALSA */
18417
18418
18419
18420 /******************************************************************************
18421
18422 PulseAudio Backend
18423
18424 ******************************************************************************/
18425 #ifdef MA_HAS_PULSEAUDIO
18426 /*
18427 It is assumed pulseaudio.h is available when compile-time linking is being used. We use this for type safety when using
18428 compile time linking (we don't have this luxury when using runtime linking without headers).
18429
18430 When using compile time linking, each of our ma_* equivalents should use the sames types as defined by the header. The
18431 reason for this is that it allow us to take advantage of proper type safety.
18432 */
18433 #ifdef MA_NO_RUNTIME_LINKING
18434 #include <pulse/pulseaudio.h>
18435
18436 #define MA_PA_OK PA_OK
18437 #define MA_PA_ERR_ACCESS PA_ERR_ACCESS
18438 #define MA_PA_ERR_INVALID PA_ERR_INVALID
18439 #define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
18440
18441 #define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
18442 #define MA_PA_RATE_MAX PA_RATE_MAX
18443
18444 typedef pa_context_flags_t ma_pa_context_flags_t;
18445 #define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
18446 #define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
18447 #define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
18448
18449 typedef pa_stream_flags_t ma_pa_stream_flags_t;
18450 #define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
18451 #define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
18452 #define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
18453 #define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
18454 #define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
18455 #define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
18456 #define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
18457 #define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
18458 #define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
18459 #define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
18460 #define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
18461 #define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
18462 #define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
18463 #define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
18464 #define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
18465 #define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
18466 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
18467 #define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
18468 #define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
18469 #define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
18470 #define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
18471
18472 typedef pa_sink_flags_t ma_pa_sink_flags_t;
18473 #define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
18474 #define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
18475 #define MA_PA_SINK_LATENCY PA_SINK_LATENCY
18476 #define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
18477 #define MA_PA_SINK_NETWORK PA_SINK_NETWORK
18478 #define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
18479 #define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
18480 #define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
18481 #define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
18482 #define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
18483
18484 typedef pa_source_flags_t ma_pa_source_flags_t;
18485 #define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
18486 #define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
18487 #define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
18488 #define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
18489 #define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
18490 #define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
18491 #define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
18492 #define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
18493 #define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
18494
18495 typedef pa_context_state_t ma_pa_context_state_t;
18496 #define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
18497 #define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
18498 #define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
18499 #define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
18500 #define MA_PA_CONTEXT_READY PA_CONTEXT_READY
18501 #define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
18502 #define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
18503
18504 typedef pa_stream_state_t ma_pa_stream_state_t;
18505 #define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
18506 #define MA_PA_STREAM_CREATING PA_STREAM_CREATING
18507 #define MA_PA_STREAM_READY PA_STREAM_READY
18508 #define MA_PA_STREAM_FAILED PA_STREAM_FAILED
18509 #define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
18510
18511 typedef pa_operation_state_t ma_pa_operation_state_t;
18512 #define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
18513 #define MA_PA_OPERATION_DONE PA_OPERATION_DONE
18514 #define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
18515
18516 typedef pa_sink_state_t ma_pa_sink_state_t;
18517 #define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
18518 #define MA_PA_SINK_RUNNING PA_SINK_RUNNING
18519 #define MA_PA_SINK_IDLE PA_SINK_IDLE
18520 #define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
18521
18522 typedef pa_source_state_t ma_pa_source_state_t;
18523 #define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
18524 #define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
18525 #define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
18526 #define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
18527
18528 typedef pa_seek_mode_t ma_pa_seek_mode_t;
18529 #define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
18530 #define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
18531 #define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
18532 #define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
18533
18534 typedef pa_channel_position_t ma_pa_channel_position_t;
18535 #define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
18536 #define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
18537 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
18538 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
18539 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
18540 #define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
18541 #define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
18542 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
18543 #define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
18544 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
18545 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
18546 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
18547 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
18548 #define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
18549 #define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
18550 #define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
18551 #define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
18552 #define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
18553 #define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
18554 #define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
18555 #define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
18556 #define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
18557 #define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
18558 #define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
18559 #define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
18560 #define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
18561 #define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
18562 #define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
18563 #define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
18564 #define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
18565 #define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
18566 #define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
18567 #define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
18568 #define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
18569 #define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
18570 #define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
18571 #define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
18572 #define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
18573 #define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
18574 #define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
18575 #define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
18576 #define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
18577 #define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
18578 #define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
18579 #define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
18580 #define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
18581 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
18582 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
18583 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
18584 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
18585 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
18586 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
18587 #define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
18588 #define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
18589 #define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
18590 #define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
18591
18592 typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
18593 #define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
18594 #define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
18595 #define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
18596 #define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
18597 #define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
18598 #define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
18599
18600 typedef pa_sample_format_t ma_pa_sample_format_t;
18601 #define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
18602 #define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
18603 #define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
18604 #define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
18605 #define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
18606 #define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
18607 #define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
18608 #define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
18609 #define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
18610 #define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
18611 #define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
18612 #define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
18613 #define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
18614 #define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
18615
18616 typedef pa_mainloop ma_pa_mainloop;
18617 typedef pa_mainloop_api ma_pa_mainloop_api;
18618 typedef pa_context ma_pa_context;
18619 typedef pa_operation ma_pa_operation;
18620 typedef pa_stream ma_pa_stream;
18621 typedef pa_spawn_api ma_pa_spawn_api;
18622 typedef pa_buffer_attr ma_pa_buffer_attr;
18623 typedef pa_channel_map ma_pa_channel_map;
18624 typedef pa_cvolume ma_pa_cvolume;
18625 typedef pa_sample_spec ma_pa_sample_spec;
18626 typedef pa_sink_info ma_pa_sink_info;
18627 typedef pa_source_info ma_pa_source_info;
18628
18629 typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
18630 typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
18631 typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
18632 typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
18633 typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
18634 typedef pa_free_cb_t ma_pa_free_cb_t;
18635 #else
18636 #define MA_PA_OK 0
18637 #define MA_PA_ERR_ACCESS 1
18638 #define MA_PA_ERR_INVALID 2
18639 #define MA_PA_ERR_NOENTITY 5
18640
18641 #define MA_PA_CHANNELS_MAX 32
18642 #define MA_PA_RATE_MAX 384000
18643
18644 typedef int ma_pa_context_flags_t;
18645 #define MA_PA_CONTEXT_NOFLAGS 0x00000000
18646 #define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
18647 #define MA_PA_CONTEXT_NOFAIL 0x00000002
18648
18649 typedef int ma_pa_stream_flags_t;
18650 #define MA_PA_STREAM_NOFLAGS 0x00000000
18651 #define MA_PA_STREAM_START_CORKED 0x00000001
18652 #define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
18653 #define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
18654 #define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
18655 #define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
18656 #define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
18657 #define MA_PA_STREAM_FIX_FORMAT 0x00000040
18658 #define MA_PA_STREAM_FIX_RATE 0x00000080
18659 #define MA_PA_STREAM_FIX_CHANNELS 0x00000100
18660 #define MA_PA_STREAM_DONT_MOVE 0x00000200
18661 #define MA_PA_STREAM_VARIABLE_RATE 0x00000400
18662 #define MA_PA_STREAM_PEAK_DETECT 0x00000800
18663 #define MA_PA_STREAM_START_MUTED 0x00001000
18664 #define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
18665 #define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
18666 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
18667 #define MA_PA_STREAM_START_UNMUTED 0x00010000
18668 #define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
18669 #define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
18670 #define MA_PA_STREAM_PASSTHROUGH 0x00080000
18671
18672 typedef int ma_pa_sink_flags_t;
18673 #define MA_PA_SINK_NOFLAGS 0x00000000
18674 #define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
18675 #define MA_PA_SINK_LATENCY 0x00000002
18676 #define MA_PA_SINK_HARDWARE 0x00000004
18677 #define MA_PA_SINK_NETWORK 0x00000008
18678 #define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
18679 #define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
18680 #define MA_PA_SINK_FLAT_VOLUME 0x00000040
18681 #define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
18682 #define MA_PA_SINK_SET_FORMATS 0x00000100
18683
18684 typedef int ma_pa_source_flags_t;
18685 #define MA_PA_SOURCE_NOFLAGS 0x00000000
18686 #define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
18687 #define MA_PA_SOURCE_LATENCY 0x00000002
18688 #define MA_PA_SOURCE_HARDWARE 0x00000004
18689 #define MA_PA_SOURCE_NETWORK 0x00000008
18690 #define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
18691 #define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
18692 #define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
18693 #define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
18694
18695 typedef int ma_pa_context_state_t;
18696 #define MA_PA_CONTEXT_UNCONNECTED 0
18697 #define MA_PA_CONTEXT_CONNECTING 1
18698 #define MA_PA_CONTEXT_AUTHORIZING 2
18699 #define MA_PA_CONTEXT_SETTING_NAME 3
18700 #define MA_PA_CONTEXT_READY 4
18701 #define MA_PA_CONTEXT_FAILED 5
18702 #define MA_PA_CONTEXT_TERMINATED 6
18703
18704 typedef int ma_pa_stream_state_t;
18705 #define MA_PA_STREAM_UNCONNECTED 0
18706 #define MA_PA_STREAM_CREATING 1
18707 #define MA_PA_STREAM_READY 2
18708 #define MA_PA_STREAM_FAILED 3
18709 #define MA_PA_STREAM_TERMINATED 4
18710
18711 typedef int ma_pa_operation_state_t;
18712 #define MA_PA_OPERATION_RUNNING 0
18713 #define MA_PA_OPERATION_DONE 1
18714 #define MA_PA_OPERATION_CANCELLED 2
18715
18716 typedef int ma_pa_sink_state_t;
18717 #define MA_PA_SINK_INVALID_STATE -1
18718 #define MA_PA_SINK_RUNNING 0
18719 #define MA_PA_SINK_IDLE 1
18720 #define MA_PA_SINK_SUSPENDED 2
18721
18722 typedef int ma_pa_source_state_t;
18723 #define MA_PA_SOURCE_INVALID_STATE -1
18724 #define MA_PA_SOURCE_RUNNING 0
18725 #define MA_PA_SOURCE_IDLE 1
18726 #define MA_PA_SOURCE_SUSPENDED 2
18727
18728 typedef int ma_pa_seek_mode_t;
18729 #define MA_PA_SEEK_RELATIVE 0
18730 #define MA_PA_SEEK_ABSOLUTE 1
18731 #define MA_PA_SEEK_RELATIVE_ON_READ 2
18732 #define MA_PA_SEEK_RELATIVE_END 3
18733
18734 typedef int ma_pa_channel_position_t;
18735 #define MA_PA_CHANNEL_POSITION_INVALID -1
18736 #define MA_PA_CHANNEL_POSITION_MONO 0
18737 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
18738 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
18739 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
18740 #define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
18741 #define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
18742 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
18743 #define MA_PA_CHANNEL_POSITION_LFE 7
18744 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
18745 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
18746 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
18747 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
18748 #define MA_PA_CHANNEL_POSITION_AUX0 12
18749 #define MA_PA_CHANNEL_POSITION_AUX1 13
18750 #define MA_PA_CHANNEL_POSITION_AUX2 14
18751 #define MA_PA_CHANNEL_POSITION_AUX3 15
18752 #define MA_PA_CHANNEL_POSITION_AUX4 16
18753 #define MA_PA_CHANNEL_POSITION_AUX5 17
18754 #define MA_PA_CHANNEL_POSITION_AUX6 18
18755 #define MA_PA_CHANNEL_POSITION_AUX7 19
18756 #define MA_PA_CHANNEL_POSITION_AUX8 20
18757 #define MA_PA_CHANNEL_POSITION_AUX9 21
18758 #define MA_PA_CHANNEL_POSITION_AUX10 22
18759 #define MA_PA_CHANNEL_POSITION_AUX11 23
18760 #define MA_PA_CHANNEL_POSITION_AUX12 24
18761 #define MA_PA_CHANNEL_POSITION_AUX13 25
18762 #define MA_PA_CHANNEL_POSITION_AUX14 26
18763 #define MA_PA_CHANNEL_POSITION_AUX15 27
18764 #define MA_PA_CHANNEL_POSITION_AUX16 28
18765 #define MA_PA_CHANNEL_POSITION_AUX17 29
18766 #define MA_PA_CHANNEL_POSITION_AUX18 30
18767 #define MA_PA_CHANNEL_POSITION_AUX19 31
18768 #define MA_PA_CHANNEL_POSITION_AUX20 32
18769 #define MA_PA_CHANNEL_POSITION_AUX21 33
18770 #define MA_PA_CHANNEL_POSITION_AUX22 34
18771 #define MA_PA_CHANNEL_POSITION_AUX23 35
18772 #define MA_PA_CHANNEL_POSITION_AUX24 36
18773 #define MA_PA_CHANNEL_POSITION_AUX25 37
18774 #define MA_PA_CHANNEL_POSITION_AUX26 38
18775 #define MA_PA_CHANNEL_POSITION_AUX27 39
18776 #define MA_PA_CHANNEL_POSITION_AUX28 40
18777 #define MA_PA_CHANNEL_POSITION_AUX29 41
18778 #define MA_PA_CHANNEL_POSITION_AUX30 42
18779 #define MA_PA_CHANNEL_POSITION_AUX31 43
18780 #define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
18781 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
18782 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
18783 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
18784 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
18785 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
18786 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
18787 #define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
18788 #define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
18789 #define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
18790 #define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
18791
18792 typedef int ma_pa_channel_map_def_t;
18793 #define MA_PA_CHANNEL_MAP_AIFF 0
18794 #define MA_PA_CHANNEL_MAP_ALSA 1
18795 #define MA_PA_CHANNEL_MAP_AUX 2
18796 #define MA_PA_CHANNEL_MAP_WAVEEX 3
18797 #define MA_PA_CHANNEL_MAP_OSS 4
18798 #define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
18799
18800 typedef int ma_pa_sample_format_t;
18801 #define MA_PA_SAMPLE_INVALID -1
18802 #define MA_PA_SAMPLE_U8 0
18803 #define MA_PA_SAMPLE_ALAW 1
18804 #define MA_PA_SAMPLE_ULAW 2
18805 #define MA_PA_SAMPLE_S16LE 3
18806 #define MA_PA_SAMPLE_S16BE 4
18807 #define MA_PA_SAMPLE_FLOAT32LE 5
18808 #define MA_PA_SAMPLE_FLOAT32BE 6
18809 #define MA_PA_SAMPLE_S32LE 7
18810 #define MA_PA_SAMPLE_S32BE 8
18811 #define MA_PA_SAMPLE_S24LE 9
18812 #define MA_PA_SAMPLE_S24BE 10
18813 #define MA_PA_SAMPLE_S24_32LE 11
18814 #define MA_PA_SAMPLE_S24_32BE 12
18815
18816 typedef struct ma_pa_mainloop ma_pa_mainloop;
18817 typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
18818 typedef struct ma_pa_context ma_pa_context;
18819 typedef struct ma_pa_operation ma_pa_operation;
18820 typedef struct ma_pa_stream ma_pa_stream;
18821 typedef struct ma_pa_spawn_api ma_pa_spawn_api;
18822
18823 typedef struct
18824 {
18825 ma_uint32 maxlength;
18826 ma_uint32 tlength;
18827 ma_uint32 prebuf;
18828 ma_uint32 minreq;
18829 ma_uint32 fragsize;
18830 } ma_pa_buffer_attr;
18831
18832 typedef struct
18833 {
18834 ma_uint8 channels;
18835 ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
18836 } ma_pa_channel_map;
18837
18838 typedef struct
18839 {
18840 ma_uint8 channels;
18841 ma_uint32 values[MA_PA_CHANNELS_MAX];
18842 } ma_pa_cvolume;
18843
18844 typedef struct
18845 {
18846 ma_pa_sample_format_t format;
18847 ma_uint32 rate;
18848 ma_uint8 channels;
18849 } ma_pa_sample_spec;
18850
18851 typedef struct
18852 {
18853 const char* name;
18854 ma_uint32 index;
18855 const char* description;
18856 ma_pa_sample_spec sample_spec;
18857 ma_pa_channel_map channel_map;
18858 ma_uint32 owner_module;
18859 ma_pa_cvolume volume;
18860 int mute;
18861 ma_uint32 monitor_source;
18862 const char* monitor_source_name;
18863 ma_uint64 latency;
18864 const char* driver;
18865 ma_pa_sink_flags_t flags;
18866 void* proplist;
18867 ma_uint64 configured_latency;
18868 ma_uint32 base_volume;
18869 ma_pa_sink_state_t state;
18870 ma_uint32 n_volume_steps;
18871 ma_uint32 card;
18872 ma_uint32 n_ports;
18873 void** ports;
18874 void* active_port;
18875 ma_uint8 n_formats;
18876 void** formats;
18877 } ma_pa_sink_info;
18878
18879 typedef struct
18880 {
18881 const char *name;
18882 ma_uint32 index;
18883 const char *description;
18884 ma_pa_sample_spec sample_spec;
18885 ma_pa_channel_map channel_map;
18886 ma_uint32 owner_module;
18887 ma_pa_cvolume volume;
18888 int mute;
18889 ma_uint32 monitor_of_sink;
18890 const char *monitor_of_sink_name;
18891 ma_uint64 latency;
18892 const char *driver;
18893 ma_pa_source_flags_t flags;
18894 void* proplist;
18895 ma_uint64 configured_latency;
18896 ma_uint32 base_volume;
18897 ma_pa_source_state_t state;
18898 ma_uint32 n_volume_steps;
18899 ma_uint32 card;
18900 ma_uint32 n_ports;
18901 void** ports;
18902 void* active_port;
18903 ma_uint8 n_formats;
18904 void** formats;
18905 } ma_pa_source_info;
18906
18907 typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
18908 typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
18909 typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
18910 typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
18911 typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
18912 typedef void (* ma_pa_free_cb_t) (void* p);
18913 #endif
18914
18915
18916 typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) ();
18917 typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
18918 typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
18919 typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
18920 typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
18921 typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
18922 typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
18923 typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api);
18924 typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
18925 typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
18926 typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c);
18927 typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata);
18928 typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata);
18929 typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata);
18930 typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata);
18931 typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
18932 typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o);
18933 typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def);
18934 typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
18935 typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
18936 typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map);
18937 typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
18938 typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream);
18939 typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags);
18940 typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
18941 typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s);
18942 typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
18943 typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
18944 typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
18945 typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata);
18946 typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s);
18947 typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
18948 typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
18949 typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
18950 typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
18951 typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s);
18952 typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
18953 typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
18954 typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
18955 typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek);
18956 typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
18957 typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
18958 typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s);
18959 typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s);
18960
18961 typedef struct
18962 {
18963 ma_uint32 count;
18964 ma_uint32 capacity;
18965 ma_device_info* pInfo;
18966 } ma_pulse_device_enum_data;
18967
18968 static ma_result ma_result_from_pulse(int result)
18969 {
18970 switch (result) {
18971 case MA_PA_OK: return MA_SUCCESS;
18972 case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
18973 case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
18974 case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
18975 default: return MA_ERROR;
18976 }
18977 }
18978
18979 #if 0
18980 static ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
18981 {
18982 if (ma_is_little_endian()) {
18983 switch (format) {
18984 case ma_format_s16: return MA_PA_SAMPLE_S16LE;
18985 case ma_format_s24: return MA_PA_SAMPLE_S24LE;
18986 case ma_format_s32: return MA_PA_SAMPLE_S32LE;
18987 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
18988 default: break;
18989 }
18990 } else {
18991 switch (format) {
18992 case ma_format_s16: return MA_PA_SAMPLE_S16BE;
18993 case ma_format_s24: return MA_PA_SAMPLE_S24BE;
18994 case ma_format_s32: return MA_PA_SAMPLE_S32BE;
18995 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
18996 default: break;
18997 }
18998 }
18999
19000 /* Endian agnostic. */
19001 switch (format) {
19002 case ma_format_u8: return MA_PA_SAMPLE_U8;
19003 default: return MA_PA_SAMPLE_INVALID;
19004 }
19005 }
19006 #endif
19007
19008 static ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
19009 {
19010 if (ma_is_little_endian()) {
19011 switch (format) {
19012 case MA_PA_SAMPLE_S16LE: return ma_format_s16;
19013 case MA_PA_SAMPLE_S24LE: return ma_format_s24;
19014 case MA_PA_SAMPLE_S32LE: return ma_format_s32;
19015 case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
19016 default: break;
19017 }
19018 } else {
19019 switch (format) {
19020 case MA_PA_SAMPLE_S16BE: return ma_format_s16;
19021 case MA_PA_SAMPLE_S24BE: return ma_format_s24;
19022 case MA_PA_SAMPLE_S32BE: return ma_format_s32;
19023 case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
19024 default: break;
19025 }
19026 }
19027
19028 /* Endian agnostic. */
19029 switch (format) {
19030 case MA_PA_SAMPLE_U8: return ma_format_u8;
19031 default: return ma_format_unknown;
19032 }
19033 }
19034
19035 static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
19036 {
19037 switch (position)
19038 {
19039 case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
19040 case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
19041 case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
19042 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
19043 case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
19044 case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
19045 case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
19046 case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
19047 case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
19048 case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
19049 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
19050 case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
19051 case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
19052 case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
19053 case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
19054 case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
19055 case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
19056 case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
19057 case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
19058 case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
19059 case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
19060 case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
19061 case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
19062 case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
19063 case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
19064 case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
19065 case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
19066 case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
19067 case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
19068 case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
19069 case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
19070 case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
19071 case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
19072 case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
19073 case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
19074 case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
19075 case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
19076 case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
19077 case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
19078 case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
19079 case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
19080 case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
19081 case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
19082 case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
19083 case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
19084 case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
19085 case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
19086 case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
19087 case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
19088 case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
19089 case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
19090 case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
19091 default: return MA_CHANNEL_NONE;
19092 }
19093 }
19094
19095 #if 0
19096 static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
19097 {
19098 switch (position)
19099 {
19100 case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
19101 case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
19102 case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
19103 case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
19104 case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
19105 case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
19106 case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
19107 case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
19108 case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
19109 case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
19110 case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
19111 case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
19112 case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
19113 case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
19114 case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
19115 case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
19116 case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
19117 case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
19118 case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
19119 case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
19120 case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
19121 case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
19122 case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
19123 case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
19124 case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
19125 case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
19126 case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
19127 case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
19128 case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
19129 case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
19130 case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
19131 case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
19132 case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
19133 default: return (ma_pa_channel_position_t)position;
19134 }
19135 }
19136 #endif
19137
19138 static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_mainloop* pMainLoop, ma_pa_operation* pOP)
19139 {
19140 MA_ASSERT(pContext != NULL);
19141 MA_ASSERT(pMainLoop != NULL);
19142 MA_ASSERT(pOP != NULL);
19143
19144 while (((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP) == MA_PA_OPERATION_RUNNING) {
19145 int error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
19146 if (error < 0) {
19147 return ma_result_from_pulse(error);
19148 }
19149 }
19150
19151 return MA_SUCCESS;
19152 }
19153
19154 static ma_result ma_device__wait_for_operation__pulse(ma_device* pDevice, ma_pa_operation* pOP)
19155 {
19156 MA_ASSERT(pDevice != NULL);
19157 MA_ASSERT(pOP != NULL);
19158
19159 return ma_wait_for_operation__pulse(pDevice->pContext, (ma_pa_mainloop*)pDevice->pulse.pMainLoop, pOP);
19160 }
19161
19162
19163 static ma_bool32 ma_context_is_device_id_equal__pulse(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
19164 {
19165 MA_ASSERT(pContext != NULL);
19166 MA_ASSERT(pID0 != NULL);
19167 MA_ASSERT(pID1 != NULL);
19168 (void)pContext;
19169
19170 return ma_strcmp(pID0->pulse, pID1->pulse) == 0;
19171 }
19172
19173
19174 typedef struct
19175 {
19176 ma_context* pContext;
19177 ma_enum_devices_callback_proc callback;
19178 void* pUserData;
19179 ma_bool32 isTerminated;
19180 } ma_context_enumerate_devices_callback_data__pulse;
19181
19182 static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
19183 {
19184 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
19185 ma_device_info deviceInfo;
19186
19187 MA_ASSERT(pData != NULL);
19188
19189 if (endOfList || pData->isTerminated) {
19190 return;
19191 }
19192
19193 MA_ZERO_OBJECT(&deviceInfo);
19194
19195 /* The name from PulseAudio is the ID for miniaudio. */
19196 if (pSinkInfo->name != NULL) {
19197 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
19198 }
19199
19200 /* The description from PulseAudio is the name for miniaudio. */
19201 if (pSinkInfo->description != NULL) {
19202 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
19203 }
19204
19205 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
19206
19207 (void)pPulseContext; /* Unused. */
19208 }
19209
19210 static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSinkInfo, int endOfList, void* pUserData)
19211 {
19212 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
19213 ma_device_info deviceInfo;
19214
19215 MA_ASSERT(pData != NULL);
19216
19217 if (endOfList || pData->isTerminated) {
19218 return;
19219 }
19220
19221 MA_ZERO_OBJECT(&deviceInfo);
19222
19223 /* The name from PulseAudio is the ID for miniaudio. */
19224 if (pSinkInfo->name != NULL) {
19225 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
19226 }
19227
19228 /* The description from PulseAudio is the name for miniaudio. */
19229 if (pSinkInfo->description != NULL) {
19230 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
19231 }
19232
19233 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
19234
19235 (void)pPulseContext; /* Unused. */
19236 }
19237
19238 static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
19239 {
19240 ma_result result = MA_SUCCESS;
19241 ma_context_enumerate_devices_callback_data__pulse callbackData;
19242 ma_pa_operation* pOP = NULL;
19243 ma_pa_mainloop* pMainLoop;
19244 ma_pa_mainloop_api* pAPI;
19245 ma_pa_context* pPulseContext;
19246 int error;
19247
19248 MA_ASSERT(pContext != NULL);
19249 MA_ASSERT(callback != NULL);
19250
19251 callbackData.pContext = pContext;
19252 callbackData.callback = callback;
19253 callbackData.pUserData = pUserData;
19254 callbackData.isTerminated = MA_FALSE;
19255
19256 pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
19257 if (pMainLoop == NULL) {
19258 return MA_FAILED_TO_INIT_BACKEND;
19259 }
19260
19261 pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
19262 if (pAPI == NULL) {
19263 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19264 return MA_FAILED_TO_INIT_BACKEND;
19265 }
19266
19267 pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
19268 if (pPulseContext == NULL) {
19269 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19270 return MA_FAILED_TO_INIT_BACKEND;
19271 }
19272
19273 error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, (pContext->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL);
19274 if (error != MA_PA_OK) {
19275 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
19276 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19277 return ma_result_from_pulse(error);
19278 }
19279
19280 for (;;) {
19281 ma_pa_context_state_t state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
19282 if (state == MA_PA_CONTEXT_READY) {
19283 break; /* Success. */
19284 }
19285 if (state == MA_PA_CONTEXT_CONNECTING || state == MA_PA_CONTEXT_AUTHORIZING || state == MA_PA_CONTEXT_SETTING_NAME) {
19286 error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
19287 if (error < 0) {
19288 result = ma_result_from_pulse(error);
19289 goto done;
19290 }
19291
19292 #ifdef MA_DEBUG_OUTPUT
19293 printf("[PulseAudio] pa_context_get_state() returned %d. Waiting.\n", state);
19294 #endif
19295 continue; /* Keep trying. */
19296 }
19297 if (state == MA_PA_CONTEXT_UNCONNECTED || state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
19298 #ifdef MA_DEBUG_OUTPUT
19299 printf("[PulseAudio] pa_context_get_state() returned %d. Failed.\n", state);
19300 #endif
19301 goto done; /* Failed. */
19302 }
19303 }
19304
19305
19306 /* Playback. */
19307 if (!callbackData.isTerminated) {
19308 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);
19309 if (pOP == NULL) {
19310 result = MA_ERROR;
19311 goto done;
19312 }
19313
19314 result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
19315 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
19316 if (result != MA_SUCCESS) {
19317 goto done;
19318 }
19319 }
19320
19321
19322 /* Capture. */
19323 if (!callbackData.isTerminated) {
19324 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);
19325 if (pOP == NULL) {
19326 result = MA_ERROR;
19327 goto done;
19328 }
19329
19330 result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
19331 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
19332 if (result != MA_SUCCESS) {
19333 goto done;
19334 }
19335 }
19336
19337 done:
19338 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
19339 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
19340 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19341 return result;
19342 }
19343
19344
19345 typedef struct
19346 {
19347 ma_device_info* pDeviceInfo;
19348 ma_bool32 foundDevice;
19349 } ma_context_get_device_info_callback_data__pulse;
19350
19351 static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
19352 {
19353 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
19354
19355 if (endOfList > 0) {
19356 return;
19357 }
19358
19359 MA_ASSERT(pData != NULL);
19360 pData->foundDevice = MA_TRUE;
19361
19362 if (pInfo->name != NULL) {
19363 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
19364 }
19365
19366 if (pInfo->description != NULL) {
19367 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
19368 }
19369
19370 pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels;
19371 pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels;
19372 pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate;
19373 pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate;
19374 pData->pDeviceInfo->formatCount = 1;
19375 pData->pDeviceInfo->formats[0] = ma_format_from_pulse(pInfo->sample_spec.format);
19376
19377 (void)pPulseContext; /* Unused. */
19378 }
19379
19380 static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
19381 {
19382 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
19383
19384 if (endOfList > 0) {
19385 return;
19386 }
19387
19388 MA_ASSERT(pData != NULL);
19389 pData->foundDevice = MA_TRUE;
19390
19391 if (pInfo->name != NULL) {
19392 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
19393 }
19394
19395 if (pInfo->description != NULL) {
19396 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
19397 }
19398
19399 pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels;
19400 pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels;
19401 pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate;
19402 pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate;
19403 pData->pDeviceInfo->formatCount = 1;
19404 pData->pDeviceInfo->formats[0] = ma_format_from_pulse(pInfo->sample_spec.format);
19405
19406 (void)pPulseContext; /* Unused. */
19407 }
19408
19409 static 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)
19410 {
19411 ma_result result = MA_SUCCESS;
19412 ma_context_get_device_info_callback_data__pulse callbackData;
19413 ma_pa_operation* pOP = NULL;
19414 ma_pa_mainloop* pMainLoop;
19415 ma_pa_mainloop_api* pAPI;
19416 ma_pa_context* pPulseContext;
19417 int error;
19418
19419 MA_ASSERT(pContext != NULL);
19420
19421 /* No exclusive mode with the PulseAudio backend. */
19422 if (shareMode == ma_share_mode_exclusive) {
19423 return MA_SHARE_MODE_NOT_SUPPORTED;
19424 }
19425
19426 callbackData.pDeviceInfo = pDeviceInfo;
19427 callbackData.foundDevice = MA_FALSE;
19428
19429 pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
19430 if (pMainLoop == NULL) {
19431 return MA_FAILED_TO_INIT_BACKEND;
19432 }
19433
19434 pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
19435 if (pAPI == NULL) {
19436 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19437 return MA_FAILED_TO_INIT_BACKEND;
19438 }
19439
19440 pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
19441 if (pPulseContext == NULL) {
19442 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19443 return MA_FAILED_TO_INIT_BACKEND;
19444 }
19445
19446 error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, 0, NULL);
19447 if (error != MA_PA_OK) {
19448 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
19449 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19450 return ma_result_from_pulse(error);
19451 }
19452
19453 for (;;) {
19454 ma_pa_context_state_t state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
19455 if (state == MA_PA_CONTEXT_READY) {
19456 break; /* Success. */
19457 }
19458 if (state == MA_PA_CONTEXT_CONNECTING || state == MA_PA_CONTEXT_AUTHORIZING || state == MA_PA_CONTEXT_SETTING_NAME) {
19459 error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
19460 if (error < 0) {
19461 result = ma_result_from_pulse(error);
19462 goto done;
19463 }
19464
19465 #ifdef MA_DEBUG_OUTPUT
19466 printf("[PulseAudio] pa_context_get_state() returned %d. Waiting.\n", state);
19467 #endif
19468 continue; /* Keep trying. */
19469 }
19470 if (state == MA_PA_CONTEXT_UNCONNECTED || state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
19471 #ifdef MA_DEBUG_OUTPUT
19472 printf("[PulseAudio] pa_context_get_state() returned %d. Failed.\n", state);
19473 #endif
19474 goto done; /* Failed. */
19475 }
19476 }
19477
19478 if (deviceType == ma_device_type_playback) {
19479 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);
19480 } else {
19481 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);
19482 }
19483
19484 if (pOP != NULL) {
19485 ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
19486 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
19487 } else {
19488 result = MA_ERROR;
19489 goto done;
19490 }
19491
19492 if (!callbackData.foundDevice) {
19493 result = MA_NO_DEVICE;
19494 goto done;
19495 }
19496
19497
19498 done:
19499 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
19500 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
19501 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
19502 return result;
19503 }
19504
19505
19506 static void ma_pulse_device_state_callback(ma_pa_context* pPulseContext, void* pUserData)
19507 {
19508 ma_device* pDevice;
19509 ma_context* pContext;
19510
19511 pDevice = (ma_device*)pUserData;
19512 MA_ASSERT(pDevice != NULL);
19513
19514 pContext = pDevice->pContext;
19515 MA_ASSERT(pContext != NULL);
19516
19517 pDevice->pulse.pulseContextState = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
19518 }
19519
19520 void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
19521 {
19522 ma_pa_sink_info* pInfoOut;
19523
19524 if (endOfList > 0) {
19525 return;
19526 }
19527
19528 pInfoOut = (ma_pa_sink_info*)pUserData;
19529 MA_ASSERT(pInfoOut != NULL);
19530
19531 *pInfoOut = *pInfo;
19532
19533 (void)pPulseContext; /* Unused. */
19534 }
19535
19536 static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
19537 {
19538 ma_pa_source_info* pInfoOut;
19539
19540 if (endOfList > 0) {
19541 return;
19542 }
19543
19544 pInfoOut = (ma_pa_source_info*)pUserData;
19545 MA_ASSERT(pInfoOut != NULL);
19546
19547 *pInfoOut = *pInfo;
19548
19549 (void)pPulseContext; /* Unused. */
19550 }
19551
19552 static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
19553 {
19554 ma_device* pDevice;
19555
19556 if (endOfList > 0) {
19557 return;
19558 }
19559
19560 pDevice = (ma_device*)pUserData;
19561 MA_ASSERT(pDevice != NULL);
19562
19563 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
19564
19565 (void)pPulseContext; /* Unused. */
19566 }
19567
19568 static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
19569 {
19570 ma_device* pDevice;
19571
19572 if (endOfList > 0) {
19573 return;
19574 }
19575
19576 pDevice = (ma_device*)pUserData;
19577 MA_ASSERT(pDevice != NULL);
19578
19579 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
19580
19581 (void)pPulseContext; /* Unused. */
19582 }
19583
19584 static void ma_device_uninit__pulse(ma_device* pDevice)
19585 {
19586 ma_context* pContext;
19587
19588 MA_ASSERT(pDevice != NULL);
19589
19590 pContext = pDevice->pContext;
19591 MA_ASSERT(pContext != NULL);
19592
19593 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19594 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
19595 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
19596 }
19597 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
19598 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
19599 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
19600 }
19601
19602 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
19603 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
19604 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
19605 }
19606
19607 static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
19608 {
19609 ma_pa_buffer_attr attr;
19610 attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);
19611 attr.tlength = attr.maxlength / periods;
19612 attr.prebuf = (ma_uint32)-1;
19613 attr.minreq = (ma_uint32)-1;
19614 attr.fragsize = attr.maxlength / periods;
19615
19616 return attr;
19617 }
19618
19619 static 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)
19620 {
19621 static int g_StreamCounter = 0;
19622 char actualStreamName[256];
19623
19624 if (pStreamName != NULL) {
19625 ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
19626 } else {
19627 ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
19628 ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
19629 }
19630 g_StreamCounter += 1;
19631
19632 return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
19633 }
19634
19635 static ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
19636 {
19637 ma_result result = MA_SUCCESS;
19638 int error = 0;
19639 const char* devPlayback = NULL;
19640 const char* devCapture = NULL;
19641 ma_uint32 periodSizeInMilliseconds;
19642 ma_pa_sink_info sinkInfo;
19643 ma_pa_source_info sourceInfo;
19644 ma_pa_operation* pOP = NULL;
19645 ma_pa_sample_spec ss;
19646 ma_pa_channel_map cmap;
19647 ma_pa_buffer_attr attr;
19648 const ma_pa_sample_spec* pActualSS = NULL;
19649 const ma_pa_channel_map* pActualCMap = NULL;
19650 const ma_pa_buffer_attr* pActualAttr = NULL;
19651 ma_uint32 iChannel;
19652 ma_pa_stream_flags_t streamFlags;
19653
19654 MA_ASSERT(pDevice != NULL);
19655 MA_ZERO_OBJECT(&pDevice->pulse);
19656
19657 if (pConfig->deviceType == ma_device_type_loopback) {
19658 return MA_DEVICE_TYPE_NOT_SUPPORTED;
19659 }
19660
19661 /* No exclusive mode with the PulseAudio backend. */
19662 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
19663 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
19664 return MA_SHARE_MODE_NOT_SUPPORTED;
19665 }
19666
19667 if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID != NULL) {
19668 devPlayback = pConfig->playback.pDeviceID->pulse;
19669 }
19670 if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID != NULL) {
19671 devCapture = pConfig->capture.pDeviceID->pulse;
19672 }
19673
19674 periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
19675 if (periodSizeInMilliseconds == 0) {
19676 periodSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->periodSizeInFrames, pConfig->sampleRate);
19677 }
19678
19679 pDevice->pulse.pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
19680 if (pDevice->pulse.pMainLoop == NULL) {
19681 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create main loop for device.", MA_FAILED_TO_INIT_BACKEND);
19682 goto on_error0;
19683 }
19684
19685 pDevice->pulse.pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
19686 if (pDevice->pulse.pAPI == NULL) {
19687 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve PulseAudio main loop.", MA_FAILED_TO_INIT_BACKEND);
19688 goto on_error1;
19689 }
19690
19691 pDevice->pulse.pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)((ma_pa_mainloop_api*)pDevice->pulse.pAPI, pContext->pulse.pApplicationName);
19692 if (pDevice->pulse.pPulseContext == NULL) {
19693 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context for device.", MA_FAILED_TO_INIT_BACKEND);
19694 goto on_error1;
19695 }
19696
19697 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);
19698 if (error != MA_PA_OK) {
19699 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", ma_result_from_pulse(error));
19700 goto on_error2;
19701 }
19702
19703
19704 pDevice->pulse.pulseContextState = MA_PA_CONTEXT_UNCONNECTED;
19705 ((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);
19706
19707 /* Wait for PulseAudio to get itself ready before returning. */
19708 for (;;) {
19709 if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_READY) {
19710 break;
19711 }
19712
19713 /* An error may have occurred. */
19714 if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_FAILED || pDevice->pulse.pulseContextState == MA_PA_CONTEXT_TERMINATED) {
19715 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.", MA_ERROR);
19716 goto on_error3;
19717 }
19718
19719 error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
19720 if (error < 0) {
19721 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));
19722 goto on_error3;
19723 }
19724 }
19725
19726 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
19727 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);
19728 if (pOP != NULL) {
19729 ma_device__wait_for_operation__pulse(pDevice, pOP);
19730 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
19731 } else {
19732 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.", ma_result_from_pulse(error));
19733 goto on_error3;
19734 }
19735
19736 ss = sourceInfo.sample_spec;
19737 cmap = sourceInfo.channel_map;
19738
19739 pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, ss.rate);
19740 pDevice->capture.internalPeriods = pConfig->periods;
19741
19742 attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalPeriodSizeInFrames, pConfig->periods, &ss);
19743 #ifdef MA_DEBUG_OUTPUT
19744 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);
19745 #endif
19746
19747 pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
19748 if (pDevice->pulse.pStreamCapture == NULL) {
19749 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19750 goto on_error3;
19751 }
19752
19753 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
19754 if (devCapture != NULL) {
19755 streamFlags |= MA_PA_STREAM_DONT_MOVE;
19756 }
19757
19758 error = ((ma_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
19759 if (error != MA_PA_OK) {
19760 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.", ma_result_from_pulse(error));
19761 goto on_error4;
19762 }
19763
19764 while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamCapture) != MA_PA_STREAM_READY) {
19765 error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
19766 if (error < 0) {
19767 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));
19768 goto on_error5;
19769 }
19770 }
19771
19772 /* Internal format. */
19773 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
19774 if (pActualSS != NULL) {
19775 /* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
19776 if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
19777 attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalPeriodSizeInFrames, pConfig->periods, pActualSS);
19778
19779 pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &attr, NULL, NULL);
19780 if (pOP != NULL) {
19781 ma_device__wait_for_operation__pulse(pDevice, pOP);
19782 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
19783 }
19784 }
19785
19786 ss = *pActualSS;
19787 }
19788
19789 pDevice->capture.internalFormat = ma_format_from_pulse(ss.format);
19790 pDevice->capture.internalChannels = ss.channels;
19791 pDevice->capture.internalSampleRate = ss.rate;
19792
19793 /* Internal channel map. */
19794 pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
19795 if (pActualCMap != NULL) {
19796 cmap = *pActualCMap;
19797 }
19798 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
19799 pDevice->capture.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
19800 }
19801
19802 /* Buffer. */
19803 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
19804 if (pActualAttr != NULL) {
19805 attr = *pActualAttr;
19806 }
19807 pDevice->capture.internalPeriods = attr.maxlength / attr.fragsize;
19808 pDevice->capture.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) / pDevice->capture.internalPeriods;
19809 #ifdef MA_DEBUG_OUTPUT
19810 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);
19811 #endif
19812
19813 /* Name. */
19814 devCapture = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
19815 if (devCapture != NULL) {
19816 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);
19817 if (pOP != NULL) {
19818 ma_device__wait_for_operation__pulse(pDevice, pOP);
19819 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
19820 }
19821 }
19822 }
19823
19824 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
19825 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);
19826 if (pOP != NULL) {
19827 ma_device__wait_for_operation__pulse(pDevice, pOP);
19828 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
19829 } else {
19830 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.", ma_result_from_pulse(error));
19831 goto on_error3;
19832 }
19833
19834 ss = sinkInfo.sample_spec;
19835 cmap = sinkInfo.channel_map;
19836
19837 pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, ss.rate);
19838 pDevice->playback.internalPeriods = pConfig->periods;
19839
19840 attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalPeriodSizeInFrames, pConfig->periods, &ss);
19841 #ifdef MA_DEBUG_OUTPUT
19842 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);
19843 #endif
19844
19845 pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
19846 if (pDevice->pulse.pStreamPlayback == NULL) {
19847 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19848 goto on_error3;
19849 }
19850
19851 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
19852 if (devPlayback != NULL) {
19853 streamFlags |= MA_PA_STREAM_DONT_MOVE;
19854 }
19855
19856 error = ((ma_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL);
19857 if (error != MA_PA_OK) {
19858 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.", ma_result_from_pulse(error));
19859 goto on_error6;
19860 }
19861
19862 while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamPlayback) != MA_PA_STREAM_READY) {
19863 error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
19864 if (error < 0) {
19865 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));
19866 goto on_error7;
19867 }
19868 }
19869
19870 /* Internal format. */
19871 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
19872 if (pActualSS != NULL) {
19873 /* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
19874 if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
19875 attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalPeriodSizeInFrames, pConfig->periods, pActualSS);
19876
19877 pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, &attr, NULL, NULL);
19878 if (pOP != NULL) {
19879 ma_device__wait_for_operation__pulse(pDevice, pOP);
19880 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
19881 }
19882 }
19883
19884 ss = *pActualSS;
19885 }
19886
19887 pDevice->playback.internalFormat = ma_format_from_pulse(ss.format);
19888 pDevice->playback.internalChannels = ss.channels;
19889 pDevice->playback.internalSampleRate = ss.rate;
19890
19891 /* Internal channel map. */
19892 pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
19893 if (pActualCMap != NULL) {
19894 cmap = *pActualCMap;
19895 }
19896 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
19897 pDevice->playback.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
19898 }
19899
19900 /* Buffer. */
19901 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
19902 if (pActualAttr != NULL) {
19903 attr = *pActualAttr;
19904 }
19905 pDevice->playback.internalPeriods = attr.maxlength / attr.tlength;
19906 pDevice->playback.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) / pDevice->playback.internalPeriods;
19907 #ifdef MA_DEBUG_OUTPUT
19908 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);
19909 #endif
19910
19911 /* Name. */
19912 devPlayback = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
19913 if (devPlayback != NULL) {
19914 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);
19915 if (pOP != NULL) {
19916 ma_device__wait_for_operation__pulse(pDevice, pOP);
19917 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
19918 }
19919 }
19920 }
19921
19922 return MA_SUCCESS;
19923
19924
19925 on_error7:
19926 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
19927 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
19928 }
19929 on_error6:
19930 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
19931 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
19932 }
19933 on_error5:
19934 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
19935 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
19936 }
19937 on_error4:
19938 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
19939 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
19940 }
19941 on_error3: ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
19942 on_error2: ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
19943 on_error1: ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
19944 on_error0:
19945 return result;
19946 }
19947
19948
19949 static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
19950 {
19951 ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
19952 MA_ASSERT(pIsSuccessful != NULL);
19953
19954 *pIsSuccessful = (ma_bool32)success;
19955
19956 (void)pStream; /* Unused. */
19957 }
19958
19959 static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
19960 {
19961 ma_context* pContext = pDevice->pContext;
19962 ma_bool32 wasSuccessful;
19963 ma_pa_stream* pStream;
19964 ma_pa_operation* pOP;
19965 ma_result result;
19966
19967 /* This should not be called with a duplex device type. */
19968 if (deviceType == ma_device_type_duplex) {
19969 return MA_INVALID_ARGS;
19970 }
19971
19972 wasSuccessful = MA_FALSE;
19973
19974 pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
19975 MA_ASSERT(pStream != NULL);
19976
19977 pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
19978 if (pOP == NULL) {
19979 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);
19980 }
19981
19982 result = ma_device__wait_for_operation__pulse(pDevice, pOP);
19983 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
19984
19985 if (result != MA_SUCCESS) {
19986 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.", result);
19987 }
19988
19989 if (!wasSuccessful) {
19990 if (cork) {
19991 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to stop PulseAudio stream.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
19992 } else {
19993 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to start PulseAudio stream.", MA_FAILED_TO_START_BACKEND_DEVICE);
19994 }
19995 }
19996
19997 return MA_SUCCESS;
19998 }
19999
20000 static ma_result ma_device_stop__pulse(ma_device* pDevice)
20001 {
20002 ma_result result;
20003 ma_bool32 wasSuccessful;
20004 ma_pa_operation* pOP;
20005
20006 MA_ASSERT(pDevice != NULL);
20007
20008 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20009 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
20010 if (result != MA_SUCCESS) {
20011 return result;
20012 }
20013 }
20014
20015 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20016 /* The stream needs to be drained if it's a playback device. */
20017 pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful);
20018 if (pOP != NULL) {
20019 ma_device__wait_for_operation__pulse(pDevice, pOP);
20020 ((ma_pa_operation_unref_proc)pDevice->pContext->pulse.pa_operation_unref)(pOP);
20021 }
20022
20023 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
20024 if (result != MA_SUCCESS) {
20025 return result;
20026 }
20027 }
20028
20029 return MA_SUCCESS;
20030 }
20031
20032 static ma_result ma_device_write__pulse(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
20033 {
20034 ma_uint32 totalFramesWritten;
20035
20036 MA_ASSERT(pDevice != NULL);
20037 MA_ASSERT(pPCMFrames != NULL);
20038 MA_ASSERT(frameCount > 0);
20039
20040 if (pFramesWritten != NULL) {
20041 *pFramesWritten = 0;
20042 }
20043
20044 totalFramesWritten = 0;
20045 while (totalFramesWritten < frameCount) {
20046 if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
20047 return MA_DEVICE_NOT_STARTED;
20048 }
20049
20050 /* Place the data into the mapped buffer if we have one. */
20051 if (pDevice->pulse.pMappedBufferPlayback != NULL && pDevice->pulse.mappedBufferFramesRemainingPlayback > 0) {
20052 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
20053 ma_uint32 mappedBufferFramesConsumed = pDevice->pulse.mappedBufferFramesCapacityPlayback - pDevice->pulse.mappedBufferFramesRemainingPlayback;
20054
20055 void* pDst = (ma_uint8*)pDevice->pulse.pMappedBufferPlayback + (mappedBufferFramesConsumed * bpf);
20056 const void* pSrc = (const ma_uint8*)pPCMFrames + (totalFramesWritten * bpf);
20057 ma_uint32 framesToCopy = ma_min(pDevice->pulse.mappedBufferFramesRemainingPlayback, (frameCount - totalFramesWritten));
20058 MA_COPY_MEMORY(pDst, pSrc, framesToCopy * bpf);
20059
20060 pDevice->pulse.mappedBufferFramesRemainingPlayback -= framesToCopy;
20061 totalFramesWritten += framesToCopy;
20062 }
20063
20064 /*
20065 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
20066 mapping another chunk. If this fails we need to wait for space to become available.
20067 */
20068 if (pDevice->pulse.mappedBufferFramesCapacityPlayback > 0 && pDevice->pulse.mappedBufferFramesRemainingPlayback == 0) {
20069 size_t nbytes = pDevice->pulse.mappedBufferFramesCapacityPlayback * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
20070
20071 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);
20072 if (error < 0) {
20073 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to write data to the PulseAudio stream.", ma_result_from_pulse(error));
20074 }
20075
20076 pDevice->pulse.pMappedBufferPlayback = NULL;
20077 pDevice->pulse.mappedBufferFramesRemainingPlayback = 0;
20078 pDevice->pulse.mappedBufferFramesCapacityPlayback = 0;
20079 }
20080
20081 MA_ASSERT(totalFramesWritten <= frameCount);
20082 if (totalFramesWritten == frameCount) {
20083 break;
20084 }
20085
20086 /* Getting here means we need to map a new buffer. If we don't have enough space we need to wait for more. */
20087 for (;;) {
20088 size_t writableSizeInBytes;
20089
20090 /* If the device has been corked, don't try to continue. */
20091 if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamPlayback)) {
20092 break;
20093 }
20094
20095 writableSizeInBytes = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
20096 if (writableSizeInBytes != (size_t)-1) {
20097 if (writableSizeInBytes > 0) {
20098 /* Data is avaialable. */
20099 size_t bytesToMap = writableSizeInBytes;
20100 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);
20101 if (error < 0) {
20102 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to map write buffer.", ma_result_from_pulse(error));
20103 }
20104
20105 pDevice->pulse.mappedBufferFramesCapacityPlayback = bytesToMap / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
20106 pDevice->pulse.mappedBufferFramesRemainingPlayback = pDevice->pulse.mappedBufferFramesCapacityPlayback;
20107
20108 break;
20109 } else {
20110 /* No data available. Need to wait for more. */
20111 int error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
20112 if (error < 0) {
20113 return ma_result_from_pulse(error);
20114 }
20115
20116 continue;
20117 }
20118 } else {
20119 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to query the stream's writable size.", MA_ERROR);
20120 }
20121 }
20122 }
20123
20124 if (pFramesWritten != NULL) {
20125 *pFramesWritten = totalFramesWritten;
20126 }
20127
20128 return MA_SUCCESS;
20129 }
20130
20131 static ma_result ma_device_read__pulse(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
20132 {
20133 ma_uint32 totalFramesRead;
20134
20135 MA_ASSERT(pDevice != NULL);
20136 MA_ASSERT(pPCMFrames != NULL);
20137 MA_ASSERT(frameCount > 0);
20138
20139 if (pFramesRead != NULL) {
20140 *pFramesRead = 0;
20141 }
20142
20143 totalFramesRead = 0;
20144 while (totalFramesRead < frameCount) {
20145 if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
20146 return MA_DEVICE_NOT_STARTED;
20147 }
20148
20149 /*
20150 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
20151 case it could be a hole. In this case we just write zeros into the output buffer.
20152 */
20153 if (pDevice->pulse.mappedBufferFramesRemainingCapture > 0) {
20154 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
20155 ma_uint32 mappedBufferFramesConsumed = pDevice->pulse.mappedBufferFramesCapacityCapture - pDevice->pulse.mappedBufferFramesRemainingCapture;
20156
20157 ma_uint32 framesToCopy = ma_min(pDevice->pulse.mappedBufferFramesRemainingCapture, (frameCount - totalFramesRead));
20158 void* pDst = (ma_uint8*)pPCMFrames + (totalFramesRead * bpf);
20159
20160 /*
20161 This little bit of logic here is specifically for PulseAudio and it's hole management. The buffer pointer will be set to NULL
20162 when the current fragment is a hole. For a hole we just output silence.
20163 */
20164 if (pDevice->pulse.pMappedBufferCapture != NULL) {
20165 const void* pSrc = (const ma_uint8*)pDevice->pulse.pMappedBufferCapture + (mappedBufferFramesConsumed * bpf);
20166 MA_COPY_MEMORY(pDst, pSrc, framesToCopy * bpf);
20167 } else {
20168 MA_ZERO_MEMORY(pDst, framesToCopy * bpf);
20169 #if defined(MA_DEBUG_OUTPUT)
20170 printf("[PulseAudio] ma_device_read__pulse: Filling hole with silence.\n");
20171 #endif
20172 }
20173
20174 pDevice->pulse.mappedBufferFramesRemainingCapture -= framesToCopy;
20175 totalFramesRead += framesToCopy;
20176 }
20177
20178 /*
20179 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
20180 mapping another chunk. If this fails we need to wait for data to become available.
20181 */
20182 if (pDevice->pulse.mappedBufferFramesCapacityCapture > 0 && pDevice->pulse.mappedBufferFramesRemainingCapture == 0) {
20183 int error;
20184
20185 #if defined(MA_DEBUG_OUTPUT)
20186 printf("[PulseAudio] ma_device_read__pulse: Call pa_stream_drop()\n");
20187 #endif
20188
20189 error = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
20190 if (error != 0) {
20191 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to drop fragment.", ma_result_from_pulse(error));
20192 }
20193
20194 pDevice->pulse.pMappedBufferCapture = NULL;
20195 pDevice->pulse.mappedBufferFramesRemainingCapture = 0;
20196 pDevice->pulse.mappedBufferFramesCapacityCapture = 0;
20197 }
20198
20199 MA_ASSERT(totalFramesRead <= frameCount);
20200 if (totalFramesRead == frameCount) {
20201 break;
20202 }
20203
20204 /* Getting here means we need to map a new buffer. If we don't have enough data we wait for more. */
20205 for (;;) {
20206 int error;
20207 size_t bytesMapped;
20208
20209 if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
20210 break;
20211 }
20212
20213 /* If the device has been corked, don't try to continue. */
20214 if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamCapture)) {
20215 #if defined(MA_DEBUG_OUTPUT)
20216 printf("[PulseAudio] ma_device_read__pulse: Corked.\n");
20217 #endif
20218 break;
20219 }
20220
20221 MA_ASSERT(pDevice->pulse.pMappedBufferCapture == NULL); /* <-- We're about to map a buffer which means we shouldn't have an existing mapping. */
20222
20223 error = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &pDevice->pulse.pMappedBufferCapture, &bytesMapped);
20224 if (error < 0) {
20225 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to peek capture buffer.", ma_result_from_pulse(error));
20226 }
20227
20228 if (bytesMapped > 0) {
20229 pDevice->pulse.mappedBufferFramesCapacityCapture = bytesMapped / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
20230 pDevice->pulse.mappedBufferFramesRemainingCapture = pDevice->pulse.mappedBufferFramesCapacityCapture;
20231
20232 #if defined(MA_DEBUG_OUTPUT)
20233 printf("[PulseAudio] ma_device_read__pulse: Mapped. mappedBufferFramesCapacityCapture=%d, mappedBufferFramesRemainingCapture=%d\n", pDevice->pulse.mappedBufferFramesCapacityCapture, pDevice->pulse.mappedBufferFramesRemainingCapture);
20234 #endif
20235
20236 if (pDevice->pulse.pMappedBufferCapture == NULL) {
20237 /* It's a hole. */
20238 #if defined(MA_DEBUG_OUTPUT)
20239 printf("[PulseAudio] ma_device_read__pulse: Call pa_stream_peek(). Hole.\n");
20240 #endif
20241 }
20242
20243 break;
20244 } else {
20245 if (pDevice->pulse.pMappedBufferCapture == NULL) {
20246 /* Nothing available yet. Need to wait for more. */
20247
20248 /*
20249 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
20250 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
20251 dispatches.
20252 */
20253 error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 0, NULL);
20254 if (error < 0) {
20255 return ma_result_from_pulse(error);
20256 }
20257
20258 /* Sleep for a bit if nothing was dispatched. */
20259 if (error == 0) {
20260 ma_sleep(1);
20261 }
20262
20263 #if defined(MA_DEBUG_OUTPUT)
20264 printf("[PulseAudio] ma_device_read__pulse: No data available. Waiting. mappedBufferFramesCapacityCapture=%d, mappedBufferFramesRemainingCapture=%d\n", pDevice->pulse.mappedBufferFramesCapacityCapture, pDevice->pulse.mappedBufferFramesRemainingCapture);
20265 #endif
20266 } else {
20267 /* Getting here means we mapped 0 bytes, but have a non-NULL buffer. I don't think this should ever happen. */
20268 MA_ASSERT(MA_FALSE);
20269 }
20270 }
20271 }
20272 }
20273
20274 if (pFramesRead != NULL) {
20275 *pFramesRead = totalFramesRead;
20276 }
20277
20278 return MA_SUCCESS;
20279 }
20280
20281 static ma_result ma_device_main_loop__pulse(ma_device* pDevice)
20282 {
20283 ma_result result = MA_SUCCESS;
20284 ma_bool32 exitLoop = MA_FALSE;
20285
20286 MA_ASSERT(pDevice != NULL);
20287
20288 /* The stream needs to be uncorked first. We do this at the top for both capture and playback for PulseAudio. */
20289 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20290 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
20291 if (result != MA_SUCCESS) {
20292 return result;
20293 }
20294 }
20295 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20296 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
20297 if (result != MA_SUCCESS) {
20298 return result;
20299 }
20300 }
20301
20302
20303 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
20304 switch (pDevice->type)
20305 {
20306 case ma_device_type_duplex:
20307 {
20308 /* The process is: device_read -> convert -> callback -> convert -> device_write */
20309 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
20310 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
20311
20312 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
20313 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20314 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20315 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
20316 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
20317 ma_uint32 capturedDeviceFramesRemaining;
20318 ma_uint32 capturedDeviceFramesProcessed;
20319 ma_uint32 capturedDeviceFramesToProcess;
20320 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
20321 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
20322 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
20323 }
20324
20325 result = ma_device_read__pulse(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
20326 if (result != MA_SUCCESS) {
20327 exitLoop = MA_TRUE;
20328 break;
20329 }
20330
20331 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
20332 capturedDeviceFramesProcessed = 0;
20333
20334 for (;;) {
20335 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20336 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20337 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
20338 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
20339 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
20340 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
20341 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
20342
20343 /* Convert capture data from device format to client format. */
20344 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
20345 if (result != MA_SUCCESS) {
20346 break;
20347 }
20348
20349 /*
20350 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
20351 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
20352 */
20353 if (capturedClientFramesToProcessThisIteration == 0) {
20354 break;
20355 }
20356
20357 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
20358
20359 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
20360 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
20361
20362 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
20363 for (;;) {
20364 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
20365 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
20366 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
20367 if (result != MA_SUCCESS) {
20368 break;
20369 }
20370
20371 result = ma_device_write__pulse(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
20372 if (result != MA_SUCCESS) {
20373 exitLoop = MA_TRUE;
20374 break;
20375 }
20376
20377 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
20378 if (capturedClientFramesToProcessThisIteration == 0) {
20379 break;
20380 }
20381 }
20382
20383 /* In case an error happened from ma_device_write__pulse()... */
20384 if (result != MA_SUCCESS) {
20385 exitLoop = MA_TRUE;
20386 break;
20387 }
20388 }
20389
20390 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
20391 }
20392 } break;
20393
20394 case ma_device_type_capture:
20395 {
20396 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20397 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
20398 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
20399 ma_uint32 framesReadThisPeriod = 0;
20400 while (framesReadThisPeriod < periodSizeInFrames) {
20401 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
20402 ma_uint32 framesProcessed;
20403 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
20404 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
20405 framesToReadThisIteration = intermediaryBufferSizeInFrames;
20406 }
20407
20408 result = ma_device_read__pulse(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
20409 if (result != MA_SUCCESS) {
20410 exitLoop = MA_TRUE;
20411 break;
20412 }
20413
20414 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
20415
20416 framesReadThisPeriod += framesProcessed;
20417 }
20418 } break;
20419
20420 case ma_device_type_playback:
20421 {
20422 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20423 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
20424 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
20425 ma_uint32 framesWrittenThisPeriod = 0;
20426 while (framesWrittenThisPeriod < periodSizeInFrames) {
20427 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
20428 ma_uint32 framesProcessed;
20429 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
20430 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
20431 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
20432 }
20433
20434 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
20435
20436 result = ma_device_write__pulse(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
20437 if (result != MA_SUCCESS) {
20438 exitLoop = MA_TRUE;
20439 break;
20440 }
20441
20442 framesWrittenThisPeriod += framesProcessed;
20443 }
20444 } break;
20445
20446 /* To silence a warning. Will never hit this. */
20447 case ma_device_type_loopback:
20448 default: break;
20449 }
20450 }
20451
20452 /* Here is where the device needs to be stopped. */
20453 ma_device_stop__pulse(pDevice);
20454
20455 return result;
20456 }
20457
20458
20459 static ma_result ma_context_uninit__pulse(ma_context* pContext)
20460 {
20461 MA_ASSERT(pContext != NULL);
20462 MA_ASSERT(pContext->backend == ma_backend_pulseaudio);
20463
20464 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
20465 pContext->pulse.pServerName = NULL;
20466
20467 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
20468 pContext->pulse.pApplicationName = NULL;
20469
20470 #ifndef MA_NO_RUNTIME_LINKING
20471 ma_dlclose(pContext, pContext->pulse.pulseSO);
20472 #endif
20473
20474 return MA_SUCCESS;
20475 }
20476
20477 static ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_context* pContext)
20478 {
20479 #ifndef MA_NO_RUNTIME_LINKING
20480 const char* libpulseNames[] = {
20481 "libpulse.so",
20482 "libpulse.so.0"
20483 };
20484 size_t i;
20485
20486 for (i = 0; i < ma_countof(libpulseNames); ++i) {
20487 pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]);
20488 if (pContext->pulse.pulseSO != NULL) {
20489 break;
20490 }
20491 }
20492
20493 if (pContext->pulse.pulseSO == NULL) {
20494 return MA_NO_BACKEND;
20495 }
20496
20497 pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new");
20498 pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free");
20499 pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api");
20500 pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate");
20501 pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup");
20502 pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new");
20503 pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref");
20504 pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect");
20505 pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect");
20506 pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback");
20507 pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state");
20508 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
20509 pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list");
20510 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
20511 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
20512 pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref");
20513 pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state");
20514 pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend");
20515 pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid");
20516 pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible");
20517 pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new");
20518 pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref");
20519 pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback");
20520 pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record");
20521 pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect");
20522 pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state");
20523 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
20524 pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map");
20525 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
20526 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
20527 pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name");
20528 pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback");
20529 pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback");
20530 pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush");
20531 pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain");
20532 pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked");
20533 pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork");
20534 pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger");
20535 pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write");
20536 pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write");
20537 pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek");
20538 pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop");
20539 pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size");
20540 pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size");
20541 #else
20542 /* This strange assignment system is just for type safety. */
20543 ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
20544 ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
20545 ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
20546 ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
20547 ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
20548 ma_pa_context_new_proc _pa_context_new = pa_context_new;
20549 ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
20550 ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
20551 ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
20552 ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
20553 ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
20554 ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
20555 ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
20556 ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
20557 ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
20558 ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
20559 ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
20560 ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
20561 ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
20562 ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
20563 ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
20564 ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
20565 ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
20566 ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
20567 ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
20568 ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
20569 ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
20570 ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
20571 ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
20572 ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
20573 ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
20574 ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
20575 ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
20576 ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
20577 ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
20578 ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
20579 ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
20580 ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
20581 ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
20582 ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
20583 ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
20584 ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
20585 ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
20586 ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
20587
20588 pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
20589 pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
20590 pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
20591 pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
20592 pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
20593 pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
20594 pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
20595 pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
20596 pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
20597 pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
20598 pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
20599 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
20600 pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
20601 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
20602 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
20603 pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
20604 pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
20605 pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
20606 pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
20607 pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
20608 pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
20609 pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
20610 pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
20611 pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
20612 pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
20613 pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
20614 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
20615 pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
20616 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
20617 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
20618 pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
20619 pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
20620 pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
20621 pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
20622 pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
20623 pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
20624 pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
20625 pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
20626 pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
20627 pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
20628 pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
20629 pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
20630 pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
20631 pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
20632 #endif
20633
20634 pContext->onUninit = ma_context_uninit__pulse;
20635 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__pulse;
20636 pContext->onEnumDevices = ma_context_enumerate_devices__pulse;
20637 pContext->onGetDeviceInfo = ma_context_get_device_info__pulse;
20638 pContext->onDeviceInit = ma_device_init__pulse;
20639 pContext->onDeviceUninit = ma_device_uninit__pulse;
20640 pContext->onDeviceStart = NULL;
20641 pContext->onDeviceStop = NULL;
20642 pContext->onDeviceMainLoop = ma_device_main_loop__pulse;
20643
20644 if (pConfig->pulse.pApplicationName) {
20645 pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);
20646 }
20647 if (pConfig->pulse.pServerName) {
20648 pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);
20649 }
20650 pContext->pulse.tryAutoSpawn = pConfig->pulse.tryAutoSpawn;
20651
20652 /*
20653 Although we have found the libpulse library, it doesn't necessarily mean PulseAudio is useable. We need to initialize
20654 and connect a dummy PulseAudio context to test PulseAudio's usability.
20655 */
20656 {
20657 ma_pa_mainloop* pMainLoop;
20658 ma_pa_mainloop_api* pAPI;
20659 ma_pa_context* pPulseContext;
20660 int error;
20661
20662 pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
20663 if (pMainLoop == NULL) {
20664 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
20665 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
20666 #ifndef MA_NO_RUNTIME_LINKING
20667 ma_dlclose(pContext, pContext->pulse.pulseSO);
20668 #endif
20669 return MA_NO_BACKEND;
20670 }
20671
20672 pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
20673 if (pAPI == NULL) {
20674 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
20675 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
20676 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
20677 #ifndef MA_NO_RUNTIME_LINKING
20678 ma_dlclose(pContext, pContext->pulse.pulseSO);
20679 #endif
20680 return MA_NO_BACKEND;
20681 }
20682
20683 pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
20684 if (pPulseContext == NULL) {
20685 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
20686 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
20687 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
20688 #ifndef MA_NO_RUNTIME_LINKING
20689 ma_dlclose(pContext, pContext->pulse.pulseSO);
20690 #endif
20691 return MA_NO_BACKEND;
20692 }
20693
20694 error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, 0, NULL);
20695 if (error != MA_PA_OK) {
20696 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
20697 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
20698 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
20699 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
20700 #ifndef MA_NO_RUNTIME_LINKING
20701 ma_dlclose(pContext, pContext->pulse.pulseSO);
20702 #endif
20703 return MA_NO_BACKEND;
20704 }
20705
20706 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
20707 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
20708 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
20709 }
20710
20711 return MA_SUCCESS;
20712 }
20713 #endif
20714
20715
20716 /******************************************************************************
20717
20718 JACK Backend
20719
20720 ******************************************************************************/
20721 #ifdef MA_HAS_JACK
20722
20723 /* It is assumed jack.h is available when compile-time linking is being used. */
20724 #ifdef MA_NO_RUNTIME_LINKING
20725 #include <jack/jack.h>
20726
20727 typedef jack_nframes_t ma_jack_nframes_t;
20728 typedef jack_options_t ma_jack_options_t;
20729 typedef jack_status_t ma_jack_status_t;
20730 typedef jack_client_t ma_jack_client_t;
20731 typedef jack_port_t ma_jack_port_t;
20732 typedef JackProcessCallback ma_JackProcessCallback;
20733 typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
20734 typedef JackShutdownCallback ma_JackShutdownCallback;
20735 #define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
20736 #define ma_JackNoStartServer JackNoStartServer
20737 #define ma_JackPortIsInput JackPortIsInput
20738 #define ma_JackPortIsOutput JackPortIsOutput
20739 #define ma_JackPortIsPhysical JackPortIsPhysical
20740 #else
20741 typedef ma_uint32 ma_jack_nframes_t;
20742 typedef int ma_jack_options_t;
20743 typedef int ma_jack_status_t;
20744 typedef struct ma_jack_client_t ma_jack_client_t;
20745 typedef struct ma_jack_port_t ma_jack_port_t;
20746 typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
20747 typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
20748 typedef void (* ma_JackShutdownCallback) (void* arg);
20749 #define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
20750 #define ma_JackNoStartServer 1
20751 #define ma_JackPortIsInput 1
20752 #define ma_JackPortIsOutput 2
20753 #define ma_JackPortIsPhysical 4
20754 #endif
20755
20756 typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
20757 typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
20758 typedef int (* ma_jack_client_name_size_proc) ();
20759 typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
20760 typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
20761 typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
20762 typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
20763 typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
20764 typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags);
20765 typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
20766 typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
20767 typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
20768 typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size);
20769 typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
20770 typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
20771 typedef void (* ma_jack_free_proc) (void* ptr);
20772
20773 static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
20774 {
20775 size_t maxClientNameSize;
20776 char clientName[256];
20777 ma_jack_status_t status;
20778 ma_jack_client_t* pClient;
20779
20780 MA_ASSERT(pContext != NULL);
20781 MA_ASSERT(ppClient != NULL);
20782
20783 if (ppClient) {
20784 *ppClient = NULL;
20785 }
20786
20787 maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */
20788 ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
20789
20790 pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL);
20791 if (pClient == NULL) {
20792 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
20793 }
20794
20795 if (ppClient) {
20796 *ppClient = pClient;
20797 }
20798
20799 return MA_SUCCESS;
20800 }
20801
20802 static ma_bool32 ma_context_is_device_id_equal__jack(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
20803 {
20804 MA_ASSERT(pContext != NULL);
20805 MA_ASSERT(pID0 != NULL);
20806 MA_ASSERT(pID1 != NULL);
20807 (void)pContext;
20808
20809 return pID0->jack == pID1->jack;
20810 }
20811
20812 static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
20813 {
20814 ma_bool32 cbResult = MA_TRUE;
20815
20816 MA_ASSERT(pContext != NULL);
20817 MA_ASSERT(callback != NULL);
20818
20819 /* Playback. */
20820 if (cbResult) {
20821 ma_device_info deviceInfo;
20822 MA_ZERO_OBJECT(&deviceInfo);
20823 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
20824 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
20825 }
20826
20827 /* Capture. */
20828 if (cbResult) {
20829 ma_device_info deviceInfo;
20830 MA_ZERO_OBJECT(&deviceInfo);
20831 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
20832 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
20833 }
20834
20835 return MA_SUCCESS;
20836 }
20837
20838 static 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)
20839 {
20840 ma_jack_client_t* pClient;
20841 ma_result result;
20842 const char** ppPorts;
20843
20844 MA_ASSERT(pContext != NULL);
20845
20846 /* No exclusive mode with the JACK backend. */
20847 if (shareMode == ma_share_mode_exclusive) {
20848 return MA_SHARE_MODE_NOT_SUPPORTED;
20849 }
20850
20851 if (pDeviceID != NULL && pDeviceID->jack != 0) {
20852 return MA_NO_DEVICE; /* Don't know the device. */
20853 }
20854
20855 /* Name / Description */
20856 if (deviceType == ma_device_type_playback) {
20857 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
20858 } else {
20859 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
20860 }
20861
20862 /* Jack only supports f32 and has a specific channel count and sample rate. */
20863 pDeviceInfo->formatCount = 1;
20864 pDeviceInfo->formats[0] = ma_format_f32;
20865
20866 /* The channel count and sample rate can only be determined by opening the device. */
20867 result = ma_context_open_client__jack(pContext, &pClient);
20868 if (result != MA_SUCCESS) {
20869 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", result);
20870 }
20871
20872 pDeviceInfo->minSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
20873 pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
20874
20875 pDeviceInfo->minChannels = 0;
20876 pDeviceInfo->maxChannels = 0;
20877
20878 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));
20879 if (ppPorts == NULL) {
20880 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
20881 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
20882 }
20883
20884 while (ppPorts[pDeviceInfo->minChannels] != NULL) {
20885 pDeviceInfo->minChannels += 1;
20886 pDeviceInfo->maxChannels += 1;
20887 }
20888
20889 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
20890 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
20891
20892 (void)pContext;
20893 return MA_SUCCESS;
20894 }
20895
20896
20897 static void ma_device_uninit__jack(ma_device* pDevice)
20898 {
20899 ma_context* pContext;
20900
20901 MA_ASSERT(pDevice != NULL);
20902
20903 pContext = pDevice->pContext;
20904 MA_ASSERT(pContext != NULL);
20905
20906 if (pDevice->jack.pClient != NULL) {
20907 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
20908 }
20909
20910 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20911 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
20912 }
20913
20914 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20915 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
20916 }
20917
20918 if (pDevice->type == ma_device_type_duplex) {
20919 ma_pcm_rb_uninit(&pDevice->jack.duplexRB);
20920 }
20921 }
20922
20923 static void ma_device__jack_shutdown_callback(void* pUserData)
20924 {
20925 /* JACK died. Stop the device. */
20926 ma_device* pDevice = (ma_device*)pUserData;
20927 MA_ASSERT(pDevice != NULL);
20928
20929 ma_device_stop(pDevice);
20930 }
20931
20932 static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
20933 {
20934 ma_device* pDevice = (ma_device*)pUserData;
20935 MA_ASSERT(pDevice != NULL);
20936
20937 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20938 size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
20939 float* pNewBuffer = (float*)ma__calloc_from_callbacks(newBufferSize, &pDevice->pContext->allocationCallbacks);
20940 if (pNewBuffer == NULL) {
20941 return MA_OUT_OF_MEMORY;
20942 }
20943
20944 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
20945
20946 pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
20947 pDevice->playback.internalPeriodSizeInFrames = frameCount;
20948 }
20949
20950 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20951 size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
20952 float* pNewBuffer = (float*)ma__calloc_from_callbacks(newBufferSize, &pDevice->pContext->allocationCallbacks);
20953 if (pNewBuffer == NULL) {
20954 return MA_OUT_OF_MEMORY;
20955 }
20956
20957 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
20958
20959 pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
20960 pDevice->playback.internalPeriodSizeInFrames = frameCount;
20961 }
20962
20963 return 0;
20964 }
20965
20966 static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
20967 {
20968 ma_device* pDevice;
20969 ma_context* pContext;
20970 ma_uint32 iChannel;
20971
20972 pDevice = (ma_device*)pUserData;
20973 MA_ASSERT(pDevice != NULL);
20974
20975 pContext = pDevice->pContext;
20976 MA_ASSERT(pContext != NULL);
20977
20978 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20979 /* Channels need to be interleaved. */
20980 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
20981 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);
20982 if (pSrc != NULL) {
20983 float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
20984 ma_jack_nframes_t iFrame;
20985 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
20986 *pDst = *pSrc;
20987
20988 pDst += pDevice->capture.internalChannels;
20989 pSrc += 1;
20990 }
20991 }
20992 }
20993
20994 if (pDevice->type == ma_device_type_duplex) {
20995 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pDevice->jack.pIntermediaryBufferCapture, &pDevice->jack.duplexRB);
20996 } else {
20997 ma_device__send_frames_to_client(pDevice, frameCount, pDevice->jack.pIntermediaryBufferCapture);
20998 }
20999 }
21000
21001 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21002 if (pDevice->type == ma_device_type_duplex) {
21003 ma_device__handle_duplex_callback_playback(pDevice, frameCount, pDevice->jack.pIntermediaryBufferPlayback, &pDevice->jack.duplexRB);
21004 } else {
21005 ma_device__read_frames_from_client(pDevice, frameCount, pDevice->jack.pIntermediaryBufferPlayback);
21006 }
21007
21008 /* Channels need to be deinterleaved. */
21009 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
21010 float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[iChannel], frameCount);
21011 if (pDst != NULL) {
21012 const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
21013 ma_jack_nframes_t iFrame;
21014 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
21015 *pDst = *pSrc;
21016
21017 pDst += 1;
21018 pSrc += pDevice->playback.internalChannels;
21019 }
21020 }
21021 }
21022 }
21023
21024 return 0;
21025 }
21026
21027 static ma_result ma_device_init__jack(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
21028 {
21029 ma_result result;
21030 ma_uint32 periods;
21031 ma_uint32 periodSizeInFrames;
21032
21033 MA_ASSERT(pContext != NULL);
21034 MA_ASSERT(pConfig != NULL);
21035 MA_ASSERT(pDevice != NULL);
21036
21037 if (pConfig->deviceType == ma_device_type_loopback) {
21038 return MA_DEVICE_TYPE_NOT_SUPPORTED;
21039 }
21040
21041 /* Only supporting default devices with JACK. */
21042 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID != NULL && pConfig->playback.pDeviceID->jack != 0) ||
21043 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID != NULL && pConfig->capture.pDeviceID->jack != 0)) {
21044 return MA_NO_DEVICE;
21045 }
21046
21047 /* No exclusive mode with the JACK backend. */
21048 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
21049 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
21050 return MA_SHARE_MODE_NOT_SUPPORTED;
21051 }
21052
21053 /* Open the client. */
21054 result = ma_context_open_client__jack(pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
21055 if (result != MA_SUCCESS) {
21056 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", result);
21057 }
21058
21059 /* Callbacks. */
21060 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) {
21061 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
21062 }
21063 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) {
21064 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
21065 }
21066
21067 ((ma_jack_on_shutdown_proc)pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
21068
21069
21070 /* The buffer size in frames can change. */
21071 periods = pConfig->periods;
21072 periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient);
21073
21074 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
21075 const char** ppPorts;
21076
21077 pDevice->capture.internalFormat = ma_format_f32;
21078 pDevice->capture.internalChannels = 0;
21079 pDevice->capture.internalSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
21080 ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
21081
21082 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);
21083 if (ppPorts == NULL) {
21084 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
21085 }
21086
21087 while (ppPorts[pDevice->capture.internalChannels] != NULL) {
21088 char name[64];
21089 ma_strcpy_s(name, sizeof(name), "capture");
21090 ma_itoa_s((int)pDevice->capture.internalChannels, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */
21091
21092 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);
21093 if (pDevice->jack.pPortsCapture[pDevice->capture.internalChannels] == NULL) {
21094 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
21095 ma_device_uninit__jack(pDevice);
21096 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
21097 }
21098
21099 pDevice->capture.internalChannels += 1;
21100 }
21101
21102 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
21103
21104 pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames;
21105 pDevice->capture.internalPeriods = periods;
21106
21107 pDevice->jack.pIntermediaryBufferCapture = (float*)ma__calloc_from_callbacks(pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels), &pContext->allocationCallbacks);
21108 if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
21109 ma_device_uninit__jack(pDevice);
21110 return MA_OUT_OF_MEMORY;
21111 }
21112 }
21113
21114 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
21115 const char** ppPorts;
21116
21117 pDevice->playback.internalFormat = ma_format_f32;
21118 pDevice->playback.internalChannels = 0;
21119 pDevice->playback.internalSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
21120 ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
21121
21122 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);
21123 if (ppPorts == NULL) {
21124 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
21125 }
21126
21127 while (ppPorts[pDevice->playback.internalChannels] != NULL) {
21128 char name[64];
21129 ma_strcpy_s(name, sizeof(name), "playback");
21130 ma_itoa_s((int)pDevice->playback.internalChannels, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */
21131
21132 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);
21133 if (pDevice->jack.pPortsPlayback[pDevice->playback.internalChannels] == NULL) {
21134 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
21135 ma_device_uninit__jack(pDevice);
21136 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
21137 }
21138
21139 pDevice->playback.internalChannels += 1;
21140 }
21141
21142 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
21143
21144 pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
21145 pDevice->playback.internalPeriods = periods;
21146
21147 pDevice->jack.pIntermediaryBufferPlayback = (float*)ma__calloc_from_callbacks(pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels), &pContext->allocationCallbacks);
21148 if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
21149 ma_device_uninit__jack(pDevice);
21150 return MA_OUT_OF_MEMORY;
21151 }
21152 }
21153
21154 if (pDevice->type == ma_device_type_duplex) {
21155 ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods);
21156 result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->jack.duplexRB);
21157 if (result != MA_SUCCESS) {
21158 ma_device_uninit__jack(pDevice);
21159 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to initialize ring buffer.", result);
21160 }
21161
21162 /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
21163 {
21164 ma_uint32 marginSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
21165 void* pMarginData;
21166 ma_pcm_rb_acquire_write(&pDevice->jack.duplexRB, &marginSizeInFrames, &pMarginData);
21167 {
21168 MA_ZERO_MEMORY(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
21169 }
21170 ma_pcm_rb_commit_write(&pDevice->jack.duplexRB, marginSizeInFrames, pMarginData);
21171 }
21172 }
21173
21174 return MA_SUCCESS;
21175 }
21176
21177
21178 static ma_result ma_device_start__jack(ma_device* pDevice)
21179 {
21180 ma_context* pContext = pDevice->pContext;
21181 int resultJACK;
21182 size_t i;
21183
21184 resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
21185 if (resultJACK != 0) {
21186 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.", MA_FAILED_TO_START_BACKEND_DEVICE);
21187 }
21188
21189 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21190 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);
21191 if (ppServerPorts == NULL) {
21192 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
21193 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
21194 }
21195
21196 for (i = 0; ppServerPorts[i] != NULL; ++i) {
21197 const char* pServerPort = ppServerPorts[i];
21198 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsCapture[i]);
21199
21200 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
21201 if (resultJACK != 0) {
21202 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
21203 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
21204 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
21205 }
21206 }
21207
21208 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
21209 }
21210
21211 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21212 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);
21213 if (ppServerPorts == NULL) {
21214 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
21215 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
21216 }
21217
21218 for (i = 0; ppServerPorts[i] != NULL; ++i) {
21219 const char* pServerPort = ppServerPorts[i];
21220 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[i]);
21221
21222 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
21223 if (resultJACK != 0) {
21224 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
21225 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
21226 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
21227 }
21228 }
21229
21230 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
21231 }
21232
21233 return MA_SUCCESS;
21234 }
21235
21236 static ma_result ma_device_stop__jack(ma_device* pDevice)
21237 {
21238 ma_context* pContext = pDevice->pContext;
21239 ma_stop_proc onStop;
21240
21241 if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
21242 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MA_ERROR);
21243 }
21244
21245 onStop = pDevice->onStop;
21246 if (onStop) {
21247 onStop(pDevice);
21248 }
21249
21250 return MA_SUCCESS;
21251 }
21252
21253
21254 static ma_result ma_context_uninit__jack(ma_context* pContext)
21255 {
21256 MA_ASSERT(pContext != NULL);
21257 MA_ASSERT(pContext->backend == ma_backend_jack);
21258
21259 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
21260 pContext->jack.pClientName = NULL;
21261
21262 #ifndef MA_NO_RUNTIME_LINKING
21263 ma_dlclose(pContext, pContext->jack.jackSO);
21264 #endif
21265
21266 return MA_SUCCESS;
21267 }
21268
21269 static ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_context* pContext)
21270 {
21271 #ifndef MA_NO_RUNTIME_LINKING
21272 const char* libjackNames[] = {
21273 #ifdef MA_WIN32
21274 "libjack.dll"
21275 #else
21276 "libjack.so",
21277 "libjack.so.0"
21278 #endif
21279 };
21280 size_t i;
21281
21282 for (i = 0; i < ma_countof(libjackNames); ++i) {
21283 pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]);
21284 if (pContext->jack.jackSO != NULL) {
21285 break;
21286 }
21287 }
21288
21289 if (pContext->jack.jackSO == NULL) {
21290 return MA_NO_BACKEND;
21291 }
21292
21293 pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open");
21294 pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close");
21295 pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size");
21296 pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback");
21297 pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback");
21298 pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown");
21299 pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate");
21300 pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size");
21301 pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports");
21302 pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate");
21303 pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate");
21304 pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect");
21305 pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register");
21306 pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name");
21307 pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer");
21308 pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free");
21309 #else
21310 /*
21311 This strange assignment system is here just to ensure type safety of miniaudio's function pointer
21312 types. If anything differs slightly the compiler should throw a warning.
21313 */
21314 ma_jack_client_open_proc _jack_client_open = jack_client_open;
21315 ma_jack_client_close_proc _jack_client_close = jack_client_close;
21316 ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
21317 ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
21318 ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
21319 ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
21320 ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
21321 ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
21322 ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
21323 ma_jack_activate_proc _jack_activate = jack_activate;
21324 ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
21325 ma_jack_connect_proc _jack_connect = jack_connect;
21326 ma_jack_port_register_proc _jack_port_register = jack_port_register;
21327 ma_jack_port_name_proc _jack_port_name = jack_port_name;
21328 ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
21329 ma_jack_free_proc _jack_free = jack_free;
21330
21331 pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
21332 pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
21333 pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
21334 pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
21335 pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
21336 pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
21337 pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
21338 pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
21339 pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
21340 pContext->jack.jack_activate = (ma_proc)_jack_activate;
21341 pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
21342 pContext->jack.jack_connect = (ma_proc)_jack_connect;
21343 pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
21344 pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
21345 pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
21346 pContext->jack.jack_free = (ma_proc)_jack_free;
21347 #endif
21348
21349 pContext->isBackendAsynchronous = MA_TRUE;
21350
21351 pContext->onUninit = ma_context_uninit__jack;
21352 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__jack;
21353 pContext->onEnumDevices = ma_context_enumerate_devices__jack;
21354 pContext->onGetDeviceInfo = ma_context_get_device_info__jack;
21355 pContext->onDeviceInit = ma_device_init__jack;
21356 pContext->onDeviceUninit = ma_device_uninit__jack;
21357 pContext->onDeviceStart = ma_device_start__jack;
21358 pContext->onDeviceStop = ma_device_stop__jack;
21359
21360 if (pConfig->jack.pClientName != NULL) {
21361 pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks);
21362 }
21363 pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
21364
21365 /*
21366 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
21367 a temporary client.
21368 */
21369 {
21370 ma_jack_client_t* pDummyClient;
21371 ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
21372 if (result != MA_SUCCESS) {
21373 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
21374 #ifndef MA_NO_RUNTIME_LINKING
21375 ma_dlclose(pContext, pContext->jack.jackSO);
21376 #endif
21377 return MA_NO_BACKEND;
21378 }
21379
21380 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
21381 }
21382
21383 return MA_SUCCESS;
21384 }
21385 #endif /* JACK */
21386
21387
21388
21389 /******************************************************************************
21390
21391 Core Audio Backend
21392
21393 ******************************************************************************/
21394 #ifdef MA_HAS_COREAUDIO
21395 #include <TargetConditionals.h>
21396
21397 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
21398 #define MA_APPLE_MOBILE
21399 #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
21400 #define MA_APPLE_TV
21401 #endif
21402 #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
21403 #define MA_APPLE_WATCH
21404 #endif
21405 #else
21406 #define MA_APPLE_DESKTOP
21407 #endif
21408
21409 #if defined(MA_APPLE_DESKTOP)
21410 #include <CoreAudio/CoreAudio.h>
21411 #else
21412 #include <AVFoundation/AVFoundation.h>
21413 #endif
21414
21415 #include <AudioToolbox/AudioToolbox.h>
21416
21417 /* CoreFoundation */
21418 typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
21419 typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
21420
21421 /* CoreAudio */
21422 #if defined(MA_APPLE_DESKTOP)
21423 typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
21424 typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
21425 typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
21426 typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
21427 typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
21428 #endif
21429
21430 /* AudioToolbox */
21431 typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
21432 typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
21433 typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
21434 typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
21435 typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
21436 typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
21437 typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
21438 typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
21439 typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
21440 typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
21441 typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
21442
21443
21444 #define MA_COREAUDIO_OUTPUT_BUS 0
21445 #define MA_COREAUDIO_INPUT_BUS 1
21446
21447 #if defined(MA_APPLE_DESKTOP)
21448 static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
21449 #endif
21450
21451 /*
21452 Core Audio
21453
21454 So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
21455 apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
21456 needing to figure out how this darn thing works, I'm going to outline a few things here.
21457
21458 Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
21459 able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen
21460 that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
21461 and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
21462 distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
21463
21464 Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
21465 retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
21466 data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
21467 devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
21468 the central APIs for retrieving information about the system and specific devices.
21469
21470 To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
21471 structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
21472 which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
21473 typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
21474 kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
21475 kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different.
21476
21477 Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
21478 of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
21479 address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
21480 size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
21481 AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
21482 */
21483
21484 static ma_result ma_result_from_OSStatus(OSStatus status)
21485 {
21486 switch (status)
21487 {
21488 case noErr: return MA_SUCCESS;
21489 #if defined(MA_APPLE_DESKTOP)
21490 case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
21491 case kAudioHardwareUnspecifiedError: return MA_ERROR;
21492 case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
21493 case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
21494 case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
21495 case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
21496 case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
21497 case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
21498 case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
21499 case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
21500 case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
21501 #endif
21502 default: return MA_ERROR;
21503 }
21504 }
21505
21506 #if 0
21507 static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
21508 {
21509 switch (bit)
21510 {
21511 case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
21512 case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
21513 case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
21514 case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
21515 case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
21516 case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
21517 case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
21518 case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
21519 case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
21520 case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
21521 case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
21522 case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
21523 case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
21524 case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
21525 case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
21526 case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
21527 case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
21528 case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
21529 default: return MA_CHANNEL_NONE;
21530 }
21531 }
21532 #endif
21533
21534 static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
21535 {
21536 MA_ASSERT(pDescription != NULL);
21537 MA_ASSERT(pFormatOut != NULL);
21538
21539 *pFormatOut = ma_format_unknown; /* Safety. */
21540
21541 /* There's a few things miniaudio doesn't support. */
21542 if (pDescription->mFormatID != kAudioFormatLinearPCM) {
21543 return MA_FORMAT_NOT_SUPPORTED;
21544 }
21545
21546 /* We don't support any non-packed formats that are aligned high. */
21547 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
21548 return MA_FORMAT_NOT_SUPPORTED;
21549 }
21550
21551 /* Only supporting native-endian. */
21552 if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
21553 return MA_FORMAT_NOT_SUPPORTED;
21554 }
21555
21556 /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */
21557 /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {
21558 return MA_FORMAT_NOT_SUPPORTED;
21559 }*/
21560
21561 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
21562 if (pDescription->mBitsPerChannel == 32) {
21563 *pFormatOut = ma_format_f32;
21564 return MA_SUCCESS;
21565 }
21566 } else {
21567 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
21568 if (pDescription->mBitsPerChannel == 16) {
21569 *pFormatOut = ma_format_s16;
21570 return MA_SUCCESS;
21571 } else if (pDescription->mBitsPerChannel == 24) {
21572 if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
21573 *pFormatOut = ma_format_s24;
21574 return MA_SUCCESS;
21575 } else {
21576 if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
21577 /* TODO: Implement ma_format_s24_32. */
21578 /**pFormatOut = ma_format_s24_32;*/
21579 /*return MA_SUCCESS;*/
21580 return MA_FORMAT_NOT_SUPPORTED;
21581 }
21582 }
21583 } else if (pDescription->mBitsPerChannel == 32) {
21584 *pFormatOut = ma_format_s32;
21585 return MA_SUCCESS;
21586 }
21587 } else {
21588 if (pDescription->mBitsPerChannel == 8) {
21589 *pFormatOut = ma_format_u8;
21590 return MA_SUCCESS;
21591 }
21592 }
21593 }
21594
21595 /* Getting here means the format is not supported. */
21596 return MA_FORMAT_NOT_SUPPORTED;
21597 }
21598
21599 #if defined(MA_APPLE_DESKTOP)
21600 static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
21601 {
21602 switch (label)
21603 {
21604 case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
21605 case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
21606 case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
21607 case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
21608 case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
21609 case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
21610 case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
21611 case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
21612 case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
21613 case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
21614 case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
21615 case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
21616 case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
21617 case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
21618 case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
21619 case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
21620 case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
21621 case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
21622 case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
21623 case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
21624 case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
21625 case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
21626 case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
21627 case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
21628 case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
21629 case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
21630 case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
21631 case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
21632 case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
21633 case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
21634 case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
21635 case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
21636 case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
21637 case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
21638 case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
21639 case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
21640 case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
21641 case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
21642 case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
21643 case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
21644 case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
21645 case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
21646 case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
21647 case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
21648 case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
21649 case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
21650 case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
21651 case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
21652 case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
21653 case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
21654 case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
21655 case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
21656 case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
21657 case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
21658 case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
21659 case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
21660 case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
21661 case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
21662 case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
21663 case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
21664 case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
21665 case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
21666 case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
21667 case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
21668
21669 #if 0 /* Introduced in a later version of macOS. */
21670 case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
21671 case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
21672 case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
21673 case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
21674 case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
21675 case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
21676 case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
21677 case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
21678 case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
21679 case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
21680 case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
21681 case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
21682 case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
21683 case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
21684 case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
21685 case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
21686 case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
21687 case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
21688 #endif
21689
21690 default: return MA_CHANNEL_NONE;
21691 }
21692 }
21693
21694 static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel channelMap[MA_MAX_CHANNELS])
21695 {
21696 MA_ASSERT(pChannelLayout != NULL);
21697
21698 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
21699 UInt32 iChannel;
21700 for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) {
21701 channelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
21702 }
21703 } else
21704 #if 0
21705 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
21706 /* This is the same kind of system that's used by Windows audio APIs. */
21707 UInt32 iChannel = 0;
21708 UInt32 iBit;
21709 AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
21710 for (iBit = 0; iBit < 32; ++iBit) {
21711 AudioChannelBitmap bit = bitmap & (1 << iBit);
21712 if (bit != 0) {
21713 channelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
21714 }
21715 }
21716 } else
21717 #endif
21718 {
21719 /*
21720 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
21721 be updated to determine the mapping based on the tag.
21722 */
21723 UInt32 channelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
21724 switch (pChannelLayout->mChannelLayoutTag)
21725 {
21726 case kAudioChannelLayoutTag_Mono:
21727 case kAudioChannelLayoutTag_Stereo:
21728 case kAudioChannelLayoutTag_StereoHeadphones:
21729 case kAudioChannelLayoutTag_MatrixStereo:
21730 case kAudioChannelLayoutTag_MidSide:
21731 case kAudioChannelLayoutTag_XY:
21732 case kAudioChannelLayoutTag_Binaural:
21733 case kAudioChannelLayoutTag_Ambisonic_B_Format:
21734 {
21735 ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, channelMap);
21736 } break;
21737
21738 case kAudioChannelLayoutTag_Octagonal:
21739 {
21740 channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
21741 channelMap[6] = MA_CHANNEL_SIDE_LEFT;
21742 } /* Intentional fallthrough. */
21743 case kAudioChannelLayoutTag_Hexagonal:
21744 {
21745 channelMap[5] = MA_CHANNEL_BACK_CENTER;
21746 } /* Intentional fallthrough. */
21747 case kAudioChannelLayoutTag_Pentagonal:
21748 {
21749 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
21750 } /* Intentional fallghrough. */
21751 case kAudioChannelLayoutTag_Quadraphonic:
21752 {
21753 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
21754 channelMap[2] = MA_CHANNEL_BACK_LEFT;
21755 channelMap[1] = MA_CHANNEL_RIGHT;
21756 channelMap[0] = MA_CHANNEL_LEFT;
21757 } break;
21758
21759 /* TODO: Add support for more tags here. */
21760
21761 default:
21762 {
21763 ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, channelMap);
21764 } break;
21765 }
21766 }
21767
21768 return MA_SUCCESS;
21769 }
21770
21771 static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
21772 {
21773 AudioObjectPropertyAddress propAddressDevices;
21774 UInt32 deviceObjectsDataSize;
21775 OSStatus status;
21776 AudioObjectID* pDeviceObjectIDs;
21777
21778 MA_ASSERT(pContext != NULL);
21779 MA_ASSERT(pDeviceCount != NULL);
21780 MA_ASSERT(ppDeviceObjectIDs != NULL);
21781
21782 /* Safety. */
21783 *pDeviceCount = 0;
21784 *ppDeviceObjectIDs = NULL;
21785
21786 propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
21787 propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
21788 propAddressDevices.mElement = kAudioObjectPropertyElementMaster;
21789
21790 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
21791 if (status != noErr) {
21792 return ma_result_from_OSStatus(status);
21793 }
21794
21795 pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks);
21796 if (pDeviceObjectIDs == NULL) {
21797 return MA_OUT_OF_MEMORY;
21798 }
21799
21800 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
21801 if (status != noErr) {
21802 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
21803 return ma_result_from_OSStatus(status);
21804 }
21805
21806 *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
21807 *ppDeviceObjectIDs = pDeviceObjectIDs;
21808
21809 return MA_SUCCESS;
21810 }
21811
21812 static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
21813 {
21814 AudioObjectPropertyAddress propAddress;
21815 UInt32 dataSize;
21816 OSStatus status;
21817
21818 MA_ASSERT(pContext != NULL);
21819
21820 propAddress.mSelector = kAudioDevicePropertyDeviceUID;
21821 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
21822 propAddress.mElement = kAudioObjectPropertyElementMaster;
21823
21824 dataSize = sizeof(*pUID);
21825 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
21826 if (status != noErr) {
21827 return ma_result_from_OSStatus(status);
21828 }
21829
21830 return MA_SUCCESS;
21831 }
21832
21833 static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
21834 {
21835 CFStringRef uid;
21836 ma_result result;
21837
21838 MA_ASSERT(pContext != NULL);
21839
21840 result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
21841 if (result != MA_SUCCESS) {
21842 return result;
21843 }
21844
21845 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
21846 return MA_ERROR;
21847 }
21848
21849 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);
21850 return MA_SUCCESS;
21851 }
21852
21853 static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
21854 {
21855 AudioObjectPropertyAddress propAddress;
21856 CFStringRef deviceName = NULL;
21857 UInt32 dataSize;
21858 OSStatus status;
21859
21860 MA_ASSERT(pContext != NULL);
21861
21862 propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
21863 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
21864 propAddress.mElement = kAudioObjectPropertyElementMaster;
21865
21866 dataSize = sizeof(deviceName);
21867 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
21868 if (status != noErr) {
21869 return ma_result_from_OSStatus(status);
21870 }
21871
21872 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
21873 return MA_ERROR;
21874 }
21875
21876 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);
21877 return MA_SUCCESS;
21878 }
21879
21880 static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
21881 {
21882 AudioObjectPropertyAddress propAddress;
21883 UInt32 dataSize;
21884 OSStatus status;
21885 AudioBufferList* pBufferList;
21886 ma_bool32 isSupported;
21887
21888 MA_ASSERT(pContext != NULL);
21889
21890 /* 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. */
21891 propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
21892 propAddress.mScope = scope;
21893 propAddress.mElement = kAudioObjectPropertyElementMaster;
21894
21895 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
21896 if (status != noErr) {
21897 return MA_FALSE;
21898 }
21899
21900 pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(dataSize, &pContext->allocationCallbacks);
21901 if (pBufferList == NULL) {
21902 return MA_FALSE; /* Out of memory. */
21903 }
21904
21905 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
21906 if (status != noErr) {
21907 ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks);
21908 return MA_FALSE;
21909 }
21910
21911 isSupported = MA_FALSE;
21912 if (pBufferList->mNumberBuffers > 0) {
21913 isSupported = MA_TRUE;
21914 }
21915
21916 ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks);
21917 return isSupported;
21918 }
21919
21920 static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
21921 {
21922 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
21923 }
21924
21925 static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
21926 {
21927 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
21928 }
21929
21930
21931 static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */
21932 {
21933 AudioObjectPropertyAddress propAddress;
21934 UInt32 dataSize;
21935 OSStatus status;
21936 AudioStreamRangedDescription* pDescriptions;
21937
21938 MA_ASSERT(pContext != NULL);
21939 MA_ASSERT(pDescriptionCount != NULL);
21940 MA_ASSERT(ppDescriptions != NULL);
21941
21942 /*
21943 TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My
21944 MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.
21945 */
21946 propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/
21947 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
21948 propAddress.mElement = kAudioObjectPropertyElementMaster;
21949
21950 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
21951 if (status != noErr) {
21952 return ma_result_from_OSStatus(status);
21953 }
21954
21955 pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks);
21956 if (pDescriptions == NULL) {
21957 return MA_OUT_OF_MEMORY;
21958 }
21959
21960 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
21961 if (status != noErr) {
21962 ma_free(pDescriptions, &pContext->allocationCallbacks);
21963 return ma_result_from_OSStatus(status);
21964 }
21965
21966 *pDescriptionCount = dataSize / sizeof(*pDescriptions);
21967 *ppDescriptions = pDescriptions;
21968 return MA_SUCCESS;
21969 }
21970
21971
21972 static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */
21973 {
21974 AudioObjectPropertyAddress propAddress;
21975 UInt32 dataSize;
21976 OSStatus status;
21977 AudioChannelLayout* pChannelLayout;
21978
21979 MA_ASSERT(pContext != NULL);
21980 MA_ASSERT(ppChannelLayout != NULL);
21981
21982 *ppChannelLayout = NULL; /* Safety. */
21983
21984 propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
21985 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
21986 propAddress.mElement = kAudioObjectPropertyElementMaster;
21987
21988 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
21989 if (status != noErr) {
21990 return ma_result_from_OSStatus(status);
21991 }
21992
21993 pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks);
21994 if (pChannelLayout == NULL) {
21995 return MA_OUT_OF_MEMORY;
21996 }
21997
21998 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
21999 if (status != noErr) {
22000 ma_free(pChannelLayout, &pContext->allocationCallbacks);
22001 return ma_result_from_OSStatus(status);
22002 }
22003
22004 *ppChannelLayout = pChannelLayout;
22005 return MA_SUCCESS;
22006 }
22007
22008 static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
22009 {
22010 AudioChannelLayout* pChannelLayout;
22011 ma_result result;
22012
22013 MA_ASSERT(pContext != NULL);
22014 MA_ASSERT(pChannelCount != NULL);
22015
22016 *pChannelCount = 0; /* Safety. */
22017
22018 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
22019 if (result != MA_SUCCESS) {
22020 return result;
22021 }
22022
22023 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
22024 *pChannelCount = pChannelLayout->mNumberChannelDescriptions;
22025 } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
22026 *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
22027 } else {
22028 *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
22029 }
22030
22031 ma_free(pChannelLayout, &pContext->allocationCallbacks);
22032 return MA_SUCCESS;
22033 }
22034
22035 #if 0
22036 static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel channelMap[MA_MAX_CHANNELS])
22037 {
22038 AudioChannelLayout* pChannelLayout;
22039 ma_result result;
22040
22041 MA_ASSERT(pContext != NULL);
22042
22043 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
22044 if (result != MA_SUCCESS) {
22045 return result; /* Rather than always failing here, would it be more robust to simply assume a default? */
22046 }
22047
22048 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
22049 if (result != MA_SUCCESS) {
22050 ma_free(pChannelLayout, &pContext->allocationCallbacks);
22051 return result;
22052 }
22053
22054 ma_free(pChannelLayout, &pContext->allocationCallbacks);
22055 return result;
22056 }
22057 #endif
22058
22059 static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */
22060 {
22061 AudioObjectPropertyAddress propAddress;
22062 UInt32 dataSize;
22063 OSStatus status;
22064 AudioValueRange* pSampleRateRanges;
22065
22066 MA_ASSERT(pContext != NULL);
22067 MA_ASSERT(pSampleRateRangesCount != NULL);
22068 MA_ASSERT(ppSampleRateRanges != NULL);
22069
22070 /* Safety. */
22071 *pSampleRateRangesCount = 0;
22072 *ppSampleRateRanges = NULL;
22073
22074 propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
22075 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
22076 propAddress.mElement = kAudioObjectPropertyElementMaster;
22077
22078 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
22079 if (status != noErr) {
22080 return ma_result_from_OSStatus(status);
22081 }
22082
22083 pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks);
22084 if (pSampleRateRanges == NULL) {
22085 return MA_OUT_OF_MEMORY;
22086 }
22087
22088 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
22089 if (status != noErr) {
22090 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
22091 return ma_result_from_OSStatus(status);
22092 }
22093
22094 *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
22095 *ppSampleRateRanges = pSampleRateRanges;
22096 return MA_SUCCESS;
22097 }
22098
22099 #if 0
22100 static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut)
22101 {
22102 UInt32 sampleRateRangeCount;
22103 AudioValueRange* pSampleRateRanges;
22104 ma_result result;
22105
22106 MA_ASSERT(pContext != NULL);
22107 MA_ASSERT(pSampleRateOut != NULL);
22108
22109 *pSampleRateOut = 0; /* Safety. */
22110
22111 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
22112 if (result != MA_SUCCESS) {
22113 return result;
22114 }
22115
22116 if (sampleRateRangeCount == 0) {
22117 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
22118 return MA_ERROR; /* Should never hit this case should we? */
22119 }
22120
22121 if (sampleRateIn == 0) {
22122 /* Search in order of miniaudio's preferred priority. */
22123 UInt32 iMALSampleRate;
22124 for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
22125 ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
22126 UInt32 iCASampleRate;
22127 for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
22128 AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
22129 if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
22130 *pSampleRateOut = malSampleRate;
22131 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
22132 return MA_SUCCESS;
22133 }
22134 }
22135 }
22136
22137 /*
22138 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
22139 case we just fall back to the first one reported by Core Audio.
22140 */
22141 MA_ASSERT(sampleRateRangeCount > 0);
22142
22143 *pSampleRateOut = pSampleRateRanges[0].mMinimum;
22144 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
22145 return MA_SUCCESS;
22146 } else {
22147 /* Find the closest match to this sample rate. */
22148 UInt32 currentAbsoluteDifference = INT32_MAX;
22149 UInt32 iCurrentClosestRange = (UInt32)-1;
22150 UInt32 iRange;
22151 for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
22152 if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
22153 *pSampleRateOut = sampleRateIn;
22154 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
22155 return MA_SUCCESS;
22156 } else {
22157 UInt32 absoluteDifference;
22158 if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
22159 absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
22160 } else {
22161 absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
22162 }
22163
22164 if (currentAbsoluteDifference > absoluteDifference) {
22165 currentAbsoluteDifference = absoluteDifference;
22166 iCurrentClosestRange = iRange;
22167 }
22168 }
22169 }
22170
22171 MA_ASSERT(iCurrentClosestRange != (UInt32)-1);
22172
22173 *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
22174 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
22175 return MA_SUCCESS;
22176 }
22177
22178 /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */
22179 /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/
22180 /*return MA_ERROR;*/
22181 }
22182 #endif
22183
22184 static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut)
22185 {
22186 AudioObjectPropertyAddress propAddress;
22187 AudioValueRange bufferSizeRange;
22188 UInt32 dataSize;
22189 OSStatus status;
22190
22191 MA_ASSERT(pContext != NULL);
22192 MA_ASSERT(pBufferSizeInFramesOut != NULL);
22193
22194 *pBufferSizeInFramesOut = 0; /* Safety. */
22195
22196 propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
22197 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
22198 propAddress.mElement = kAudioObjectPropertyElementMaster;
22199
22200 dataSize = sizeof(bufferSizeRange);
22201 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
22202 if (status != noErr) {
22203 return ma_result_from_OSStatus(status);
22204 }
22205
22206 /* This is just a clamp. */
22207 if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
22208 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
22209 } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
22210 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
22211 } else {
22212 *pBufferSizeInFramesOut = bufferSizeInFramesIn;
22213 }
22214
22215 return MA_SUCCESS;
22216 }
22217
22218 static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut)
22219 {
22220 ma_result result;
22221 ma_uint32 chosenBufferSizeInFrames;
22222 AudioObjectPropertyAddress propAddress;
22223 UInt32 dataSize;
22224 OSStatus status;
22225
22226 MA_ASSERT(pContext != NULL);
22227
22228 result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames);
22229 if (result != MA_SUCCESS) {
22230 return result;
22231 }
22232
22233 /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */
22234 propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
22235 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
22236 propAddress.mElement = kAudioObjectPropertyElementMaster;
22237
22238 ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
22239
22240 /* Get the actual size of the buffer. */
22241 dataSize = sizeof(*pPeriodSizeInOut);
22242 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
22243 if (status != noErr) {
22244 return ma_result_from_OSStatus(status);
22245 }
22246
22247 *pPeriodSizeInOut = chosenBufferSizeInFrames;
22248 return MA_SUCCESS;
22249 }
22250
22251
22252 static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
22253 {
22254 MA_ASSERT(pContext != NULL);
22255 MA_ASSERT(pDeviceObjectID != NULL);
22256
22257 /* Safety. */
22258 *pDeviceObjectID = 0;
22259
22260 if (pDeviceID == NULL) {
22261 /* Default device. */
22262 AudioObjectPropertyAddress propAddressDefaultDevice;
22263 UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
22264 AudioObjectID defaultDeviceObjectID;
22265 OSStatus status;
22266
22267 propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
22268 propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster;
22269 if (deviceType == ma_device_type_playback) {
22270 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
22271 } else {
22272 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
22273 }
22274
22275 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
22276 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
22277 if (status == noErr) {
22278 *pDeviceObjectID = defaultDeviceObjectID;
22279 return MA_SUCCESS;
22280 }
22281 } else {
22282 /* Explicit device. */
22283 UInt32 deviceCount;
22284 AudioObjectID* pDeviceObjectIDs;
22285 ma_result result;
22286 UInt32 iDevice;
22287
22288 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
22289 if (result != MA_SUCCESS) {
22290 return result;
22291 }
22292
22293 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
22294 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
22295
22296 char uid[256];
22297 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
22298 continue;
22299 }
22300
22301 if (deviceType == ma_device_type_playback) {
22302 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
22303 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
22304 *pDeviceObjectID = deviceObjectID;
22305 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
22306 return MA_SUCCESS;
22307 }
22308 }
22309 } else {
22310 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
22311 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
22312 *pDeviceObjectID = deviceObjectID;
22313 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
22314 return MA_SUCCESS;
22315 }
22316 }
22317 }
22318 }
22319
22320 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
22321 }
22322
22323 /* If we get here it means we couldn't find the device. */
22324 return MA_NO_DEVICE;
22325 }
22326
22327
22328 static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_bool32 usingDefaultFormat, ma_bool32 usingDefaultChannels, ma_bool32 usingDefaultSampleRate, AudioStreamBasicDescription* pFormat)
22329 {
22330 UInt32 deviceFormatDescriptionCount;
22331 AudioStreamRangedDescription* pDeviceFormatDescriptions;
22332 ma_result result;
22333 ma_uint32 desiredSampleRate;
22334 ma_uint32 desiredChannelCount;
22335 ma_format desiredFormat;
22336 AudioStreamBasicDescription bestDeviceFormatSoFar;
22337 ma_bool32 hasSupportedFormat;
22338 UInt32 iFormat;
22339
22340 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
22341 if (result != MA_SUCCESS) {
22342 return result;
22343 }
22344
22345 desiredSampleRate = sampleRate;
22346 if (usingDefaultSampleRate) {
22347 /*
22348 When using the device's default sample rate, we get the highest priority standard rate supported by the device. Otherwise
22349 we just use the pre-set rate.
22350 */
22351 ma_uint32 iStandardRate;
22352 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
22353 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
22354 ma_bool32 foundRate = MA_FALSE;
22355 UInt32 iDeviceRate;
22356
22357 for (iDeviceRate = 0; iDeviceRate < deviceFormatDescriptionCount; ++iDeviceRate) {
22358 ma_uint32 deviceRate = (ma_uint32)pDeviceFormatDescriptions[iDeviceRate].mFormat.mSampleRate;
22359
22360 if (deviceRate == standardRate) {
22361 desiredSampleRate = standardRate;
22362 foundRate = MA_TRUE;
22363 break;
22364 }
22365 }
22366
22367 if (foundRate) {
22368 break;
22369 }
22370 }
22371 }
22372
22373 desiredChannelCount = channels;
22374 if (usingDefaultChannels) {
22375 ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &desiredChannelCount); /* <-- Not critical if this fails. */
22376 }
22377
22378 desiredFormat = format;
22379 if (usingDefaultFormat) {
22380 desiredFormat = g_maFormatPriorities[0];
22381 }
22382
22383 /*
22384 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
22385 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.
22386 */
22387 MA_ZERO_OBJECT(&bestDeviceFormatSoFar);
22388
22389 hasSupportedFormat = MA_FALSE;
22390 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
22391 ma_format format;
22392 ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format);
22393 if (formatResult == MA_SUCCESS && format != ma_format_unknown) {
22394 hasSupportedFormat = MA_TRUE;
22395 bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
22396 break;
22397 }
22398 }
22399
22400 if (!hasSupportedFormat) {
22401 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
22402 return MA_FORMAT_NOT_SUPPORTED;
22403 }
22404
22405
22406 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
22407 AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
22408 ma_format thisSampleFormat;
22409 ma_result formatResult;
22410 ma_format bestSampleFormatSoFar;
22411
22412 /* If the format is not supported by miniaudio we need to skip this one entirely. */
22413 formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
22414 if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
22415 continue; /* The format is not supported by miniaudio. Skip. */
22416 }
22417
22418 ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
22419
22420 /* Getting here means the format is supported by miniaudio which makes this format a candidate. */
22421 if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
22422 /*
22423 The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format
22424 so far has an equal sample rate we can just ignore this one.
22425 */
22426 if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
22427 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. */
22428 } else {
22429 /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */
22430 if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
22431 /* This format has a different sample rate _and_ a different channel count. */
22432 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
22433 continue; /* No change to the best format. */
22434 } else {
22435 /*
22436 Both this format and the best so far have different sample rates and different channel counts. Whichever has the
22437 best format is the new best.
22438 */
22439 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
22440 bestDeviceFormatSoFar = thisDeviceFormat;
22441 continue;
22442 } else {
22443 continue; /* No change to the best format. */
22444 }
22445 }
22446 } else {
22447 /* This format has a different sample rate but the desired channel count. */
22448 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
22449 /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */
22450 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
22451 bestDeviceFormatSoFar = thisDeviceFormat;
22452 continue;
22453 } else {
22454 continue; /* No change to the best format for now. */
22455 }
22456 } else {
22457 /* This format has the desired channel count, but the best so far does not. We have a new best. */
22458 bestDeviceFormatSoFar = thisDeviceFormat;
22459 continue;
22460 }
22461 }
22462 }
22463 } else {
22464 /*
22465 The sample rates match which makes this format a very high priority contender. If the best format so far has a different
22466 sample rate it needs to be replaced with this one.
22467 */
22468 if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
22469 bestDeviceFormatSoFar = thisDeviceFormat;
22470 continue;
22471 } else {
22472 /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */
22473 if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
22474 /*
22475 In this case this format has the same channel count as what the client is requesting. If the best format so far has
22476 a different count, this one becomes the new best.
22477 */
22478 if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
22479 bestDeviceFormatSoFar = thisDeviceFormat;
22480 continue;
22481 } else {
22482 /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
22483 if (thisSampleFormat == desiredFormat) {
22484 bestDeviceFormatSoFar = thisDeviceFormat;
22485 break; /* Found the exact match. */
22486 } else {
22487 /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
22488 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
22489 bestDeviceFormatSoFar = thisDeviceFormat;
22490 continue;
22491 } else {
22492 continue; /* No change to the best format for now. */
22493 }
22494 }
22495 }
22496 } else {
22497 /*
22498 In this case the channel count is different to what the client has requested. If the best so far has the same channel
22499 count as the requested count then it remains the best.
22500 */
22501 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
22502 continue;
22503 } else {
22504 /*
22505 This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
22506 the same priority, but we need to compare the format now.
22507 */
22508 if (thisSampleFormat == bestSampleFormatSoFar) {
22509 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
22510 bestDeviceFormatSoFar = thisDeviceFormat;
22511 continue;
22512 } else {
22513 continue; /* No change to the best format for now. */
22514 }
22515 }
22516 }
22517 }
22518 }
22519 }
22520 }
22521
22522 *pFormat = bestDeviceFormatSoFar;
22523
22524 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
22525 return MA_SUCCESS;
22526 }
22527
22528 static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel channelMap[MA_MAX_CHANNELS])
22529 {
22530 AudioUnitScope deviceScope;
22531 AudioUnitElement deviceBus;
22532 UInt32 channelLayoutSize;
22533 OSStatus status;
22534 AudioChannelLayout* pChannelLayout;
22535 ma_result result;
22536
22537 MA_ASSERT(pContext != NULL);
22538
22539 if (deviceType == ma_device_type_playback) {
22540 deviceScope = kAudioUnitScope_Output;
22541 deviceBus = MA_COREAUDIO_OUTPUT_BUS;
22542 } else {
22543 deviceScope = kAudioUnitScope_Input;
22544 deviceBus = MA_COREAUDIO_INPUT_BUS;
22545 }
22546
22547 status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
22548 if (status != noErr) {
22549 return ma_result_from_OSStatus(status);
22550 }
22551
22552 pChannelLayout = (AudioChannelLayout*)ma__malloc_from_callbacks(channelLayoutSize, &pContext->allocationCallbacks);
22553 if (pChannelLayout == NULL) {
22554 return MA_OUT_OF_MEMORY;
22555 }
22556
22557 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
22558 if (status != noErr) {
22559 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
22560 return ma_result_from_OSStatus(status);
22561 }
22562
22563 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
22564 if (result != MA_SUCCESS) {
22565 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
22566 return result;
22567 }
22568
22569 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
22570 return MA_SUCCESS;
22571 }
22572 #endif /* MA_APPLE_DESKTOP */
22573
22574 static ma_bool32 ma_context_is_device_id_equal__coreaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
22575 {
22576 MA_ASSERT(pContext != NULL);
22577 MA_ASSERT(pID0 != NULL);
22578 MA_ASSERT(pID1 != NULL);
22579 (void)pContext;
22580
22581 return strcmp(pID0->coreaudio, pID1->coreaudio) == 0;
22582 }
22583
22584 static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
22585 {
22586 #if defined(MA_APPLE_DESKTOP)
22587 UInt32 deviceCount;
22588 AudioObjectID* pDeviceObjectIDs;
22589 ma_result result;
22590 UInt32 iDevice;
22591
22592 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
22593 if (result != MA_SUCCESS) {
22594 return result;
22595 }
22596
22597 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
22598 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
22599 ma_device_info info;
22600
22601 MA_ZERO_OBJECT(&info);
22602 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
22603 continue;
22604 }
22605 if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
22606 continue;
22607 }
22608
22609 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
22610 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
22611 break;
22612 }
22613 }
22614 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
22615 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
22616 break;
22617 }
22618 }
22619 }
22620
22621 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
22622 #else
22623 /* Only supporting default devices on non-Desktop platforms. */
22624 ma_device_info info;
22625
22626 MA_ZERO_OBJECT(&info);
22627 ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
22628 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
22629 return MA_SUCCESS;
22630 }
22631
22632 MA_ZERO_OBJECT(&info);
22633 ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
22634 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
22635 return MA_SUCCESS;
22636 }
22637 #endif
22638
22639 return MA_SUCCESS;
22640 }
22641
22642 static 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)
22643 {
22644 ma_result result;
22645
22646 MA_ASSERT(pContext != NULL);
22647
22648 /* No exclusive mode with the Core Audio backend for now. */
22649 if (shareMode == ma_share_mode_exclusive) {
22650 return MA_SHARE_MODE_NOT_SUPPORTED;
22651 }
22652
22653 #if defined(MA_APPLE_DESKTOP)
22654 /* Desktop */
22655 {
22656 AudioObjectID deviceObjectID;
22657 UInt32 streamDescriptionCount;
22658 AudioStreamRangedDescription* pStreamDescriptions;
22659 UInt32 iStreamDescription;
22660 UInt32 sampleRateRangeCount;
22661 AudioValueRange* pSampleRateRanges;
22662
22663 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
22664 if (result != MA_SUCCESS) {
22665 return result;
22666 }
22667
22668 result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
22669 if (result != MA_SUCCESS) {
22670 return result;
22671 }
22672
22673 result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
22674 if (result != MA_SUCCESS) {
22675 return result;
22676 }
22677
22678 /* Formats. */
22679 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
22680 if (result != MA_SUCCESS) {
22681 return result;
22682 }
22683
22684 for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
22685 ma_format format;
22686 ma_bool32 formatExists = MA_FALSE;
22687 ma_uint32 iOutputFormat;
22688
22689 result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
22690 if (result != MA_SUCCESS) {
22691 continue;
22692 }
22693
22694 MA_ASSERT(format != ma_format_unknown);
22695
22696 /* Make sure the format isn't already in the output list. */
22697 for (iOutputFormat = 0; iOutputFormat < pDeviceInfo->formatCount; ++iOutputFormat) {
22698 if (pDeviceInfo->formats[iOutputFormat] == format) {
22699 formatExists = MA_TRUE;
22700 break;
22701 }
22702 }
22703
22704 if (!formatExists) {
22705 pDeviceInfo->formats[pDeviceInfo->formatCount++] = format;
22706 }
22707 }
22708
22709 ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
22710
22711
22712 /* Channels. */
22713 result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &pDeviceInfo->minChannels);
22714 if (result != MA_SUCCESS) {
22715 return result;
22716 }
22717 pDeviceInfo->maxChannels = pDeviceInfo->minChannels;
22718
22719
22720 /* Sample rates. */
22721 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
22722 if (result != MA_SUCCESS) {
22723 return result;
22724 }
22725
22726 if (sampleRateRangeCount > 0) {
22727 UInt32 iSampleRate;
22728 pDeviceInfo->minSampleRate = UINT32_MAX;
22729 pDeviceInfo->maxSampleRate = 0;
22730 for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
22731 if (pDeviceInfo->minSampleRate > pSampleRateRanges[iSampleRate].mMinimum) {
22732 pDeviceInfo->minSampleRate = pSampleRateRanges[iSampleRate].mMinimum;
22733 }
22734 if (pDeviceInfo->maxSampleRate < pSampleRateRanges[iSampleRate].mMaximum) {
22735 pDeviceInfo->maxSampleRate = pSampleRateRanges[iSampleRate].mMaximum;
22736 }
22737 }
22738 }
22739 }
22740 #else
22741 /* Mobile */
22742 {
22743 AudioComponentDescription desc;
22744 AudioComponent component;
22745 AudioUnit audioUnit;
22746 OSStatus status;
22747 AudioUnitScope formatScope;
22748 AudioUnitElement formatElement;
22749 AudioStreamBasicDescription bestFormat;
22750 UInt32 propSize;
22751
22752 if (deviceType == ma_device_type_playback) {
22753 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
22754 } else {
22755 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
22756 }
22757
22758 /*
22759 Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
22760 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
22761 retrieve from the AVAudioSession shared instance.
22762 */
22763 desc.componentType = kAudioUnitType_Output;
22764 desc.componentSubType = kAudioUnitSubType_RemoteIO;
22765 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
22766 desc.componentFlags = 0;
22767 desc.componentFlagsMask = 0;
22768
22769 component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
22770 if (component == NULL) {
22771 return MA_FAILED_TO_INIT_BACKEND;
22772 }
22773
22774 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
22775 if (status != noErr) {
22776 return ma_result_from_OSStatus(status);
22777 }
22778
22779 formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
22780 formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
22781
22782 propSize = sizeof(bestFormat);
22783 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
22784 if (status != noErr) {
22785 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
22786 return ma_result_from_OSStatus(status);
22787 }
22788
22789 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
22790 audioUnit = NULL;
22791
22792
22793 pDeviceInfo->minChannels = bestFormat.mChannelsPerFrame;
22794 pDeviceInfo->maxChannels = bestFormat.mChannelsPerFrame;
22795
22796 pDeviceInfo->formatCount = 1;
22797 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->formats[0]);
22798 if (result != MA_SUCCESS) {
22799 return result;
22800 }
22801
22802 /*
22803 It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
22804 this we just get the shared instance and inspect.
22805 */
22806 @autoreleasepool {
22807 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
22808 MA_ASSERT(pAudioSession != NULL);
22809
22810 pDeviceInfo->minSampleRate = (ma_uint32)pAudioSession.sampleRate;
22811 pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
22812 }
22813 }
22814 #endif
22815
22816 (void)pDeviceInfo; /* Unused. */
22817 return MA_SUCCESS;
22818 }
22819
22820
22821 static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
22822 {
22823 ma_device* pDevice = (ma_device*)pUserData;
22824 ma_stream_layout layout;
22825
22826 MA_ASSERT(pDevice != NULL);
22827
22828 #if defined(MA_DEBUG_OUTPUT)
22829 printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
22830 #endif
22831
22832 /* 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. */
22833 layout = ma_stream_layout_interleaved;
22834 if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
22835 layout = ma_stream_layout_deinterleaved;
22836 }
22837
22838 if (layout == ma_stream_layout_interleaved) {
22839 /* For now we can assume everything is interleaved. */
22840 UInt32 iBuffer;
22841 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
22842 if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
22843 ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22844 if (frameCountForThisBuffer > 0) {
22845 if (pDevice->type == ma_device_type_duplex) {
22846 ma_device__handle_duplex_callback_playback(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
22847 } else {
22848 ma_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
22849 }
22850 }
22851
22852 #if defined(MA_DEBUG_OUTPUT)
22853 printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
22854 #endif
22855 } else {
22856 /*
22857 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
22858 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
22859 output silence here.
22860 */
22861 MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
22862
22863 #if defined(MA_DEBUG_OUTPUT)
22864 printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
22865 #endif
22866 }
22867 }
22868 } else {
22869 /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
22870
22871 /*
22872 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
22873 very strange has happened and we're not going to support it.
22874 */
22875 if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
22876 ma_uint8 tempBuffer[4096];
22877 UInt32 iBuffer;
22878
22879 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
22880 ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
22881 ma_uint32 framesRemaining = frameCountPerBuffer;
22882
22883 while (framesRemaining > 0) {
22884 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
22885 ma_uint32 iChannel;
22886 ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22887 if (framesToRead > framesRemaining) {
22888 framesToRead = framesRemaining;
22889 }
22890
22891 if (pDevice->type == ma_device_type_duplex) {
22892 ma_device__handle_duplex_callback_playback(pDevice, framesToRead, tempBuffer, &pDevice->coreaudio.duplexRB);
22893 } else {
22894 ma_device__read_frames_from_client(pDevice, framesToRead, tempBuffer);
22895 }
22896
22897 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
22898 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
22899 }
22900
22901 ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
22902
22903 framesRemaining -= framesToRead;
22904 }
22905 }
22906 }
22907 }
22908
22909 (void)pActionFlags;
22910 (void)pTimeStamp;
22911 (void)busNumber;
22912 (void)frameCount;
22913
22914 return noErr;
22915 }
22916
22917 static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
22918 {
22919 ma_device* pDevice = (ma_device*)pUserData;
22920 AudioBufferList* pRenderedBufferList;
22921 ma_stream_layout layout;
22922 OSStatus status;
22923
22924 MA_ASSERT(pDevice != NULL);
22925
22926 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
22927 MA_ASSERT(pRenderedBufferList);
22928
22929 /* 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. */
22930 layout = ma_stream_layout_interleaved;
22931 if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
22932 layout = ma_stream_layout_deinterleaved;
22933 }
22934
22935 #if defined(MA_DEBUG_OUTPUT)
22936 printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
22937 #endif
22938
22939 status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
22940 if (status != noErr) {
22941 #if defined(MA_DEBUG_OUTPUT)
22942 printf(" ERROR: AudioUnitRender() failed with %d\n", status);
22943 #endif
22944 return status;
22945 }
22946
22947 if (layout == ma_stream_layout_interleaved) {
22948 UInt32 iBuffer;
22949 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
22950 if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
22951 if (pDevice->type == ma_device_type_duplex) {
22952 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
22953 } else {
22954 ma_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
22955 }
22956 #if defined(MA_DEBUG_OUTPUT)
22957 printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
22958 #endif
22959 } else {
22960 /*
22961 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
22962 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
22963 */
22964 ma_uint8 silentBuffer[4096];
22965 ma_uint32 framesRemaining;
22966
22967 MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
22968
22969 framesRemaining = frameCount;
22970 while (framesRemaining > 0) {
22971 ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
22972 if (framesToSend > framesRemaining) {
22973 framesToSend = framesRemaining;
22974 }
22975
22976 if (pDevice->type == ma_device_type_duplex) {
22977 ma_device__handle_duplex_callback_capture(pDevice, framesToSend, silentBuffer, &pDevice->coreaudio.duplexRB);
22978 } else {
22979 ma_device__send_frames_to_client(pDevice, framesToSend, silentBuffer);
22980 }
22981
22982 framesRemaining -= framesToSend;
22983 }
22984
22985 #if defined(MA_DEBUG_OUTPUT)
22986 printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
22987 #endif
22988 }
22989 }
22990 } else {
22991 /* 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. */
22992
22993 /*
22994 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
22995 very strange has happened and we're not going to support it.
22996 */
22997 if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
22998 ma_uint8 tempBuffer[4096];
22999 UInt32 iBuffer;
23000 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
23001 ma_uint32 framesRemaining = frameCount;
23002 while (framesRemaining > 0) {
23003 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
23004 ma_uint32 iChannel;
23005 ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_sample(pDevice->capture.internalFormat);
23006 if (framesToSend > framesRemaining) {
23007 framesToSend = framesRemaining;
23008 }
23009
23010 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
23011 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
23012 }
23013
23014 ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
23015
23016 if (pDevice->type == ma_device_type_duplex) {
23017 ma_device__handle_duplex_callback_capture(pDevice, framesToSend, tempBuffer, &pDevice->coreaudio.duplexRB);
23018 } else {
23019 ma_device__send_frames_to_client(pDevice, framesToSend, tempBuffer);
23020 }
23021
23022 framesRemaining -= framesToSend;
23023 }
23024 }
23025 }
23026 }
23027
23028 (void)pActionFlags;
23029 (void)pTimeStamp;
23030 (void)busNumber;
23031 (void)frameCount;
23032 (void)pUnusedBufferList;
23033
23034 return noErr;
23035 }
23036
23037 static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
23038 {
23039 ma_device* pDevice = (ma_device*)pUserData;
23040 MA_ASSERT(pDevice != NULL);
23041
23042 /*
23043 There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
23044 AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
23045 can try waiting on the same lock. I'm going to try working around this by not calling any Core
23046 Audio APIs in the callback when the device has been stopped or uninitialized.
23047 */
23048 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) {
23049 ma_stop_proc onStop = pDevice->onStop;
23050 if (onStop) {
23051 onStop(pDevice);
23052 }
23053
23054 ma_event_signal(&pDevice->coreaudio.stopEvent);
23055 } else {
23056 UInt32 isRunning;
23057 UInt32 isRunningSize = sizeof(isRunning);
23058 OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
23059 if (status != noErr) {
23060 return; /* Don't really know what to do in this case... just ignore it, I suppose... */
23061 }
23062
23063 if (!isRunning) {
23064 ma_stop_proc onStop;
23065
23066 /*
23067 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:
23068
23069 1) When the device is unplugged, this will be called _before_ the default device change notification.
23070 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
23071
23072 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.
23073 */
23074 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
23075 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
23076 /*
23077 It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
23078 via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
23079 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
23080 hasn't!).
23081 */
23082 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
23083 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
23084 return;
23085 }
23086
23087 /*
23088 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
23089 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
23090 likely be successful in switching to the new device.
23091
23092 TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
23093 */
23094 return;
23095 }
23096
23097 /* Getting here means we need to stop the device. */
23098 onStop = pDevice->onStop;
23099 if (onStop) {
23100 onStop(pDevice);
23101 }
23102 }
23103 }
23104
23105 (void)propertyID; /* Unused. */
23106 }
23107
23108 #if defined(MA_APPLE_DESKTOP)
23109 static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0;
23110 static ma_mutex g_DeviceTrackingMutex_CoreAudio;
23111 static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
23112 static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0;
23113 static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0;
23114
23115 static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
23116 {
23117 ma_device_type deviceType;
23118
23119 /* Not sure if I really need to check this, but it makes me feel better. */
23120 if (addressCount == 0) {
23121 return noErr;
23122 }
23123
23124 if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
23125 deviceType = ma_device_type_playback;
23126 } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
23127 deviceType = ma_device_type_capture;
23128 } else {
23129 return noErr; /* Should never hit this. */
23130 }
23131
23132 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
23133 {
23134 ma_uint32 iDevice;
23135 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
23136 ma_result reinitResult;
23137 ma_device* pDevice;
23138
23139 pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
23140 if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
23141 if (deviceType == ma_device_type_playback) {
23142 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
23143 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
23144 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
23145 } else {
23146 pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
23147 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
23148 pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;
23149 }
23150
23151 if (reinitResult == MA_SUCCESS) {
23152 ma_device__post_init_setup(pDevice, deviceType);
23153
23154 /* Restart the device if required. If this fails we need to stop the device entirely. */
23155 if (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
23156 OSStatus status;
23157 if (deviceType == ma_device_type_playback) {
23158 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
23159 if (status != noErr) {
23160 if (pDevice->type == ma_device_type_duplex) {
23161 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
23162 }
23163 ma_device__set_state(pDevice, MA_STATE_STOPPED);
23164 }
23165 } else if (deviceType == ma_device_type_capture) {
23166 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
23167 if (status != noErr) {
23168 if (pDevice->type == ma_device_type_duplex) {
23169 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
23170 }
23171 ma_device__set_state(pDevice, MA_STATE_STOPPED);
23172 }
23173 }
23174 }
23175 }
23176 }
23177 }
23178 }
23179 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
23180
23181 /* Unused parameters. */
23182 (void)objectID;
23183 (void)pUserData;
23184
23185 return noErr;
23186 }
23187
23188 static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)
23189 {
23190 MA_ASSERT(pContext != NULL);
23191
23192 if (ma_atomic_increment_32(&g_DeviceTrackingInitCounter_CoreAudio) == 1) {
23193 AudioObjectPropertyAddress propAddress;
23194 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
23195 propAddress.mElement = kAudioObjectPropertyElementMaster;
23196
23197 ma_mutex_init(pContext, &g_DeviceTrackingMutex_CoreAudio);
23198
23199 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
23200 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
23201
23202 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
23203 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
23204 }
23205
23206 return MA_SUCCESS;
23207 }
23208
23209 static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)
23210 {
23211 MA_ASSERT(pContext != NULL);
23212
23213 if (ma_atomic_decrement_32(&g_DeviceTrackingInitCounter_CoreAudio) == 0) {
23214 AudioObjectPropertyAddress propAddress;
23215 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
23216 propAddress.mElement = kAudioObjectPropertyElementMaster;
23217
23218 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
23219 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
23220
23221 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
23222 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
23223
23224 /* At this point there should be no tracked devices. If so there's an error somewhere. */
23225 MA_ASSERT(g_ppTrackedDevices_CoreAudio == NULL);
23226 MA_ASSERT(g_TrackedDeviceCount_CoreAudio == 0);
23227
23228 ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
23229 }
23230
23231 return MA_SUCCESS;
23232 }
23233
23234 static ma_result ma_device__track__coreaudio(ma_device* pDevice)
23235 {
23236 ma_result result;
23237
23238 MA_ASSERT(pDevice != NULL);
23239
23240 result = ma_context__init_device_tracking__coreaudio(pDevice->pContext);
23241 if (result != MA_SUCCESS) {
23242 return result;
23243 }
23244
23245 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
23246 {
23247 /* Allocate memory if required. */
23248 if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {
23249 ma_uint32 oldCap;
23250 ma_uint32 newCap;
23251 ma_device** ppNewDevices;
23252
23253 oldCap = g_TrackedDeviceCap_CoreAudio;
23254 newCap = g_TrackedDeviceCap_CoreAudio * 2;
23255 if (newCap == 0) {
23256 newCap = 1;
23257 }
23258
23259 ppNewDevices = (ma_device**)ma__realloc_from_callbacks(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, sizeof(*g_ppTrackedDevices_CoreAudio)*oldCap, &pDevice->pContext->allocationCallbacks);
23260 if (ppNewDevices == NULL) {
23261 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
23262 return MA_OUT_OF_MEMORY;
23263 }
23264
23265 g_ppTrackedDevices_CoreAudio = ppNewDevices;
23266 g_TrackedDeviceCap_CoreAudio = newCap;
23267 }
23268
23269 g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;
23270 g_TrackedDeviceCount_CoreAudio += 1;
23271 }
23272 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
23273
23274 return MA_SUCCESS;
23275 }
23276
23277 static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
23278 {
23279 ma_result result;
23280
23281 MA_ASSERT(pDevice != NULL);
23282
23283 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
23284 {
23285 ma_uint32 iDevice;
23286 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
23287 if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {
23288 /* We've found the device. We now need to remove it from the list. */
23289 ma_uint32 jDevice;
23290 for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
23291 g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
23292 }
23293
23294 g_TrackedDeviceCount_CoreAudio -= 1;
23295
23296 /* If there's nothing else in the list we need to free memory. */
23297 if (g_TrackedDeviceCount_CoreAudio == 0) {
23298 ma__free_from_callbacks(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);
23299 g_ppTrackedDevices_CoreAudio = NULL;
23300 g_TrackedDeviceCap_CoreAudio = 0;
23301 }
23302
23303 break;
23304 }
23305 }
23306 }
23307 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
23308
23309 result = ma_context__uninit_device_tracking__coreaudio(pDevice->pContext);
23310 if (result != MA_SUCCESS) {
23311 return result;
23312 }
23313
23314 return MA_SUCCESS;
23315 }
23316 #endif
23317
23318 #if defined(MA_APPLE_MOBILE)
23319 @interface ma_router_change_handler:NSObject {
23320 ma_device* m_pDevice;
23321 }
23322 @end
23323
23324 @implementation ma_router_change_handler
23325 -(id)init:(ma_device*)pDevice
23326 {
23327 self = [super init];
23328 m_pDevice = pDevice;
23329
23330 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
23331
23332 return self;
23333 }
23334
23335 -(void)dealloc
23336 {
23337 [self remove_handler];
23338 }
23339
23340 -(void)remove_handler
23341 {
23342 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVAudioSessionRouteChangeNotification" object:nil];
23343 }
23344
23345 -(void)handle_route_change:(NSNotification*)pNotification
23346 {
23347 AVAudioSession* pSession = [AVAudioSession sharedInstance];
23348
23349 NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
23350 switch (reason)
23351 {
23352 case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
23353 {
23354 #if defined(MA_DEBUG_OUTPUT)
23355 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
23356 #endif
23357 } break;
23358
23359 case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
23360 {
23361 #if defined(MA_DEBUG_OUTPUT)
23362 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
23363 #endif
23364 } break;
23365
23366 case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
23367 {
23368 #if defined(MA_DEBUG_OUTPUT)
23369 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
23370 #endif
23371 } break;
23372
23373 case AVAudioSessionRouteChangeReasonWakeFromSleep:
23374 {
23375 #if defined(MA_DEBUG_OUTPUT)
23376 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
23377 #endif
23378 } break;
23379
23380 case AVAudioSessionRouteChangeReasonOverride:
23381 {
23382 #if defined(MA_DEBUG_OUTPUT)
23383 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
23384 #endif
23385 } break;
23386
23387 case AVAudioSessionRouteChangeReasonCategoryChange:
23388 {
23389 #if defined(MA_DEBUG_OUTPUT)
23390 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
23391 #endif
23392 } break;
23393
23394 case AVAudioSessionRouteChangeReasonUnknown:
23395 default:
23396 {
23397 #if defined(MA_DEBUG_OUTPUT)
23398 printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
23399 #endif
23400 } break;
23401 }
23402
23403 m_pDevice->sampleRate = (ma_uint32)pSession.sampleRate;
23404
23405 if (m_pDevice->type == ma_device_type_capture || m_pDevice->type == ma_device_type_duplex) {
23406 m_pDevice->capture.channels = (ma_uint32)pSession.inputNumberOfChannels;
23407 ma_device__post_init_setup(m_pDevice, ma_device_type_capture);
23408 }
23409 if (m_pDevice->type == ma_device_type_playback || m_pDevice->type == ma_device_type_duplex) {
23410 m_pDevice->playback.channels = (ma_uint32)pSession.outputNumberOfChannels;
23411 ma_device__post_init_setup(m_pDevice, ma_device_type_playback);
23412 }
23413 }
23414 @end
23415 #endif
23416
23417 static void ma_device_uninit__coreaudio(ma_device* pDevice)
23418 {
23419 MA_ASSERT(pDevice != NULL);
23420 MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED);
23421
23422 #if defined(MA_APPLE_DESKTOP)
23423 /*
23424 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
23425 just gracefully ignore it.
23426 */
23427 ma_device__untrack__coreaudio(pDevice);
23428 #endif
23429 #if defined(MA_APPLE_MOBILE)
23430 if (pDevice->coreaudio.pRouteChangeHandler != NULL) {
23431 ma_router_change_handler* pRouteChangeHandler = (__bridge_transfer ma_router_change_handler*)pDevice->coreaudio.pRouteChangeHandler;
23432 [pRouteChangeHandler remove_handler];
23433 }
23434 #endif
23435
23436 if (pDevice->coreaudio.audioUnitCapture != NULL) {
23437 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
23438 }
23439 if (pDevice->coreaudio.audioUnitPlayback != NULL) {
23440 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
23441 }
23442
23443 if (pDevice->coreaudio.pAudioBufferList) {
23444 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
23445 }
23446
23447 if (pDevice->type == ma_device_type_duplex) {
23448 ma_pcm_rb_uninit(&pDevice->coreaudio.duplexRB);
23449 }
23450 }
23451
23452 typedef struct
23453 {
23454 /* Input. */
23455 ma_format formatIn;
23456 ma_uint32 channelsIn;
23457 ma_uint32 sampleRateIn;
23458 ma_channel channelMapIn[MA_MAX_CHANNELS];
23459 ma_uint32 periodSizeInFramesIn;
23460 ma_uint32 periodSizeInMillisecondsIn;
23461 ma_uint32 periodsIn;
23462 ma_bool32 usingDefaultFormat;
23463 ma_bool32 usingDefaultChannels;
23464 ma_bool32 usingDefaultSampleRate;
23465 ma_bool32 usingDefaultChannelMap;
23466 ma_share_mode shareMode;
23467 ma_bool32 registerStopEvent;
23468
23469 /* Output. */
23470 #if defined(MA_APPLE_DESKTOP)
23471 AudioObjectID deviceObjectID;
23472 #endif
23473 AudioComponent component;
23474 AudioUnit audioUnit;
23475 AudioBufferList* pAudioBufferList; /* Only used for input devices. */
23476 ma_format formatOut;
23477 ma_uint32 channelsOut;
23478 ma_uint32 sampleRateOut;
23479 ma_channel channelMapOut[MA_MAX_CHANNELS];
23480 ma_uint32 periodSizeInFramesOut;
23481 ma_uint32 periodsOut;
23482 char deviceName[256];
23483 } ma_device_init_internal_data__coreaudio;
23484
23485 static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */
23486 {
23487 ma_result result;
23488 OSStatus status;
23489 UInt32 enableIOFlag;
23490 AudioStreamBasicDescription bestFormat;
23491 ma_uint32 actualPeriodSizeInFrames;
23492 AURenderCallbackStruct callbackInfo;
23493 #if defined(MA_APPLE_DESKTOP)
23494 AudioObjectID deviceObjectID;
23495 #endif
23496
23497 /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
23498 if (deviceType == ma_device_type_duplex) {
23499 return MA_INVALID_ARGS;
23500 }
23501
23502 MA_ASSERT(pContext != NULL);
23503 MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
23504
23505 #if defined(MA_APPLE_DESKTOP)
23506 pData->deviceObjectID = 0;
23507 #endif
23508 pData->component = NULL;
23509 pData->audioUnit = NULL;
23510 pData->pAudioBufferList = NULL;
23511
23512 #if defined(MA_APPLE_DESKTOP)
23513 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
23514 if (result != MA_SUCCESS) {
23515 return result;
23516 }
23517
23518 pData->deviceObjectID = deviceObjectID;
23519 #endif
23520
23521 /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
23522 pData->periodsOut = pData->periodsIn;
23523 if (pData->periodsOut == 0) {
23524 pData->periodsOut = MA_DEFAULT_PERIODS;
23525 }
23526 if (pData->periodsOut > 16) {
23527 pData->periodsOut = 16;
23528 }
23529
23530
23531 /* Audio unit. */
23532 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
23533 if (status != noErr) {
23534 return ma_result_from_OSStatus(status);
23535 }
23536
23537
23538 /* 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. */
23539 enableIOFlag = 1;
23540 if (deviceType == ma_device_type_capture) {
23541 enableIOFlag = 0;
23542 }
23543
23544 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
23545 if (status != noErr) {
23546 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23547 return ma_result_from_OSStatus(status);
23548 }
23549
23550 enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
23551 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
23552 if (status != noErr) {
23553 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23554 return ma_result_from_OSStatus(status);
23555 }
23556
23557
23558 /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
23559 #if defined(MA_APPLE_DESKTOP)
23560 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));
23561 if (status != noErr) {
23562 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23563 return ma_result_from_OSStatus(result);
23564 }
23565 #endif
23566
23567 /*
23568 Format. This is the hardest part of initialization because there's a few variables to take into account.
23569 1) The format must be supported by the device.
23570 2) The format must be supported miniaudio.
23571 3) There's a priority that miniaudio prefers.
23572
23573 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
23574 most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
23575 for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
23576
23577 On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
23578 */
23579 {
23580 AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
23581 AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
23582
23583 #if defined(MA_APPLE_DESKTOP)
23584 AudioStreamBasicDescription origFormat;
23585 UInt32 origFormatSize;
23586
23587 result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, pData->usingDefaultFormat, pData->usingDefaultChannels, pData->usingDefaultSampleRate, &bestFormat);
23588 if (result != MA_SUCCESS) {
23589 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23590 return result;
23591 }
23592
23593 /* From what I can see, Apple's documentation implies that we should keep the sample rate consistent. */
23594 origFormatSize = sizeof(origFormat);
23595 if (deviceType == ma_device_type_playback) {
23596 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
23597 } else {
23598 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
23599 }
23600
23601 if (status != noErr) {
23602 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23603 return result;
23604 }
23605
23606 bestFormat.mSampleRate = origFormat.mSampleRate;
23607
23608 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
23609 if (status != noErr) {
23610 /* We failed to set the format, so fall back to the current format of the audio unit. */
23611 bestFormat = origFormat;
23612 }
23613 #else
23614 UInt32 propSize = sizeof(bestFormat);
23615 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
23616 if (status != noErr) {
23617 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23618 return ma_result_from_OSStatus(status);
23619 }
23620
23621 /*
23622 Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
23623 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
23624 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
23625 can tell, it looks like the sample rate is shared between playback and capture for everything.
23626 */
23627 @autoreleasepool {
23628 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
23629 MA_ASSERT(pAudioSession != NULL);
23630
23631 [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
23632 bestFormat.mSampleRate = pAudioSession.sampleRate;
23633
23634 /*
23635 I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
23636 AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.
23637 */
23638 if (deviceType == ma_device_type_playback) {
23639 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
23640 }
23641 if (deviceType == ma_device_type_capture) {
23642 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
23643 }
23644 }
23645
23646 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
23647 if (status != noErr) {
23648 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23649 return ma_result_from_OSStatus(status);
23650 }
23651 #endif
23652
23653 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
23654 if (result != MA_SUCCESS) {
23655 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23656 return result;
23657 }
23658
23659 if (pData->formatOut == ma_format_unknown) {
23660 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23661 return MA_FORMAT_NOT_SUPPORTED;
23662 }
23663
23664 pData->channelsOut = bestFormat.mChannelsPerFrame;
23665 pData->sampleRateOut = bestFormat.mSampleRate;
23666 }
23667
23668 /*
23669 Internal channel map. This is weird in my testing. If I use the AudioObject to get the
23670 channel map, the channel descriptions are set to "Unknown" for some reason. To work around
23671 this it looks like retrieving it from the AudioUnit will work. However, and this is where
23672 it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
23673 I'm going to fall back to a default assumption in these cases.
23674 */
23675 #if defined(MA_APPLE_DESKTOP)
23676 result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut);
23677 if (result != MA_SUCCESS) {
23678 #if 0
23679 /* Try falling back to the channel map from the AudioObject. */
23680 result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut);
23681 if (result != MA_SUCCESS) {
23682 return result;
23683 }
23684 #else
23685 /* Fall back to default assumptions. */
23686 ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
23687 #endif
23688 }
23689 #else
23690 /* TODO: Figure out how to get the channel map using AVAudioSession. */
23691 ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
23692 #endif
23693
23694
23695 /* Buffer size. Not allowing this to be configurable on iOS. */
23696 actualPeriodSizeInFrames = pData->periodSizeInFramesIn;
23697
23698 #if defined(MA_APPLE_DESKTOP)
23699 if (actualPeriodSizeInFrames == 0) {
23700 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);
23701 }
23702
23703 result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);
23704 if (result != MA_SUCCESS) {
23705 return result;
23706 }
23707
23708 pData->periodSizeInFramesOut = actualPeriodSizeInFrames;
23709 #else
23710 actualPeriodSizeInFrames = 2048;
23711 pData->periodSizeInFramesOut = actualPeriodSizeInFrames;
23712 #endif
23713
23714
23715 /*
23716 During testing I discovered that the buffer size can be too big. You'll get an error like this:
23717
23718 kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
23719
23720 Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
23721 of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
23722 */
23723 {
23724 /*AudioUnitScope propScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
23725 AudioUnitElement propBus = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
23726
23727 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, propScope, propBus, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames));
23728 if (status != noErr) {
23729 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23730 return ma_result_from_OSStatus(status);
23731 }*/
23732
23733 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));
23734 if (status != noErr) {
23735 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23736 return ma_result_from_OSStatus(status);
23737 }
23738 }
23739
23740 /* We need a buffer list if this is an input device. We render into this in the input callback. */
23741 if (deviceType == ma_device_type_capture) {
23742 ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
23743 size_t allocationSize;
23744 AudioBufferList* pBufferList;
23745
23746 allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
23747 if (isInterleaved) {
23748 /* Interleaved case. This is the simple case because we just have one buffer. */
23749 allocationSize += sizeof(AudioBuffer) * 1;
23750 allocationSize += actualPeriodSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut);
23751 } else {
23752 /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
23753 allocationSize += sizeof(AudioBuffer) * pData->channelsOut;
23754 allocationSize += actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * pData->channelsOut;
23755 }
23756
23757 pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(allocationSize, &pContext->allocationCallbacks);
23758 if (pBufferList == NULL) {
23759 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23760 return MA_OUT_OF_MEMORY;
23761 }
23762
23763 if (isInterleaved) {
23764 pBufferList->mNumberBuffers = 1;
23765 pBufferList->mBuffers[0].mNumberChannels = pData->channelsOut;
23766 pBufferList->mBuffers[0].mDataByteSize = actualPeriodSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut);
23767 pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
23768 } else {
23769 ma_uint32 iBuffer;
23770 pBufferList->mNumberBuffers = pData->channelsOut;
23771 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
23772 pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
23773 pBufferList->mBuffers[iBuffer].mDataByteSize = actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->formatOut);
23774 pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * pData->channelsOut)) + (actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * iBuffer);
23775 }
23776 }
23777
23778 pData->pAudioBufferList = pBufferList;
23779 }
23780
23781 /* Callbacks. */
23782 callbackInfo.inputProcRefCon = pDevice_DoNotReference;
23783 if (deviceType == ma_device_type_playback) {
23784 callbackInfo.inputProc = ma_on_output__coreaudio;
23785 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, MA_COREAUDIO_OUTPUT_BUS, &callbackInfo, sizeof(callbackInfo));
23786 if (status != noErr) {
23787 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23788 return ma_result_from_OSStatus(status);
23789 }
23790 } else {
23791 callbackInfo.inputProc = ma_on_input__coreaudio;
23792 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, MA_COREAUDIO_INPUT_BUS, &callbackInfo, sizeof(callbackInfo));
23793 if (status != noErr) {
23794 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23795 return ma_result_from_OSStatus(status);
23796 }
23797 }
23798
23799 /* We need to listen for stop events. */
23800 if (pData->registerStopEvent) {
23801 status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
23802 if (status != noErr) {
23803 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23804 return ma_result_from_OSStatus(status);
23805 }
23806 }
23807
23808 /* Initialize the audio unit. */
23809 status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
23810 if (status != noErr) {
23811 ma__free_from_callbacks(pData->pAudioBufferList, &pContext->allocationCallbacks);
23812 pData->pAudioBufferList = NULL;
23813 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
23814 return ma_result_from_OSStatus(status);
23815 }
23816
23817 /* Grab the name. */
23818 #if defined(MA_APPLE_DESKTOP)
23819 ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
23820 #else
23821 if (deviceType == ma_device_type_playback) {
23822 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
23823 } else {
23824 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
23825 }
23826 #endif
23827
23828 return result;
23829 }
23830
23831 #if defined(MA_APPLE_DESKTOP)
23832 static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
23833 {
23834 ma_device_init_internal_data__coreaudio data;
23835 ma_result result;
23836
23837 /* This should only be called for playback or capture, not duplex. */
23838 if (deviceType == ma_device_type_duplex) {
23839 return MA_INVALID_ARGS;
23840 }
23841
23842 if (deviceType == ma_device_type_capture) {
23843 data.formatIn = pDevice->capture.format;
23844 data.channelsIn = pDevice->capture.channels;
23845 data.sampleRateIn = pDevice->sampleRate;
23846 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
23847 data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
23848 data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
23849 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
23850 data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
23851 data.shareMode = pDevice->capture.shareMode;
23852 data.registerStopEvent = MA_TRUE;
23853
23854 if (disposePreviousAudioUnit) {
23855 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
23856 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
23857 }
23858 if (pDevice->coreaudio.pAudioBufferList) {
23859 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
23860 }
23861 } else if (deviceType == ma_device_type_playback) {
23862 data.formatIn = pDevice->playback.format;
23863 data.channelsIn = pDevice->playback.channels;
23864 data.sampleRateIn = pDevice->sampleRate;
23865 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
23866 data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
23867 data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
23868 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
23869 data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
23870 data.shareMode = pDevice->playback.shareMode;
23871 data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
23872
23873 if (disposePreviousAudioUnit) {
23874 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
23875 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
23876 }
23877 }
23878 data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames;
23879 data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds;
23880 data.periodsIn = pDevice->coreaudio.originalPeriods;
23881
23882 /* Need at least 3 periods for duplex. */
23883 if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {
23884 data.periodsIn = 3;
23885 }
23886
23887 result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
23888 if (result != MA_SUCCESS) {
23889 return result;
23890 }
23891
23892 if (deviceType == ma_device_type_capture) {
23893 #if defined(MA_APPLE_DESKTOP)
23894 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
23895 #endif
23896 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
23897 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
23898
23899 pDevice->capture.internalFormat = data.formatOut;
23900 pDevice->capture.internalChannels = data.channelsOut;
23901 pDevice->capture.internalSampleRate = data.sampleRateOut;
23902 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
23903 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
23904 pDevice->capture.internalPeriods = data.periodsOut;
23905 } else if (deviceType == ma_device_type_playback) {
23906 #if defined(MA_APPLE_DESKTOP)
23907 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
23908 #endif
23909 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
23910
23911 pDevice->playback.internalFormat = data.formatOut;
23912 pDevice->playback.internalChannels = data.channelsOut;
23913 pDevice->playback.internalSampleRate = data.sampleRateOut;
23914 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
23915 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
23916 pDevice->playback.internalPeriods = data.periodsOut;
23917 }
23918
23919 return MA_SUCCESS;
23920 }
23921 #endif /* MA_APPLE_DESKTOP */
23922
23923 static ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
23924 {
23925 ma_result result;
23926
23927 MA_ASSERT(pContext != NULL);
23928 MA_ASSERT(pConfig != NULL);
23929 MA_ASSERT(pDevice != NULL);
23930
23931 if (pConfig->deviceType == ma_device_type_loopback) {
23932 return MA_DEVICE_TYPE_NOT_SUPPORTED;
23933 }
23934
23935 /* No exclusive mode with the Core Audio backend for now. */
23936 if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive) ||
23937 ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive)) {
23938 return MA_SHARE_MODE_NOT_SUPPORTED;
23939 }
23940
23941 /* Capture needs to be initialized first. */
23942 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
23943 ma_device_init_internal_data__coreaudio data;
23944 data.formatIn = pConfig->capture.format;
23945 data.channelsIn = pConfig->capture.channels;
23946 data.sampleRateIn = pConfig->sampleRate;
23947 MA_COPY_MEMORY(data.channelMapIn, pConfig->capture.channelMap, sizeof(pConfig->capture.channelMap));
23948 data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
23949 data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
23950 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
23951 data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
23952 data.shareMode = pConfig->capture.shareMode;
23953 data.periodSizeInFramesIn = pConfig->periodSizeInFrames;
23954 data.periodSizeInMillisecondsIn = pConfig->periodSizeInMilliseconds;
23955 data.periodsIn = pConfig->periods;
23956 data.registerStopEvent = MA_TRUE;
23957
23958 /* Need at least 3 periods for duplex. */
23959 if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
23960 data.periodsIn = 3;
23961 }
23962
23963 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pConfig->capture.pDeviceID, &data, (void*)pDevice);
23964 if (result != MA_SUCCESS) {
23965 return result;
23966 }
23967
23968 pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
23969 #if defined(MA_APPLE_DESKTOP)
23970 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
23971 #endif
23972 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
23973 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
23974
23975 pDevice->capture.internalFormat = data.formatOut;
23976 pDevice->capture.internalChannels = data.channelsOut;
23977 pDevice->capture.internalSampleRate = data.sampleRateOut;
23978 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
23979 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
23980 pDevice->capture.internalPeriods = data.periodsOut;
23981
23982 #if defined(MA_APPLE_DESKTOP)
23983 /*
23984 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
23985 switch the device in the background.
23986 */
23987 if (pConfig->capture.pDeviceID == NULL) {
23988 ma_device__track__coreaudio(pDevice);
23989 }
23990 #endif
23991 }
23992
23993 /* Playback. */
23994 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
23995 ma_device_init_internal_data__coreaudio data;
23996 data.formatIn = pConfig->playback.format;
23997 data.channelsIn = pConfig->playback.channels;
23998 data.sampleRateIn = pConfig->sampleRate;
23999 MA_COPY_MEMORY(data.channelMapIn, pConfig->playback.channelMap, sizeof(pConfig->playback.channelMap));
24000 data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
24001 data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
24002 data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
24003 data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
24004 data.shareMode = pConfig->playback.shareMode;
24005
24006 /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
24007 if (pConfig->deviceType == ma_device_type_duplex) {
24008 data.periodSizeInFramesIn = pDevice->capture.internalPeriodSizeInFrames;
24009 data.periodsIn = pDevice->capture.internalPeriods;
24010 data.registerStopEvent = MA_FALSE;
24011 } else {
24012 data.periodSizeInFramesIn = pConfig->periodSizeInFrames;
24013 data.periodSizeInMillisecondsIn = pConfig->periodSizeInMilliseconds;
24014 data.periodsIn = pConfig->periods;
24015 data.registerStopEvent = MA_TRUE;
24016 }
24017
24018 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pConfig->playback.pDeviceID, &data, (void*)pDevice);
24019 if (result != MA_SUCCESS) {
24020 if (pConfig->deviceType == ma_device_type_duplex) {
24021 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
24022 if (pDevice->coreaudio.pAudioBufferList) {
24023 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
24024 }
24025 }
24026 return result;
24027 }
24028
24029 pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
24030 #if defined(MA_APPLE_DESKTOP)
24031 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
24032 #endif
24033 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
24034
24035 pDevice->playback.internalFormat = data.formatOut;
24036 pDevice->playback.internalChannels = data.channelsOut;
24037 pDevice->playback.internalSampleRate = data.sampleRateOut;
24038 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
24039 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
24040 pDevice->playback.internalPeriods = data.periodsOut;
24041
24042 #if defined(MA_APPLE_DESKTOP)
24043 /*
24044 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
24045 switch the device in the background.
24046 */
24047 if (pConfig->playback.pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pConfig->capture.pDeviceID != NULL)) {
24048 ma_device__track__coreaudio(pDevice);
24049 }
24050 #endif
24051 }
24052
24053 pDevice->coreaudio.originalPeriodSizeInFrames = pConfig->periodSizeInFrames;
24054 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
24055 pDevice->coreaudio.originalPeriods = pConfig->periods;
24056
24057 /*
24058 When stopping the device, a callback is called on another thread. We need to wait for this callback
24059 before returning from ma_device_stop(). This event is used for this.
24060 */
24061 ma_event_init(pContext, &pDevice->coreaudio.stopEvent);
24062
24063 /* Need a ring buffer for duplex mode. */
24064 if (pConfig->deviceType == ma_device_type_duplex) {
24065 ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods);
24066 ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->coreaudio.duplexRB);
24067 if (result != MA_SUCCESS) {
24068 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[Core Audio] Failed to initialize ring buffer.", result);
24069 }
24070
24071 /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
24072 {
24073 ma_uint32 bufferSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
24074 void* pBufferData;
24075 ma_pcm_rb_acquire_write(&pDevice->coreaudio.duplexRB, &bufferSizeInFrames, &pBufferData);
24076 {
24077 MA_ZERO_MEMORY(pBufferData, bufferSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
24078 }
24079 ma_pcm_rb_commit_write(&pDevice->coreaudio.duplexRB, bufferSizeInFrames, pBufferData);
24080 }
24081 }
24082
24083 /*
24084 We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done
24085 differently on non-Desktop Apple platforms.
24086 */
24087 #if defined(MA_APPLE_MOBILE)
24088 pDevice->coreaudio.pRouteChangeHandler = (__bridge_retained void*)[[ma_router_change_handler alloc] init:pDevice];
24089 #endif
24090
24091 return MA_SUCCESS;
24092 }
24093
24094
24095 static ma_result ma_device_start__coreaudio(ma_device* pDevice)
24096 {
24097 MA_ASSERT(pDevice != NULL);
24098
24099 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24100 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
24101 if (status != noErr) {
24102 return ma_result_from_OSStatus(status);
24103 }
24104 }
24105
24106 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24107 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
24108 if (status != noErr) {
24109 if (pDevice->type == ma_device_type_duplex) {
24110 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
24111 }
24112 return ma_result_from_OSStatus(status);
24113 }
24114 }
24115
24116 return MA_SUCCESS;
24117 }
24118
24119 static ma_result ma_device_stop__coreaudio(ma_device* pDevice)
24120 {
24121 MA_ASSERT(pDevice != NULL);
24122
24123 /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */
24124
24125 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24126 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
24127 if (status != noErr) {
24128 return ma_result_from_OSStatus(status);
24129 }
24130 }
24131
24132 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24133 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
24134 if (status != noErr) {
24135 return ma_result_from_OSStatus(status);
24136 }
24137 }
24138
24139 /* We need to wait for the callback to finish before returning. */
24140 ma_event_wait(&pDevice->coreaudio.stopEvent);
24141 return MA_SUCCESS;
24142 }
24143
24144
24145 static ma_result ma_context_uninit__coreaudio(ma_context* pContext)
24146 {
24147 MA_ASSERT(pContext != NULL);
24148 MA_ASSERT(pContext->backend == ma_backend_coreaudio);
24149
24150 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
24151 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
24152 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
24153 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
24154 #endif
24155
24156 (void)pContext;
24157 return MA_SUCCESS;
24158 }
24159
24160 #if defined(MA_APPLE_MOBILE)
24161 static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category)
24162 {
24163 /* The "default" and "none" categories are treated different and should not be used as an input into this function. */
24164 MA_ASSERT(category != ma_ios_session_category_default);
24165 MA_ASSERT(category != ma_ios_session_category_none);
24166
24167 switch (category) {
24168 case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient;
24169 case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient;
24170 case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback;
24171 case ma_ios_session_category_record: return AVAudioSessionCategoryRecord;
24172 case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord;
24173 case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute;
24174 case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient;
24175 case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient;
24176 default: return AVAudioSessionCategoryAmbient;
24177 }
24178 }
24179 #endif
24180
24181 static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma_context* pContext)
24182 {
24183 MA_ASSERT(pConfig != NULL);
24184 MA_ASSERT(pContext != NULL);
24185
24186 #if defined(MA_APPLE_MOBILE)
24187 @autoreleasepool {
24188 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
24189 AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions;
24190
24191 MA_ASSERT(pAudioSession != NULL);
24192
24193 if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) {
24194 /*
24195 I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails
24196 we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category.
24197 */
24198 #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)
24199 options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
24200 #endif
24201
24202 if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {
24203 /* Using PlayAndRecord */
24204 } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {
24205 /* Using Playback */
24206 } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {
24207 /* Using Record */
24208 } else {
24209 /* Leave as default? */
24210 }
24211 } else {
24212 if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) {
24213 if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {
24214 return MA_INVALID_OPERATION; /* Failed to set session category. */
24215 }
24216 }
24217 }
24218 }
24219 #endif
24220
24221 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
24222 pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation");
24223 if (pContext->coreaudio.hCoreFoundation == NULL) {
24224 return MA_API_NOT_FOUND;
24225 }
24226
24227 pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
24228 pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease");
24229
24230
24231 pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio");
24232 if (pContext->coreaudio.hCoreAudio == NULL) {
24233 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
24234 return MA_API_NOT_FOUND;
24235 }
24236
24237 pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
24238 pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
24239 pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
24240 pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
24241 pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");
24242
24243 /*
24244 It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
24245 defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
24246 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
24247 AudioToolbox.
24248 */
24249 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit");
24250 if (pContext->coreaudio.hAudioUnit == NULL) {
24251 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
24252 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
24253 return MA_API_NOT_FOUND;
24254 }
24255
24256 if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
24257 /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
24258 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
24259 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox");
24260 if (pContext->coreaudio.hAudioUnit == NULL) {
24261 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
24262 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
24263 return MA_API_NOT_FOUND;
24264 }
24265 }
24266
24267 pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
24268 pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
24269 pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
24270 pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
24271 pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
24272 pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
24273 pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
24274 pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
24275 pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
24276 pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
24277 pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender");
24278 #else
24279 pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
24280 pContext->coreaudio.CFRelease = (ma_proc)CFRelease;
24281
24282 #if defined(MA_APPLE_DESKTOP)
24283 pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
24284 pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
24285 pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
24286 pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
24287 pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
24288 #endif
24289
24290 pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
24291 pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
24292 pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
24293 pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
24294 pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
24295 pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
24296 pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
24297 pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
24298 pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
24299 pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
24300 pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
24301 #endif
24302
24303 pContext->isBackendAsynchronous = MA_TRUE;
24304
24305 pContext->onUninit = ma_context_uninit__coreaudio;
24306 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__coreaudio;
24307 pContext->onEnumDevices = ma_context_enumerate_devices__coreaudio;
24308 pContext->onGetDeviceInfo = ma_context_get_device_info__coreaudio;
24309 pContext->onDeviceInit = ma_device_init__coreaudio;
24310 pContext->onDeviceUninit = ma_device_uninit__coreaudio;
24311 pContext->onDeviceStart = ma_device_start__coreaudio;
24312 pContext->onDeviceStop = ma_device_stop__coreaudio;
24313
24314 /* Audio component. */
24315 {
24316 AudioComponentDescription desc;
24317 desc.componentType = kAudioUnitType_Output;
24318 #if defined(MA_APPLE_DESKTOP)
24319 desc.componentSubType = kAudioUnitSubType_HALOutput;
24320 #else
24321 desc.componentSubType = kAudioUnitSubType_RemoteIO;
24322 #endif
24323 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
24324 desc.componentFlags = 0;
24325 desc.componentFlagsMask = 0;
24326
24327 pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
24328 if (pContext->coreaudio.component == NULL) {
24329 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
24330 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
24331 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
24332 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
24333 #endif
24334 return MA_FAILED_TO_INIT_BACKEND;
24335 }
24336 }
24337
24338 return MA_SUCCESS;
24339 }
24340 #endif /* Core Audio */
24341
24342
24343
24344 /******************************************************************************
24345
24346 sndio Backend
24347
24348 ******************************************************************************/
24349 #ifdef MA_HAS_SNDIO
24350 #include <fcntl.h>
24351 #include <sys/stat.h>
24352
24353 /*
24354 Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
24355 to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device
24356 just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
24357 demand for it or if I can get it tested and debugged more thoroughly.
24358 */
24359 #if 0
24360 #if defined(__NetBSD__) || defined(__OpenBSD__)
24361 #include <sys/audioio.h>
24362 #endif
24363 #if defined(__FreeBSD__) || defined(__DragonFly__)
24364 #include <sys/soundcard.h>
24365 #endif
24366 #endif
24367
24368 #define MA_SIO_DEVANY "default"
24369 #define MA_SIO_PLAY 1
24370 #define MA_SIO_REC 2
24371 #define MA_SIO_NENC 8
24372 #define MA_SIO_NCHAN 8
24373 #define MA_SIO_NRATE 16
24374 #define MA_SIO_NCONF 4
24375
24376 struct ma_sio_hdl; /* <-- Opaque */
24377
24378 struct ma_sio_par
24379 {
24380 unsigned int bits;
24381 unsigned int bps;
24382 unsigned int sig;
24383 unsigned int le;
24384 unsigned int msb;
24385 unsigned int rchan;
24386 unsigned int pchan;
24387 unsigned int rate;
24388 unsigned int bufsz;
24389 unsigned int xrun;
24390 unsigned int round;
24391 unsigned int appbufsz;
24392 int __pad[3];
24393 unsigned int __magic;
24394 };
24395
24396 struct ma_sio_enc
24397 {
24398 unsigned int bits;
24399 unsigned int bps;
24400 unsigned int sig;
24401 unsigned int le;
24402 unsigned int msb;
24403 };
24404
24405 struct ma_sio_conf
24406 {
24407 unsigned int enc;
24408 unsigned int rchan;
24409 unsigned int pchan;
24410 unsigned int rate;
24411 };
24412
24413 struct ma_sio_cap
24414 {
24415 struct ma_sio_enc enc[MA_SIO_NENC];
24416 unsigned int rchan[MA_SIO_NCHAN];
24417 unsigned int pchan[MA_SIO_NCHAN];
24418 unsigned int rate[MA_SIO_NRATE];
24419 int __pad[7];
24420 unsigned int nconf;
24421 struct ma_sio_conf confs[MA_SIO_NCONF];
24422 };
24423
24424 typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
24425 typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
24426 typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
24427 typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
24428 typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
24429 typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
24430 typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
24431 typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
24432 typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
24433 typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
24434
24435 static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */
24436 {
24437 ma_uint32 i;
24438 for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
24439 if (g_maStandardSampleRatePriorities[i] == sampleRate) {
24440 return i;
24441 }
24442 }
24443
24444 return (ma_uint32)-1;
24445 }
24446
24447 static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
24448 {
24449 /* We only support native-endian right now. */
24450 if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
24451 return ma_format_unknown;
24452 }
24453
24454 if (bits == 8 && bps == 1 && sig == 0) {
24455 return ma_format_u8;
24456 }
24457 if (bits == 16 && bps == 2 && sig == 1) {
24458 return ma_format_s16;
24459 }
24460 if (bits == 24 && bps == 3 && sig == 1) {
24461 return ma_format_s24;
24462 }
24463 if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
24464 /*return ma_format_s24_32;*/
24465 }
24466 if (bits == 32 && bps == 4 && sig == 1) {
24467 return ma_format_s32;
24468 }
24469
24470 return ma_format_unknown;
24471 }
24472
24473 static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
24474 {
24475 ma_format bestFormat;
24476 unsigned int iConfig;
24477
24478 MA_ASSERT(caps != NULL);
24479
24480 bestFormat = ma_format_unknown;
24481 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
24482 unsigned int iEncoding;
24483 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
24484 unsigned int bits;
24485 unsigned int bps;
24486 unsigned int sig;
24487 unsigned int le;
24488 unsigned int msb;
24489 ma_format format;
24490
24491 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
24492 continue;
24493 }
24494
24495 bits = caps->enc[iEncoding].bits;
24496 bps = caps->enc[iEncoding].bps;
24497 sig = caps->enc[iEncoding].sig;
24498 le = caps->enc[iEncoding].le;
24499 msb = caps->enc[iEncoding].msb;
24500 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
24501 if (format == ma_format_unknown) {
24502 continue; /* Format not supported. */
24503 }
24504
24505 if (bestFormat == ma_format_unknown) {
24506 bestFormat = format;
24507 } else {
24508 if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */
24509 bestFormat = format;
24510 }
24511 }
24512 }
24513 }
24514
24515 return bestFormat;
24516 }
24517
24518 static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
24519 {
24520 ma_uint32 maxChannels;
24521 unsigned int iConfig;
24522
24523 MA_ASSERT(caps != NULL);
24524 MA_ASSERT(requiredFormat != ma_format_unknown);
24525
24526 /* Just pick whatever configuration has the most channels. */
24527 maxChannels = 0;
24528 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
24529 /* The encoding should be of requiredFormat. */
24530 unsigned int iEncoding;
24531 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
24532 unsigned int iChannel;
24533 unsigned int bits;
24534 unsigned int bps;
24535 unsigned int sig;
24536 unsigned int le;
24537 unsigned int msb;
24538 ma_format format;
24539
24540 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
24541 continue;
24542 }
24543
24544 bits = caps->enc[iEncoding].bits;
24545 bps = caps->enc[iEncoding].bps;
24546 sig = caps->enc[iEncoding].sig;
24547 le = caps->enc[iEncoding].le;
24548 msb = caps->enc[iEncoding].msb;
24549 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
24550 if (format != requiredFormat) {
24551 continue;
24552 }
24553
24554 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
24555 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
24556 unsigned int chan = 0;
24557 unsigned int channels;
24558
24559 if (deviceType == ma_device_type_playback) {
24560 chan = caps->confs[iConfig].pchan;
24561 } else {
24562 chan = caps->confs[iConfig].rchan;
24563 }
24564
24565 if ((chan & (1UL << iChannel)) == 0) {
24566 continue;
24567 }
24568
24569 if (deviceType == ma_device_type_playback) {
24570 channels = caps->pchan[iChannel];
24571 } else {
24572 channels = caps->rchan[iChannel];
24573 }
24574
24575 if (maxChannels < channels) {
24576 maxChannels = channels;
24577 }
24578 }
24579 }
24580 }
24581
24582 return maxChannels;
24583 }
24584
24585 static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels)
24586 {
24587 ma_uint32 firstSampleRate;
24588 ma_uint32 bestSampleRate;
24589 unsigned int iConfig;
24590
24591 MA_ASSERT(caps != NULL);
24592 MA_ASSERT(requiredFormat != ma_format_unknown);
24593 MA_ASSERT(requiredChannels > 0);
24594 MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS);
24595
24596 firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */
24597 bestSampleRate = 0;
24598
24599 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
24600 /* The encoding should be of requiredFormat. */
24601 unsigned int iEncoding;
24602 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
24603 unsigned int iChannel;
24604 unsigned int bits;
24605 unsigned int bps;
24606 unsigned int sig;
24607 unsigned int le;
24608 unsigned int msb;
24609 ma_format format;
24610
24611 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
24612 continue;
24613 }
24614
24615 bits = caps->enc[iEncoding].bits;
24616 bps = caps->enc[iEncoding].bps;
24617 sig = caps->enc[iEncoding].sig;
24618 le = caps->enc[iEncoding].le;
24619 msb = caps->enc[iEncoding].msb;
24620 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
24621 if (format != requiredFormat) {
24622 continue;
24623 }
24624
24625 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
24626 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
24627 unsigned int chan = 0;
24628 unsigned int channels;
24629 unsigned int iRate;
24630
24631 if (deviceType == ma_device_type_playback) {
24632 chan = caps->confs[iConfig].pchan;
24633 } else {
24634 chan = caps->confs[iConfig].rchan;
24635 }
24636
24637 if ((chan & (1UL << iChannel)) == 0) {
24638 continue;
24639 }
24640
24641 if (deviceType == ma_device_type_playback) {
24642 channels = caps->pchan[iChannel];
24643 } else {
24644 channels = caps->rchan[iChannel];
24645 }
24646
24647 if (channels != requiredChannels) {
24648 continue;
24649 }
24650
24651 /* Getting here means we have found a compatible encoding/channel pair. */
24652 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
24653 ma_uint32 rate = (ma_uint32)caps->rate[iRate];
24654 ma_uint32 ratePriority;
24655
24656 if (firstSampleRate == 0) {
24657 firstSampleRate = rate;
24658 }
24659
24660 /* Disregard this rate if it's not a standard one. */
24661 ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate);
24662 if (ratePriority == (ma_uint32)-1) {
24663 continue;
24664 }
24665
24666 if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */
24667 bestSampleRate = rate;
24668 }
24669 }
24670 }
24671 }
24672 }
24673
24674 /* If a standard sample rate was not found just fall back to the first one that was iterated. */
24675 if (bestSampleRate == 0) {
24676 bestSampleRate = firstSampleRate;
24677 }
24678
24679 return bestSampleRate;
24680 }
24681
24682
24683 static ma_bool32 ma_context_is_device_id_equal__sndio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
24684 {
24685 MA_ASSERT(pContext != NULL);
24686 MA_ASSERT(pID0 != NULL);
24687 MA_ASSERT(pID1 != NULL);
24688 (void)pContext;
24689
24690 return ma_strcmp(pID0->sndio, pID1->sndio) == 0;
24691 }
24692
24693 static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
24694 {
24695 ma_bool32 isTerminating = MA_FALSE;
24696 struct ma_sio_hdl* handle;
24697
24698 MA_ASSERT(pContext != NULL);
24699 MA_ASSERT(callback != NULL);
24700
24701 /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */
24702
24703 /* Playback. */
24704 if (!isTerminating) {
24705 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
24706 if (handle != NULL) {
24707 /* Supports playback. */
24708 ma_device_info deviceInfo;
24709 MA_ZERO_OBJECT(&deviceInfo);
24710 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
24711 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
24712
24713 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
24714
24715 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
24716 }
24717 }
24718
24719 /* Capture. */
24720 if (!isTerminating) {
24721 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
24722 if (handle != NULL) {
24723 /* Supports capture. */
24724 ma_device_info deviceInfo;
24725 MA_ZERO_OBJECT(&deviceInfo);
24726 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
24727 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
24728
24729 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
24730
24731 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
24732 }
24733 }
24734
24735 return MA_SUCCESS;
24736 }
24737
24738 static 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)
24739 {
24740 char devid[256];
24741 struct ma_sio_hdl* handle;
24742 struct ma_sio_cap caps;
24743 unsigned int iConfig;
24744
24745 MA_ASSERT(pContext != NULL);
24746 (void)shareMode;
24747
24748 /* We need to open the device before we can get information about it. */
24749 if (pDeviceID == NULL) {
24750 ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
24751 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
24752 } else {
24753 ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
24754 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
24755 }
24756
24757 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
24758 if (handle == NULL) {
24759 return MA_NO_DEVICE;
24760 }
24761
24762 if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
24763 return MA_ERROR;
24764 }
24765
24766 for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
24767 /*
24768 The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give
24769 preference to some formats over others.
24770 */
24771 unsigned int iEncoding;
24772 unsigned int iChannel;
24773 unsigned int iRate;
24774
24775 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
24776 unsigned int bits;
24777 unsigned int bps;
24778 unsigned int sig;
24779 unsigned int le;
24780 unsigned int msb;
24781 ma_format format;
24782 ma_bool32 formatExists = MA_FALSE;
24783 ma_uint32 iExistingFormat;
24784
24785 if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
24786 continue;
24787 }
24788
24789 bits = caps.enc[iEncoding].bits;
24790 bps = caps.enc[iEncoding].bps;
24791 sig = caps.enc[iEncoding].sig;
24792 le = caps.enc[iEncoding].le;
24793 msb = caps.enc[iEncoding].msb;
24794 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
24795 if (format == ma_format_unknown) {
24796 continue; /* Format not supported. */
24797 }
24798
24799 /* Add this format if it doesn't already exist. */
24800 for (iExistingFormat = 0; iExistingFormat < pDeviceInfo->formatCount; iExistingFormat += 1) {
24801 if (pDeviceInfo->formats[iExistingFormat] == format) {
24802 formatExists = MA_TRUE;
24803 break;
24804 }
24805 }
24806
24807 if (!formatExists) {
24808 pDeviceInfo->formats[pDeviceInfo->formatCount++] = format;
24809 }
24810 }
24811
24812 /* Channels. */
24813 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
24814 unsigned int chan = 0;
24815 unsigned int channels;
24816
24817 if (deviceType == ma_device_type_playback) {
24818 chan = caps.confs[iConfig].pchan;
24819 } else {
24820 chan = caps.confs[iConfig].rchan;
24821 }
24822
24823 if ((chan & (1UL << iChannel)) == 0) {
24824 continue;
24825 }
24826
24827 if (deviceType == ma_device_type_playback) {
24828 channels = caps.pchan[iChannel];
24829 } else {
24830 channels = caps.rchan[iChannel];
24831 }
24832
24833 if (pDeviceInfo->minChannels > channels) {
24834 pDeviceInfo->minChannels = channels;
24835 }
24836 if (pDeviceInfo->maxChannels < channels) {
24837 pDeviceInfo->maxChannels = channels;
24838 }
24839 }
24840
24841 /* Sample rates. */
24842 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
24843 if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
24844 unsigned int rate = caps.rate[iRate];
24845 if (pDeviceInfo->minSampleRate > rate) {
24846 pDeviceInfo->minSampleRate = rate;
24847 }
24848 if (pDeviceInfo->maxSampleRate < rate) {
24849 pDeviceInfo->maxSampleRate = rate;
24850 }
24851 }
24852 }
24853 }
24854
24855 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
24856 return MA_SUCCESS;
24857 }
24858
24859 static void ma_device_uninit__sndio(ma_device* pDevice)
24860 {
24861 MA_ASSERT(pDevice != NULL);
24862
24863 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24864 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
24865 }
24866
24867 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24868 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
24869 }
24870 }
24871
24872 static ma_result ma_device_init_handle__sndio(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
24873 {
24874 const char* pDeviceName;
24875 ma_ptr handle;
24876 int openFlags = 0;
24877 struct ma_sio_cap caps;
24878 struct ma_sio_par par;
24879 ma_device_id* pDeviceID;
24880 ma_format format;
24881 ma_uint32 channels;
24882 ma_uint32 sampleRate;
24883 ma_format internalFormat;
24884 ma_uint32 internalChannels;
24885 ma_uint32 internalSampleRate;
24886 ma_uint32 internalPeriodSizeInFrames;
24887 ma_uint32 internalPeriods;
24888
24889 MA_ASSERT(pContext != NULL);
24890 MA_ASSERT(pConfig != NULL);
24891 MA_ASSERT(deviceType != ma_device_type_duplex);
24892 MA_ASSERT(pDevice != NULL);
24893
24894 if (deviceType == ma_device_type_capture) {
24895 openFlags = MA_SIO_REC;
24896 pDeviceID = pConfig->capture.pDeviceID;
24897 format = pConfig->capture.format;
24898 channels = pConfig->capture.channels;
24899 sampleRate = pConfig->sampleRate;
24900 } else {
24901 openFlags = MA_SIO_PLAY;
24902 pDeviceID = pConfig->playback.pDeviceID;
24903 format = pConfig->playback.format;
24904 channels = pConfig->playback.channels;
24905 sampleRate = pConfig->sampleRate;
24906 }
24907
24908 pDeviceName = MA_SIO_DEVANY;
24909 if (pDeviceID != NULL) {
24910 pDeviceName = pDeviceID->sndio;
24911 }
24912
24913 handle = (ma_ptr)((ma_sio_open_proc)pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
24914 if (handle == NULL) {
24915 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24916 }
24917
24918 /* We need to retrieve the device caps to determine the most appropriate format to use. */
24919 if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
24920 ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
24921 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.", MA_ERROR);
24922 }
24923
24924 /*
24925 Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
24926 way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
24927 to the requested channels, regardless of whether or not the default channel count is requested.
24928
24929 For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
24930 value returned by ma_find_best_channels_from_sio_cap__sndio().
24931 */
24932 if (deviceType == ma_device_type_capture) {
24933 if (pDevice->capture.usingDefaultFormat) {
24934 format = ma_find_best_format_from_sio_cap__sndio(&caps);
24935 }
24936 if (pDevice->capture.usingDefaultChannels) {
24937 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
24938 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
24939 }
24940 }
24941 } else {
24942 if (pDevice->playback.usingDefaultFormat) {
24943 format = ma_find_best_format_from_sio_cap__sndio(&caps);
24944 }
24945 if (pDevice->playback.usingDefaultChannels) {
24946 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
24947 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
24948 }
24949 }
24950 }
24951
24952 if (pDevice->usingDefaultSampleRate) {
24953 sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
24954 }
24955
24956
24957 ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
24958 par.msb = 0;
24959 par.le = ma_is_little_endian();
24960
24961 switch (format) {
24962 case ma_format_u8:
24963 {
24964 par.bits = 8;
24965 par.bps = 1;
24966 par.sig = 0;
24967 } break;
24968
24969 case ma_format_s24:
24970 {
24971 par.bits = 24;
24972 par.bps = 3;
24973 par.sig = 1;
24974 } break;
24975
24976 case ma_format_s32:
24977 {
24978 par.bits = 32;
24979 par.bps = 4;
24980 par.sig = 1;
24981 } break;
24982
24983 case ma_format_s16:
24984 case ma_format_f32:
24985 default:
24986 {
24987 par.bits = 16;
24988 par.bps = 2;
24989 par.sig = 1;
24990 } break;
24991 }
24992
24993 if (deviceType == ma_device_type_capture) {
24994 par.rchan = channels;
24995 } else {
24996 par.pchan = channels;
24997 }
24998
24999 par.rate = sampleRate;
25000
25001 internalPeriodSizeInFrames = pConfig->periodSizeInFrames;
25002 if (internalPeriodSizeInFrames == 0) {
25003 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, par.rate);
25004 }
25005
25006 par.round = internalPeriodSizeInFrames;
25007 par.appbufsz = par.round * pConfig->periods;
25008
25009 if (((ma_sio_setpar_proc)pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
25010 ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
25011 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MA_FORMAT_NOT_SUPPORTED);
25012 }
25013 if (((ma_sio_getpar_proc)pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
25014 ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
25015 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MA_FORMAT_NOT_SUPPORTED);
25016 }
25017
25018 internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
25019 internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
25020 internalSampleRate = par.rate;
25021 internalPeriods = par.appbufsz / par.round;
25022 internalPeriodSizeInFrames = par.round;
25023
25024 if (deviceType == ma_device_type_capture) {
25025 pDevice->sndio.handleCapture = handle;
25026 pDevice->capture.internalFormat = internalFormat;
25027 pDevice->capture.internalChannels = internalChannels;
25028 pDevice->capture.internalSampleRate = internalSampleRate;
25029 ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
25030 pDevice->capture.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
25031 pDevice->capture.internalPeriods = internalPeriods;
25032 } else {
25033 pDevice->sndio.handlePlayback = handle;
25034 pDevice->playback.internalFormat = internalFormat;
25035 pDevice->playback.internalChannels = internalChannels;
25036 pDevice->playback.internalSampleRate = internalSampleRate;
25037 ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
25038 pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
25039 pDevice->playback.internalPeriods = internalPeriods;
25040 }
25041
25042 #ifdef MA_DEBUG_OUTPUT
25043 printf("DEVICE INFO\n");
25044 printf(" Format: %s\n", ma_get_format_name(internalFormat));
25045 printf(" Channels: %d\n", internalChannels);
25046 printf(" Sample Rate: %d\n", internalSampleRate);
25047 printf(" Period Size: %d\n", internalPeriodSizeInFrames);
25048 printf(" Periods: %d\n", internalPeriods);
25049 printf(" appbufsz: %d\n", par.appbufsz);
25050 printf(" round: %d\n", par.round);
25051 #endif
25052
25053 return MA_SUCCESS;
25054 }
25055
25056 static ma_result ma_device_init__sndio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
25057 {
25058 MA_ASSERT(pDevice != NULL);
25059
25060 MA_ZERO_OBJECT(&pDevice->sndio);
25061
25062 if (pConfig->deviceType == ma_device_type_loopback) {
25063 return MA_DEVICE_TYPE_NOT_SUPPORTED;
25064 }
25065
25066 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
25067 ma_result result = ma_device_init_handle__sndio(pContext, pConfig, ma_device_type_capture, pDevice);
25068 if (result != MA_SUCCESS) {
25069 return result;
25070 }
25071 }
25072
25073 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
25074 ma_result result = ma_device_init_handle__sndio(pContext, pConfig, ma_device_type_playback, pDevice);
25075 if (result != MA_SUCCESS) {
25076 return result;
25077 }
25078 }
25079
25080 return MA_SUCCESS;
25081 }
25082
25083 static ma_result ma_device_stop__sndio(ma_device* pDevice)
25084 {
25085 MA_ASSERT(pDevice != NULL);
25086
25087 /*
25088 From the documentation:
25089
25090 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
25091 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
25092 buffer is drained. In no case are samples in the play buffer discarded.
25093
25094 Therefore, sio_stop() performs all of the necessary draining for us.
25095 */
25096
25097 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25098 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
25099 }
25100
25101 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25102 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
25103 }
25104
25105 return MA_SUCCESS;
25106 }
25107
25108 static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
25109 {
25110 int result;
25111
25112 if (pFramesWritten != NULL) {
25113 *pFramesWritten = 0;
25114 }
25115
25116 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));
25117 if (result == 0) {
25118 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.", MA_IO_ERROR);
25119 }
25120
25121 if (pFramesWritten != NULL) {
25122 *pFramesWritten = frameCount;
25123 }
25124
25125 return MA_SUCCESS;
25126 }
25127
25128 static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
25129 {
25130 int result;
25131
25132 if (pFramesRead != NULL) {
25133 *pFramesRead = 0;
25134 }
25135
25136 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));
25137 if (result == 0) {
25138 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device.", MA_IO_ERROR);
25139 }
25140
25141 if (pFramesRead != NULL) {
25142 *pFramesRead = frameCount;
25143 }
25144
25145 return MA_SUCCESS;
25146 }
25147
25148 static ma_result ma_device_main_loop__sndio(ma_device* pDevice)
25149 {
25150 ma_result result = MA_SUCCESS;
25151 ma_bool32 exitLoop = MA_FALSE;
25152
25153 /* Devices need to be started here. */
25154 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25155 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
25156 }
25157 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25158 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */
25159 }
25160
25161 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
25162 switch (pDevice->type)
25163 {
25164 case ma_device_type_duplex:
25165 {
25166 /* The process is: device_read -> convert -> callback -> convert -> device_write */
25167 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
25168 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
25169
25170 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
25171 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
25172 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
25173 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
25174 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
25175 ma_uint32 capturedDeviceFramesRemaining;
25176 ma_uint32 capturedDeviceFramesProcessed;
25177 ma_uint32 capturedDeviceFramesToProcess;
25178 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
25179 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
25180 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
25181 }
25182
25183 result = ma_device_read__sndio(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
25184 if (result != MA_SUCCESS) {
25185 exitLoop = MA_TRUE;
25186 break;
25187 }
25188
25189 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
25190 capturedDeviceFramesProcessed = 0;
25191
25192 for (;;) {
25193 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
25194 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
25195 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
25196 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
25197 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
25198 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
25199 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
25200
25201 /* Convert capture data from device format to client format. */
25202 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
25203 if (result != MA_SUCCESS) {
25204 break;
25205 }
25206
25207 /*
25208 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
25209 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
25210 */
25211 if (capturedClientFramesToProcessThisIteration == 0) {
25212 break;
25213 }
25214
25215 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
25216
25217 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
25218 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
25219
25220 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
25221 for (;;) {
25222 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
25223 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
25224 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
25225 if (result != MA_SUCCESS) {
25226 break;
25227 }
25228
25229 result = ma_device_write__sndio(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
25230 if (result != MA_SUCCESS) {
25231 exitLoop = MA_TRUE;
25232 break;
25233 }
25234
25235 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
25236 if (capturedClientFramesToProcessThisIteration == 0) {
25237 break;
25238 }
25239 }
25240
25241 /* In case an error happened from ma_device_write__sndio()... */
25242 if (result != MA_SUCCESS) {
25243 exitLoop = MA_TRUE;
25244 break;
25245 }
25246 }
25247
25248 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
25249 }
25250 } break;
25251
25252 case ma_device_type_capture:
25253 {
25254 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
25255 ma_uint8 intermediaryBuffer[8192];
25256 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
25257 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
25258 ma_uint32 framesReadThisPeriod = 0;
25259 while (framesReadThisPeriod < periodSizeInFrames) {
25260 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
25261 ma_uint32 framesProcessed;
25262 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
25263 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
25264 framesToReadThisIteration = intermediaryBufferSizeInFrames;
25265 }
25266
25267 result = ma_device_read__sndio(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
25268 if (result != MA_SUCCESS) {
25269 exitLoop = MA_TRUE;
25270 break;
25271 }
25272
25273 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
25274
25275 framesReadThisPeriod += framesProcessed;
25276 }
25277 } break;
25278
25279 case ma_device_type_playback:
25280 {
25281 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
25282 ma_uint8 intermediaryBuffer[8192];
25283 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
25284 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
25285 ma_uint32 framesWrittenThisPeriod = 0;
25286 while (framesWrittenThisPeriod < periodSizeInFrames) {
25287 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
25288 ma_uint32 framesProcessed;
25289 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
25290 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
25291 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
25292 }
25293
25294 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
25295
25296 result = ma_device_write__sndio(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
25297 if (result != MA_SUCCESS) {
25298 exitLoop = MA_TRUE;
25299 break;
25300 }
25301
25302 framesWrittenThisPeriod += framesProcessed;
25303 }
25304 } break;
25305
25306 /* To silence a warning. Will never hit this. */
25307 case ma_device_type_loopback:
25308 default: break;
25309 }
25310 }
25311
25312
25313 /* Here is where the device is stopped. */
25314 ma_device_stop__sndio(pDevice);
25315
25316 return result;
25317 }
25318
25319 static ma_result ma_context_uninit__sndio(ma_context* pContext)
25320 {
25321 MA_ASSERT(pContext != NULL);
25322 MA_ASSERT(pContext->backend == ma_backend_sndio);
25323
25324 (void)pContext;
25325 return MA_SUCCESS;
25326 }
25327
25328 static ma_result ma_context_init__sndio(const ma_context_config* pConfig, ma_context* pContext)
25329 {
25330 #ifndef MA_NO_RUNTIME_LINKING
25331 const char* libsndioNames[] = {
25332 "libsndio.so"
25333 };
25334 size_t i;
25335
25336 for (i = 0; i < ma_countof(libsndioNames); ++i) {
25337 pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]);
25338 if (pContext->sndio.sndioSO != NULL) {
25339 break;
25340 }
25341 }
25342
25343 if (pContext->sndio.sndioSO == NULL) {
25344 return MA_NO_BACKEND;
25345 }
25346
25347 pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open");
25348 pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close");
25349 pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar");
25350 pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar");
25351 pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap");
25352 pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write");
25353 pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read");
25354 pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start");
25355 pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop");
25356 pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar");
25357 #else
25358 pContext->sndio.sio_open = sio_open;
25359 pContext->sndio.sio_close = sio_close;
25360 pContext->sndio.sio_setpar = sio_setpar;
25361 pContext->sndio.sio_getpar = sio_getpar;
25362 pContext->sndio.sio_getcap = sio_getcap;
25363 pContext->sndio.sio_write = sio_write;
25364 pContext->sndio.sio_read = sio_read;
25365 pContext->sndio.sio_start = sio_start;
25366 pContext->sndio.sio_stop = sio_stop;
25367 pContext->sndio.sio_initpar = sio_initpar;
25368 #endif
25369
25370 pContext->onUninit = ma_context_uninit__sndio;
25371 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__sndio;
25372 pContext->onEnumDevices = ma_context_enumerate_devices__sndio;
25373 pContext->onGetDeviceInfo = ma_context_get_device_info__sndio;
25374 pContext->onDeviceInit = ma_device_init__sndio;
25375 pContext->onDeviceUninit = ma_device_uninit__sndio;
25376 pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
25377 pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
25378 pContext->onDeviceMainLoop = ma_device_main_loop__sndio;
25379
25380 (void)pConfig;
25381 return MA_SUCCESS;
25382 }
25383 #endif /* sndio */
25384
25385
25386
25387 /******************************************************************************
25388
25389 audio(4) Backend
25390
25391 ******************************************************************************/
25392 #ifdef MA_HAS_AUDIO4
25393 #include <fcntl.h>
25394 #include <poll.h>
25395 #include <errno.h>
25396 #include <sys/stat.h>
25397 #include <sys/types.h>
25398 #include <sys/ioctl.h>
25399 #include <sys/audioio.h>
25400
25401 #if defined(__OpenBSD__)
25402 #include <sys/param.h>
25403 #if defined(OpenBSD) && OpenBSD >= 201709
25404 #define MA_AUDIO4_USE_NEW_API
25405 #endif
25406 #endif
25407
25408 static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
25409 {
25410 size_t baseLen;
25411
25412 MA_ASSERT(id != NULL);
25413 MA_ASSERT(idSize > 0);
25414 MA_ASSERT(deviceIndex >= 0);
25415
25416 baseLen = strlen(base);
25417 MA_ASSERT(idSize > baseLen);
25418
25419 ma_strcpy_s(id, idSize, base);
25420 ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
25421 }
25422
25423 static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
25424 {
25425 size_t idLen;
25426 size_t baseLen;
25427 const char* deviceIndexStr;
25428
25429 MA_ASSERT(id != NULL);
25430 MA_ASSERT(base != NULL);
25431 MA_ASSERT(pIndexOut != NULL);
25432
25433 idLen = strlen(id);
25434 baseLen = strlen(base);
25435 if (idLen <= baseLen) {
25436 return MA_ERROR; /* Doesn't look like the id starts with the base. */
25437 }
25438
25439 if (strncmp(id, base, baseLen) != 0) {
25440 return MA_ERROR; /* ID does not begin with base. */
25441 }
25442
25443 deviceIndexStr = id + baseLen;
25444 if (deviceIndexStr[0] == '\0') {
25445 return MA_ERROR; /* No index specified in the ID. */
25446 }
25447
25448 if (pIndexOut) {
25449 *pIndexOut = atoi(deviceIndexStr);
25450 }
25451
25452 return MA_SUCCESS;
25453 }
25454
25455 static ma_bool32 ma_context_is_device_id_equal__audio4(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
25456 {
25457 MA_ASSERT(pContext != NULL);
25458 MA_ASSERT(pID0 != NULL);
25459 MA_ASSERT(pID1 != NULL);
25460 (void)pContext;
25461
25462 return ma_strcmp(pID0->audio4, pID1->audio4) == 0;
25463 }
25464
25465 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
25466 static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
25467 {
25468 if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
25469 return ma_format_u8;
25470 } else {
25471 if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
25472 if (precision == 16) {
25473 return ma_format_s16;
25474 } else if (precision == 24) {
25475 return ma_format_s24;
25476 } else if (precision == 32) {
25477 return ma_format_s32;
25478 }
25479 } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
25480 if (precision == 16) {
25481 return ma_format_s16;
25482 } else if (precision == 24) {
25483 return ma_format_s24;
25484 } else if (precision == 32) {
25485 return ma_format_s32;
25486 }
25487 }
25488 }
25489
25490 return ma_format_unknown; /* Encoding not supported. */
25491 }
25492
25493 static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
25494 {
25495 MA_ASSERT(format != ma_format_unknown);
25496 MA_ASSERT(pEncoding != NULL);
25497 MA_ASSERT(pPrecision != NULL);
25498
25499 switch (format)
25500 {
25501 case ma_format_u8:
25502 {
25503 *pEncoding = AUDIO_ENCODING_ULINEAR;
25504 *pPrecision = 8;
25505 } break;
25506
25507 case ma_format_s24:
25508 {
25509 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
25510 *pPrecision = 24;
25511 } break;
25512
25513 case ma_format_s32:
25514 {
25515 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
25516 *pPrecision = 32;
25517 } break;
25518
25519 case ma_format_s16:
25520 case ma_format_f32:
25521 default:
25522 {
25523 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
25524 *pPrecision = 16;
25525 } break;
25526 }
25527 }
25528
25529 static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
25530 {
25531 return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
25532 }
25533 #else
25534 static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
25535 {
25536 if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
25537 return ma_format_u8;
25538 }
25539 if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
25540 return ma_format_s16;
25541 }
25542 if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
25543 return ma_format_s24;
25544 }
25545 if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
25546 return ma_format_f32;
25547 }
25548
25549 /* Format not supported. */
25550 return ma_format_unknown;
25551 }
25552 #endif
25553
25554 static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pInfoOut)
25555 {
25556 audio_device_t fdDevice;
25557 #if !defined(MA_AUDIO4_USE_NEW_API)
25558 int counter = 0;
25559 audio_info_t fdInfo;
25560 #else
25561 struct audio_swpar fdPar;
25562 ma_format format;
25563 #endif
25564
25565 MA_ASSERT(pContext != NULL);
25566 MA_ASSERT(fd >= 0);
25567 MA_ASSERT(pInfoOut != NULL);
25568
25569 (void)pContext;
25570 (void)deviceType;
25571
25572 if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
25573 return MA_ERROR; /* Failed to retrieve device info. */
25574 }
25575
25576 /* Name. */
25577 ma_strcpy_s(pInfoOut->name, sizeof(pInfoOut->name), fdDevice.name);
25578
25579 #if !defined(MA_AUDIO4_USE_NEW_API)
25580 /* Supported formats. We get this by looking at the encodings. */
25581 for (;;) {
25582 audio_encoding_t encoding;
25583 ma_format format;
25584
25585 MA_ZERO_OBJECT(&encoding);
25586 encoding.index = counter;
25587 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
25588 break;
25589 }
25590
25591 format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
25592 if (format != ma_format_unknown) {
25593 pInfoOut->formats[pInfoOut->formatCount++] = format;
25594 }
25595
25596 counter += 1;
25597 }
25598
25599 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
25600 return MA_ERROR;
25601 }
25602
25603 if (deviceType == ma_device_type_playback) {
25604 pInfoOut->minChannels = fdInfo.play.channels;
25605 pInfoOut->maxChannels = fdInfo.play.channels;
25606 pInfoOut->minSampleRate = fdInfo.play.sample_rate;
25607 pInfoOut->maxSampleRate = fdInfo.play.sample_rate;
25608 } else {
25609 pInfoOut->minChannels = fdInfo.record.channels;
25610 pInfoOut->maxChannels = fdInfo.record.channels;
25611 pInfoOut->minSampleRate = fdInfo.record.sample_rate;
25612 pInfoOut->maxSampleRate = fdInfo.record.sample_rate;
25613 }
25614 #else
25615 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
25616 return MA_ERROR;
25617 }
25618
25619 format = ma_format_from_swpar__audio4(&fdPar);
25620 if (format == ma_format_unknown) {
25621 return MA_FORMAT_NOT_SUPPORTED;
25622 }
25623 pInfoOut->formats[pInfoOut->formatCount++] = format;
25624
25625 if (deviceType == ma_device_type_playback) {
25626 pInfoOut->minChannels = fdPar.pchan;
25627 pInfoOut->maxChannels = fdPar.pchan;
25628 } else {
25629 pInfoOut->minChannels = fdPar.rchan;
25630 pInfoOut->maxChannels = fdPar.rchan;
25631 }
25632
25633 pInfoOut->minSampleRate = fdPar.rate;
25634 pInfoOut->maxSampleRate = fdPar.rate;
25635 #endif
25636
25637 return MA_SUCCESS;
25638 }
25639
25640 static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
25641 {
25642 const int maxDevices = 64;
25643 char devpath[256];
25644 int iDevice;
25645
25646 MA_ASSERT(pContext != NULL);
25647 MA_ASSERT(callback != NULL);
25648
25649 /*
25650 Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
25651 version here since we can open it even when another process has control of the "/dev/audioN" device.
25652 */
25653 for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
25654 struct stat st;
25655 int fd;
25656 ma_bool32 isTerminating = MA_FALSE;
25657
25658 ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
25659 ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
25660
25661 if (stat(devpath, &st) < 0) {
25662 break;
25663 }
25664
25665 /* The device exists, but we need to check if it's usable as playback and/or capture. */
25666
25667 /* Playback. */
25668 if (!isTerminating) {
25669 fd = open(devpath, O_RDONLY, 0);
25670 if (fd >= 0) {
25671 /* Supports playback. */
25672 ma_device_info deviceInfo;
25673 MA_ZERO_OBJECT(&deviceInfo);
25674 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
25675 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
25676 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
25677 }
25678
25679 close(fd);
25680 }
25681 }
25682
25683 /* Capture. */
25684 if (!isTerminating) {
25685 fd = open(devpath, O_WRONLY, 0);
25686 if (fd >= 0) {
25687 /* Supports capture. */
25688 ma_device_info deviceInfo;
25689 MA_ZERO_OBJECT(&deviceInfo);
25690 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
25691 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
25692 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
25693 }
25694
25695 close(fd);
25696 }
25697 }
25698
25699 if (isTerminating) {
25700 break;
25701 }
25702 }
25703
25704 return MA_SUCCESS;
25705 }
25706
25707 static 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)
25708 {
25709 int fd = -1;
25710 int deviceIndex = -1;
25711 char ctlid[256];
25712 ma_result result;
25713
25714 MA_ASSERT(pContext != NULL);
25715 (void)shareMode;
25716
25717 /*
25718 We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
25719 from the device ID which will be in "/dev/audioN" format.
25720 */
25721 if (pDeviceID == NULL) {
25722 /* Default device. */
25723 ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
25724 } else {
25725 /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */
25726 result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
25727 if (result != MA_SUCCESS) {
25728 return result;
25729 }
25730
25731 ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
25732 }
25733
25734 fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
25735 if (fd == -1) {
25736 return MA_NO_DEVICE;
25737 }
25738
25739 if (deviceIndex == -1) {
25740 ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
25741 } else {
25742 ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
25743 }
25744
25745 result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
25746
25747 close(fd);
25748 return result;
25749 }
25750
25751 static void ma_device_uninit__audio4(ma_device* pDevice)
25752 {
25753 MA_ASSERT(pDevice != NULL);
25754
25755 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25756 close(pDevice->audio4.fdCapture);
25757 }
25758
25759 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25760 close(pDevice->audio4.fdPlayback);
25761 }
25762 }
25763
25764 static ma_result ma_device_init_fd__audio4(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
25765 {
25766 const char* pDefaultDeviceNames[] = {
25767 "/dev/audio",
25768 "/dev/audio0"
25769 };
25770 int fd;
25771 int fdFlags = 0;
25772 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
25773 audio_info_t fdInfo;
25774 #else
25775 struct audio_swpar fdPar;
25776 #endif
25777 ma_format internalFormat;
25778 ma_uint32 internalChannels;
25779 ma_uint32 internalSampleRate;
25780 ma_uint32 internalPeriodSizeInFrames;
25781 ma_uint32 internalPeriods;
25782
25783 MA_ASSERT(pContext != NULL);
25784 MA_ASSERT(pConfig != NULL);
25785 MA_ASSERT(deviceType != ma_device_type_duplex);
25786 MA_ASSERT(pDevice != NULL);
25787
25788 (void)pContext;
25789
25790 /* The first thing to do is open the file. */
25791 if (deviceType == ma_device_type_capture) {
25792 fdFlags = O_RDONLY;
25793 } else {
25794 fdFlags = O_WRONLY;
25795 }
25796 /*fdFlags |= O_NONBLOCK;*/
25797
25798 if ((deviceType == ma_device_type_capture && pConfig->capture.pDeviceID == NULL) || (deviceType == ma_device_type_playback && pConfig->playback.pDeviceID == NULL)) {
25799 /* Default device. */
25800 size_t iDevice;
25801 for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) {
25802 fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0);
25803 if (fd != -1) {
25804 break;
25805 }
25806 }
25807 } else {
25808 /* Specific device. */
25809 fd = open((deviceType == ma_device_type_capture) ? pConfig->capture.pDeviceID->audio4 : pConfig->playback.pDeviceID->audio4, fdFlags, 0);
25810 }
25811
25812 if (fd == -1) {
25813 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.", ma_result_from_errno(errno));
25814 }
25815
25816 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
25817 AUDIO_INITINFO(&fdInfo);
25818
25819 /* We get the driver to do as much of the data conversion as possible. */
25820 if (deviceType == ma_device_type_capture) {
25821 fdInfo.mode = AUMODE_RECORD;
25822 ma_encoding_from_format__audio4(pConfig->capture.format, &fdInfo.record.encoding, &fdInfo.record.precision);
25823 fdInfo.record.channels = pConfig->capture.channels;
25824 fdInfo.record.sample_rate = pConfig->sampleRate;
25825 } else {
25826 fdInfo.mode = AUMODE_PLAY;
25827 ma_encoding_from_format__audio4(pConfig->playback.format, &fdInfo.play.encoding, &fdInfo.play.precision);
25828 fdInfo.play.channels = pConfig->playback.channels;
25829 fdInfo.play.sample_rate = pConfig->sampleRate;
25830 }
25831
25832 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
25833 close(fd);
25834 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
25835 }
25836
25837 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
25838 close(fd);
25839 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
25840 }
25841
25842 if (deviceType == ma_device_type_capture) {
25843 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
25844 internalChannels = fdInfo.record.channels;
25845 internalSampleRate = fdInfo.record.sample_rate;
25846 } else {
25847 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
25848 internalChannels = fdInfo.play.channels;
25849 internalSampleRate = fdInfo.play.sample_rate;
25850 }
25851
25852 if (internalFormat == ma_format_unknown) {
25853 close(fd);
25854 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);
25855 }
25856
25857 /* Buffer. */
25858 {
25859 ma_uint32 internalPeriodSizeInBytes;
25860
25861 internalPeriodSizeInFrames = pConfig->periodSizeInFrames;
25862 if (internalPeriodSizeInFrames == 0) {
25863 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, internalSampleRate);
25864 }
25865
25866 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
25867 if (internalPeriodSizeInBytes < 16) {
25868 internalPeriodSizeInBytes = 16;
25869 }
25870
25871 internalPeriods = pConfig->periods;
25872 if (internalPeriods < 2) {
25873 internalPeriods = 2;
25874 }
25875
25876 /* What miniaudio calls a period, audio4 calls a block. */
25877 AUDIO_INITINFO(&fdInfo);
25878 fdInfo.hiwat = internalPeriods;
25879 fdInfo.lowat = internalPeriods-1;
25880 fdInfo.blocksize = internalPeriodSizeInBytes;
25881 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
25882 close(fd);
25883 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
25884 }
25885
25886 internalPeriods = fdInfo.hiwat;
25887 internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels);
25888 }
25889 #else
25890 /* 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. */
25891 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
25892 close(fd);
25893 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.", MA_FORMAT_NOT_SUPPORTED);
25894 }
25895
25896 internalFormat = ma_format_from_swpar__audio4(&fdPar);
25897 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
25898 internalSampleRate = fdPar.rate;
25899
25900 if (internalFormat == ma_format_unknown) {
25901 close(fd);
25902 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);
25903 }
25904
25905 /* Buffer. */
25906 {
25907 ma_uint32 internalPeriodSizeInBytes;
25908
25909 internalPeriodSizeInFrames = pConfig->periodSizeInFrames;
25910 if (internalPeriodSizeInFrames == 0) {
25911 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, internalSampleRate);
25912 }
25913
25914 /* What miniaudio calls a period, audio4 calls a block. */
25915 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
25916 if (internalPeriodSizeInBytes < 16) {
25917 internalPeriodSizeInBytes = 16;
25918 }
25919
25920 fdPar.nblks = pConfig->periods;
25921 fdPar.round = internalPeriodSizeInBytes;
25922
25923 if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
25924 close(fd);
25925 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.", MA_FORMAT_NOT_SUPPORTED);
25926 }
25927
25928 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
25929 close(fd);
25930 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.", MA_FORMAT_NOT_SUPPORTED);
25931 }
25932 }
25933
25934 internalFormat = ma_format_from_swpar__audio4(&fdPar);
25935 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
25936 internalSampleRate = fdPar.rate;
25937 internalPeriods = fdPar.nblks;
25938 internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels);
25939 #endif
25940
25941 if (internalFormat == ma_format_unknown) {
25942 close(fd);
25943 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);
25944 }
25945
25946 if (deviceType == ma_device_type_capture) {
25947 pDevice->audio4.fdCapture = fd;
25948 pDevice->capture.internalFormat = internalFormat;
25949 pDevice->capture.internalChannels = internalChannels;
25950 pDevice->capture.internalSampleRate = internalSampleRate;
25951 ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDevice->capture.internalChannelMap);
25952 pDevice->capture.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
25953 pDevice->capture.internalPeriods = internalPeriods;
25954 } else {
25955 pDevice->audio4.fdPlayback = fd;
25956 pDevice->playback.internalFormat = internalFormat;
25957 pDevice->playback.internalChannels = internalChannels;
25958 pDevice->playback.internalSampleRate = internalSampleRate;
25959 ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDevice->playback.internalChannelMap);
25960 pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
25961 pDevice->playback.internalPeriods = internalPeriods;
25962 }
25963
25964 return MA_SUCCESS;
25965 }
25966
25967 static ma_result ma_device_init__audio4(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
25968 {
25969 MA_ASSERT(pDevice != NULL);
25970
25971 MA_ZERO_OBJECT(&pDevice->audio4);
25972
25973 if (pConfig->deviceType == ma_device_type_loopback) {
25974 return MA_DEVICE_TYPE_NOT_SUPPORTED;
25975 }
25976
25977 pDevice->audio4.fdCapture = -1;
25978 pDevice->audio4.fdPlayback = -1;
25979
25980 /*
25981 The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD
25982 introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as
25983 I'm aware.
25984 */
25985 #if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
25986 /* NetBSD 8.0+ */
25987 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
25988 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
25989 return MA_SHARE_MODE_NOT_SUPPORTED;
25990 }
25991 #else
25992 /* All other flavors. */
25993 #endif
25994
25995 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
25996 ma_result result = ma_device_init_fd__audio4(pContext, pConfig, ma_device_type_capture, pDevice);
25997 if (result != MA_SUCCESS) {
25998 return result;
25999 }
26000 }
26001
26002 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
26003 ma_result result = ma_device_init_fd__audio4(pContext, pConfig, ma_device_type_playback, pDevice);
26004 if (result != MA_SUCCESS) {
26005 if (pConfig->deviceType == ma_device_type_duplex) {
26006 close(pDevice->audio4.fdCapture);
26007 }
26008 return result;
26009 }
26010 }
26011
26012 return MA_SUCCESS;
26013 }
26014
26015 #if 0
26016 static ma_result ma_device_start__audio4(ma_device* pDevice)
26017 {
26018 MA_ASSERT(pDevice != NULL);
26019
26020 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26021 if (pDevice->audio4.fdCapture == -1) {
26022 return MA_INVALID_ARGS;
26023 }
26024 }
26025
26026 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26027 if (pDevice->audio4.fdPlayback == -1) {
26028 return MA_INVALID_ARGS;
26029 }
26030 }
26031
26032 return MA_SUCCESS;
26033 }
26034 #endif
26035
26036 static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
26037 {
26038 if (fd == -1) {
26039 return MA_INVALID_ARGS;
26040 }
26041
26042 #if !defined(MA_AUDIO4_USE_NEW_API)
26043 if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
26044 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.", ma_result_from_errno(errno));
26045 }
26046 #else
26047 if (ioctl(fd, AUDIO_STOP, 0) < 0) {
26048 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.", ma_result_from_errno(errno));
26049 }
26050 #endif
26051
26052 return MA_SUCCESS;
26053 }
26054
26055 static ma_result ma_device_stop__audio4(ma_device* pDevice)
26056 {
26057 MA_ASSERT(pDevice != NULL);
26058
26059 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26060 ma_result result;
26061
26062 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
26063 if (result != MA_SUCCESS) {
26064 return result;
26065 }
26066 }
26067
26068 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26069 ma_result result;
26070
26071 /* 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. */
26072 #if !defined(MA_AUDIO4_USE_NEW_API)
26073 ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0);
26074 #endif
26075
26076 /* Here is where the device is stopped immediately. */
26077 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
26078 if (result != MA_SUCCESS) {
26079 return result;
26080 }
26081 }
26082
26083 return MA_SUCCESS;
26084 }
26085
26086 static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
26087 {
26088 int result;
26089
26090 if (pFramesWritten != NULL) {
26091 *pFramesWritten = 0;
26092 }
26093
26094 result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
26095 if (result < 0) {
26096 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.", ma_result_from_errno(errno));
26097 }
26098
26099 if (pFramesWritten != NULL) {
26100 *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26101 }
26102
26103 return MA_SUCCESS;
26104 }
26105
26106 static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
26107 {
26108 int result;
26109
26110 if (pFramesRead != NULL) {
26111 *pFramesRead = 0;
26112 }
26113
26114 result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
26115 if (result < 0) {
26116 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.", ma_result_from_errno(errno));
26117 }
26118
26119 if (pFramesRead != NULL) {
26120 *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26121 }
26122
26123 return MA_SUCCESS;
26124 }
26125
26126 static ma_result ma_device_main_loop__audio4(ma_device* pDevice)
26127 {
26128 ma_result result = MA_SUCCESS;
26129 ma_bool32 exitLoop = MA_FALSE;
26130
26131 /* No need to explicitly start the device like the other backends. */
26132
26133 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
26134 switch (pDevice->type)
26135 {
26136 case ma_device_type_duplex:
26137 {
26138 /* The process is: device_read -> convert -> callback -> convert -> device_write */
26139 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
26140 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
26141
26142 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
26143 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26144 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26145 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26146 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26147 ma_uint32 capturedDeviceFramesRemaining;
26148 ma_uint32 capturedDeviceFramesProcessed;
26149 ma_uint32 capturedDeviceFramesToProcess;
26150 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
26151 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
26152 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
26153 }
26154
26155 result = ma_device_read__audio4(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
26156 if (result != MA_SUCCESS) {
26157 exitLoop = MA_TRUE;
26158 break;
26159 }
26160
26161 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
26162 capturedDeviceFramesProcessed = 0;
26163
26164 for (;;) {
26165 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26166 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26167 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
26168 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
26169 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
26170 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
26171 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
26172
26173 /* Convert capture data from device format to client format. */
26174 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
26175 if (result != MA_SUCCESS) {
26176 break;
26177 }
26178
26179 /*
26180 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
26181 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
26182 */
26183 if (capturedClientFramesToProcessThisIteration == 0) {
26184 break;
26185 }
26186
26187 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
26188
26189 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
26190 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
26191
26192 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
26193 for (;;) {
26194 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
26195 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
26196 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
26197 if (result != MA_SUCCESS) {
26198 break;
26199 }
26200
26201 result = ma_device_write__audio4(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
26202 if (result != MA_SUCCESS) {
26203 exitLoop = MA_TRUE;
26204 break;
26205 }
26206
26207 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
26208 if (capturedClientFramesToProcessThisIteration == 0) {
26209 break;
26210 }
26211 }
26212
26213 /* In case an error happened from ma_device_write__audio4()... */
26214 if (result != MA_SUCCESS) {
26215 exitLoop = MA_TRUE;
26216 break;
26217 }
26218 }
26219
26220 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
26221 }
26222 } break;
26223
26224 case ma_device_type_capture:
26225 {
26226 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
26227 ma_uint8 intermediaryBuffer[8192];
26228 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26229 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
26230 ma_uint32 framesReadThisPeriod = 0;
26231 while (framesReadThisPeriod < periodSizeInFrames) {
26232 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
26233 ma_uint32 framesProcessed;
26234 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
26235 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
26236 framesToReadThisIteration = intermediaryBufferSizeInFrames;
26237 }
26238
26239 result = ma_device_read__audio4(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
26240 if (result != MA_SUCCESS) {
26241 exitLoop = MA_TRUE;
26242 break;
26243 }
26244
26245 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
26246
26247 framesReadThisPeriod += framesProcessed;
26248 }
26249 } break;
26250
26251 case ma_device_type_playback:
26252 {
26253 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
26254 ma_uint8 intermediaryBuffer[8192];
26255 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26256 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
26257 ma_uint32 framesWrittenThisPeriod = 0;
26258 while (framesWrittenThisPeriod < periodSizeInFrames) {
26259 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
26260 ma_uint32 framesProcessed;
26261 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
26262 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
26263 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
26264 }
26265
26266 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
26267
26268 result = ma_device_write__audio4(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
26269 if (result != MA_SUCCESS) {
26270 exitLoop = MA_TRUE;
26271 break;
26272 }
26273
26274 framesWrittenThisPeriod += framesProcessed;
26275 }
26276 } break;
26277
26278 /* To silence a warning. Will never hit this. */
26279 case ma_device_type_loopback:
26280 default: break;
26281 }
26282 }
26283
26284
26285 /* Here is where the device is stopped. */
26286 ma_device_stop__audio4(pDevice);
26287
26288 return result;
26289 }
26290
26291 static ma_result ma_context_uninit__audio4(ma_context* pContext)
26292 {
26293 MA_ASSERT(pContext != NULL);
26294 MA_ASSERT(pContext->backend == ma_backend_audio4);
26295
26296 (void)pContext;
26297 return MA_SUCCESS;
26298 }
26299
26300 static ma_result ma_context_init__audio4(const ma_context_config* pConfig, ma_context* pContext)
26301 {
26302 MA_ASSERT(pContext != NULL);
26303
26304 (void)pConfig;
26305
26306 pContext->onUninit = ma_context_uninit__audio4;
26307 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__audio4;
26308 pContext->onEnumDevices = ma_context_enumerate_devices__audio4;
26309 pContext->onGetDeviceInfo = ma_context_get_device_info__audio4;
26310 pContext->onDeviceInit = ma_device_init__audio4;
26311 pContext->onDeviceUninit = ma_device_uninit__audio4;
26312 pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
26313 pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
26314 pContext->onDeviceMainLoop = ma_device_main_loop__audio4;
26315
26316 return MA_SUCCESS;
26317 }
26318 #endif /* audio4 */
26319
26320
26321 /******************************************************************************
26322
26323 OSS Backend
26324
26325 ******************************************************************************/
26326 #ifdef MA_HAS_OSS
26327 #include <sys/ioctl.h>
26328 #include <unistd.h>
26329 #include <fcntl.h>
26330 #include <sys/soundcard.h>
26331
26332 #ifndef SNDCTL_DSP_HALT
26333 #define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
26334 #endif
26335
26336 static int ma_open_temp_device__oss()
26337 {
26338 /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */
26339 int fd = open("/dev/mixer", O_RDONLY, 0);
26340 if (fd >= 0) {
26341 return fd;
26342 }
26343
26344 return -1;
26345 }
26346
26347 static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd)
26348 {
26349 const char* deviceName;
26350 int flags;
26351
26352 MA_ASSERT(pContext != NULL);
26353 MA_ASSERT(pfd != NULL);
26354 (void)pContext;
26355
26356 *pfd = -1;
26357
26358 /* This function should only be called for playback or capture, not duplex. */
26359 if (deviceType == ma_device_type_duplex) {
26360 return MA_INVALID_ARGS;
26361 }
26362
26363 deviceName = "/dev/dsp";
26364 if (pDeviceID != NULL) {
26365 deviceName = pDeviceID->oss;
26366 }
26367
26368 flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
26369 if (shareMode == ma_share_mode_exclusive) {
26370 flags |= O_EXCL;
26371 }
26372
26373 *pfd = open(deviceName, flags, 0);
26374 if (*pfd == -1) {
26375 return ma_result_from_errno(errno);
26376 }
26377
26378 return MA_SUCCESS;
26379 }
26380
26381 static ma_bool32 ma_context_is_device_id_equal__oss(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
26382 {
26383 MA_ASSERT(pContext != NULL);
26384 MA_ASSERT(pID0 != NULL);
26385 MA_ASSERT(pID1 != NULL);
26386 (void)pContext;
26387
26388 return ma_strcmp(pID0->oss, pID1->oss) == 0;
26389 }
26390
26391 static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
26392 {
26393 int fd;
26394 oss_sysinfo si;
26395 int result;
26396
26397 MA_ASSERT(pContext != NULL);
26398 MA_ASSERT(callback != NULL);
26399
26400 fd = ma_open_temp_device__oss();
26401 if (fd == -1) {
26402 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);
26403 }
26404
26405 result = ioctl(fd, SNDCTL_SYSINFO, &si);
26406 if (result != -1) {
26407 int iAudioDevice;
26408 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
26409 oss_audioinfo ai;
26410 ai.dev = iAudioDevice;
26411 result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
26412 if (result != -1) {
26413 if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
26414 ma_device_info deviceInfo;
26415 ma_bool32 isTerminating = MA_FALSE;
26416
26417 MA_ZERO_OBJECT(&deviceInfo);
26418
26419 /* ID */
26420 ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
26421
26422 /*
26423 The human readable device name should be in the "ai.handle" variable, but it can
26424 sometimes be empty in which case we just fall back to "ai.name" which is less user
26425 friendly, but usually has a value.
26426 */
26427 if (ai.handle[0] != '\0') {
26428 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
26429 } else {
26430 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
26431 }
26432
26433 /* The device can be both playback and capture. */
26434 if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
26435 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
26436 }
26437 if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
26438 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
26439 }
26440
26441 if (isTerminating) {
26442 break;
26443 }
26444 }
26445 }
26446 }
26447 } else {
26448 close(fd);
26449 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
26450 }
26451
26452 close(fd);
26453 return MA_SUCCESS;
26454 }
26455
26456 static 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)
26457 {
26458 ma_bool32 foundDevice;
26459 int fdTemp;
26460 oss_sysinfo si;
26461 int result;
26462
26463 MA_ASSERT(pContext != NULL);
26464 (void)shareMode;
26465
26466 /* Handle the default device a little differently. */
26467 if (pDeviceID == NULL) {
26468 if (deviceType == ma_device_type_playback) {
26469 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
26470 } else {
26471 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
26472 }
26473
26474 return MA_SUCCESS;
26475 }
26476
26477
26478 /* If we get here it means we are _not_ using the default device. */
26479 foundDevice = MA_FALSE;
26480
26481 fdTemp = ma_open_temp_device__oss();
26482 if (fdTemp == -1) {
26483 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);
26484 }
26485
26486 result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
26487 if (result != -1) {
26488 int iAudioDevice;
26489 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
26490 oss_audioinfo ai;
26491 ai.dev = iAudioDevice;
26492 result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
26493 if (result != -1) {
26494 if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
26495 /* It has the same name, so now just confirm the type. */
26496 if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
26497 (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
26498 unsigned int formatMask;
26499
26500 /* ID */
26501 ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
26502
26503 /*
26504 The human readable device name should be in the "ai.handle" variable, but it can
26505 sometimes be empty in which case we just fall back to "ai.name" which is less user
26506 friendly, but usually has a value.
26507 */
26508 if (ai.handle[0] != '\0') {
26509 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
26510 } else {
26511 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
26512 }
26513
26514 pDeviceInfo->minChannels = ai.min_channels;
26515 pDeviceInfo->maxChannels = ai.max_channels;
26516 pDeviceInfo->minSampleRate = ai.min_rate;
26517 pDeviceInfo->maxSampleRate = ai.max_rate;
26518 pDeviceInfo->formatCount = 0;
26519
26520 if (deviceType == ma_device_type_playback) {
26521 formatMask = ai.oformats;
26522 } else {
26523 formatMask = ai.iformats;
26524 }
26525
26526 if ((formatMask & AFMT_U8) != 0) {
26527 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_u8;
26528 }
26529 if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
26530 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s16;
26531 }
26532 if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
26533 pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s32;
26534 }
26535
26536 foundDevice = MA_TRUE;
26537 break;
26538 }
26539 }
26540 }
26541 }
26542 } else {
26543 close(fdTemp);
26544 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
26545 }
26546
26547
26548 close(fdTemp);
26549
26550 if (!foundDevice) {
26551 return MA_NO_DEVICE;
26552 }
26553
26554 return MA_SUCCESS;
26555 }
26556
26557 static void ma_device_uninit__oss(ma_device* pDevice)
26558 {
26559 MA_ASSERT(pDevice != NULL);
26560
26561 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26562 close(pDevice->oss.fdCapture);
26563 }
26564
26565 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26566 close(pDevice->oss.fdPlayback);
26567 }
26568 }
26569
26570 static int ma_format_to_oss(ma_format format)
26571 {
26572 int ossFormat = AFMT_U8;
26573 switch (format) {
26574 case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
26575 case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
26576 case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
26577 case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
26578 case ma_format_u8:
26579 default: ossFormat = AFMT_U8; break;
26580 }
26581
26582 return ossFormat;
26583 }
26584
26585 static ma_format ma_format_from_oss(int ossFormat)
26586 {
26587 if (ossFormat == AFMT_U8) {
26588 return ma_format_u8;
26589 } else {
26590 if (ma_is_little_endian()) {
26591 switch (ossFormat) {
26592 case AFMT_S16_LE: return ma_format_s16;
26593 case AFMT_S32_LE: return ma_format_s32;
26594 default: return ma_format_unknown;
26595 }
26596 } else {
26597 switch (ossFormat) {
26598 case AFMT_S16_BE: return ma_format_s16;
26599 case AFMT_S32_BE: return ma_format_s32;
26600 default: return ma_format_unknown;
26601 }
26602 }
26603 }
26604
26605 return ma_format_unknown;
26606 }
26607
26608 static ma_result ma_device_init_fd__oss(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
26609 {
26610 ma_result result;
26611 int ossResult;
26612 int fd;
26613 const ma_device_id* pDeviceID = NULL;
26614 ma_share_mode shareMode;
26615 int ossFormat;
26616 int ossChannels;
26617 int ossSampleRate;
26618 int ossFragment;
26619
26620 MA_ASSERT(pContext != NULL);
26621 MA_ASSERT(pConfig != NULL);
26622 MA_ASSERT(deviceType != ma_device_type_duplex);
26623 MA_ASSERT(pDevice != NULL);
26624
26625 (void)pContext;
26626
26627 if (deviceType == ma_device_type_capture) {
26628 pDeviceID = pConfig->capture.pDeviceID;
26629 shareMode = pConfig->capture.shareMode;
26630 ossFormat = ma_format_to_oss(pConfig->capture.format);
26631 ossChannels = (int)pConfig->capture.channels;
26632 ossSampleRate = (int)pConfig->sampleRate;
26633 } else {
26634 pDeviceID = pConfig->playback.pDeviceID;
26635 shareMode = pConfig->playback.shareMode;
26636 ossFormat = ma_format_to_oss(pConfig->playback.format);
26637 ossChannels = (int)pConfig->playback.channels;
26638 ossSampleRate = (int)pConfig->sampleRate;
26639 }
26640
26641 result = ma_context_open_device__oss(pContext, deviceType, pDeviceID, shareMode, &fd);
26642 if (result != MA_SUCCESS) {
26643 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
26644 }
26645
26646 /*
26647 The OSS documantation is very clear about the order we should be initializing the device's properties:
26648 1) Format
26649 2) Channels
26650 3) Sample rate.
26651 */
26652
26653 /* Format. */
26654 ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
26655 if (ossResult == -1) {
26656 close(fd);
26657 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MA_FORMAT_NOT_SUPPORTED);
26658 }
26659
26660 /* Channels. */
26661 ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
26662 if (ossResult == -1) {
26663 close(fd);
26664 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.", MA_FORMAT_NOT_SUPPORTED);
26665 }
26666
26667 /* Sample Rate. */
26668 ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
26669 if (ossResult == -1) {
26670 close(fd);
26671 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.", MA_FORMAT_NOT_SUPPORTED);
26672 }
26673
26674 /*
26675 Buffer.
26676
26677 The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
26678 it should be done before or after format/channels/rate.
26679
26680 OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
26681 value.
26682 */
26683 {
26684 ma_uint32 periodSizeInFrames;
26685 ma_uint32 periodSizeInBytes;
26686 ma_uint32 ossFragmentSizePower;
26687
26688 periodSizeInFrames = pConfig->periodSizeInFrames;
26689 if (periodSizeInFrames == 0) {
26690 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, (ma_uint32)ossSampleRate);
26691 }
26692
26693 periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
26694 if (periodSizeInBytes < 16) {
26695 periodSizeInBytes = 16;
26696 }
26697
26698 ossFragmentSizePower = 4;
26699 periodSizeInBytes >>= 4;
26700 while (periodSizeInBytes >>= 1) {
26701 ossFragmentSizePower += 1;
26702 }
26703
26704 ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
26705 ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
26706 if (ossResult == -1) {
26707 close(fd);
26708 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.", MA_FORMAT_NOT_SUPPORTED);
26709 }
26710 }
26711
26712 /* Internal settings. */
26713 if (deviceType == ma_device_type_capture) {
26714 pDevice->oss.fdCapture = fd;
26715 pDevice->capture.internalFormat = ma_format_from_oss(ossFormat);
26716 pDevice->capture.internalChannels = ossChannels;
26717 pDevice->capture.internalSampleRate = ossSampleRate;
26718 ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
26719 pDevice->capture.internalPeriods = (ma_uint32)(ossFragment >> 16);
26720 pDevice->capture.internalPeriodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26721
26722 if (pDevice->capture.internalFormat == ma_format_unknown) {
26723 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
26724 }
26725 } else {
26726 pDevice->oss.fdPlayback = fd;
26727 pDevice->playback.internalFormat = ma_format_from_oss(ossFormat);
26728 pDevice->playback.internalChannels = ossChannels;
26729 pDevice->playback.internalSampleRate = ossSampleRate;
26730 ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
26731 pDevice->playback.internalPeriods = (ma_uint32)(ossFragment >> 16);
26732 pDevice->playback.internalPeriodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26733
26734 if (pDevice->playback.internalFormat == ma_format_unknown) {
26735 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
26736 }
26737 }
26738
26739 return MA_SUCCESS;
26740 }
26741
26742 static ma_result ma_device_init__oss(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
26743 {
26744 MA_ASSERT(pContext != NULL);
26745 MA_ASSERT(pConfig != NULL);
26746 MA_ASSERT(pDevice != NULL);
26747
26748 MA_ZERO_OBJECT(&pDevice->oss);
26749
26750 if (pConfig->deviceType == ma_device_type_loopback) {
26751 return MA_DEVICE_TYPE_NOT_SUPPORTED;
26752 }
26753
26754 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
26755 ma_result result = ma_device_init_fd__oss(pContext, pConfig, ma_device_type_capture, pDevice);
26756 if (result != MA_SUCCESS) {
26757 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
26758 }
26759 }
26760
26761 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
26762 ma_result result = ma_device_init_fd__oss(pContext, pConfig, ma_device_type_playback, pDevice);
26763 if (result != MA_SUCCESS) {
26764 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
26765 }
26766 }
26767
26768 return MA_SUCCESS;
26769 }
26770
26771 static ma_result ma_device_stop__oss(ma_device* pDevice)
26772 {
26773 MA_ASSERT(pDevice != NULL);
26774
26775 /*
26776 We want to use SNDCTL_DSP_HALT. From the documentation:
26777
26778 In multithreaded applications SNDCTL_DSP_HALT (SNDCTL_DSP_RESET) must only be called by the thread
26779 that actually reads/writes the audio device. It must not be called by some master thread to kill the
26780 audio thread. The audio thread will not stop or get any kind of notification that the device was
26781 stopped by the master thread. The device gets stopped but the next read or write call will silently
26782 restart the device.
26783
26784 This is actually safe in our case, because this function is only ever called from within our worker
26785 thread anyway. Just keep this in mind, though...
26786 */
26787
26788 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26789 int result = ioctl(pDevice->oss.fdCapture, SNDCTL_DSP_HALT, 0);
26790 if (result == -1) {
26791 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", ma_result_from_errno(errno));
26792 }
26793 }
26794
26795 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26796 int result = ioctl(pDevice->oss.fdPlayback, SNDCTL_DSP_HALT, 0);
26797 if (result == -1) {
26798 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", ma_result_from_errno(errno));
26799 }
26800 }
26801
26802 return MA_SUCCESS;
26803 }
26804
26805 static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
26806 {
26807 int resultOSS;
26808
26809 if (pFramesWritten != NULL) {
26810 *pFramesWritten = 0;
26811 }
26812
26813 resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
26814 if (resultOSS < 0) {
26815 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.", ma_result_from_errno(errno));
26816 }
26817
26818 if (pFramesWritten != NULL) {
26819 *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26820 }
26821
26822 return MA_SUCCESS;
26823 }
26824
26825 static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
26826 {
26827 int resultOSS;
26828
26829 if (pFramesRead != NULL) {
26830 *pFramesRead = 0;
26831 }
26832
26833 resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
26834 if (resultOSS < 0) {
26835 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client.", ma_result_from_errno(errno));
26836 }
26837
26838 if (pFramesRead != NULL) {
26839 *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26840 }
26841
26842 return MA_SUCCESS;
26843 }
26844
26845 static ma_result ma_device_main_loop__oss(ma_device* pDevice)
26846 {
26847 ma_result result = MA_SUCCESS;
26848 ma_bool32 exitLoop = MA_FALSE;
26849
26850 /* No need to explicitly start the device like the other backends. */
26851
26852 while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
26853 switch (pDevice->type)
26854 {
26855 case ma_device_type_duplex:
26856 {
26857 /* The process is: device_read -> convert -> callback -> convert -> device_write */
26858 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
26859 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
26860
26861 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
26862 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26863 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26864 ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26865 ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26866 ma_uint32 capturedDeviceFramesRemaining;
26867 ma_uint32 capturedDeviceFramesProcessed;
26868 ma_uint32 capturedDeviceFramesToProcess;
26869 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
26870 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
26871 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
26872 }
26873
26874 result = ma_device_read__oss(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
26875 if (result != MA_SUCCESS) {
26876 exitLoop = MA_TRUE;
26877 break;
26878 }
26879
26880 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
26881 capturedDeviceFramesProcessed = 0;
26882
26883 for (;;) {
26884 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26885 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26886 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
26887 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
26888 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
26889 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
26890 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
26891
26892 /* Convert capture data from device format to client format. */
26893 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
26894 if (result != MA_SUCCESS) {
26895 break;
26896 }
26897
26898 /*
26899 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
26900 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
26901 */
26902 if (capturedClientFramesToProcessThisIteration == 0) {
26903 break;
26904 }
26905
26906 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
26907
26908 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
26909 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
26910
26911 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
26912 for (;;) {
26913 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
26914 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
26915 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
26916 if (result != MA_SUCCESS) {
26917 break;
26918 }
26919
26920 result = ma_device_write__oss(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
26921 if (result != MA_SUCCESS) {
26922 exitLoop = MA_TRUE;
26923 break;
26924 }
26925
26926 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
26927 if (capturedClientFramesToProcessThisIteration == 0) {
26928 break;
26929 }
26930 }
26931
26932 /* In case an error happened from ma_device_write__oss()... */
26933 if (result != MA_SUCCESS) {
26934 exitLoop = MA_TRUE;
26935 break;
26936 }
26937 }
26938
26939 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
26940 }
26941 } break;
26942
26943 case ma_device_type_capture:
26944 {
26945 /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
26946 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26947 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26948 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
26949 ma_uint32 framesReadThisPeriod = 0;
26950 while (framesReadThisPeriod < periodSizeInFrames) {
26951 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
26952 ma_uint32 framesProcessed;
26953 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
26954 if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
26955 framesToReadThisIteration = intermediaryBufferSizeInFrames;
26956 }
26957
26958 result = ma_device_read__oss(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
26959 if (result != MA_SUCCESS) {
26960 exitLoop = MA_TRUE;
26961 break;
26962 }
26963
26964 ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
26965
26966 framesReadThisPeriod += framesProcessed;
26967 }
26968 } break;
26969
26970 case ma_device_type_playback:
26971 {
26972 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
26973 ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26974 ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26975 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
26976 ma_uint32 framesWrittenThisPeriod = 0;
26977 while (framesWrittenThisPeriod < periodSizeInFrames) {
26978 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
26979 ma_uint32 framesProcessed;
26980 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
26981 if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
26982 framesToWriteThisIteration = intermediaryBufferSizeInFrames;
26983 }
26984
26985 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
26986
26987 result = ma_device_write__oss(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
26988 if (result != MA_SUCCESS) {
26989 exitLoop = MA_TRUE;
26990 break;
26991 }
26992
26993 framesWrittenThisPeriod += framesProcessed;
26994 }
26995 } break;
26996
26997 /* To silence a warning. Will never hit this. */
26998 case ma_device_type_loopback:
26999 default: break;
27000 }
27001 }
27002
27003
27004 /* Here is where the device is stopped. */
27005 ma_device_stop__oss(pDevice);
27006
27007 return result;
27008 }
27009
27010 static ma_result ma_context_uninit__oss(ma_context* pContext)
27011 {
27012 MA_ASSERT(pContext != NULL);
27013 MA_ASSERT(pContext->backend == ma_backend_oss);
27014
27015 (void)pContext;
27016 return MA_SUCCESS;
27017 }
27018
27019 static ma_result ma_context_init__oss(const ma_context_config* pConfig, ma_context* pContext)
27020 {
27021 int fd;
27022 int ossVersion;
27023 int result;
27024
27025 MA_ASSERT(pContext != NULL);
27026
27027 (void)pConfig;
27028
27029 /* Try opening a temporary device first so we can get version information. This is closed at the end. */
27030 fd = ma_open_temp_device__oss();
27031 if (fd == -1) {
27032 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. */
27033 }
27034
27035 /* Grab the OSS version. */
27036 ossVersion = 0;
27037 result = ioctl(fd, OSS_GETVERSION, &ossVersion);
27038 if (result == -1) {
27039 close(fd);
27040 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.", MA_NO_BACKEND);
27041 }
27042
27043 pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
27044 pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
27045
27046 pContext->onUninit = ma_context_uninit__oss;
27047 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__oss;
27048 pContext->onEnumDevices = ma_context_enumerate_devices__oss;
27049 pContext->onGetDeviceInfo = ma_context_get_device_info__oss;
27050 pContext->onDeviceInit = ma_device_init__oss;
27051 pContext->onDeviceUninit = ma_device_uninit__oss;
27052 pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
27053 pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
27054 pContext->onDeviceMainLoop = ma_device_main_loop__oss;
27055
27056 close(fd);
27057 return MA_SUCCESS;
27058 }
27059 #endif /* OSS */
27060
27061
27062 /******************************************************************************
27063
27064 AAudio Backend
27065
27066 ******************************************************************************/
27067 #ifdef MA_HAS_AAUDIO
27068 /*#include <AAudio/AAudio.h>*/
27069
27070 #define MA_AAUDIO_UNSPECIFIED 0
27071
27072 typedef int32_t ma_aaudio_result_t;
27073 typedef int32_t ma_aaudio_direction_t;
27074 typedef int32_t ma_aaudio_sharing_mode_t;
27075 typedef int32_t ma_aaudio_format_t;
27076 typedef int32_t ma_aaudio_stream_state_t;
27077 typedef int32_t ma_aaudio_performance_mode_t;
27078 typedef int32_t ma_aaudio_data_callback_result_t;
27079
27080 /* Result codes. miniaudio only cares about the success code. */
27081 #define MA_AAUDIO_OK 0
27082
27083 /* Directions. */
27084 #define MA_AAUDIO_DIRECTION_OUTPUT 0
27085 #define MA_AAUDIO_DIRECTION_INPUT 1
27086
27087 /* Sharing modes. */
27088 #define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
27089 #define MA_AAUDIO_SHARING_MODE_SHARED 1
27090
27091 /* Formats. */
27092 #define MA_AAUDIO_FORMAT_PCM_I16 1
27093 #define MA_AAUDIO_FORMAT_PCM_FLOAT 2
27094
27095 /* Stream states. */
27096 #define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
27097 #define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
27098 #define MA_AAUDIO_STREAM_STATE_OPEN 2
27099 #define MA_AAUDIO_STREAM_STATE_STARTING 3
27100 #define MA_AAUDIO_STREAM_STATE_STARTED 4
27101 #define MA_AAUDIO_STREAM_STATE_PAUSING 5
27102 #define MA_AAUDIO_STREAM_STATE_PAUSED 6
27103 #define MA_AAUDIO_STREAM_STATE_FLUSHING 7
27104 #define MA_AAUDIO_STREAM_STATE_FLUSHED 8
27105 #define MA_AAUDIO_STREAM_STATE_STOPPING 9
27106 #define MA_AAUDIO_STREAM_STATE_STOPPED 10
27107 #define MA_AAUDIO_STREAM_STATE_CLOSING 11
27108 #define MA_AAUDIO_STREAM_STATE_CLOSED 12
27109 #define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
27110
27111 /* Performance modes. */
27112 #define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
27113 #define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
27114 #define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
27115
27116 /* Callback results. */
27117 #define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
27118 #define MA_AAUDIO_CALLBACK_RESULT_STOP 1
27119
27120 /* Objects. */
27121 typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
27122 typedef struct ma_AAudioStream_t* ma_AAudioStream;
27123
27124 typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
27125 typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);
27126
27127 typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
27128 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
27129 typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
27130 typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
27131 typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
27132 typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
27133 typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
27134 typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
27135 typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
27136 typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
27137 typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
27138 typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
27139 typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
27140 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
27141 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
27142 typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
27143 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds);
27144 typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
27145 typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
27146 typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
27147 typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
27148 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
27149 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
27150 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
27151 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
27152
27153 static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
27154 {
27155 switch (resultAA)
27156 {
27157 case MA_AAUDIO_OK: return MA_SUCCESS;
27158 default: break;
27159 }
27160
27161 return MA_ERROR;
27162 }
27163
27164 static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
27165 {
27166 ma_device* pDevice = (ma_device*)pUserData;
27167 MA_ASSERT(pDevice != NULL);
27168
27169 (void)error;
27170
27171 #if defined(MA_DEBUG_OUTPUT)
27172 printf("[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream));
27173 #endif
27174
27175 /*
27176 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
27177 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.
27178 */
27179 if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
27180 #if defined(MA_DEBUG_OUTPUT)
27181 printf("[AAudio] Device Disconnected.\n");
27182 #endif
27183 }
27184 }
27185
27186 static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
27187 {
27188 ma_device* pDevice = (ma_device*)pUserData;
27189 MA_ASSERT(pDevice != NULL);
27190
27191 if (pDevice->type == ma_device_type_duplex) {
27192 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pAudioData, &pDevice->aaudio.duplexRB);
27193 } else {
27194 ma_device__send_frames_to_client(pDevice, frameCount, pAudioData); /* Send directly to the client. */
27195 }
27196
27197 (void)pStream;
27198 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
27199 }
27200
27201 static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
27202 {
27203 ma_device* pDevice = (ma_device*)pUserData;
27204 MA_ASSERT(pDevice != NULL);
27205
27206 if (pDevice->type == ma_device_type_duplex) {
27207 ma_device__handle_duplex_callback_playback(pDevice, frameCount, pAudioData, &pDevice->aaudio.duplexRB);
27208 } else {
27209 ma_device__read_frames_from_client(pDevice, frameCount, pAudioData); /* Read directly from the client. */
27210 }
27211
27212 (void)pStream;
27213 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
27214 }
27215
27216 static 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)
27217 {
27218 ma_AAudioStreamBuilder* pBuilder;
27219 ma_aaudio_result_t resultAA;
27220
27221 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */
27222
27223 *ppStream = NULL;
27224
27225 resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
27226 if (resultAA != MA_AAUDIO_OK) {
27227 return ma_result_from_aaudio(resultAA);
27228 }
27229
27230 if (pDeviceID != NULL) {
27231 ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
27232 }
27233
27234 ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
27235 ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
27236
27237 if (pConfig != NULL) {
27238 ma_uint32 bufferCapacityInFrames;
27239
27240 if (pDevice == NULL || !pDevice->usingDefaultSampleRate) {
27241 ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pConfig->sampleRate);
27242 }
27243
27244 if (deviceType == ma_device_type_capture) {
27245 if (pDevice == NULL || !pDevice->capture.usingDefaultChannels) {
27246 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->capture.channels);
27247 }
27248 if (pDevice == NULL || !pDevice->capture.usingDefaultFormat) {
27249 ((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);
27250 }
27251 } else {
27252 if (pDevice == NULL || !pDevice->playback.usingDefaultChannels) {
27253 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->playback.channels);
27254 }
27255 if (pDevice == NULL || !pDevice->playback.usingDefaultFormat) {
27256 ((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);
27257 }
27258 }
27259
27260 bufferCapacityInFrames = pConfig->periodSizeInFrames * pConfig->periods;
27261 if (bufferCapacityInFrames == 0) {
27262 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pConfig->sampleRate) * pConfig->periods;
27263 }
27264
27265 ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
27266 ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pConfig->periods);
27267
27268 if (deviceType == ma_device_type_capture) {
27269 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
27270 } else {
27271 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
27272 }
27273
27274 /* 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. */
27275 ((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);
27276 }
27277
27278 ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);
27279
27280 resultAA = ((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream);
27281 if (resultAA != MA_AAUDIO_OK) {
27282 *ppStream = NULL;
27283 ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
27284 return ma_result_from_aaudio(resultAA);
27285 }
27286
27287 ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
27288 return MA_SUCCESS;
27289 }
27290
27291 static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
27292 {
27293 return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
27294 }
27295
27296 static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
27297 {
27298 /* The only way to know this is to try creating a stream. */
27299 ma_AAudioStream* pStream;
27300 ma_result result = ma_open_stream__aaudio(pContext, deviceType, NULL, ma_share_mode_shared, NULL, NULL, &pStream);
27301 if (result != MA_SUCCESS) {
27302 return MA_FALSE;
27303 }
27304
27305 ma_close_stream__aaudio(pContext, pStream);
27306 return MA_TRUE;
27307 }
27308
27309 static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState)
27310 {
27311 ma_aaudio_stream_state_t actualNewState;
27312 ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
27313 if (resultAA != MA_AAUDIO_OK) {
27314 return ma_result_from_aaudio(resultAA);
27315 }
27316
27317 if (newState != actualNewState) {
27318 return MA_ERROR; /* Failed to transition into the expected state. */
27319 }
27320
27321 return MA_SUCCESS;
27322 }
27323
27324
27325 static ma_bool32 ma_context_is_device_id_equal__aaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
27326 {
27327 MA_ASSERT(pContext != NULL);
27328 MA_ASSERT(pID0 != NULL);
27329 MA_ASSERT(pID1 != NULL);
27330 (void)pContext;
27331
27332 return pID0->aaudio == pID1->aaudio;
27333 }
27334
27335 static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
27336 {
27337 ma_bool32 cbResult = MA_TRUE;
27338
27339 MA_ASSERT(pContext != NULL);
27340 MA_ASSERT(callback != NULL);
27341
27342 /* 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. */
27343
27344 /* Playback. */
27345 if (cbResult) {
27346 ma_device_info deviceInfo;
27347 MA_ZERO_OBJECT(&deviceInfo);
27348 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
27349 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
27350
27351 if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
27352 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
27353 }
27354 }
27355
27356 /* Capture. */
27357 if (cbResult) {
27358 ma_device_info deviceInfo;
27359 MA_ZERO_OBJECT(&deviceInfo);
27360 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
27361 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
27362
27363 if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
27364 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
27365 }
27366 }
27367
27368 return MA_SUCCESS;
27369 }
27370
27371 static 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)
27372 {
27373 ma_AAudioStream* pStream;
27374 ma_result result;
27375
27376 MA_ASSERT(pContext != NULL);
27377
27378 /* No exclusive mode with AAudio. */
27379 if (shareMode == ma_share_mode_exclusive) {
27380 return MA_SHARE_MODE_NOT_SUPPORTED;
27381 }
27382
27383 /* ID */
27384 if (pDeviceID != NULL) {
27385 pDeviceInfo->id.aaudio = pDeviceID->aaudio;
27386 } else {
27387 pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
27388 }
27389
27390 /* Name */
27391 if (deviceType == ma_device_type_playback) {
27392 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
27393 } else {
27394 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
27395 }
27396
27397
27398 /* We'll need to open the device to get accurate sample rate and channel count information. */
27399 result = ma_open_stream__aaudio(pContext, deviceType, pDeviceID, shareMode, NULL, NULL, &pStream);
27400 if (result != MA_SUCCESS) {
27401 return result;
27402 }
27403
27404 pDeviceInfo->minChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
27405 pDeviceInfo->maxChannels = pDeviceInfo->minChannels;
27406 pDeviceInfo->minSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
27407 pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
27408
27409 ma_close_stream__aaudio(pContext, pStream);
27410 pStream = NULL;
27411
27412
27413 /* AAudio supports s16 and f32. */
27414 pDeviceInfo->formatCount = 2;
27415 pDeviceInfo->formats[0] = ma_format_s16;
27416 pDeviceInfo->formats[1] = ma_format_f32;
27417
27418 return MA_SUCCESS;
27419 }
27420
27421
27422 static void ma_device_uninit__aaudio(ma_device* pDevice)
27423 {
27424 MA_ASSERT(pDevice != NULL);
27425
27426 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27427 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
27428 pDevice->aaudio.pStreamCapture = NULL;
27429 }
27430
27431 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27432 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
27433 pDevice->aaudio.pStreamPlayback = NULL;
27434 }
27435
27436 if (pDevice->type == ma_device_type_duplex) {
27437 ma_pcm_rb_uninit(&pDevice->aaudio.duplexRB);
27438 }
27439 }
27440
27441 static ma_result ma_device_init__aaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
27442 {
27443 ma_result result;
27444
27445 MA_ASSERT(pDevice != NULL);
27446
27447 if (pConfig->deviceType == ma_device_type_loopback) {
27448 return MA_DEVICE_TYPE_NOT_SUPPORTED;
27449 }
27450
27451 /* No exclusive mode with AAudio. */
27452 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
27453 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
27454 return MA_SHARE_MODE_NOT_SUPPORTED;
27455 }
27456
27457 /* We first need to try opening the stream. */
27458 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
27459 int32_t bufferCapacityInFrames;
27460 int32_t framesPerDataCallback;
27461
27462 result = ma_open_stream__aaudio(pContext, ma_device_type_capture, pConfig->capture.pDeviceID, pConfig->capture.shareMode, pConfig, pDevice, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
27463 if (result != MA_SUCCESS) {
27464 return result; /* Failed to open the AAudio stream. */
27465 }
27466
27467 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;
27468 pDevice->capture.internalChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
27469 pDevice->capture.internalSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
27470 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. */
27471
27472 bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pContext->aaudio.AAudioStream_getBufferCapacityInFrames)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
27473 framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pContext->aaudio.AAudioStream_getFramesPerDataCallback)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
27474
27475 if (framesPerDataCallback > 0) {
27476 pDevice->capture.internalPeriodSizeInFrames = framesPerDataCallback;
27477 pDevice->capture.internalPeriods = bufferCapacityInFrames / framesPerDataCallback;
27478 } else {
27479 pDevice->capture.internalPeriodSizeInFrames = bufferCapacityInFrames;
27480 pDevice->capture.internalPeriods = 1;
27481 }
27482 }
27483
27484 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
27485 int32_t bufferCapacityInFrames;
27486 int32_t framesPerDataCallback;
27487
27488 result = ma_open_stream__aaudio(pContext, ma_device_type_playback, pConfig->playback.pDeviceID, pConfig->playback.shareMode, pConfig, pDevice, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
27489 if (result != MA_SUCCESS) {
27490 return result; /* Failed to open the AAudio stream. */
27491 }
27492
27493 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;
27494 pDevice->playback.internalChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
27495 pDevice->playback.internalSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
27496 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. */
27497
27498 bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pContext->aaudio.AAudioStream_getBufferCapacityInFrames)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
27499 framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pContext->aaudio.AAudioStream_getFramesPerDataCallback)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
27500
27501 if (framesPerDataCallback > 0) {
27502 pDevice->playback.internalPeriodSizeInFrames = framesPerDataCallback;
27503 pDevice->playback.internalPeriods = bufferCapacityInFrames / framesPerDataCallback;
27504 } else {
27505 pDevice->playback.internalPeriodSizeInFrames = bufferCapacityInFrames;
27506 pDevice->playback.internalPeriods = 1;
27507 }
27508 }
27509
27510 if (pConfig->deviceType == ma_device_type_duplex) {
27511 ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames) * pDevice->capture.internalPeriods;
27512 ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->aaudio.duplexRB);
27513 if (result != MA_SUCCESS) {
27514 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27515 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
27516 }
27517 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27518 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
27519 }
27520 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[AAudio] Failed to initialize ring buffer.", result);
27521 }
27522
27523 /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
27524 {
27525 ma_uint32 marginSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
27526 void* pMarginData;
27527 ma_pcm_rb_acquire_write(&pDevice->aaudio.duplexRB, &marginSizeInFrames, &pMarginData);
27528 {
27529 MA_ZERO_MEMORY(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
27530 }
27531 ma_pcm_rb_commit_write(&pDevice->aaudio.duplexRB, marginSizeInFrames, pMarginData);
27532 }
27533 }
27534
27535 return MA_SUCCESS;
27536 }
27537
27538 static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
27539 {
27540 ma_aaudio_result_t resultAA;
27541 ma_aaudio_stream_state_t currentState;
27542
27543 MA_ASSERT(pDevice != NULL);
27544
27545 resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
27546 if (resultAA != MA_AAUDIO_OK) {
27547 return ma_result_from_aaudio(resultAA);
27548 }
27549
27550 /* Do we actually need to wait for the device to transition into it's started state? */
27551
27552 /* 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. */
27553 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
27554 if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
27555 ma_result result;
27556
27557 if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
27558 return MA_ERROR; /* Expecting the stream to be a starting or started state. */
27559 }
27560
27561 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
27562 if (result != MA_SUCCESS) {
27563 return result;
27564 }
27565 }
27566
27567 return MA_SUCCESS;
27568 }
27569
27570 static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
27571 {
27572 ma_aaudio_result_t resultAA;
27573 ma_aaudio_stream_state_t currentState;
27574
27575 MA_ASSERT(pDevice != NULL);
27576
27577 /*
27578 From the AAudio documentation:
27579
27580 The stream will stop after all of the data currently buffered has been played.
27581
27582 This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic.
27583 */
27584
27585 resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
27586 if (resultAA != MA_AAUDIO_OK) {
27587 return ma_result_from_aaudio(resultAA);
27588 }
27589
27590 /* 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. */
27591 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
27592 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
27593 ma_result result;
27594
27595 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
27596 return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */
27597 }
27598
27599 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
27600 if (result != MA_SUCCESS) {
27601 return result;
27602 }
27603 }
27604
27605 return MA_SUCCESS;
27606 }
27607
27608 static ma_result ma_device_start__aaudio(ma_device* pDevice)
27609 {
27610 MA_ASSERT(pDevice != NULL);
27611
27612 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27613 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
27614 if (result != MA_SUCCESS) {
27615 return result;
27616 }
27617 }
27618
27619 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27620 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
27621 if (result != MA_SUCCESS) {
27622 if (pDevice->type == ma_device_type_duplex) {
27623 ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
27624 }
27625 return result;
27626 }
27627 }
27628
27629 return MA_SUCCESS;
27630 }
27631
27632 static ma_result ma_device_stop__aaudio(ma_device* pDevice)
27633 {
27634 ma_stop_proc onStop;
27635
27636 MA_ASSERT(pDevice != NULL);
27637
27638 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27639 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
27640 if (result != MA_SUCCESS) {
27641 return result;
27642 }
27643 }
27644
27645 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27646 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
27647 if (result != MA_SUCCESS) {
27648 return result;
27649 }
27650 }
27651
27652 onStop = pDevice->onStop;
27653 if (onStop) {
27654 onStop(pDevice);
27655 }
27656
27657 return MA_SUCCESS;
27658 }
27659
27660
27661 static ma_result ma_context_uninit__aaudio(ma_context* pContext)
27662 {
27663 MA_ASSERT(pContext != NULL);
27664 MA_ASSERT(pContext->backend == ma_backend_aaudio);
27665
27666 ma_dlclose(pContext, pContext->aaudio.hAAudio);
27667 pContext->aaudio.hAAudio = NULL;
27668
27669 return MA_SUCCESS;
27670 }
27671
27672 static ma_result ma_context_init__aaudio(const ma_context_config* pConfig, ma_context* pContext)
27673 {
27674 const char* libNames[] = {
27675 "libaaudio.so"
27676 };
27677 size_t i;
27678
27679 for (i = 0; i < ma_countof(libNames); ++i) {
27680 pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]);
27681 if (pContext->aaudio.hAAudio != NULL) {
27682 break;
27683 }
27684 }
27685
27686 if (pContext->aaudio.hAAudio == NULL) {
27687 return MA_FAILED_TO_INIT_BACKEND;
27688 }
27689
27690 pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
27691 pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
27692 pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
27693 pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
27694 pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
27695 pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
27696 pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
27697 pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
27698 pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
27699 pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
27700 pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
27701 pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
27702 pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
27703 pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
27704 pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close");
27705 pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState");
27706 pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
27707 pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat");
27708 pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
27709 pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
27710 pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
27711 pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
27712 pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
27713 pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart");
27714 pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop");
27715
27716 pContext->isBackendAsynchronous = MA_TRUE;
27717
27718 pContext->onUninit = ma_context_uninit__aaudio;
27719 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__aaudio;
27720 pContext->onEnumDevices = ma_context_enumerate_devices__aaudio;
27721 pContext->onGetDeviceInfo = ma_context_get_device_info__aaudio;
27722 pContext->onDeviceInit = ma_device_init__aaudio;
27723 pContext->onDeviceUninit = ma_device_uninit__aaudio;
27724 pContext->onDeviceStart = ma_device_start__aaudio;
27725 pContext->onDeviceStop = ma_device_stop__aaudio;
27726
27727 (void)pConfig;
27728 return MA_SUCCESS;
27729 }
27730 #endif /* AAudio */
27731
27732
27733 /******************************************************************************
27734
27735 OpenSL|ES Backend
27736
27737 ******************************************************************************/
27738 #ifdef MA_HAS_OPENSL
27739 #include <SLES/OpenSLES.h>
27740 #ifdef MA_ANDROID
27741 #include <SLES/OpenSLES_Android.h>
27742 #endif
27743
27744 /* OpenSL|ES has one-per-application objects :( */
27745 SLObjectItf g_maEngineObjectSL = NULL;
27746 SLEngineItf g_maEngineSL = NULL;
27747 ma_uint32 g_maOpenSLInitCounter = 0;
27748
27749 #define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
27750 #define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
27751 #define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
27752 #define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
27753
27754 #ifdef MA_ANDROID
27755 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
27756 #else
27757 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
27758 #endif
27759
27760 static ma_result ma_result_from_OpenSL(SLuint32 result)
27761 {
27762 switch (result)
27763 {
27764 case SL_RESULT_SUCCESS: return MA_SUCCESS;
27765 case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR;
27766 case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS;
27767 case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY;
27768 case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA;
27769 case SL_RESULT_RESOURCE_LOST: return MA_ERROR;
27770 case SL_RESULT_IO_ERROR: return MA_IO_ERROR;
27771 case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE;
27772 case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA;
27773 case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED;
27774 case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR;
27775 case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED;
27776 case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED;
27777 case SL_RESULT_INTERNAL_ERROR: return MA_ERROR;
27778 case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR;
27779 case SL_RESULT_OPERATION_ABORTED: return MA_ERROR;
27780 case SL_RESULT_CONTROL_LOST: return MA_ERROR;
27781 default: return MA_ERROR;
27782 }
27783 }
27784
27785 /* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
27786 static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
27787 {
27788 switch (id)
27789 {
27790 case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
27791 case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
27792 case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
27793 case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
27794 case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
27795 case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
27796 case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
27797 case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
27798 case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
27799 case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
27800 case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
27801 case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
27802 case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
27803 case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
27804 case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
27805 case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
27806 case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
27807 case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
27808 default: return 0;
27809 }
27810 }
27811
27812 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */
27813 static SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
27814 {
27815 switch (id)
27816 {
27817 case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
27818 case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
27819 case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
27820 case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
27821 case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
27822 case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
27823 case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
27824 case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
27825 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
27826 case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
27827 case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
27828 case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
27829 case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
27830 case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
27831 case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
27832 case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
27833 case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
27834 case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
27835 case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
27836 default: return 0;
27837 }
27838 }
27839
27840 /* Converts a channel mapping to an OpenSL-style channel mask. */
27841 static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel channelMap[MA_MAX_CHANNELS], ma_uint32 channels)
27842 {
27843 SLuint32 channelMask = 0;
27844 ma_uint32 iChannel;
27845 for (iChannel = 0; iChannel < channels; ++iChannel) {
27846 channelMask |= ma_channel_id_to_opensl(channelMap[iChannel]);
27847 }
27848
27849 return channelMask;
27850 }
27851
27852 /* Converts an OpenSL-style channel mask to a miniaudio channel map. */
27853 static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
27854 {
27855 if (channels == 1 && channelMask == 0) {
27856 channelMap[0] = MA_CHANNEL_MONO;
27857 } else if (channels == 2 && channelMask == 0) {
27858 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27859 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27860 } else {
27861 if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
27862 channelMap[0] = MA_CHANNEL_MONO;
27863 } else {
27864 /* Just iterate over each bit. */
27865 ma_uint32 iChannel = 0;
27866 ma_uint32 iBit;
27867 for (iBit = 0; iBit < 32; ++iBit) {
27868 SLuint32 bitValue = (channelMask & (1UL << iBit));
27869 if (bitValue != 0) {
27870 /* The bit is set. */
27871 channelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
27872 iChannel += 1;
27873 }
27874 }
27875 }
27876 }
27877 }
27878
27879 static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
27880 {
27881 if (samplesPerSec <= SL_SAMPLINGRATE_8) {
27882 return SL_SAMPLINGRATE_8;
27883 }
27884 if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
27885 return SL_SAMPLINGRATE_11_025;
27886 }
27887 if (samplesPerSec <= SL_SAMPLINGRATE_12) {
27888 return SL_SAMPLINGRATE_12;
27889 }
27890 if (samplesPerSec <= SL_SAMPLINGRATE_16) {
27891 return SL_SAMPLINGRATE_16;
27892 }
27893 if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
27894 return SL_SAMPLINGRATE_22_05;
27895 }
27896 if (samplesPerSec <= SL_SAMPLINGRATE_24) {
27897 return SL_SAMPLINGRATE_24;
27898 }
27899 if (samplesPerSec <= SL_SAMPLINGRATE_32) {
27900 return SL_SAMPLINGRATE_32;
27901 }
27902 if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
27903 return SL_SAMPLINGRATE_44_1;
27904 }
27905 if (samplesPerSec <= SL_SAMPLINGRATE_48) {
27906 return SL_SAMPLINGRATE_48;
27907 }
27908
27909 /* Android doesn't support more than 48000. */
27910 #ifndef MA_ANDROID
27911 if (samplesPerSec <= SL_SAMPLINGRATE_64) {
27912 return SL_SAMPLINGRATE_64;
27913 }
27914 if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
27915 return SL_SAMPLINGRATE_88_2;
27916 }
27917 if (samplesPerSec <= SL_SAMPLINGRATE_96) {
27918 return SL_SAMPLINGRATE_96;
27919 }
27920 if (samplesPerSec <= SL_SAMPLINGRATE_192) {
27921 return SL_SAMPLINGRATE_192;
27922 }
27923 #endif
27924
27925 return SL_SAMPLINGRATE_16;
27926 }
27927
27928
27929 static ma_bool32 ma_context_is_device_id_equal__opensl(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
27930 {
27931 MA_ASSERT(pContext != NULL);
27932 MA_ASSERT(pID0 != NULL);
27933 MA_ASSERT(pID1 != NULL);
27934 (void)pContext;
27935
27936 return pID0->opensl == pID1->opensl;
27937 }
27938
27939 static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
27940 {
27941 ma_bool32 cbResult;
27942
27943 MA_ASSERT(pContext != NULL);
27944 MA_ASSERT(callback != NULL);
27945
27946 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. */
27947 if (g_maOpenSLInitCounter == 0) {
27948 return MA_INVALID_OPERATION;
27949 }
27950
27951 /*
27952 TODO: Test Me.
27953
27954 This is currently untested, so for now we are just returning default devices.
27955 */
27956 #if 0 && !defined(MA_ANDROID)
27957 ma_bool32 isTerminated = MA_FALSE;
27958
27959 SLuint32 pDeviceIDs[128];
27960 SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
27961
27962 SLAudioIODeviceCapabilitiesItf deviceCaps;
27963 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
27964 if (resultSL != SL_RESULT_SUCCESS) {
27965 /* The interface may not be supported so just report a default device. */
27966 goto return_default_device;
27967 }
27968
27969 /* Playback */
27970 if (!isTerminated) {
27971 resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
27972 if (resultSL != SL_RESULT_SUCCESS) {
27973 return ma_result_from_OpenSL(resultSL);
27974 }
27975
27976 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
27977 ma_device_info deviceInfo;
27978 MA_ZERO_OBJECT(&deviceInfo);
27979 deviceInfo.id.opensl = pDeviceIDs[iDevice];
27980
27981 SLAudioOutputDescriptor desc;
27982 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
27983 if (resultSL == SL_RESULT_SUCCESS) {
27984 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
27985
27986 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
27987 if (cbResult == MA_FALSE) {
27988 isTerminated = MA_TRUE;
27989 break;
27990 }
27991 }
27992 }
27993 }
27994
27995 /* Capture */
27996 if (!isTerminated) {
27997 resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
27998 if (resultSL != SL_RESULT_SUCCESS) {
27999 return ma_result_from_OpenSL(resultSL);
28000 }
28001
28002 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
28003 ma_device_info deviceInfo;
28004 MA_ZERO_OBJECT(&deviceInfo);
28005 deviceInfo.id.opensl = pDeviceIDs[iDevice];
28006
28007 SLAudioInputDescriptor desc;
28008 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
28009 if (resultSL == SL_RESULT_SUCCESS) {
28010 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
28011
28012 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
28013 if (cbResult == MA_FALSE) {
28014 isTerminated = MA_TRUE;
28015 break;
28016 }
28017 }
28018 }
28019 }
28020
28021 return MA_SUCCESS;
28022 #else
28023 goto return_default_device;
28024 #endif
28025
28026 return_default_device:;
28027 cbResult = MA_TRUE;
28028
28029 /* Playback. */
28030 if (cbResult) {
28031 ma_device_info deviceInfo;
28032 MA_ZERO_OBJECT(&deviceInfo);
28033 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
28034 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
28035 }
28036
28037 /* Capture. */
28038 if (cbResult) {
28039 ma_device_info deviceInfo;
28040 MA_ZERO_OBJECT(&deviceInfo);
28041 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
28042 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
28043 }
28044
28045 return MA_SUCCESS;
28046 }
28047
28048 static 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)
28049 {
28050 MA_ASSERT(pContext != NULL);
28051
28052 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. */
28053 if (g_maOpenSLInitCounter == 0) {
28054 return MA_INVALID_OPERATION;
28055 }
28056
28057 /* No exclusive mode with OpenSL|ES. */
28058 if (shareMode == ma_share_mode_exclusive) {
28059 return MA_SHARE_MODE_NOT_SUPPORTED;
28060 }
28061
28062 /*
28063 TODO: Test Me.
28064
28065 This is currently untested, so for now we are just returning default devices.
28066 */
28067 #if 0 && !defined(MA_ANDROID)
28068 SLAudioIODeviceCapabilitiesItf deviceCaps;
28069 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
28070 if (resultSL != SL_RESULT_SUCCESS) {
28071 /* The interface may not be supported so just report a default device. */
28072 goto return_default_device;
28073 }
28074
28075 if (deviceType == ma_device_type_playback) {
28076 SLAudioOutputDescriptor desc;
28077 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
28078 if (resultSL != SL_RESULT_SUCCESS) {
28079 return ma_result_from_OpenSL(resultSL);
28080 }
28081
28082 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
28083 } else {
28084 SLAudioInputDescriptor desc;
28085 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
28086 if (resultSL != SL_RESULT_SUCCESS) {
28087 return ma_result_from_OpenSL(resultSL);
28088 }
28089
28090 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
28091 }
28092
28093 goto return_detailed_info;
28094 #else
28095 goto return_default_device;
28096 #endif
28097
28098 return_default_device:
28099 if (pDeviceID != NULL) {
28100 if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
28101 (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
28102 return MA_NO_DEVICE; /* Don't know the device. */
28103 }
28104 }
28105
28106 /* Name / Description */
28107 if (deviceType == ma_device_type_playback) {
28108 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
28109 } else {
28110 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
28111 }
28112
28113 goto return_detailed_info;
28114
28115
28116 return_detailed_info:
28117
28118 /*
28119 For now we're just outputting a set of values that are supported by the API but not necessarily supported
28120 by the device natively. Later on we should work on this so that it more closely reflects the device's
28121 actual native format.
28122 */
28123 pDeviceInfo->minChannels = 1;
28124 pDeviceInfo->maxChannels = 2;
28125 pDeviceInfo->minSampleRate = 8000;
28126 pDeviceInfo->maxSampleRate = 48000;
28127 pDeviceInfo->formatCount = 2;
28128 pDeviceInfo->formats[0] = ma_format_u8;
28129 pDeviceInfo->formats[1] = ma_format_s16;
28130 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
28131 pDeviceInfo->formats[pDeviceInfo->formatCount] = ma_format_f32;
28132 pDeviceInfo->formatCount += 1;
28133 #endif
28134
28135 return MA_SUCCESS;
28136 }
28137
28138
28139 #ifdef MA_ANDROID
28140 /*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
28141 static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
28142 {
28143 ma_device* pDevice = (ma_device*)pUserData;
28144 size_t periodSizeInBytes;
28145 ma_uint8* pBuffer;
28146 SLresult resultSL;
28147
28148 MA_ASSERT(pDevice != NULL);
28149
28150 (void)pBufferQueue;
28151
28152 /*
28153 For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
28154 OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
28155 but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
28156 */
28157
28158 /* Don't do anything if the device is not started. */
28159 if (pDevice->state != MA_STATE_STARTED) {
28160 return;
28161 }
28162
28163 /* Don't do anything if the device is being drained. */
28164 if (pDevice->opensl.isDrainingCapture) {
28165 return;
28166 }
28167
28168 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
28169 pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
28170
28171 if (pDevice->type == ma_device_type_duplex) {
28172 ma_device__handle_duplex_callback_capture(pDevice, pDevice->capture.internalPeriodSizeInFrames, pBuffer, &pDevice->opensl.duplexRB);
28173 } else {
28174 ma_device__send_frames_to_client(pDevice, pDevice->capture.internalPeriodSizeInFrames, pBuffer);
28175 }
28176
28177 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
28178 if (resultSL != SL_RESULT_SUCCESS) {
28179 return;
28180 }
28181
28182 pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
28183 }
28184
28185 static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
28186 {
28187 ma_device* pDevice = (ma_device*)pUserData;
28188 size_t periodSizeInBytes;
28189 ma_uint8* pBuffer;
28190 SLresult resultSL;
28191
28192 MA_ASSERT(pDevice != NULL);
28193
28194 (void)pBufferQueue;
28195
28196 /* Don't do anything if the device is not started. */
28197 if (pDevice->state != MA_STATE_STARTED) {
28198 return;
28199 }
28200
28201 /* Don't do anything if the device is being drained. */
28202 if (pDevice->opensl.isDrainingPlayback) {
28203 return;
28204 }
28205
28206 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
28207 pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
28208
28209 if (pDevice->type == ma_device_type_duplex) {
28210 ma_device__handle_duplex_callback_playback(pDevice, pDevice->playback.internalPeriodSizeInFrames, pBuffer, &pDevice->opensl.duplexRB);
28211 } else {
28212 ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames, pBuffer);
28213 }
28214
28215 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
28216 if (resultSL != SL_RESULT_SUCCESS) {
28217 return;
28218 }
28219
28220 pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
28221 }
28222 #endif
28223
28224 static void ma_device_uninit__opensl(ma_device* pDevice)
28225 {
28226 MA_ASSERT(pDevice != NULL);
28227
28228 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. */
28229 if (g_maOpenSLInitCounter == 0) {
28230 return;
28231 }
28232
28233 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28234 if (pDevice->opensl.pAudioRecorderObj) {
28235 MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
28236 }
28237
28238 ma__free_from_callbacks(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks);
28239 }
28240
28241 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28242 if (pDevice->opensl.pAudioPlayerObj) {
28243 MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
28244 }
28245 if (pDevice->opensl.pOutputMixObj) {
28246 MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
28247 }
28248
28249 ma__free_from_callbacks(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks);
28250 }
28251
28252 if (pDevice->type == ma_device_type_duplex) {
28253 ma_pcm_rb_uninit(&pDevice->opensl.duplexRB);
28254 }
28255 }
28256
28257 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
28258 typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
28259 #else
28260 typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
28261 #endif
28262
28263 static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat)
28264 {
28265 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
28266 if (format == ma_format_f32) {
28267 pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
28268 pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
28269 } else {
28270 pDataFormat->formatType = SL_DATAFORMAT_PCM;
28271 }
28272 #else
28273 pDataFormat->formatType = SL_DATAFORMAT_PCM;
28274 #endif
28275
28276 pDataFormat->numChannels = channels;
28277 ((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 */
28278 pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format)*8;
28279 pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
28280 pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
28281
28282 /*
28283 Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
28284 - Only mono and stereo is supported.
28285 - Only u8 and s16 formats are supported.
28286 - Maximum sample rate of 48000.
28287 */
28288 #ifdef MA_ANDROID
28289 if (pDataFormat->numChannels > 2) {
28290 pDataFormat->numChannels = 2;
28291 }
28292 #if __ANDROID_API__ >= 21
28293 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
28294 /* It's floating point. */
28295 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
28296 if (pDataFormat->bitsPerSample > 32) {
28297 pDataFormat->bitsPerSample = 32;
28298 }
28299 } else {
28300 if (pDataFormat->bitsPerSample > 16) {
28301 pDataFormat->bitsPerSample = 16;
28302 }
28303 }
28304 #else
28305 if (pDataFormat->bitsPerSample > 16) {
28306 pDataFormat->bitsPerSample = 16;
28307 }
28308 #endif
28309 if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
28310 ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
28311 }
28312 #endif
28313
28314 pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */
28315
28316 return MA_SUCCESS;
28317 }
28318
28319 static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap)
28320 {
28321 ma_bool32 isFloatingPoint = MA_FALSE;
28322 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
28323 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
28324 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
28325 isFloatingPoint = MA_TRUE;
28326 }
28327 #endif
28328 if (isFloatingPoint) {
28329 if (pDataFormat->bitsPerSample == 32) {
28330 *pFormat = ma_format_f32;
28331 }
28332 } else {
28333 if (pDataFormat->bitsPerSample == 8) {
28334 *pFormat = ma_format_u8;
28335 } else if (pDataFormat->bitsPerSample == 16) {
28336 *pFormat = ma_format_s16;
28337 } else if (pDataFormat->bitsPerSample == 24) {
28338 *pFormat = ma_format_s24;
28339 } else if (pDataFormat->bitsPerSample == 32) {
28340 *pFormat = ma_format_s32;
28341 }
28342 }
28343
28344 *pChannels = pDataFormat->numChannels;
28345 *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
28346 ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, pDataFormat->numChannels, pChannelMap);
28347
28348 return MA_SUCCESS;
28349 }
28350
28351 static ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
28352 {
28353 #ifdef MA_ANDROID
28354 SLDataLocator_AndroidSimpleBufferQueue queue;
28355 SLresult resultSL;
28356 ma_uint32 periodSizeInFrames;
28357 size_t bufferSizeInBytes;
28358 const SLInterfaceID itfIDs1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
28359 const SLboolean itfIDsRequired1[] = {SL_BOOLEAN_TRUE};
28360 #endif
28361
28362 (void)pContext;
28363
28364 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. */
28365 if (g_maOpenSLInitCounter == 0) {
28366 return MA_INVALID_OPERATION;
28367 }
28368
28369 if (pConfig->deviceType == ma_device_type_loopback) {
28370 return MA_DEVICE_TYPE_NOT_SUPPORTED;
28371 }
28372
28373 /*
28374 For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
28375 been able to test with and I currently depend on Android-specific extensions (simple buffer
28376 queues).
28377 */
28378 #ifdef MA_ANDROID
28379 /* No exclusive mode with OpenSL|ES. */
28380 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
28381 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
28382 return MA_SHARE_MODE_NOT_SUPPORTED;
28383 }
28384
28385 /* Now we can start initializing the device properly. */
28386 MA_ASSERT(pDevice != NULL);
28387 MA_ZERO_OBJECT(&pDevice->opensl);
28388
28389 queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
28390 queue.numBuffers = pConfig->periods;
28391
28392
28393 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
28394 ma_SLDataFormat_PCM pcm;
28395 SLDataLocator_IODevice locatorDevice;
28396 SLDataSource source;
28397 SLDataSink sink;
28398
28399 ma_SLDataFormat_PCM_init__opensl(pConfig->capture.format, pConfig->capture.channels, pConfig->sampleRate, pConfig->capture.channelMap, &pcm);
28400
28401 locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
28402 locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
28403 locatorDevice.deviceID = (pConfig->capture.pDeviceID == NULL) ? SL_DEFAULTDEVICEID_AUDIOINPUT : pConfig->capture.pDeviceID->opensl;
28404 locatorDevice.device = NULL;
28405
28406 source.pLocator = &locatorDevice;
28407 source.pFormat = NULL;
28408
28409 sink.pLocator = &queue;
28410 sink.pFormat = (SLDataFormat_PCM*)&pcm;
28411
28412 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
28413 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
28414 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
28415 pcm.formatType = SL_DATAFORMAT_PCM;
28416 pcm.numChannels = 1;
28417 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
28418 pcm.bitsPerSample = 16;
28419 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
28420 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
28421 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
28422 }
28423
28424 if (resultSL != SL_RESULT_SUCCESS) {
28425 ma_device_uninit__opensl(pDevice);
28426 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.", ma_result_from_OpenSL(resultSL));
28427 }
28428
28429 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);
28430 if (resultSL != SL_RESULT_SUCCESS) {
28431 ma_device_uninit__opensl(pDevice);
28432 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", ma_result_from_OpenSL(resultSL));
28433 }
28434
28435 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
28436 if (resultSL != SL_RESULT_SUCCESS) {
28437 ma_device_uninit__opensl(pDevice);
28438 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", ma_result_from_OpenSL(resultSL));
28439 }
28440
28441 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
28442 if (resultSL != SL_RESULT_SUCCESS) {
28443 ma_device_uninit__opensl(pDevice);
28444 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL));
28445 }
28446
28447 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);
28448 if (resultSL != SL_RESULT_SUCCESS) {
28449 ma_device_uninit__opensl(pDevice);
28450 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", ma_result_from_OpenSL(resultSL));
28451 }
28452
28453 /* The internal format is determined by the "pcm" object. */
28454 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->capture.internalFormat, &pDevice->capture.internalChannels, &pDevice->capture.internalSampleRate, pDevice->capture.internalChannelMap);
28455
28456 /* Buffer. */
28457 periodSizeInFrames = pConfig->periodSizeInFrames;
28458 if (periodSizeInFrames == 0) {
28459 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->capture.internalSampleRate);
28460 }
28461 pDevice->capture.internalPeriods = pConfig->periods;
28462 pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames;
28463 pDevice->opensl.currentBufferIndexCapture = 0;
28464
28465 bufferSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) * pDevice->capture.internalPeriods;
28466 pDevice->opensl.pBufferCapture = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pContext->allocationCallbacks);
28467 if (pDevice->opensl.pBufferCapture == NULL) {
28468 ma_device_uninit__opensl(pDevice);
28469 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
28470 }
28471 MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
28472 }
28473
28474 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
28475 ma_SLDataFormat_PCM pcm;
28476 SLDataSource source;
28477 SLDataLocator_OutputMix outmixLocator;
28478 SLDataSink sink;
28479
28480 ma_SLDataFormat_PCM_init__opensl(pConfig->playback.format, pConfig->playback.channels, pConfig->sampleRate, pConfig->playback.channelMap, &pcm);
28481
28482 resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
28483 if (resultSL != SL_RESULT_SUCCESS) {
28484 ma_device_uninit__opensl(pDevice);
28485 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.", ma_result_from_OpenSL(resultSL));
28486 }
28487
28488 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);
28489 if (resultSL != SL_RESULT_SUCCESS) {
28490 ma_device_uninit__opensl(pDevice);
28491 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", ma_result_from_OpenSL(resultSL));
28492 }
28493
28494 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
28495 if (resultSL != SL_RESULT_SUCCESS) {
28496 ma_device_uninit__opensl(pDevice);
28497 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", ma_result_from_OpenSL(resultSL));
28498 }
28499
28500 /* Set the output device. */
28501 if (pConfig->playback.pDeviceID != NULL) {
28502 SLuint32 deviceID_OpenSL = pConfig->playback.pDeviceID->opensl;
28503 MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
28504 }
28505
28506 source.pLocator = &queue;
28507 source.pFormat = (SLDataFormat_PCM*)&pcm;
28508
28509 outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
28510 outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
28511
28512 sink.pLocator = &outmixLocator;
28513 sink.pFormat = NULL;
28514
28515 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
28516 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
28517 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
28518 pcm.formatType = SL_DATAFORMAT_PCM;
28519 pcm.numChannels = 2;
28520 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
28521 pcm.bitsPerSample = 16;
28522 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
28523 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
28524 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
28525 }
28526
28527 if (resultSL != SL_RESULT_SUCCESS) {
28528 ma_device_uninit__opensl(pDevice);
28529 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.", ma_result_from_OpenSL(resultSL));
28530 }
28531
28532 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);
28533 if (resultSL != SL_RESULT_SUCCESS) {
28534 ma_device_uninit__opensl(pDevice);
28535 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", ma_result_from_OpenSL(resultSL));
28536 }
28537
28538 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
28539 if (resultSL != SL_RESULT_SUCCESS) {
28540 ma_device_uninit__opensl(pDevice);
28541 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", ma_result_from_OpenSL(resultSL));
28542 }
28543
28544 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
28545 if (resultSL != SL_RESULT_SUCCESS) {
28546 ma_device_uninit__opensl(pDevice);
28547 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL));
28548 }
28549
28550 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);
28551 if (resultSL != SL_RESULT_SUCCESS) {
28552 ma_device_uninit__opensl(pDevice);
28553 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", ma_result_from_OpenSL(resultSL));
28554 }
28555
28556 /* The internal format is determined by the "pcm" object. */
28557 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->playback.internalFormat, &pDevice->playback.internalChannels, &pDevice->playback.internalSampleRate, pDevice->playback.internalChannelMap);
28558
28559 /* Buffer. */
28560 periodSizeInFrames = pConfig->periodSizeInFrames;
28561 if (periodSizeInFrames == 0) {
28562 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->playback.internalSampleRate);
28563 }
28564 pDevice->playback.internalPeriods = pConfig->periods;
28565 pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
28566 pDevice->opensl.currentBufferIndexPlayback = 0;
28567
28568 bufferSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) * pDevice->playback.internalPeriods;
28569 pDevice->opensl.pBufferPlayback = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pContext->allocationCallbacks);
28570 if (pDevice->opensl.pBufferPlayback == NULL) {
28571 ma_device_uninit__opensl(pDevice);
28572 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
28573 }
28574 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
28575 }
28576
28577 if (pConfig->deviceType == ma_device_type_duplex) {
28578 ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames) * pDevice->capture.internalPeriods;
28579 ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->opensl.duplexRB);
28580 if (result != MA_SUCCESS) {
28581 ma_device_uninit__opensl(pDevice);
28582 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to initialize ring buffer.", result);
28583 }
28584
28585 /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
28586 {
28587 ma_uint32 marginSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
28588 void* pMarginData;
28589 ma_pcm_rb_acquire_write(&pDevice->opensl.duplexRB, &marginSizeInFrames, &pMarginData);
28590 {
28591 MA_ZERO_MEMORY(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
28592 }
28593 ma_pcm_rb_commit_write(&pDevice->opensl.duplexRB, marginSizeInFrames, pMarginData);
28594 }
28595 }
28596
28597 return MA_SUCCESS;
28598 #else
28599 return MA_NO_BACKEND; /* Non-Android implementations are not supported. */
28600 #endif
28601 }
28602
28603 static ma_result ma_device_start__opensl(ma_device* pDevice)
28604 {
28605 SLresult resultSL;
28606 size_t periodSizeInBytes;
28607 ma_uint32 iPeriod;
28608
28609 MA_ASSERT(pDevice != NULL);
28610
28611 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. */
28612 if (g_maOpenSLInitCounter == 0) {
28613 return MA_INVALID_OPERATION;
28614 }
28615
28616 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28617 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
28618 if (resultSL != SL_RESULT_SUCCESS) {
28619 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.", ma_result_from_OpenSL(resultSL));
28620 }
28621
28622 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
28623 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
28624 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
28625 if (resultSL != SL_RESULT_SUCCESS) {
28626 MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
28627 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.", ma_result_from_OpenSL(resultSL));
28628 }
28629 }
28630 }
28631
28632 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28633 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
28634 if (resultSL != SL_RESULT_SUCCESS) {
28635 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.", ma_result_from_OpenSL(resultSL));
28636 }
28637
28638 /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */
28639 if (pDevice->type == ma_device_type_duplex) {
28640 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
28641 } else {
28642 ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback);
28643 }
28644
28645 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
28646 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
28647 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
28648 if (resultSL != SL_RESULT_SUCCESS) {
28649 MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
28650 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.", ma_result_from_OpenSL(resultSL));
28651 }
28652 }
28653 }
28654
28655 return MA_SUCCESS;
28656 }
28657
28658 static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType)
28659 {
28660 SLAndroidSimpleBufferQueueItf pBufferQueue;
28661
28662 MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);
28663
28664 if (pDevice->type == ma_device_type_capture) {
28665 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture;
28666 pDevice->opensl.isDrainingCapture = MA_TRUE;
28667 } else {
28668 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback;
28669 pDevice->opensl.isDrainingPlayback = MA_TRUE;
28670 }
28671
28672 for (;;) {
28673 SLAndroidSimpleBufferQueueState state;
28674
28675 MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state);
28676 if (state.count == 0) {
28677 break;
28678 }
28679
28680 ma_sleep(10);
28681 }
28682
28683 if (pDevice->type == ma_device_type_capture) {
28684 pDevice->opensl.isDrainingCapture = MA_FALSE;
28685 } else {
28686 pDevice->opensl.isDrainingPlayback = MA_FALSE;
28687 }
28688
28689 return MA_SUCCESS;
28690 }
28691
28692 static ma_result ma_device_stop__opensl(ma_device* pDevice)
28693 {
28694 SLresult resultSL;
28695 ma_stop_proc onStop;
28696
28697 MA_ASSERT(pDevice != NULL);
28698
28699 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. */
28700 if (g_maOpenSLInitCounter == 0) {
28701 return MA_INVALID_OPERATION;
28702 }
28703
28704 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28705 ma_device_drain__opensl(pDevice, ma_device_type_capture);
28706
28707 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
28708 if (resultSL != SL_RESULT_SUCCESS) {
28709 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.", ma_result_from_OpenSL(resultSL));
28710 }
28711
28712 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
28713 }
28714
28715 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28716 ma_device_drain__opensl(pDevice, ma_device_type_playback);
28717
28718 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
28719 if (resultSL != SL_RESULT_SUCCESS) {
28720 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.", ma_result_from_OpenSL(resultSL));
28721 }
28722
28723 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
28724 }
28725
28726 /* 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. */
28727 onStop = pDevice->onStop;
28728 if (onStop) {
28729 onStop(pDevice);
28730 }
28731
28732 return MA_SUCCESS;
28733 }
28734
28735
28736 static ma_result ma_context_uninit__opensl(ma_context* pContext)
28737 {
28738 MA_ASSERT(pContext != NULL);
28739 MA_ASSERT(pContext->backend == ma_backend_opensl);
28740 (void)pContext;
28741
28742 /* Uninit global data. */
28743 if (g_maOpenSLInitCounter > 0) {
28744 if (ma_atomic_decrement_32(&g_maOpenSLInitCounter) == 0) {
28745 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
28746 }
28747 }
28748
28749 return MA_SUCCESS;
28750 }
28751
28752 static ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_context* pContext)
28753 {
28754 MA_ASSERT(pContext != NULL);
28755
28756 (void)pConfig;
28757
28758 /* Initialize global data first if applicable. */
28759 if (ma_atomic_increment_32(&g_maOpenSLInitCounter) == 1) {
28760 SLresult resultSL = slCreateEngine(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
28761 if (resultSL != SL_RESULT_SUCCESS) {
28762 ma_atomic_decrement_32(&g_maOpenSLInitCounter);
28763 return ma_result_from_OpenSL(resultSL);
28764 }
28765
28766 (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
28767
28768 resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_ENGINE, &g_maEngineSL);
28769 if (resultSL != SL_RESULT_SUCCESS) {
28770 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
28771 ma_atomic_decrement_32(&g_maOpenSLInitCounter);
28772 return ma_result_from_OpenSL(resultSL);
28773 }
28774 }
28775
28776 pContext->isBackendAsynchronous = MA_TRUE;
28777
28778 pContext->onUninit = ma_context_uninit__opensl;
28779 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__opensl;
28780 pContext->onEnumDevices = ma_context_enumerate_devices__opensl;
28781 pContext->onGetDeviceInfo = ma_context_get_device_info__opensl;
28782 pContext->onDeviceInit = ma_device_init__opensl;
28783 pContext->onDeviceUninit = ma_device_uninit__opensl;
28784 pContext->onDeviceStart = ma_device_start__opensl;
28785 pContext->onDeviceStop = ma_device_stop__opensl;
28786
28787 return MA_SUCCESS;
28788 }
28789 #endif /* OpenSL|ES */
28790
28791
28792 /******************************************************************************
28793
28794 Web Audio Backend
28795
28796 ******************************************************************************/
28797 #ifdef MA_HAS_WEBAUDIO
28798 #include <emscripten/emscripten.h>
28799
28800 static ma_bool32 ma_is_capture_supported__webaudio()
28801 {
28802 return EM_ASM_INT({
28803 return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
28804 }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
28805 }
28806
28807 #ifdef __cplusplus
28808 extern "C" {
28809 #endif
28810 void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
28811 {
28812 if (pDevice->type == ma_device_type_duplex) {
28813 ma_device__handle_duplex_callback_capture(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB);
28814 } else {
28815 ma_device__send_frames_to_client(pDevice, (ma_uint32)frameCount, pFrames); /* Send directly to the client. */
28816 }
28817 }
28818
28819 void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
28820 {
28821 if (pDevice->type == ma_device_type_duplex) {
28822 ma_device__handle_duplex_callback_playback(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB);
28823 } else {
28824 ma_device__read_frames_from_client(pDevice, (ma_uint32)frameCount, pFrames); /* Read directly from the device. */
28825 }
28826 }
28827 #ifdef __cplusplus
28828 }
28829 #endif
28830
28831 static ma_bool32 ma_context_is_device_id_equal__webaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
28832 {
28833 MA_ASSERT(pContext != NULL);
28834 MA_ASSERT(pID0 != NULL);
28835 MA_ASSERT(pID1 != NULL);
28836 (void)pContext;
28837
28838 return ma_strcmp(pID0->webaudio, pID1->webaudio) == 0;
28839 }
28840
28841 static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
28842 {
28843 ma_bool32 cbResult = MA_TRUE;
28844
28845 MA_ASSERT(pContext != NULL);
28846 MA_ASSERT(callback != NULL);
28847
28848 /* Only supporting default devices for now. */
28849
28850 /* Playback. */
28851 if (cbResult) {
28852 ma_device_info deviceInfo;
28853 MA_ZERO_OBJECT(&deviceInfo);
28854 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
28855 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
28856 }
28857
28858 /* Capture. */
28859 if (cbResult) {
28860 if (ma_is_capture_supported__webaudio()) {
28861 ma_device_info deviceInfo;
28862 MA_ZERO_OBJECT(&deviceInfo);
28863 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
28864 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
28865 }
28866 }
28867
28868 return MA_SUCCESS;
28869 }
28870
28871 static 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)
28872 {
28873 MA_ASSERT(pContext != NULL);
28874
28875 /* No exclusive mode with Web Audio. */
28876 if (shareMode == ma_share_mode_exclusive) {
28877 return MA_SHARE_MODE_NOT_SUPPORTED;
28878 }
28879
28880 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
28881 return MA_NO_DEVICE;
28882 }
28883
28884
28885 MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
28886
28887 /* Only supporting default devices for now. */
28888 (void)pDeviceID;
28889 if (deviceType == ma_device_type_playback) {
28890 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
28891 } else {
28892 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
28893 }
28894
28895 /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
28896 pDeviceInfo->minChannels = 1;
28897 pDeviceInfo->maxChannels = MA_MAX_CHANNELS;
28898 if (pDeviceInfo->maxChannels > 32) {
28899 pDeviceInfo->maxChannels = 32; /* Maximum output channel count is 32 for createScriptProcessor() (JavaScript). */
28900 }
28901
28902 /* We can query the sample rate by just using a temporary audio context. */
28903 pDeviceInfo->minSampleRate = EM_ASM_INT({
28904 try {
28905 var temp = new (window.AudioContext || window.webkitAudioContext)();
28906 var sampleRate = temp.sampleRate;
28907 temp.close();
28908 return sampleRate;
28909 } catch(e) {
28910 return 0;
28911 }
28912 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
28913 pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
28914 if (pDeviceInfo->minSampleRate == 0) {
28915 return MA_NO_DEVICE;
28916 }
28917
28918 /* Web Audio only supports f32. */
28919 pDeviceInfo->formatCount = 1;
28920 pDeviceInfo->formats[0] = ma_format_f32;
28921
28922 return MA_SUCCESS;
28923 }
28924
28925
28926 static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex)
28927 {
28928 MA_ASSERT(pDevice != NULL);
28929
28930 EM_ASM({
28931 var device = miniaudio.get_device_by_index($0);
28932
28933 /* Make sure all nodes are disconnected and marked for collection. */
28934 if (device.scriptNode !== undefined) {
28935 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... */
28936 device.scriptNode.disconnect();
28937 device.scriptNode = undefined;
28938 }
28939 if (device.streamNode !== undefined) {
28940 device.streamNode.disconnect();
28941 device.streamNode = undefined;
28942 }
28943
28944 /*
28945 Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
28946 to clear the callback before closing.
28947 */
28948 device.webaudio.close();
28949 device.webaudio = undefined;
28950
28951 /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */
28952 if (device.intermediaryBuffer !== undefined) {
28953 Module._free(device.intermediaryBuffer);
28954 device.intermediaryBuffer = undefined;
28955 device.intermediaryBufferView = undefined;
28956 device.intermediaryBufferSizeInBytes = undefined;
28957 }
28958
28959 /* Make sure the device is untracked so the slot can be reused later. */
28960 miniaudio.untrack_device_by_index($0);
28961 }, deviceIndex, deviceType);
28962 }
28963
28964 static void ma_device_uninit__webaudio(ma_device* pDevice)
28965 {
28966 MA_ASSERT(pDevice != NULL);
28967
28968 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28969 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
28970 }
28971
28972 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28973 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
28974 }
28975
28976 if (pDevice->type == ma_device_type_duplex) {
28977 ma_pcm_rb_uninit(&pDevice->webaudio.duplexRB);
28978 }
28979 }
28980
28981 static ma_result ma_device_init_by_type__webaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
28982 {
28983 int deviceIndex;
28984 ma_uint32 internalPeriodSizeInFrames;
28985
28986 MA_ASSERT(pContext != NULL);
28987 MA_ASSERT(pConfig != NULL);
28988 MA_ASSERT(deviceType != ma_device_type_duplex);
28989 MA_ASSERT(pDevice != NULL);
28990
28991 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
28992 return MA_NO_DEVICE;
28993 }
28994
28995 /* Try calculating an appropriate buffer size. */
28996 internalPeriodSizeInFrames = pConfig->periodSizeInFrames;
28997 if (internalPeriodSizeInFrames == 0) {
28998 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pConfig->sampleRate);
28999 }
29000
29001 /* The size of the buffer must be a power of 2 and between 256 and 16384. */
29002 if (internalPeriodSizeInFrames < 256) {
29003 internalPeriodSizeInFrames = 256;
29004 } else if (internalPeriodSizeInFrames > 16384) {
29005 internalPeriodSizeInFrames = 16384;
29006 } else {
29007 internalPeriodSizeInFrames = ma_next_power_of_2(internalPeriodSizeInFrames);
29008 }
29009
29010 /* 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. */
29011 deviceIndex = EM_ASM_INT({
29012 var channels = $0;
29013 var sampleRate = $1;
29014 var bufferSize = $2; /* In PCM frames. */
29015 var isCapture = $3;
29016 var pDevice = $4;
29017
29018 if (typeof(miniaudio) === 'undefined') {
29019 return -1; /* Context not initialized. */
29020 }
29021
29022 var device = {};
29023
29024 /* The AudioContext must be created in a suspended state. */
29025 device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate});
29026 device.webaudio.suspend();
29027
29028 /*
29029 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
29030 JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free().
29031 */
29032 device.intermediaryBufferSizeInBytes = channels * bufferSize * 4;
29033 device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes);
29034 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
29035
29036 /*
29037 Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations.
29038
29039 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
29040 that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of
29041 something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to
29042 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
29043 implementation. I'll be avoiding that insane AudioWorklet API like the plague...
29044
29045 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
29046 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
29047 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
29048 been unable to figure out how to get this as raw PCM. The closest I can think is to use the MIME type for WAV files and just parse it, but I don't know
29049 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
29050 this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know!
29051 */
29052 device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channels, channels);
29053
29054 if (isCapture) {
29055 device.scriptNode.onaudioprocess = function(e) {
29056 if (device.intermediaryBuffer === undefined) {
29057 return; /* This means the device has been uninitialized. */
29058 }
29059
29060 /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */
29061 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
29062 e.outputBuffer.getChannelData(iChannel).fill(0.0);
29063 }
29064
29065 /* There are some situations where we may want to send silence to the client. */
29066 var sendSilence = false;
29067 if (device.streamNode === undefined) {
29068 sendSilence = true;
29069 }
29070
29071 /* Sanity check. This will never happen, right? */
29072 if (e.inputBuffer.numberOfChannels != channels) {
29073 console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence.");
29074 sendSilence = true;
29075 }
29076
29077 /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */
29078 var totalFramesProcessed = 0;
29079 while (totalFramesProcessed < e.inputBuffer.length) {
29080 var framesRemaining = e.inputBuffer.length - totalFramesProcessed;
29081 var framesToProcess = framesRemaining;
29082 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
29083 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
29084 }
29085
29086 /* 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. */
29087 if (sendSilence) {
29088 device.intermediaryBufferView.fill(0.0);
29089 } else {
29090 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
29091 for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
29092 device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame];
29093 }
29094 }
29095 }
29096
29097 /* Send data to the client from our intermediary buffer. */
29098 ccall("ma_device_process_pcm_frames_capture__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
29099
29100 totalFramesProcessed += framesToProcess;
29101 }
29102 };
29103
29104 navigator.mediaDevices.getUserMedia({audio:true, video:false})
29105 .then(function(stream) {
29106 device.streamNode = device.webaudio.createMediaStreamSource(stream);
29107 device.streamNode.connect(device.scriptNode);
29108 device.scriptNode.connect(device.webaudio.destination);
29109 })
29110 .catch(function(error) {
29111 /* I think this should output silence... */
29112 device.scriptNode.connect(device.webaudio.destination);
29113 });
29114 } else {
29115 device.scriptNode.onaudioprocess = function(e) {
29116 if (device.intermediaryBuffer === undefined) {
29117 return; /* This means the device has been uninitialized. */
29118 }
29119
29120 var outputSilence = false;
29121
29122 /* Sanity check. This will never happen, right? */
29123 if (e.outputBuffer.numberOfChannels != channels) {
29124 console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence.");
29125 outputSilence = true;
29126 return;
29127 }
29128
29129 /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */
29130 var totalFramesProcessed = 0;
29131 while (totalFramesProcessed < e.outputBuffer.length) {
29132 var framesRemaining = e.outputBuffer.length - totalFramesProcessed;
29133 var framesToProcess = framesRemaining;
29134 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
29135 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
29136 }
29137
29138 /* Read data from the client into our intermediary buffer. */
29139 ccall("ma_device_process_pcm_frames_playback__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
29140
29141 /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */
29142 if (outputSilence) {
29143 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
29144 e.outputBuffer.getChannelData(iChannel).fill(0.0);
29145 }
29146 } else {
29147 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
29148 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
29149 e.outputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame] = device.intermediaryBufferView[iFrame*channels + iChannel];
29150 }
29151 }
29152 }
29153
29154 totalFramesProcessed += framesToProcess;
29155 }
29156 };
29157
29158 device.scriptNode.connect(device.webaudio.destination);
29159 }
29160
29161 return miniaudio.track_device(device);
29162 }, (deviceType == ma_device_type_capture) ? pConfig->capture.channels : pConfig->playback.channels, pConfig->sampleRate, internalPeriodSizeInFrames, deviceType == ma_device_type_capture, pDevice);
29163
29164 if (deviceIndex < 0) {
29165 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
29166 }
29167
29168 if (deviceType == ma_device_type_capture) {
29169 pDevice->webaudio.indexCapture = deviceIndex;
29170 pDevice->capture.internalFormat = ma_format_f32;
29171 pDevice->capture.internalChannels = pConfig->capture.channels;
29172 ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
29173 pDevice->capture.internalSampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
29174 pDevice->capture.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
29175 pDevice->capture.internalPeriods = 1;
29176 } else {
29177 pDevice->webaudio.indexPlayback = deviceIndex;
29178 pDevice->playback.internalFormat = ma_format_f32;
29179 pDevice->playback.internalChannels = pConfig->playback.channels;
29180 ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
29181 pDevice->playback.internalSampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
29182 pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
29183 pDevice->playback.internalPeriods = 1;
29184 }
29185
29186 return MA_SUCCESS;
29187 }
29188
29189 static ma_result ma_device_init__webaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
29190 {
29191 ma_result result;
29192
29193 if (pConfig->deviceType == ma_device_type_loopback) {
29194 return MA_DEVICE_TYPE_NOT_SUPPORTED;
29195 }
29196
29197 /* No exclusive mode with Web Audio. */
29198 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
29199 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
29200 return MA_SHARE_MODE_NOT_SUPPORTED;
29201 }
29202
29203 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
29204 result = ma_device_init_by_type__webaudio(pContext, pConfig, ma_device_type_capture, pDevice);
29205 if (result != MA_SUCCESS) {
29206 return result;
29207 }
29208 }
29209
29210 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
29211 result = ma_device_init_by_type__webaudio(pContext, pConfig, ma_device_type_playback, pDevice);
29212 if (result != MA_SUCCESS) {
29213 if (pConfig->deviceType == ma_device_type_duplex) {
29214 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
29215 }
29216 return result;
29217 }
29218 }
29219
29220 /*
29221 We need a ring buffer for moving data from the capture device to the playback device. The capture callback is the producer
29222 and the playback callback is the consumer. The buffer needs to be large enough to hold internalPeriodSizeInFrames based on
29223 the external sample rate.
29224 */
29225 if (pConfig->deviceType == ma_device_type_duplex) {
29226 ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames) * 2;
29227 result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->pContext->allocationCallbacks, &pDevice->webaudio.duplexRB);
29228 if (result != MA_SUCCESS) {
29229 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29230 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
29231 }
29232 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29233 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
29234 }
29235 return result;
29236 }
29237
29238 /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
29239 {
29240 ma_uint32 marginSizeInFrames = rbSizeInFrames / 3; /* <-- Dividing by 3 because internalPeriods is always set to 1 for WebAudio. */
29241 void* pMarginData;
29242 ma_pcm_rb_acquire_write(&pDevice->webaudio.duplexRB, &marginSizeInFrames, &pMarginData);
29243 {
29244 MA_ZERO_MEMORY(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
29245 }
29246 ma_pcm_rb_commit_write(&pDevice->webaudio.duplexRB, marginSizeInFrames, pMarginData);
29247 }
29248 }
29249
29250 return MA_SUCCESS;
29251 }
29252
29253 static ma_result ma_device_start__webaudio(ma_device* pDevice)
29254 {
29255 MA_ASSERT(pDevice != NULL);
29256
29257 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29258 EM_ASM({
29259 miniaudio.get_device_by_index($0).webaudio.resume();
29260 }, pDevice->webaudio.indexCapture);
29261 }
29262
29263 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29264 EM_ASM({
29265 miniaudio.get_device_by_index($0).webaudio.resume();
29266 }, pDevice->webaudio.indexPlayback);
29267 }
29268
29269 return MA_SUCCESS;
29270 }
29271
29272 static ma_result ma_device_stop__webaudio(ma_device* pDevice)
29273 {
29274 MA_ASSERT(pDevice != NULL);
29275
29276 /*
29277 From the WebAudio API documentation for AudioContext.suspend():
29278
29279 Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the
29280 destination, and then allows the system to release its claim on audio hardware.
29281
29282 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
29283 do any kind of explicit draining.
29284 */
29285
29286 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29287 EM_ASM({
29288 miniaudio.get_device_by_index($0).webaudio.suspend();
29289 }, pDevice->webaudio.indexCapture);
29290 }
29291
29292 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29293 EM_ASM({
29294 miniaudio.get_device_by_index($0).webaudio.suspend();
29295 }, pDevice->webaudio.indexPlayback);
29296 }
29297
29298 ma_stop_proc onStop = pDevice->onStop;
29299 if (onStop) {
29300 onStop(pDevice);
29301 }
29302
29303 return MA_SUCCESS;
29304 }
29305
29306 static ma_result ma_context_uninit__webaudio(ma_context* pContext)
29307 {
29308 MA_ASSERT(pContext != NULL);
29309 MA_ASSERT(pContext->backend == ma_backend_webaudio);
29310
29311 /* Nothing needs to be done here. */
29312 (void)pContext;
29313
29314 return MA_SUCCESS;
29315 }
29316
29317 static ma_result ma_context_init__webaudio(const ma_context_config* pConfig, ma_context* pContext)
29318 {
29319 int resultFromJS;
29320
29321 MA_ASSERT(pContext != NULL);
29322
29323 /* Here is where our global JavaScript object is initialized. */
29324 resultFromJS = EM_ASM_INT({
29325 if ((window.AudioContext || window.webkitAudioContext) === undefined) {
29326 return 0; /* Web Audio not supported. */
29327 }
29328
29329 if (typeof(miniaudio) === 'undefined') {
29330 miniaudio = {};
29331 miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */
29332
29333 miniaudio.track_device = function(device) {
29334 /* Try inserting into a free slot first. */
29335 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
29336 if (miniaudio.devices[iDevice] == null) {
29337 miniaudio.devices[iDevice] = device;
29338 return iDevice;
29339 }
29340 }
29341
29342 /* Getting here means there is no empty slots in the array so we just push to the end. */
29343 miniaudio.devices.push(device);
29344 return miniaudio.devices.length - 1;
29345 };
29346
29347 miniaudio.untrack_device_by_index = function(deviceIndex) {
29348 /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */
29349 miniaudio.devices[deviceIndex] = null;
29350
29351 /* Trim the array if possible. */
29352 while (miniaudio.devices.length > 0) {
29353 if (miniaudio.devices[miniaudio.devices.length-1] == null) {
29354 miniaudio.devices.pop();
29355 } else {
29356 break;
29357 }
29358 }
29359 };
29360
29361 miniaudio.untrack_device = function(device) {
29362 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
29363 if (miniaudio.devices[iDevice] == device) {
29364 return miniaudio.untrack_device_by_index(iDevice);
29365 }
29366 }
29367 };
29368
29369 miniaudio.get_device_by_index = function(deviceIndex) {
29370 return miniaudio.devices[deviceIndex];
29371 };
29372 }
29373
29374 return 1;
29375 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
29376
29377 if (resultFromJS != 1) {
29378 return MA_FAILED_TO_INIT_BACKEND;
29379 }
29380
29381
29382 pContext->isBackendAsynchronous = MA_TRUE;
29383
29384 pContext->onUninit = ma_context_uninit__webaudio;
29385 pContext->onDeviceIDEqual = ma_context_is_device_id_equal__webaudio;
29386 pContext->onEnumDevices = ma_context_enumerate_devices__webaudio;
29387 pContext->onGetDeviceInfo = ma_context_get_device_info__webaudio;
29388 pContext->onDeviceInit = ma_device_init__webaudio;
29389 pContext->onDeviceUninit = ma_device_uninit__webaudio;
29390 pContext->onDeviceStart = ma_device_start__webaudio;
29391 pContext->onDeviceStop = ma_device_stop__webaudio;
29392
29393 (void)pConfig; /* Unused. */
29394 return MA_SUCCESS;
29395 }
29396 #endif /* Web Audio */
29397
29398
29399
29400 static ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels)
29401 {
29402 /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */
29403 if (channelMap[0] != MA_CHANNEL_NONE) {
29404 ma_uint32 iChannel;
29405
29406 if (channels == 0) {
29407 return MA_FALSE; /* No channels. */
29408 }
29409
29410 /* A channel cannot be present in the channel map more than once. */
29411 for (iChannel = 0; iChannel < channels; ++iChannel) {
29412 ma_uint32 jChannel;
29413 for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
29414 if (channelMap[iChannel] == channelMap[jChannel]) {
29415 return MA_FALSE;
29416 }
29417 }
29418 }
29419 }
29420
29421 return MA_TRUE;
29422 }
29423
29424
29425 static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
29426 {
29427 ma_result result;
29428
29429 MA_ASSERT(pDevice != NULL);
29430
29431 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
29432 if (pDevice->capture.usingDefaultFormat) {
29433 pDevice->capture.format = pDevice->capture.internalFormat;
29434 }
29435 if (pDevice->capture.usingDefaultChannels) {
29436 pDevice->capture.channels = pDevice->capture.internalChannels;
29437 }
29438 if (pDevice->capture.usingDefaultChannelMap) {
29439 if (pDevice->capture.internalChannels == pDevice->capture.channels) {
29440 ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);
29441 } else {
29442 ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.channels, pDevice->capture.channelMap);
29443 }
29444 }
29445 }
29446
29447 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
29448 if (pDevice->playback.usingDefaultFormat) {
29449 pDevice->playback.format = pDevice->playback.internalFormat;
29450 }
29451 if (pDevice->playback.usingDefaultChannels) {
29452 pDevice->playback.channels = pDevice->playback.internalChannels;
29453 }
29454 if (pDevice->playback.usingDefaultChannelMap) {
29455 if (pDevice->playback.internalChannels == pDevice->playback.channels) {
29456 ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);
29457 } else {
29458 ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.channels, pDevice->playback.channelMap);
29459 }
29460 }
29461 }
29462
29463 if (pDevice->usingDefaultSampleRate) {
29464 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
29465 pDevice->sampleRate = pDevice->capture.internalSampleRate;
29466 } else {
29467 pDevice->sampleRate = pDevice->playback.internalSampleRate;
29468 }
29469 }
29470
29471 /* PCM converters. */
29472 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
29473 /* Converting from internal device format to client format. */
29474 ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
29475 converterConfig.formatIn = pDevice->capture.internalFormat;
29476 converterConfig.channelsIn = pDevice->capture.internalChannels;
29477 converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
29478 ma_channel_map_copy(converterConfig.channelMapIn, pDevice->capture.internalChannelMap, pDevice->capture.internalChannels);
29479 converterConfig.formatOut = pDevice->capture.format;
29480 converterConfig.channelsOut = pDevice->capture.channels;
29481 converterConfig.sampleRateOut = pDevice->sampleRate;
29482 ma_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, pDevice->capture.channels);
29483 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
29484 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
29485 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
29486 converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality;
29487
29488 result = ma_data_converter_init(&converterConfig, &pDevice->capture.converter);
29489 if (result != MA_SUCCESS) {
29490 return result;
29491 }
29492 }
29493
29494 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
29495 /* Converting from client format to device format. */
29496 ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
29497 converterConfig.formatIn = pDevice->playback.format;
29498 converterConfig.channelsIn = pDevice->playback.channels;
29499 converterConfig.sampleRateIn = pDevice->sampleRate;
29500 ma_channel_map_copy(converterConfig.channelMapIn, pDevice->playback.channelMap, pDevice->playback.channels);
29501 converterConfig.formatOut = pDevice->playback.internalFormat;
29502 converterConfig.channelsOut = pDevice->playback.internalChannels;
29503 converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
29504 ma_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, pDevice->playback.internalChannels);
29505 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
29506 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
29507 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
29508 converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality;
29509
29510 result = ma_data_converter_init(&converterConfig, &pDevice->playback.converter);
29511 if (result != MA_SUCCESS) {
29512 return result;
29513 }
29514 }
29515
29516 return MA_SUCCESS;
29517 }
29518
29519
29520 static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
29521 {
29522 ma_device* pDevice = (ma_device*)pData;
29523 MA_ASSERT(pDevice != NULL);
29524
29525 #ifdef MA_WIN32
29526 ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
29527 #endif
29528
29529 /*
29530 When the device is being initialized it's initial state is set to MA_STATE_UNINITIALIZED. Before returning from
29531 ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
29532 after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
29533 thread to signal an event to know when the worker thread is ready for action.
29534 */
29535 ma_device__set_state(pDevice, MA_STATE_STOPPED);
29536 ma_event_signal(&pDevice->stopEvent);
29537
29538 for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
29539 ma_stop_proc onStop;
29540
29541 /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
29542 ma_event_wait(&pDevice->wakeupEvent);
29543
29544 /* Default result code. */
29545 pDevice->workResult = MA_SUCCESS;
29546
29547 /* If the reason for the wake up is that we are terminating, just break from the loop. */
29548 if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
29549 break;
29550 }
29551
29552 /*
29553 Getting to this point means the device is wanting to get started. The function that has requested that the device
29554 be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
29555 in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
29556 */
29557 MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_STARTING);
29558
29559 /* Make sure the state is set appropriately. */
29560 ma_device__set_state(pDevice, MA_STATE_STARTED);
29561 ma_event_signal(&pDevice->startEvent);
29562
29563 if (pDevice->pContext->onDeviceMainLoop != NULL) {
29564 pDevice->pContext->onDeviceMainLoop(pDevice);
29565 } else {
29566 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "No main loop implementation.", MA_API_NOT_FOUND);
29567 }
29568
29569 /*
29570 Getting here means we have broken from the main loop which happens the application has requested that device be stopped. Note that this
29571 may have actually already happened above if the device was lost and miniaudio has attempted to re-initialize the device. In this case we
29572 don't want to be doing this a second time.
29573 */
29574 if (ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED) {
29575 if (pDevice->pContext->onDeviceStop) {
29576 pDevice->pContext->onDeviceStop(pDevice);
29577 }
29578 }
29579
29580 /* After the device has stopped, make sure an event is posted. */
29581 onStop = pDevice->onStop;
29582 if (onStop) {
29583 onStop(pDevice);
29584 }
29585
29586 /*
29587 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
29588 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
29589 uninitialized state to stopped state.
29590 */
29591 if (ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED) {
29592 ma_device__set_state(pDevice, MA_STATE_STOPPED);
29593 ma_event_signal(&pDevice->stopEvent);
29594 }
29595 }
29596
29597 /* Make sure we aren't continuously waiting on a stop event. */
29598 ma_event_signal(&pDevice->stopEvent); /* <-- Is this still needed? */
29599
29600 #ifdef MA_WIN32
29601 ma_CoUninitialize(pDevice->pContext);
29602 #endif
29603
29604 return (ma_thread_result)0;
29605 }
29606
29607
29608 /* Helper for determining whether or not the given device is initialized. */
29609 static ma_bool32 ma_device__is_initialized(ma_device* pDevice)
29610 {
29611 if (pDevice == NULL) {
29612 return MA_FALSE;
29613 }
29614
29615 return ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED;
29616 }
29617
29618
29619 #ifdef MA_WIN32
29620 static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
29621 {
29622 ma_CoUninitialize(pContext);
29623 ma_dlclose(pContext, pContext->win32.hUser32DLL);
29624 ma_dlclose(pContext, pContext->win32.hOle32DLL);
29625 ma_dlclose(pContext, pContext->win32.hAdvapi32DLL);
29626
29627 return MA_SUCCESS;
29628 }
29629
29630 static ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
29631 {
29632 #ifdef MA_WIN32_DESKTOP
29633 /* Ole32.dll */
29634 pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll");
29635 if (pContext->win32.hOle32DLL == NULL) {
29636 return MA_FAILED_TO_INIT_BACKEND;
29637 }
29638
29639 pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx");
29640 pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize");
29641 pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance");
29642 pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree");
29643 pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear");
29644 pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2");
29645
29646
29647 /* User32.dll */
29648 pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll");
29649 if (pContext->win32.hUser32DLL == NULL) {
29650 return MA_FAILED_TO_INIT_BACKEND;
29651 }
29652
29653 pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow");
29654 pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow");
29655
29656
29657 /* Advapi32.dll */
29658 pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll");
29659 if (pContext->win32.hAdvapi32DLL == NULL) {
29660 return MA_FAILED_TO_INIT_BACKEND;
29661 }
29662
29663 pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
29664 pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey");
29665 pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
29666 #endif
29667
29668 ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
29669 return MA_SUCCESS;
29670 }
29671 #else
29672 static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
29673 {
29674 #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
29675 ma_dlclose(pContext, pContext->posix.pthreadSO);
29676 #else
29677 (void)pContext;
29678 #endif
29679
29680 return MA_SUCCESS;
29681 }
29682
29683 static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
29684 {
29685 /* pthread */
29686 #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
29687 const char* libpthreadFileNames[] = {
29688 "libpthread.so",
29689 "libpthread.so.0",
29690 "libpthread.dylib"
29691 };
29692 size_t i;
29693
29694 for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) {
29695 pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]);
29696 if (pContext->posix.pthreadSO != NULL) {
29697 break;
29698 }
29699 }
29700
29701 if (pContext->posix.pthreadSO == NULL) {
29702 return MA_FAILED_TO_INIT_BACKEND;
29703 }
29704
29705 pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create");
29706 pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join");
29707 pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init");
29708 pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy");
29709 pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock");
29710 pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock");
29711 pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init");
29712 pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy");
29713 pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait");
29714 pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal");
29715 pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init");
29716 pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy");
29717 pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy");
29718 pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam");
29719 pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam");
29720 #else
29721 pContext->posix.pthread_create = (ma_proc)pthread_create;
29722 pContext->posix.pthread_join = (ma_proc)pthread_join;
29723 pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init;
29724 pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy;
29725 pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock;
29726 pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock;
29727 pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init;
29728 pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy;
29729 pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait;
29730 pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
29731 pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
29732 pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
29733 #if !defined(__EMSCRIPTEN__)
29734 pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
29735 pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
29736 pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
29737 #endif
29738 #endif
29739
29740 return MA_SUCCESS;
29741 }
29742 #endif
29743
29744 static ma_result ma_context_init_backend_apis(ma_context* pContext)
29745 {
29746 ma_result result;
29747 #ifdef MA_WIN32
29748 result = ma_context_init_backend_apis__win32(pContext);
29749 #else
29750 result = ma_context_init_backend_apis__nix(pContext);
29751 #endif
29752
29753 return result;
29754 }
29755
29756 static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
29757 {
29758 ma_result result;
29759 #ifdef MA_WIN32
29760 result = ma_context_uninit_backend_apis__win32(pContext);
29761 #else
29762 result = ma_context_uninit_backend_apis__nix(pContext);
29763 #endif
29764
29765 return result;
29766 }
29767
29768
29769 static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
29770 {
29771 return pContext->isBackendAsynchronous;
29772 }
29773
29774
29775 MA_API ma_context_config ma_context_config_init()
29776 {
29777 ma_context_config config;
29778 MA_ZERO_OBJECT(&config);
29779
29780 return config;
29781 }
29782
29783 MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
29784 {
29785 ma_result result;
29786 ma_context_config config;
29787 ma_backend defaultBackends[ma_backend_null+1];
29788 ma_uint32 iBackend;
29789 ma_backend* pBackendsToIterate;
29790 ma_uint32 backendsToIterateCount;
29791
29792 if (pContext == NULL) {
29793 return MA_INVALID_ARGS;
29794 }
29795
29796 MA_ZERO_OBJECT(pContext);
29797
29798 /* Always make sure the config is set first to ensure properties are available as soon as possible. */
29799 if (pConfig != NULL) {
29800 config = *pConfig;
29801 } else {
29802 config = ma_context_config_init();
29803 }
29804
29805 pContext->logCallback = config.logCallback;
29806 pContext->threadPriority = config.threadPriority;
29807 pContext->pUserData = config.pUserData;
29808
29809 result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &config.allocationCallbacks);
29810 if (result != MA_SUCCESS) {
29811 return result;
29812 }
29813
29814 /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
29815 result = ma_context_init_backend_apis(pContext);
29816 if (result != MA_SUCCESS) {
29817 return result;
29818 }
29819
29820 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
29821 defaultBackends[iBackend] = (ma_backend)iBackend;
29822 }
29823
29824 pBackendsToIterate = (ma_backend*)backends;
29825 backendsToIterateCount = backendCount;
29826 if (pBackendsToIterate == NULL) {
29827 pBackendsToIterate = (ma_backend*)defaultBackends;
29828 backendsToIterateCount = ma_countof(defaultBackends);
29829 }
29830
29831 MA_ASSERT(pBackendsToIterate != NULL);
29832
29833 for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
29834 ma_backend backend = pBackendsToIterate[iBackend];
29835
29836 result = MA_NO_BACKEND;
29837 switch (backend) {
29838 #ifdef MA_HAS_WASAPI
29839 case ma_backend_wasapi:
29840 {
29841 result = ma_context_init__wasapi(&config, pContext);
29842 } break;
29843 #endif
29844 #ifdef MA_HAS_DSOUND
29845 case ma_backend_dsound:
29846 {
29847 result = ma_context_init__dsound(&config, pContext);
29848 } break;
29849 #endif
29850 #ifdef MA_HAS_WINMM
29851 case ma_backend_winmm:
29852 {
29853 result = ma_context_init__winmm(&config, pContext);
29854 } break;
29855 #endif
29856 #ifdef MA_HAS_ALSA
29857 case ma_backend_alsa:
29858 {
29859 result = ma_context_init__alsa(&config, pContext);
29860 } break;
29861 #endif
29862 #ifdef MA_HAS_PULSEAUDIO
29863 case ma_backend_pulseaudio:
29864 {
29865 result = ma_context_init__pulse(&config, pContext);
29866 } break;
29867 #endif
29868 #ifdef MA_HAS_JACK
29869 case ma_backend_jack:
29870 {
29871 result = ma_context_init__jack(&config, pContext);
29872 } break;
29873 #endif
29874 #ifdef MA_HAS_COREAUDIO
29875 case ma_backend_coreaudio:
29876 {
29877 result = ma_context_init__coreaudio(&config, pContext);
29878 } break;
29879 #endif
29880 #ifdef MA_HAS_SNDIO
29881 case ma_backend_sndio:
29882 {
29883 result = ma_context_init__sndio(&config, pContext);
29884 } break;
29885 #endif
29886 #ifdef MA_HAS_AUDIO4
29887 case ma_backend_audio4:
29888 {
29889 result = ma_context_init__audio4(&config, pContext);
29890 } break;
29891 #endif
29892 #ifdef MA_HAS_OSS
29893 case ma_backend_oss:
29894 {
29895 result = ma_context_init__oss(&config, pContext);
29896 } break;
29897 #endif
29898 #ifdef MA_HAS_AAUDIO
29899 case ma_backend_aaudio:
29900 {
29901 result = ma_context_init__aaudio(&config, pContext);
29902 } break;
29903 #endif
29904 #ifdef MA_HAS_OPENSL
29905 case ma_backend_opensl:
29906 {
29907 result = ma_context_init__opensl(&config, pContext);
29908 } break;
29909 #endif
29910 #ifdef MA_HAS_WEBAUDIO
29911 case ma_backend_webaudio:
29912 {
29913 result = ma_context_init__webaudio(&config, pContext);
29914 } break;
29915 #endif
29916 #ifdef MA_HAS_NULL
29917 case ma_backend_null:
29918 {
29919 result = ma_context_init__null(&config, pContext);
29920 } break;
29921 #endif
29922
29923 default: break;
29924 }
29925
29926 /* If this iteration was successful, return. */
29927 if (result == MA_SUCCESS) {
29928 result = ma_mutex_init(pContext, &pContext->deviceEnumLock);
29929 if (result != MA_SUCCESS) {
29930 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.", result);
29931 }
29932 result = ma_mutex_init(pContext, &pContext->deviceInfoLock);
29933 if (result != MA_SUCCESS) {
29934 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.", result);
29935 }
29936
29937 #ifdef MA_DEBUG_OUTPUT
29938 printf("[miniaudio] Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
29939 printf("[miniaudio] SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
29940 printf("[miniaudio] AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
29941 printf("[miniaudio] AVX512F: %s\n", ma_has_avx512f() ? "YES" : "NO");
29942 printf("[miniaudio] NEON: %s\n", ma_has_neon() ? "YES" : "NO");
29943 #endif
29944
29945 pContext->backend = backend;
29946 return result;
29947 }
29948 }
29949
29950 /* If we get here it means an error occurred. */
29951 MA_ZERO_OBJECT(pContext); /* Safety. */
29952 return MA_NO_BACKEND;
29953 }
29954
29955 MA_API ma_result ma_context_uninit(ma_context* pContext)
29956 {
29957 if (pContext == NULL) {
29958 return MA_INVALID_ARGS;
29959 }
29960
29961 pContext->onUninit(pContext);
29962
29963 ma_mutex_uninit(&pContext->deviceEnumLock);
29964 ma_mutex_uninit(&pContext->deviceInfoLock);
29965 ma__free_from_callbacks(pContext->pDeviceInfos, &pContext->allocationCallbacks);
29966 ma_context_uninit_backend_apis(pContext);
29967
29968 return MA_SUCCESS;
29969 }
29970
29971 MA_API size_t ma_context_sizeof()
29972 {
29973 return sizeof(ma_context);
29974 }
29975
29976
29977 MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
29978 {
29979 ma_result result;
29980
29981 if (pContext == NULL || pContext->onEnumDevices == NULL || callback == NULL) {
29982 return MA_INVALID_ARGS;
29983 }
29984
29985 ma_mutex_lock(&pContext->deviceEnumLock);
29986 {
29987 result = pContext->onEnumDevices(pContext, callback, pUserData);
29988 }
29989 ma_mutex_unlock(&pContext->deviceEnumLock);
29990
29991 return result;
29992 }
29993
29994
29995 static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
29996 {
29997 /*
29998 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
29999 it's just appended to the end. If it's a playback device it's inserted just before the first capture device.
30000 */
30001
30002 /*
30003 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
30004 simple fixed size increment for buffer expansion.
30005 */
30006 const ma_uint32 bufferExpansionCount = 2;
30007 const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
30008
30009 if (pContext->deviceInfoCapacity >= totalDeviceInfoCount) {
30010 ma_uint32 oldCapacity = pContext->deviceInfoCapacity;
30011 ma_uint32 newCapacity = oldCapacity + bufferExpansionCount;
30012 ma_device_info* pNewInfos = (ma_device_info*)ma__realloc_from_callbacks(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, sizeof(*pContext->pDeviceInfos)*oldCapacity, &pContext->allocationCallbacks);
30013 if (pNewInfos == NULL) {
30014 return MA_FALSE; /* Out of memory. */
30015 }
30016
30017 pContext->pDeviceInfos = pNewInfos;
30018 pContext->deviceInfoCapacity = newCapacity;
30019 }
30020
30021 if (deviceType == ma_device_type_playback) {
30022 /* Playback. Insert just before the first capture device. */
30023
30024 /* The first thing to do is move all of the capture devices down a slot. */
30025 ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
30026 size_t iCaptureDevice;
30027 for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
30028 pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
30029 }
30030
30031 /* Now just insert where the first capture device was before moving it down a slot. */
30032 pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
30033 pContext->playbackDeviceInfoCount += 1;
30034 } else {
30035 /* Capture. Insert at the end. */
30036 pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
30037 pContext->captureDeviceInfoCount += 1;
30038 }
30039
30040 (void)pUserData;
30041 return MA_TRUE;
30042 }
30043
30044 MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount)
30045 {
30046 ma_result result;
30047
30048 /* Safety. */
30049 if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
30050 if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
30051 if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
30052 if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
30053
30054 if (pContext == NULL || pContext->onEnumDevices == NULL) {
30055 return MA_INVALID_ARGS;
30056 }
30057
30058 /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
30059 ma_mutex_lock(&pContext->deviceEnumLock);
30060 {
30061 /* Reset everything first. */
30062 pContext->playbackDeviceInfoCount = 0;
30063 pContext->captureDeviceInfoCount = 0;
30064
30065 /* Now enumerate over available devices. */
30066 result = pContext->onEnumDevices(pContext, ma_context_get_devices__enum_callback, NULL);
30067 if (result == MA_SUCCESS) {
30068 /* Playback devices. */
30069 if (ppPlaybackDeviceInfos != NULL) {
30070 *ppPlaybackDeviceInfos = pContext->pDeviceInfos;
30071 }
30072 if (pPlaybackDeviceCount != NULL) {
30073 *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
30074 }
30075
30076 /* Capture devices. */
30077 if (ppCaptureDeviceInfos != NULL) {
30078 *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; /* Capture devices come after playback devices. */
30079 }
30080 if (pCaptureDeviceCount != NULL) {
30081 *pCaptureDeviceCount = pContext->captureDeviceInfoCount;
30082 }
30083 }
30084 }
30085 ma_mutex_unlock(&pContext->deviceEnumLock);
30086
30087 return result;
30088 }
30089
30090 MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
30091 {
30092 ma_device_info deviceInfo;
30093
30094 /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
30095 if (pContext == NULL || pDeviceInfo == NULL) {
30096 return MA_INVALID_ARGS;
30097 }
30098
30099 MA_ZERO_OBJECT(&deviceInfo);
30100
30101 /* Help the backend out by copying over the device ID if we have one. */
30102 if (pDeviceID != NULL) {
30103 MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
30104 }
30105
30106 /* The backend may have an optimized device info retrieval function. If so, try that first. */
30107 if (pContext->onGetDeviceInfo != NULL) {
30108 ma_result result;
30109 ma_mutex_lock(&pContext->deviceInfoLock);
30110 {
30111 result = pContext->onGetDeviceInfo(pContext, deviceType, pDeviceID, shareMode, &deviceInfo);
30112 }
30113 ma_mutex_unlock(&pContext->deviceInfoLock);
30114
30115 /* Clamp ranges. */
30116 deviceInfo.minChannels = ma_max(deviceInfo.minChannels, MA_MIN_CHANNELS);
30117 deviceInfo.maxChannels = ma_min(deviceInfo.maxChannels, MA_MAX_CHANNELS);
30118 deviceInfo.minSampleRate = ma_max(deviceInfo.minSampleRate, MA_MIN_SAMPLE_RATE);
30119 deviceInfo.maxSampleRate = ma_min(deviceInfo.maxSampleRate, MA_MAX_SAMPLE_RATE);
30120
30121 *pDeviceInfo = deviceInfo;
30122 return result;
30123 }
30124
30125 /* Getting here means onGetDeviceInfo has not been set. */
30126 return MA_ERROR;
30127 }
30128
30129 MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext)
30130 {
30131 if (pContext == NULL) {
30132 return MA_FALSE;
30133 }
30134
30135 return ma_is_loopback_supported(pContext->backend);
30136 }
30137
30138
30139 MA_API ma_device_config ma_device_config_init(ma_device_type deviceType)
30140 {
30141 ma_device_config config;
30142 MA_ZERO_OBJECT(&config);
30143 config.deviceType = deviceType;
30144
30145 /* Resampling defaults. We must never use the Speex backend by default because it uses licensed third party code. */
30146 config.resampling.algorithm = ma_resample_algorithm_linear;
30147 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
30148 config.resampling.speex.quality = 3;
30149
30150 return config;
30151 }
30152
30153 MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
30154 {
30155 ma_result result;
30156 ma_device_config config;
30157
30158 if (pContext == NULL) {
30159 return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
30160 }
30161 if (pDevice == NULL) {
30162 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
30163 }
30164 if (pConfig == NULL) {
30165 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pConfig == NULL).", MA_INVALID_ARGS);
30166 }
30167
30168 /* We need to make a copy of the config so we can set default values if they were left unset in the input config. */
30169 config = *pConfig;
30170
30171 /* Basic config validation. */
30172 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) {
30173 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);
30174 }
30175
30176 if (config.deviceType == ma_device_type_capture || config.deviceType == ma_device_type_duplex) {
30177 if (config.capture.channels > MA_MAX_CHANNELS) {
30178 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);
30179 }
30180 if (!ma__is_channel_map_valid(config.capture.channelMap, config.capture.channels)) {
30181 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);
30182 }
30183 }
30184
30185 if (config.deviceType == ma_device_type_playback || config.deviceType == ma_device_type_duplex || config.deviceType == ma_device_type_loopback) {
30186 if (config.playback.channels > MA_MAX_CHANNELS) {
30187 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);
30188 }
30189 if (!ma__is_channel_map_valid(config.playback.channelMap, config.playback.channels)) {
30190 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);
30191 }
30192 }
30193
30194
30195 MA_ZERO_OBJECT(pDevice);
30196 pDevice->pContext = pContext;
30197
30198 /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
30199 pDevice->pUserData = config.pUserData;
30200 pDevice->onData = config.dataCallback;
30201 pDevice->onStop = config.stopCallback;
30202
30203 if (((ma_uintptr)pDevice % sizeof(pDevice)) != 0) {
30204 if (pContext->logCallback) {
30205 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.");
30206 }
30207 }
30208
30209 pDevice->noPreZeroedOutputBuffer = config.noPreZeroedOutputBuffer;
30210 pDevice->noClip = config.noClip;
30211 pDevice->masterVolumeFactor = 1;
30212
30213 /*
30214 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
30215 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.
30216 */
30217 if (config.sampleRate == 0) {
30218 config.sampleRate = MA_DEFAULT_SAMPLE_RATE;
30219 pDevice->usingDefaultSampleRate = MA_TRUE;
30220 }
30221
30222 if (config.capture.format == ma_format_unknown) {
30223 config.capture.format = MA_DEFAULT_FORMAT;
30224 pDevice->capture.usingDefaultFormat = MA_TRUE;
30225 }
30226 if (config.capture.channels == 0) {
30227 config.capture.channels = MA_DEFAULT_CHANNELS;
30228 pDevice->capture.usingDefaultChannels = MA_TRUE;
30229 }
30230 if (config.capture.channelMap[0] == MA_CHANNEL_NONE) {
30231 pDevice->capture.usingDefaultChannelMap = MA_TRUE;
30232 }
30233
30234 if (config.playback.format == ma_format_unknown) {
30235 config.playback.format = MA_DEFAULT_FORMAT;
30236 pDevice->playback.usingDefaultFormat = MA_TRUE;
30237 }
30238 if (config.playback.channels == 0) {
30239 config.playback.channels = MA_DEFAULT_CHANNELS;
30240 pDevice->playback.usingDefaultChannels = MA_TRUE;
30241 }
30242 if (config.playback.channelMap[0] == MA_CHANNEL_NONE) {
30243 pDevice->playback.usingDefaultChannelMap = MA_TRUE;
30244 }
30245
30246
30247 /* Default periods. */
30248 if (config.periods == 0) {
30249 config.periods = MA_DEFAULT_PERIODS;
30250 pDevice->usingDefaultPeriods = MA_TRUE;
30251 }
30252
30253 /*
30254 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
30255 periods acting as a buffer in case the capture and playback devices get's slightly out of sync.
30256 */
30257 if (config.deviceType == ma_device_type_duplex && config.periods < 3) {
30258 config.periods = 3;
30259 }
30260
30261 /* Default buffer size. */
30262 if (config.periodSizeInMilliseconds == 0 && config.periodSizeInFrames == 0) {
30263 config.periodSizeInMilliseconds = (config.performanceProfile == ma_performance_profile_low_latency) ? MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY : MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE;
30264 pDevice->usingDefaultBufferSize = MA_TRUE;
30265 }
30266
30267
30268
30269 pDevice->type = config.deviceType;
30270 pDevice->sampleRate = config.sampleRate;
30271 pDevice->resampling.algorithm = config.resampling.algorithm;
30272 pDevice->resampling.linear.lpfOrder = config.resampling.linear.lpfOrder;
30273 pDevice->resampling.speex.quality = config.resampling.speex.quality;
30274
30275 pDevice->capture.shareMode = config.capture.shareMode;
30276 pDevice->capture.format = config.capture.format;
30277 pDevice->capture.channels = config.capture.channels;
30278 ma_channel_map_copy(pDevice->capture.channelMap, config.capture.channelMap, config.capture.channels);
30279
30280 pDevice->playback.shareMode = config.playback.shareMode;
30281 pDevice->playback.format = config.playback.format;
30282 pDevice->playback.channels = config.playback.channels;
30283 ma_channel_map_copy(pDevice->playback.channelMap, config.playback.channelMap, config.playback.channels);
30284
30285
30286 /* The internal format, channel count and sample rate can be modified by the backend. */
30287 pDevice->capture.internalFormat = pDevice->capture.format;
30288 pDevice->capture.internalChannels = pDevice->capture.channels;
30289 pDevice->capture.internalSampleRate = pDevice->sampleRate;
30290 ma_channel_map_copy(pDevice->capture.internalChannelMap, pDevice->capture.channelMap, pDevice->capture.channels);
30291
30292 pDevice->playback.internalFormat = pDevice->playback.format;
30293 pDevice->playback.internalChannels = pDevice->playback.channels;
30294 pDevice->playback.internalSampleRate = pDevice->sampleRate;
30295 ma_channel_map_copy(pDevice->playback.internalChannelMap, pDevice->playback.channelMap, pDevice->playback.channels);
30296
30297 result = ma_mutex_init(pContext, &pDevice->lock);
30298 if (result != MA_SUCCESS) {
30299 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create mutex.", result);
30300 }
30301
30302 /*
30303 When the device is started, the worker thread is the one that does the actual startup of the backend device. We
30304 use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
30305
30306 Each of these semaphores is released internally by the worker thread when the work is completed. The start
30307 semaphore is also used to wake up the worker thread.
30308 */
30309 result = ma_event_init(pContext, &pDevice->wakeupEvent);
30310 if (result != MA_SUCCESS) {
30311 ma_mutex_uninit(&pDevice->lock);
30312 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread wakeup event.", result);
30313 }
30314
30315 result = ma_event_init(pContext, &pDevice->startEvent);
30316 if (result != MA_SUCCESS) {
30317 ma_event_uninit(&pDevice->wakeupEvent);
30318 ma_mutex_uninit(&pDevice->lock);
30319 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread start event.", result);
30320 }
30321
30322 result = ma_event_init(pContext, &pDevice->stopEvent);
30323 if (result != MA_SUCCESS) {
30324 ma_event_uninit(&pDevice->startEvent);
30325 ma_event_uninit(&pDevice->wakeupEvent);
30326 ma_mutex_uninit(&pDevice->lock);
30327 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread stop event.", result);
30328 }
30329
30330
30331 result = pContext->onDeviceInit(pContext, &config, pDevice);
30332 if (result != MA_SUCCESS) {
30333 return result;
30334 }
30335
30336 ma_device__post_init_setup(pDevice, pConfig->deviceType);
30337
30338
30339 /* If the backend did not fill out a name for the device, try a generic method. */
30340 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30341 if (pDevice->capture.name[0] == '\0') {
30342 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) {
30343 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), (config.capture.pDeviceID == NULL) ? MA_DEFAULT_CAPTURE_DEVICE_NAME : "Capture Device", (size_t)-1);
30344 }
30345 }
30346 }
30347 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
30348 if (pDevice->playback.name[0] == '\0') {
30349 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) {
30350 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), (config.playback.pDeviceID == NULL) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : "Playback Device", (size_t)-1);
30351 }
30352 }
30353 }
30354
30355
30356 /* Some backends don't require the worker thread. */
30357 if (!ma_context_is_backend_asynchronous(pContext)) {
30358 /* The worker thread. */
30359 result = ma_thread_create(pContext, &pDevice->thread, ma_worker_thread, pDevice);
30360 if (result != MA_SUCCESS) {
30361 ma_device_uninit(pDevice);
30362 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread.", result);
30363 }
30364
30365 /* Wait for the worker thread to put the device into it's stopped state for real. */
30366 ma_event_wait(&pDevice->stopEvent);
30367 } else {
30368 ma_device__set_state(pDevice, MA_STATE_STOPPED);
30369 }
30370
30371
30372 #ifdef MA_DEBUG_OUTPUT
30373 printf("[%s]\n", ma_get_backend_name(pDevice->pContext->backend));
30374 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30375 printf(" %s (%s)\n", pDevice->capture.name, "Capture");
30376 printf(" Format: %s -> %s\n", ma_get_format_name(pDevice->capture.format), ma_get_format_name(pDevice->capture.internalFormat));
30377 printf(" Channels: %d -> %d\n", pDevice->capture.channels, pDevice->capture.internalChannels);
30378 printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->capture.internalSampleRate);
30379 printf(" Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods));
30380 printf(" Conversion:\n");
30381 printf(" Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO");
30382 printf(" Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO");
30383 printf(" Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO");
30384 printf(" Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO");
30385 printf(" Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
30386 }
30387 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30388 printf(" %s (%s)\n", pDevice->playback.name, "Playback");
30389 printf(" Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat));
30390 printf(" Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
30391 printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
30392 printf(" Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods));
30393 printf(" Conversion:\n");
30394 printf(" Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO");
30395 printf(" Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO");
30396 printf(" Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO");
30397 printf(" Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO");
30398 printf(" Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
30399 }
30400 #endif
30401
30402
30403 MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_STOPPED);
30404 return MA_SUCCESS;
30405 }
30406
30407 MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice)
30408 {
30409 ma_result result;
30410 ma_context* pContext;
30411 ma_backend defaultBackends[ma_backend_null+1];
30412 ma_uint32 iBackend;
30413 ma_backend* pBackendsToIterate;
30414 ma_uint32 backendsToIterateCount;
30415 ma_allocation_callbacks allocationCallbacks;
30416
30417 if (pConfig == NULL) {
30418 return MA_INVALID_ARGS;
30419 }
30420
30421 if (pContextConfig != NULL) {
30422 result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);
30423 if (result != MA_SUCCESS) {
30424 return result;
30425 }
30426 } else {
30427 allocationCallbacks = ma_allocation_callbacks_init_default();
30428 }
30429
30430
30431 pContext = (ma_context*)ma__malloc_from_callbacks(sizeof(*pContext), &allocationCallbacks);
30432 if (pContext == NULL) {
30433 return MA_OUT_OF_MEMORY;
30434 }
30435
30436 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
30437 defaultBackends[iBackend] = (ma_backend)iBackend;
30438 }
30439
30440 pBackendsToIterate = (ma_backend*)backends;
30441 backendsToIterateCount = backendCount;
30442 if (pBackendsToIterate == NULL) {
30443 pBackendsToIterate = (ma_backend*)defaultBackends;
30444 backendsToIterateCount = ma_countof(defaultBackends);
30445 }
30446
30447 result = MA_NO_BACKEND;
30448
30449 for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
30450 result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
30451 if (result == MA_SUCCESS) {
30452 result = ma_device_init(pContext, pConfig, pDevice);
30453 if (result == MA_SUCCESS) {
30454 break; /* Success. */
30455 } else {
30456 ma_context_uninit(pContext); /* Failure. */
30457 }
30458 }
30459 }
30460
30461 if (result != MA_SUCCESS) {
30462 ma__free_from_callbacks(pContext, &allocationCallbacks);
30463 return result;
30464 }
30465
30466 pDevice->isOwnerOfContext = MA_TRUE;
30467 return result;
30468 }
30469
30470 MA_API void ma_device_uninit(ma_device* pDevice)
30471 {
30472 if (!ma_device__is_initialized(pDevice)) {
30473 return;
30474 }
30475
30476 /* 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. */
30477 if (ma_device_is_started(pDevice)) {
30478 ma_device_stop(pDevice);
30479 }
30480
30481 /* Putting the device into an uninitialized state will make the worker thread return. */
30482 ma_device__set_state(pDevice, MA_STATE_UNINITIALIZED);
30483
30484 /* Wake up the worker thread and wait for it to properly terminate. */
30485 if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
30486 ma_event_signal(&pDevice->wakeupEvent);
30487 ma_thread_wait(&pDevice->thread);
30488 }
30489
30490 pDevice->pContext->onDeviceUninit(pDevice);
30491
30492 ma_event_uninit(&pDevice->stopEvent);
30493 ma_event_uninit(&pDevice->startEvent);
30494 ma_event_uninit(&pDevice->wakeupEvent);
30495 ma_mutex_uninit(&pDevice->lock);
30496
30497 if (pDevice->isOwnerOfContext) {
30498 ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;
30499
30500 ma_context_uninit(pDevice->pContext);
30501 ma__free_from_callbacks(pDevice->pContext, &allocationCallbacks);
30502 }
30503
30504 MA_ZERO_OBJECT(pDevice);
30505 }
30506
30507 MA_API ma_result ma_device_start(ma_device* pDevice)
30508 {
30509 ma_result result;
30510
30511 if (pDevice == NULL) {
30512 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
30513 }
30514
30515 if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
30516 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
30517 }
30518
30519 if (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
30520 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. */
30521 }
30522
30523 result = MA_ERROR;
30524 ma_mutex_lock(&pDevice->lock);
30525 {
30526 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
30527 MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_STOPPED);
30528
30529 ma_device__set_state(pDevice, MA_STATE_STARTING);
30530
30531 /* Asynchronous backends need to be handled differently. */
30532 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
30533 result = pDevice->pContext->onDeviceStart(pDevice);
30534 if (result == MA_SUCCESS) {
30535 ma_device__set_state(pDevice, MA_STATE_STARTED);
30536 }
30537 } else {
30538 /*
30539 Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
30540 thread and then wait for the start event.
30541 */
30542 ma_event_signal(&pDevice->wakeupEvent);
30543
30544 /*
30545 Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
30546 into the started state. Don't call ma_device__set_state() here.
30547 */
30548 ma_event_wait(&pDevice->startEvent);
30549 result = pDevice->workResult;
30550 }
30551 }
30552 ma_mutex_unlock(&pDevice->lock);
30553
30554 return result;
30555 }
30556
30557 MA_API ma_result ma_device_stop(ma_device* pDevice)
30558 {
30559 ma_result result;
30560
30561 if (pDevice == NULL) {
30562 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
30563 }
30564
30565 if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
30566 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
30567 }
30568
30569 if (ma_device__get_state(pDevice) == MA_STATE_STOPPED) {
30570 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. */
30571 }
30572
30573 result = MA_ERROR;
30574 ma_mutex_lock(&pDevice->lock);
30575 {
30576 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
30577 MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_STARTED);
30578
30579 ma_device__set_state(pDevice, MA_STATE_STOPPING);
30580
30581 /* There's no need to wake up the thread like we do when starting. */
30582
30583 if (pDevice->pContext->onDeviceStop) {
30584 result = pDevice->pContext->onDeviceStop(pDevice);
30585 } else {
30586 result = MA_SUCCESS;
30587 }
30588
30589 /* Asynchronous backends need to be handled differently. */
30590 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
30591 ma_device__set_state(pDevice, MA_STATE_STOPPED);
30592 } else {
30593 /* Synchronous backends. */
30594
30595 /*
30596 We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
30597 the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
30598 */
30599 ma_event_wait(&pDevice->stopEvent);
30600 result = MA_SUCCESS;
30601 }
30602 }
30603 ma_mutex_unlock(&pDevice->lock);
30604
30605 return result;
30606 }
30607
30608 MA_API ma_bool32 ma_device_is_started(ma_device* pDevice)
30609 {
30610 if (pDevice == NULL) {
30611 return MA_FALSE;
30612 }
30613
30614 return ma_device__get_state(pDevice) == MA_STATE_STARTED;
30615 }
30616
30617 MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume)
30618 {
30619 if (pDevice == NULL) {
30620 return MA_INVALID_ARGS;
30621 }
30622
30623 if (volume < 0.0f || volume > 1.0f) {
30624 return MA_INVALID_ARGS;
30625 }
30626
30627 pDevice->masterVolumeFactor = volume;
30628
30629 return MA_SUCCESS;
30630 }
30631
30632 MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
30633 {
30634 if (pVolume == NULL) {
30635 return MA_INVALID_ARGS;
30636 }
30637
30638 if (pDevice == NULL) {
30639 *pVolume = 0;
30640 return MA_INVALID_ARGS;
30641 }
30642
30643 *pVolume = pDevice->masterVolumeFactor;
30644
30645 return MA_SUCCESS;
30646 }
30647
30648 MA_API ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB)
30649 {
30650 if (gainDB > 0) {
30651 return MA_INVALID_ARGS;
30652 }
30653
30654 return ma_device_set_master_volume(pDevice, ma_gain_db_to_factor(gainDB));
30655 }
30656
30657 MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB)
30658 {
30659 float factor;
30660 ma_result result;
30661
30662 if (pGainDB == NULL) {
30663 return MA_INVALID_ARGS;
30664 }
30665
30666 result = ma_device_get_master_volume(pDevice, &factor);
30667 if (result != MA_SUCCESS) {
30668 *pGainDB = 0;
30669 return result;
30670 }
30671
30672 *pGainDB = ma_factor_to_gain_db(factor);
30673
30674 return MA_SUCCESS;
30675 }
30676 #endif /* MA_NO_DEVICE_IO */
30677
30678
30679 /**************************************************************************************************************************************************************
30680
30681 Biquad Filter
30682
30683 **************************************************************************************************************************************************************/
30684 #ifndef MA_BIQUAD_FIXED_POINT_SHIFT
30685 #define MA_BIQUAD_FIXED_POINT_SHIFT 14
30686 #endif
30687
30688 static ma_int32 ma_biquad_float_to_fp(double x)
30689 {
30690 return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT));
30691 }
30692
30693 MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2)
30694 {
30695 ma_biquad_config config;
30696
30697 MA_ZERO_OBJECT(&config);
30698 config.format = format;
30699 config.channels = channels;
30700 config.b0 = b0;
30701 config.b1 = b1;
30702 config.b2 = b2;
30703 config.a0 = a0;
30704 config.a1 = a1;
30705 config.a2 = a2;
30706
30707 return config;
30708 }
30709
30710 MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ)
30711 {
30712 if (pBQ == NULL) {
30713 return MA_INVALID_ARGS;
30714 }
30715
30716 MA_ZERO_OBJECT(pBQ);
30717
30718 if (pConfig == NULL) {
30719 return MA_INVALID_ARGS;
30720 }
30721
30722 return ma_biquad_reinit(pConfig, pBQ);
30723 }
30724
30725 MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)
30726 {
30727 if (pBQ == NULL || pConfig == NULL) {
30728 return MA_INVALID_ARGS;
30729 }
30730
30731 if (pConfig->a0 == 0) {
30732 return MA_INVALID_ARGS; /* Division by zero. */
30733 }
30734
30735 /* Only supporting f32 and s16. */
30736 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
30737 return MA_INVALID_ARGS;
30738 }
30739
30740 /* The format cannot be changed after initialization. */
30741 if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) {
30742 return MA_INVALID_OPERATION;
30743 }
30744
30745 /* The channel count cannot be changed after initialization. */
30746 if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) {
30747 return MA_INVALID_OPERATION;
30748 }
30749
30750
30751 pBQ->format = pConfig->format;
30752 pBQ->channels = pConfig->channels;
30753
30754 /* Normalize. */
30755 if (pConfig->format == ma_format_f32) {
30756 pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0);
30757 pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0);
30758 pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0);
30759 pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0);
30760 pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0);
30761 } else {
30762 pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0);
30763 pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0);
30764 pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0);
30765 pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0);
30766 pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0);
30767 }
30768
30769 return MA_SUCCESS;
30770 }
30771
30772 static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)
30773 {
30774 ma_uint32 c;
30775 const float b0 = pBQ->b0.f32;
30776 const float b1 = pBQ->b1.f32;
30777 const float b2 = pBQ->b2.f32;
30778 const float a1 = pBQ->a1.f32;
30779 const float a2 = pBQ->a2.f32;
30780
30781 for (c = 0; c < pBQ->channels; c += 1) {
30782 float r1 = pBQ->r1[c].f32;
30783 float r2 = pBQ->r2[c].f32;
30784 float x = pX[c];
30785 float y;
30786
30787 y = b0*x + r1;
30788 r1 = b1*x - a1*y + r2;
30789 r2 = b2*x - a2*y;
30790
30791 pY[c] = y;
30792 pBQ->r1[c].f32 = r1;
30793 pBQ->r2[c].f32 = r2;
30794 }
30795 }
30796
30797 static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)
30798 {
30799 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
30800 }
30801
30802 static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
30803 {
30804 ma_uint32 c;
30805 const ma_int32 b0 = pBQ->b0.s32;
30806 const ma_int32 b1 = pBQ->b1.s32;
30807 const ma_int32 b2 = pBQ->b2.s32;
30808 const ma_int32 a1 = pBQ->a1.s32;
30809 const ma_int32 a2 = pBQ->a2.s32;
30810
30811 for (c = 0; c < pBQ->channels; c += 1) {
30812 ma_int32 r1 = pBQ->r1[c].s32;
30813 ma_int32 r2 = pBQ->r2[c].s32;
30814 ma_int32 x = pX[c];
30815 ma_int32 y;
30816
30817 y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
30818 r1 = (b1*x - a1*y + r2);
30819 r2 = (b2*x - a2*y);
30820
30821 pY[c] = (ma_int16)ma_clamp(y, -32768, 32767);
30822 pBQ->r1[c].s32 = r1;
30823 pBQ->r2[c].s32 = r2;
30824 }
30825 }
30826
30827 static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
30828 {
30829 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
30830 }
30831
30832 MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
30833 {
30834 ma_uint32 n;
30835
30836 if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
30837 return MA_INVALID_ARGS;
30838 }
30839
30840 /* 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. */
30841
30842 if (pBQ->format == ma_format_f32) {
30843 /* */ float* pY = ( float*)pFramesOut;
30844 const float* pX = (const float*)pFramesIn;
30845
30846 for (n = 0; n < frameCount; n += 1) {
30847 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
30848 pY += pBQ->channels;
30849 pX += pBQ->channels;
30850 }
30851 } else if (pBQ->format == ma_format_s16) {
30852 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
30853 const ma_int16* pX = (const ma_int16*)pFramesIn;
30854
30855 for (n = 0; n < frameCount; n += 1) {
30856 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
30857 pY += pBQ->channels;
30858 pX += pBQ->channels;
30859 }
30860 } else {
30861 MA_ASSERT(MA_FALSE);
30862 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
30863 }
30864
30865 return MA_SUCCESS;
30866 }
30867
30868 MA_API ma_uint32 ma_biquad_get_latency(ma_biquad* pBQ)
30869 {
30870 if (pBQ == NULL) {
30871 return 0;
30872 }
30873
30874 return 2;
30875 }
30876
30877
30878 /**************************************************************************************************************************************************************
30879
30880 Low-Pass Filter
30881
30882 **************************************************************************************************************************************************************/
30883 MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
30884 {
30885 ma_lpf1_config config;
30886
30887 MA_ZERO_OBJECT(&config);
30888 config.format = format;
30889 config.channels = channels;
30890 config.sampleRate = sampleRate;
30891 config.cutoffFrequency = cutoffFrequency;
30892 config.q = 0.5;
30893
30894 return config;
30895 }
30896
30897 MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
30898 {
30899 ma_lpf2_config config;
30900
30901 MA_ZERO_OBJECT(&config);
30902 config.format = format;
30903 config.channels = channels;
30904 config.sampleRate = sampleRate;
30905 config.cutoffFrequency = cutoffFrequency;
30906 config.q = q;
30907
30908 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
30909 if (config.q == 0) {
30910 config.q = 0.707107;
30911 }
30912
30913 return config;
30914 }
30915
30916
30917 MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)
30918 {
30919 if (pLPF == NULL) {
30920 return MA_INVALID_ARGS;
30921 }
30922
30923 MA_ZERO_OBJECT(pLPF);
30924
30925 if (pConfig == NULL) {
30926 return MA_INVALID_ARGS;
30927 }
30928
30929 return ma_lpf1_reinit(pConfig, pLPF);
30930 }
30931
30932 MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)
30933 {
30934 double a;
30935
30936 if (pLPF == NULL || pConfig == NULL) {
30937 return MA_INVALID_ARGS;
30938 }
30939
30940 /* Only supporting f32 and s16. */
30941 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
30942 return MA_INVALID_ARGS;
30943 }
30944
30945 /* The format cannot be changed after initialization. */
30946 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
30947 return MA_INVALID_OPERATION;
30948 }
30949
30950 /* The channel count cannot be changed after initialization. */
30951 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
30952 return MA_INVALID_OPERATION;
30953 }
30954
30955 pLPF->format = pConfig->format;
30956 pLPF->channels = pConfig->channels;
30957
30958 a = ma_exp(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
30959 if (pConfig->format == ma_format_f32) {
30960 pLPF->a.f32 = (float)a;
30961 } else {
30962 pLPF->a.s32 = ma_biquad_float_to_fp(a);
30963 }
30964
30965 return MA_SUCCESS;
30966 }
30967
30968 static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX)
30969 {
30970 ma_uint32 c;
30971 const float a = pLPF->a.f32;
30972 const float b = 1 - a;
30973
30974 for (c = 0; c < pLPF->channels; c += 1) {
30975 float r1 = pLPF->r1[c].f32;
30976 float x = pX[c];
30977 float y;
30978
30979 y = b*x + a*r1;
30980
30981 pY[c] = y;
30982 pLPF->r1[c].f32 = y;
30983 }
30984 }
30985
30986 static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX)
30987 {
30988 ma_uint32 c;
30989 const ma_int32 a = pLPF->a.s32;
30990 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
30991
30992 for (c = 0; c < pLPF->channels; c += 1) {
30993 ma_int32 r1 = pLPF->r1[c].s32;
30994 ma_int32 x = pX[c];
30995 ma_int32 y;
30996
30997 y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
30998
30999 pY[c] = (ma_int16)y;
31000 pLPF->r1[c].s32 = (ma_int32)y;
31001 }
31002 }
31003
31004 MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31005 {
31006 ma_uint32 n;
31007
31008 if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
31009 return MA_INVALID_ARGS;
31010 }
31011
31012 /* 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. */
31013
31014 if (pLPF->format == ma_format_f32) {
31015 /* */ float* pY = ( float*)pFramesOut;
31016 const float* pX = (const float*)pFramesIn;
31017
31018 for (n = 0; n < frameCount; n += 1) {
31019 ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX);
31020 pY += pLPF->channels;
31021 pX += pLPF->channels;
31022 }
31023 } else if (pLPF->format == ma_format_s16) {
31024 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
31025 const ma_int16* pX = (const ma_int16*)pFramesIn;
31026
31027 for (n = 0; n < frameCount; n += 1) {
31028 ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX);
31029 pY += pLPF->channels;
31030 pX += pLPF->channels;
31031 }
31032 } else {
31033 MA_ASSERT(MA_FALSE);
31034 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
31035 }
31036
31037 return MA_SUCCESS;
31038 }
31039
31040 MA_API ma_uint32 ma_lpf1_get_latency(ma_lpf1* pLPF)
31041 {
31042 if (pLPF == NULL) {
31043 return 0;
31044 }
31045
31046 return 1;
31047 }
31048
31049
31050 static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig)
31051 {
31052 ma_biquad_config bqConfig;
31053 double q;
31054 double w;
31055 double s;
31056 double c;
31057 double a;
31058
31059 MA_ASSERT(pConfig != NULL);
31060
31061 q = pConfig->q;
31062 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
31063 s = ma_sin(w);
31064 c = ma_cos(w);
31065 a = s / (2*q);
31066
31067 bqConfig.b0 = (1 - c) / 2;
31068 bqConfig.b1 = 1 - c;
31069 bqConfig.b2 = (1 - c) / 2;
31070 bqConfig.a0 = 1 + a;
31071 bqConfig.a1 = -2 * c;
31072 bqConfig.a2 = 1 - a;
31073
31074 bqConfig.format = pConfig->format;
31075 bqConfig.channels = pConfig->channels;
31076
31077 return bqConfig;
31078 }
31079
31080 MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)
31081 {
31082 ma_result result;
31083 ma_biquad_config bqConfig;
31084
31085 if (pLPF == NULL) {
31086 return MA_INVALID_ARGS;
31087 }
31088
31089 MA_ZERO_OBJECT(pLPF);
31090
31091 if (pConfig == NULL) {
31092 return MA_INVALID_ARGS;
31093 }
31094
31095 bqConfig = ma_lpf2__get_biquad_config(pConfig);
31096 result = ma_biquad_init(&bqConfig, &pLPF->bq);
31097 if (result != MA_SUCCESS) {
31098 return result;
31099 }
31100
31101 return MA_SUCCESS;
31102 }
31103
31104 MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)
31105 {
31106 ma_result result;
31107 ma_biquad_config bqConfig;
31108
31109 if (pLPF == NULL || pConfig == NULL) {
31110 return MA_INVALID_ARGS;
31111 }
31112
31113 bqConfig = ma_lpf2__get_biquad_config(pConfig);
31114 result = ma_biquad_reinit(&bqConfig, &pLPF->bq);
31115 if (result != MA_SUCCESS) {
31116 return result;
31117 }
31118
31119 return MA_SUCCESS;
31120 }
31121
31122 static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
31123 {
31124 ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
31125 }
31126
31127 static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn)
31128 {
31129 ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);
31130 }
31131
31132 MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31133 {
31134 if (pLPF == NULL) {
31135 return MA_INVALID_ARGS;
31136 }
31137
31138 return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);
31139 }
31140
31141 MA_API ma_uint32 ma_lpf2_get_latency(ma_lpf2* pLPF)
31142 {
31143 if (pLPF == NULL) {
31144 return 0;
31145 }
31146
31147 return ma_biquad_get_latency(&pLPF->bq);
31148 }
31149
31150
31151 MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
31152 {
31153 ma_lpf_config config;
31154
31155 MA_ZERO_OBJECT(&config);
31156 config.format = format;
31157 config.channels = channels;
31158 config.sampleRate = sampleRate;
31159 config.cutoffFrequency = cutoffFrequency;
31160 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
31161
31162 return config;
31163 }
31164
31165 static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* pLPF, ma_bool32 isNew)
31166 {
31167 ma_result result;
31168 ma_uint32 lpf1Count;
31169 ma_uint32 lpf2Count;
31170 ma_uint32 ilpf1;
31171 ma_uint32 ilpf2;
31172
31173 if (pLPF == NULL || pConfig == NULL) {
31174 return MA_INVALID_ARGS;
31175 }
31176
31177 /* Only supporting f32 and s16. */
31178 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
31179 return MA_INVALID_ARGS;
31180 }
31181
31182 /* The format cannot be changed after initialization. */
31183 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
31184 return MA_INVALID_OPERATION;
31185 }
31186
31187 /* The channel count cannot be changed after initialization. */
31188 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
31189 return MA_INVALID_OPERATION;
31190 }
31191
31192 if (pConfig->order > MA_MAX_FILTER_ORDER) {
31193 return MA_INVALID_ARGS;
31194 }
31195
31196 lpf1Count = pConfig->order % 2;
31197 lpf2Count = pConfig->order / 2;
31198
31199 MA_ASSERT(lpf1Count <= ma_countof(pLPF->lpf1));
31200 MA_ASSERT(lpf2Count <= ma_countof(pLPF->lpf2));
31201
31202 /* The filter order can't change between reinits. */
31203 if (!isNew) {
31204 if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) {
31205 return MA_INVALID_OPERATION;
31206 }
31207 }
31208
31209 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
31210 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
31211
31212 if (isNew) {
31213 result = ma_lpf1_init(&lpf1Config, &pLPF->lpf1[ilpf1]);
31214 } else {
31215 result = ma_lpf1_reinit(&lpf1Config, &pLPF->lpf1[ilpf1]);
31216 }
31217
31218 if (result != MA_SUCCESS) {
31219 return result;
31220 }
31221 }
31222
31223 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
31224 ma_lpf2_config lpf2Config;
31225 double q;
31226 double a;
31227
31228 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
31229 if (lpf1Count == 1) {
31230 a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
31231 } else {
31232 a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
31233 }
31234 q = 1 / (2*ma_cos(a));
31235
31236 lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
31237
31238 if (isNew) {
31239 result = ma_lpf2_init(&lpf2Config, &pLPF->lpf2[ilpf2]);
31240 } else {
31241 result = ma_lpf2_reinit(&lpf2Config, &pLPF->lpf2[ilpf2]);
31242 }
31243
31244 if (result != MA_SUCCESS) {
31245 return result;
31246 }
31247 }
31248
31249 pLPF->lpf1Count = lpf1Count;
31250 pLPF->lpf2Count = lpf2Count;
31251 pLPF->format = pConfig->format;
31252 pLPF->channels = pConfig->channels;
31253
31254 return MA_SUCCESS;
31255 }
31256
31257 MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
31258 {
31259 if (pLPF == NULL) {
31260 return MA_INVALID_ARGS;
31261 }
31262
31263 MA_ZERO_OBJECT(pLPF);
31264
31265 if (pConfig == NULL) {
31266 return MA_INVALID_ARGS;
31267 }
31268
31269 return ma_lpf_reinit__internal(pConfig, pLPF, /*isNew*/MA_TRUE);
31270 }
31271
31272 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
31273 {
31274 return ma_lpf_reinit__internal(pConfig, pLPF, /*isNew*/MA_FALSE);
31275 }
31276
31277 static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX)
31278 {
31279 ma_uint32 ilpf1;
31280 ma_uint32 ilpf2;
31281
31282 MA_ASSERT(pLPF->format == ma_format_f32);
31283
31284 MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
31285
31286 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
31287 ma_lpf1_process_pcm_frame_f32(&pLPF->lpf1[ilpf1], pY, pY);
31288 }
31289
31290 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
31291 ma_lpf2_process_pcm_frame_f32(&pLPF->lpf2[ilpf2], pY, pY);
31292 }
31293 }
31294
31295 static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX)
31296 {
31297 ma_uint32 ilpf1;
31298 ma_uint32 ilpf2;
31299
31300 MA_ASSERT(pLPF->format == ma_format_s16);
31301
31302 MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
31303
31304 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
31305 ma_lpf1_process_pcm_frame_s16(&pLPF->lpf1[ilpf1], pY, pY);
31306 }
31307
31308 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
31309 ma_lpf2_process_pcm_frame_s16(&pLPF->lpf2[ilpf2], pY, pY);
31310 }
31311 }
31312
31313 MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31314 {
31315 ma_result result;
31316 ma_uint32 ilpf1;
31317 ma_uint32 ilpf2;
31318
31319 if (pLPF == NULL) {
31320 return MA_INVALID_ARGS;
31321 }
31322
31323 /* Faster path for in-place. */
31324 if (pFramesOut == pFramesIn) {
31325 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
31326 result = ma_lpf1_process_pcm_frames(&pLPF->lpf1[ilpf1], pFramesOut, pFramesOut, frameCount);
31327 if (result != MA_SUCCESS) {
31328 return result;
31329 }
31330 }
31331
31332 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
31333 result = ma_lpf2_process_pcm_frames(&pLPF->lpf2[ilpf2], pFramesOut, pFramesOut, frameCount);
31334 if (result != MA_SUCCESS) {
31335 return result;
31336 }
31337 }
31338 }
31339
31340 /* Slightly slower path for copying. */
31341 if (pFramesOut != pFramesIn) {
31342 ma_uint32 iFrame;
31343
31344 /* */ if (pLPF->format == ma_format_f32) {
31345 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
31346 const float* pFramesInF32 = (const float*)pFramesIn;
31347
31348 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
31349 ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32);
31350 pFramesOutF32 += pLPF->channels;
31351 pFramesInF32 += pLPF->channels;
31352 }
31353 } else if (pLPF->format == ma_format_s16) {
31354 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
31355 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
31356
31357 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
31358 ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16);
31359 pFramesOutS16 += pLPF->channels;
31360 pFramesInS16 += pLPF->channels;
31361 }
31362 } else {
31363 MA_ASSERT(MA_FALSE);
31364 return MA_INVALID_OPERATION; /* Should never hit this. */
31365 }
31366 }
31367
31368 return MA_SUCCESS;
31369 }
31370
31371 MA_API ma_uint32 ma_lpf_get_latency(ma_lpf* pLPF)
31372 {
31373 if (pLPF == NULL) {
31374 return 0;
31375 }
31376
31377 return pLPF->lpf2Count*2 + pLPF->lpf1Count;
31378 }
31379
31380
31381 /**************************************************************************************************************************************************************
31382
31383 High-Pass Filtering
31384
31385 **************************************************************************************************************************************************************/
31386 MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
31387 {
31388 ma_hpf1_config config;
31389
31390 MA_ZERO_OBJECT(&config);
31391 config.format = format;
31392 config.channels = channels;
31393 config.sampleRate = sampleRate;
31394 config.cutoffFrequency = cutoffFrequency;
31395
31396 return config;
31397 }
31398
31399 MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
31400 {
31401 ma_hpf2_config config;
31402
31403 MA_ZERO_OBJECT(&config);
31404 config.format = format;
31405 config.channels = channels;
31406 config.sampleRate = sampleRate;
31407 config.cutoffFrequency = cutoffFrequency;
31408 config.q = q;
31409
31410 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
31411 if (config.q == 0) {
31412 config.q = 0.707107;
31413 }
31414
31415 return config;
31416 }
31417
31418
31419 MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, ma_hpf1* pHPF)
31420 {
31421 if (pHPF == NULL) {
31422 return MA_INVALID_ARGS;
31423 }
31424
31425 MA_ZERO_OBJECT(pHPF);
31426
31427 if (pConfig == NULL) {
31428 return MA_INVALID_ARGS;
31429 }
31430
31431 return ma_hpf1_reinit(pConfig, pHPF);
31432 }
31433
31434 MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF)
31435 {
31436 double a;
31437
31438 if (pHPF == NULL || pConfig == NULL) {
31439 return MA_INVALID_ARGS;
31440 }
31441
31442 /* Only supporting f32 and s16. */
31443 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
31444 return MA_INVALID_ARGS;
31445 }
31446
31447 /* The format cannot be changed after initialization. */
31448 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
31449 return MA_INVALID_OPERATION;
31450 }
31451
31452 /* The channel count cannot be changed after initialization. */
31453 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
31454 return MA_INVALID_OPERATION;
31455 }
31456
31457 pHPF->format = pConfig->format;
31458 pHPF->channels = pConfig->channels;
31459
31460 a = ma_exp(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
31461 if (pConfig->format == ma_format_f32) {
31462 pHPF->a.f32 = (float)a;
31463 } else {
31464 pHPF->a.s32 = ma_biquad_float_to_fp(a);
31465 }
31466
31467 return MA_SUCCESS;
31468 }
31469
31470 static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX)
31471 {
31472 ma_uint32 c;
31473 const float a = 1 - pHPF->a.f32;
31474 const float b = 1 - a;
31475
31476 for (c = 0; c < pHPF->channels; c += 1) {
31477 float r1 = pHPF->r1[c].f32;
31478 float x = pX[c];
31479 float y;
31480
31481 y = b*x - a*r1;
31482
31483 pY[c] = y;
31484 pHPF->r1[c].f32 = y;
31485 }
31486 }
31487
31488 static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX)
31489 {
31490 ma_uint32 c;
31491 const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32);
31492 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
31493
31494 for (c = 0; c < pHPF->channels; c += 1) {
31495 ma_int32 r1 = pHPF->r1[c].s32;
31496 ma_int32 x = pX[c];
31497 ma_int32 y;
31498
31499 y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
31500
31501 pY[c] = (ma_int16)y;
31502 pHPF->r1[c].s32 = (ma_int32)y;
31503 }
31504 }
31505
31506 MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31507 {
31508 ma_uint32 n;
31509
31510 if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
31511 return MA_INVALID_ARGS;
31512 }
31513
31514 /* 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. */
31515
31516 if (pHPF->format == ma_format_f32) {
31517 /* */ float* pY = ( float*)pFramesOut;
31518 const float* pX = (const float*)pFramesIn;
31519
31520 for (n = 0; n < frameCount; n += 1) {
31521 ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX);
31522 pY += pHPF->channels;
31523 pX += pHPF->channels;
31524 }
31525 } else if (pHPF->format == ma_format_s16) {
31526 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
31527 const ma_int16* pX = (const ma_int16*)pFramesIn;
31528
31529 for (n = 0; n < frameCount; n += 1) {
31530 ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX);
31531 pY += pHPF->channels;
31532 pX += pHPF->channels;
31533 }
31534 } else {
31535 MA_ASSERT(MA_FALSE);
31536 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
31537 }
31538
31539 return MA_SUCCESS;
31540 }
31541
31542 MA_API ma_uint32 ma_hpf1_get_latency(ma_hpf1* pHPF)
31543 {
31544 if (pHPF == NULL) {
31545 return 0;
31546 }
31547
31548 return 1;
31549 }
31550
31551
31552 static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig)
31553 {
31554 ma_biquad_config bqConfig;
31555 double q;
31556 double w;
31557 double s;
31558 double c;
31559 double a;
31560
31561 MA_ASSERT(pConfig != NULL);
31562
31563 q = pConfig->q;
31564 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
31565 s = ma_sin(w);
31566 c = ma_cos(w);
31567 a = s / (2*q);
31568
31569 bqConfig.b0 = (1 + c) / 2;
31570 bqConfig.b1 = -(1 + c);
31571 bqConfig.b2 = (1 + c) / 2;
31572 bqConfig.a0 = 1 + a;
31573 bqConfig.a1 = -2 * c;
31574 bqConfig.a2 = 1 - a;
31575
31576 bqConfig.format = pConfig->format;
31577 bqConfig.channels = pConfig->channels;
31578
31579 return bqConfig;
31580 }
31581
31582 MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, ma_hpf2* pHPF)
31583 {
31584 ma_result result;
31585 ma_biquad_config bqConfig;
31586
31587 if (pHPF == NULL) {
31588 return MA_INVALID_ARGS;
31589 }
31590
31591 MA_ZERO_OBJECT(pHPF);
31592
31593 if (pConfig == NULL) {
31594 return MA_INVALID_ARGS;
31595 }
31596
31597 bqConfig = ma_hpf2__get_biquad_config(pConfig);
31598 result = ma_biquad_init(&bqConfig, &pHPF->bq);
31599 if (result != MA_SUCCESS) {
31600 return result;
31601 }
31602
31603 return MA_SUCCESS;
31604 }
31605
31606 MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF)
31607 {
31608 ma_result result;
31609 ma_biquad_config bqConfig;
31610
31611 if (pHPF == NULL || pConfig == NULL) {
31612 return MA_INVALID_ARGS;
31613 }
31614
31615 bqConfig = ma_hpf2__get_biquad_config(pConfig);
31616 result = ma_biquad_reinit(&bqConfig, &pHPF->bq);
31617 if (result != MA_SUCCESS) {
31618 return result;
31619 }
31620
31621 return MA_SUCCESS;
31622 }
31623
31624 static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
31625 {
31626 ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn);
31627 }
31628
31629 static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn)
31630 {
31631 ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn);
31632 }
31633
31634 MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31635 {
31636 if (pHPF == NULL) {
31637 return MA_INVALID_ARGS;
31638 }
31639
31640 return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount);
31641 }
31642
31643 MA_API ma_uint32 ma_hpf2_get_latency(ma_hpf2* pHPF)
31644 {
31645 if (pHPF == NULL) {
31646 return 0;
31647 }
31648
31649 return ma_biquad_get_latency(&pHPF->bq);
31650 }
31651
31652
31653 MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
31654 {
31655 ma_hpf_config config;
31656
31657 MA_ZERO_OBJECT(&config);
31658 config.format = format;
31659 config.channels = channels;
31660 config.sampleRate = sampleRate;
31661 config.cutoffFrequency = cutoffFrequency;
31662 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
31663
31664 return config;
31665 }
31666
31667 static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* pHPF, ma_bool32 isNew)
31668 {
31669 ma_result result;
31670 ma_uint32 hpf1Count;
31671 ma_uint32 hpf2Count;
31672 ma_uint32 ihpf1;
31673 ma_uint32 ihpf2;
31674
31675 if (pHPF == NULL || pConfig == NULL) {
31676 return MA_INVALID_ARGS;
31677 }
31678
31679 /* Only supporting f32 and s16. */
31680 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
31681 return MA_INVALID_ARGS;
31682 }
31683
31684 /* The format cannot be changed after initialization. */
31685 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
31686 return MA_INVALID_OPERATION;
31687 }
31688
31689 /* The channel count cannot be changed after initialization. */
31690 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
31691 return MA_INVALID_OPERATION;
31692 }
31693
31694 if (pConfig->order > MA_MAX_FILTER_ORDER) {
31695 return MA_INVALID_ARGS;
31696 }
31697
31698 hpf1Count = pConfig->order % 2;
31699 hpf2Count = pConfig->order / 2;
31700
31701 MA_ASSERT(hpf1Count <= ma_countof(pHPF->hpf1));
31702 MA_ASSERT(hpf2Count <= ma_countof(pHPF->hpf2));
31703
31704 /* The filter order can't change between reinits. */
31705 if (!isNew) {
31706 if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) {
31707 return MA_INVALID_OPERATION;
31708 }
31709 }
31710
31711 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
31712 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
31713
31714 if (isNew) {
31715 result = ma_hpf1_init(&hpf1Config, &pHPF->hpf1[ihpf1]);
31716 } else {
31717 result = ma_hpf1_reinit(&hpf1Config, &pHPF->hpf1[ihpf1]);
31718 }
31719
31720 if (result != MA_SUCCESS) {
31721 return result;
31722 }
31723 }
31724
31725 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
31726 ma_hpf2_config hpf2Config;
31727 double q;
31728 double a;
31729
31730 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
31731 if (hpf1Count == 1) {
31732 a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
31733 } else {
31734 a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
31735 }
31736 q = 1 / (2*ma_cos(a));
31737
31738 hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
31739
31740 if (isNew) {
31741 result = ma_hpf2_init(&hpf2Config, &pHPF->hpf2[ihpf2]);
31742 } else {
31743 result = ma_hpf2_reinit(&hpf2Config, &pHPF->hpf2[ihpf2]);
31744 }
31745
31746 if (result != MA_SUCCESS) {
31747 return result;
31748 }
31749 }
31750
31751 pHPF->hpf1Count = hpf1Count;
31752 pHPF->hpf2Count = hpf2Count;
31753 pHPF->format = pConfig->format;
31754 pHPF->channels = pConfig->channels;
31755
31756 return MA_SUCCESS;
31757 }
31758
31759 MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, ma_hpf* pHPF)
31760 {
31761 if (pHPF == NULL) {
31762 return MA_INVALID_ARGS;
31763 }
31764
31765 MA_ZERO_OBJECT(pHPF);
31766
31767 if (pConfig == NULL) {
31768 return MA_INVALID_ARGS;
31769 }
31770
31771 return ma_hpf_reinit__internal(pConfig, pHPF, /*isNew*/MA_TRUE);
31772 }
31773
31774 MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)
31775 {
31776 return ma_hpf_reinit__internal(pConfig, pHPF, /*isNew*/MA_FALSE);
31777 }
31778
31779 MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31780 {
31781 ma_result result;
31782 ma_uint32 ihpf1;
31783 ma_uint32 ihpf2;
31784
31785 if (pHPF == NULL) {
31786 return MA_INVALID_ARGS;
31787 }
31788
31789 /* Faster path for in-place. */
31790 if (pFramesOut == pFramesIn) {
31791 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
31792 result = ma_hpf1_process_pcm_frames(&pHPF->hpf1[ihpf1], pFramesOut, pFramesOut, frameCount);
31793 if (result != MA_SUCCESS) {
31794 return result;
31795 }
31796 }
31797
31798 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
31799 result = ma_hpf2_process_pcm_frames(&pHPF->hpf2[ihpf2], pFramesOut, pFramesOut, frameCount);
31800 if (result != MA_SUCCESS) {
31801 return result;
31802 }
31803 }
31804 }
31805
31806 /* Slightly slower path for copying. */
31807 if (pFramesOut != pFramesIn) {
31808 ma_uint32 iFrame;
31809
31810 /* */ if (pHPF->format == ma_format_f32) {
31811 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
31812 const float* pFramesInF32 = (const float*)pFramesIn;
31813
31814 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
31815 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
31816
31817 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
31818 ma_hpf1_process_pcm_frame_f32(&pHPF->hpf1[ihpf1], pFramesOutF32, pFramesOutF32);
31819 }
31820
31821 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
31822 ma_hpf2_process_pcm_frame_f32(&pHPF->hpf2[ihpf2], pFramesOutF32, pFramesOutF32);
31823 }
31824
31825 pFramesOutF32 += pHPF->channels;
31826 pFramesInF32 += pHPF->channels;
31827 }
31828 } else if (pHPF->format == ma_format_s16) {
31829 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
31830 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
31831
31832 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
31833 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
31834
31835 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
31836 ma_hpf1_process_pcm_frame_s16(&pHPF->hpf1[ihpf1], pFramesOutS16, pFramesOutS16);
31837 }
31838
31839 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
31840 ma_hpf2_process_pcm_frame_s16(&pHPF->hpf2[ihpf2], pFramesOutS16, pFramesOutS16);
31841 }
31842
31843 pFramesOutS16 += pHPF->channels;
31844 pFramesInS16 += pHPF->channels;
31845 }
31846 } else {
31847 MA_ASSERT(MA_FALSE);
31848 return MA_INVALID_OPERATION; /* Should never hit this. */
31849 }
31850 }
31851
31852 return MA_SUCCESS;
31853 }
31854
31855 MA_API ma_uint32 ma_hpf_get_latency(ma_hpf* pHPF)
31856 {
31857 if (pHPF == NULL) {
31858 return 0;
31859 }
31860
31861 return pHPF->hpf2Count*2 + pHPF->hpf1Count;
31862 }
31863
31864
31865 /**************************************************************************************************************************************************************
31866
31867 Band-Pass Filtering
31868
31869 **************************************************************************************************************************************************************/
31870 MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
31871 {
31872 ma_bpf2_config config;
31873
31874 MA_ZERO_OBJECT(&config);
31875 config.format = format;
31876 config.channels = channels;
31877 config.sampleRate = sampleRate;
31878 config.cutoffFrequency = cutoffFrequency;
31879 config.q = q;
31880
31881 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
31882 if (config.q == 0) {
31883 config.q = 0.707107;
31884 }
31885
31886 return config;
31887 }
31888
31889
31890 static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig)
31891 {
31892 ma_biquad_config bqConfig;
31893 double q;
31894 double w;
31895 double s;
31896 double c;
31897 double a;
31898
31899 MA_ASSERT(pConfig != NULL);
31900
31901 q = pConfig->q;
31902 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
31903 s = ma_sin(w);
31904 c = ma_cos(w);
31905 a = s / (2*q);
31906
31907 bqConfig.b0 = q * a;
31908 bqConfig.b1 = 0;
31909 bqConfig.b2 = -q * a;
31910 bqConfig.a0 = 1 + a;
31911 bqConfig.a1 = -2 * c;
31912 bqConfig.a2 = 1 - a;
31913
31914 bqConfig.format = pConfig->format;
31915 bqConfig.channels = pConfig->channels;
31916
31917 return bqConfig;
31918 }
31919
31920 MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, ma_bpf2* pBPF)
31921 {
31922 ma_result result;
31923 ma_biquad_config bqConfig;
31924
31925 if (pBPF == NULL) {
31926 return MA_INVALID_ARGS;
31927 }
31928
31929 MA_ZERO_OBJECT(pBPF);
31930
31931 if (pConfig == NULL) {
31932 return MA_INVALID_ARGS;
31933 }
31934
31935 bqConfig = ma_bpf2__get_biquad_config(pConfig);
31936 result = ma_biquad_init(&bqConfig, &pBPF->bq);
31937 if (result != MA_SUCCESS) {
31938 return result;
31939 }
31940
31941 return MA_SUCCESS;
31942 }
31943
31944 MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF)
31945 {
31946 ma_result result;
31947 ma_biquad_config bqConfig;
31948
31949 if (pBPF == NULL || pConfig == NULL) {
31950 return MA_INVALID_ARGS;
31951 }
31952
31953 bqConfig = ma_bpf2__get_biquad_config(pConfig);
31954 result = ma_biquad_reinit(&bqConfig, &pBPF->bq);
31955 if (result != MA_SUCCESS) {
31956 return result;
31957 }
31958
31959 return MA_SUCCESS;
31960 }
31961
31962 static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
31963 {
31964 ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn);
31965 }
31966
31967 static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn)
31968 {
31969 ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn);
31970 }
31971
31972 MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
31973 {
31974 if (pBPF == NULL) {
31975 return MA_INVALID_ARGS;
31976 }
31977
31978 return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount);
31979 }
31980
31981 MA_API ma_uint32 ma_bpf2_get_latency(ma_bpf2* pBPF)
31982 {
31983 if (pBPF == NULL) {
31984 return 0;
31985 }
31986
31987 return ma_biquad_get_latency(&pBPF->bq);
31988 }
31989
31990
31991 MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
31992 {
31993 ma_bpf_config config;
31994
31995 MA_ZERO_OBJECT(&config);
31996 config.format = format;
31997 config.channels = channels;
31998 config.sampleRate = sampleRate;
31999 config.cutoffFrequency = cutoffFrequency;
32000 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
32001
32002 return config;
32003 }
32004
32005 static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, ma_bpf* pBPF, ma_bool32 isNew)
32006 {
32007 ma_result result;
32008 ma_uint32 bpf2Count;
32009 ma_uint32 ibpf2;
32010
32011 if (pBPF == NULL || pConfig == NULL) {
32012 return MA_INVALID_ARGS;
32013 }
32014
32015 /* Only supporting f32 and s16. */
32016 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
32017 return MA_INVALID_ARGS;
32018 }
32019
32020 /* The format cannot be changed after initialization. */
32021 if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) {
32022 return MA_INVALID_OPERATION;
32023 }
32024
32025 /* The channel count cannot be changed after initialization. */
32026 if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) {
32027 return MA_INVALID_OPERATION;
32028 }
32029
32030 if (pConfig->order > MA_MAX_FILTER_ORDER) {
32031 return MA_INVALID_ARGS;
32032 }
32033
32034 /* We must have an even number of order. */
32035 if ((pConfig->order & 0x1) != 0) {
32036 return MA_INVALID_ARGS;
32037 }
32038
32039 bpf2Count = pConfig->order / 2;
32040
32041 MA_ASSERT(bpf2Count <= ma_countof(pBPF->bpf2));
32042
32043 /* The filter order can't change between reinits. */
32044 if (!isNew) {
32045 if (pBPF->bpf2Count != bpf2Count) {
32046 return MA_INVALID_OPERATION;
32047 }
32048 }
32049
32050 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
32051 ma_bpf2_config bpf2Config;
32052 double q;
32053
32054 /* TODO: Calculate Q to make this a proper Butterworth filter. */
32055 q = 0.707107;
32056
32057 bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
32058
32059 if (isNew) {
32060 result = ma_bpf2_init(&bpf2Config, &pBPF->bpf2[ibpf2]);
32061 } else {
32062 result = ma_bpf2_reinit(&bpf2Config, &pBPF->bpf2[ibpf2]);
32063 }
32064
32065 if (result != MA_SUCCESS) {
32066 return result;
32067 }
32068 }
32069
32070 pBPF->bpf2Count = bpf2Count;
32071 pBPF->format = pConfig->format;
32072 pBPF->channels = pConfig->channels;
32073
32074 return MA_SUCCESS;
32075 }
32076
32077 MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF)
32078 {
32079 if (pBPF == NULL) {
32080 return MA_INVALID_ARGS;
32081 }
32082
32083 MA_ZERO_OBJECT(pBPF);
32084
32085 if (pConfig == NULL) {
32086 return MA_INVALID_ARGS;
32087 }
32088
32089 return ma_bpf_reinit__internal(pConfig, pBPF, /*isNew*/MA_TRUE);
32090 }
32091
32092 MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)
32093 {
32094 return ma_bpf_reinit__internal(pConfig, pBPF, /*isNew*/MA_FALSE);
32095 }
32096
32097 MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
32098 {
32099 ma_result result;
32100 ma_uint32 ibpf2;
32101
32102 if (pBPF == NULL) {
32103 return MA_INVALID_ARGS;
32104 }
32105
32106 /* Faster path for in-place. */
32107 if (pFramesOut == pFramesIn) {
32108 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
32109 result = ma_bpf2_process_pcm_frames(&pBPF->bpf2[ibpf2], pFramesOut, pFramesOut, frameCount);
32110 if (result != MA_SUCCESS) {
32111 return result;
32112 }
32113 }
32114 }
32115
32116 /* Slightly slower path for copying. */
32117 if (pFramesOut != pFramesIn) {
32118 ma_uint32 iFrame;
32119
32120 /* */ if (pBPF->format == ma_format_f32) {
32121 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
32122 const float* pFramesInF32 = (const float*)pFramesIn;
32123
32124 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
32125 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
32126
32127 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
32128 ma_bpf2_process_pcm_frame_f32(&pBPF->bpf2[ibpf2], pFramesOutF32, pFramesOutF32);
32129 }
32130
32131 pFramesOutF32 += pBPF->channels;
32132 pFramesInF32 += pBPF->channels;
32133 }
32134 } else if (pBPF->format == ma_format_s16) {
32135 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
32136 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
32137
32138 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
32139 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
32140
32141 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
32142 ma_bpf2_process_pcm_frame_s16(&pBPF->bpf2[ibpf2], pFramesOutS16, pFramesOutS16);
32143 }
32144
32145 pFramesOutS16 += pBPF->channels;
32146 pFramesInS16 += pBPF->channels;
32147 }
32148 } else {
32149 MA_ASSERT(MA_FALSE);
32150 return MA_INVALID_OPERATION; /* Should never hit this. */
32151 }
32152 }
32153
32154 return MA_SUCCESS;
32155 }
32156
32157 MA_API ma_uint32 ma_bpf_get_latency(ma_bpf* pBPF)
32158 {
32159 if (pBPF == NULL) {
32160 return 0;
32161 }
32162
32163 return pBPF->bpf2Count*2;
32164 }
32165
32166
32167 /**************************************************************************************************************************************************************
32168
32169 Notching Filter
32170
32171 **************************************************************************************************************************************************************/
32172 MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
32173 {
32174 ma_notch2_config config;
32175
32176 MA_ZERO_OBJECT(&config);
32177 config.format = format;
32178 config.channels = channels;
32179 config.sampleRate = sampleRate;
32180 config.q = q;
32181 config.frequency = frequency;
32182
32183 if (config.q == 0) {
32184 config.q = 0.707107;
32185 }
32186
32187 return config;
32188 }
32189
32190
32191 static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig)
32192 {
32193 ma_biquad_config bqConfig;
32194 double q;
32195 double w;
32196 double s;
32197 double c;
32198 double a;
32199
32200 MA_ASSERT(pConfig != NULL);
32201
32202 q = pConfig->q;
32203 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
32204 s = ma_sin(w);
32205 c = ma_cos(w);
32206 a = s / (2*q);
32207
32208 bqConfig.b0 = 1;
32209 bqConfig.b1 = -2 * c;
32210 bqConfig.b2 = 1;
32211 bqConfig.a0 = 1 + a;
32212 bqConfig.a1 = -2 * c;
32213 bqConfig.a2 = 1 - a;
32214
32215 bqConfig.format = pConfig->format;
32216 bqConfig.channels = pConfig->channels;
32217
32218 return bqConfig;
32219 }
32220
32221 MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, ma_notch2* pFilter)
32222 {
32223 ma_result result;
32224 ma_biquad_config bqConfig;
32225
32226 if (pFilter == NULL) {
32227 return MA_INVALID_ARGS;
32228 }
32229
32230 MA_ZERO_OBJECT(pFilter);
32231
32232 if (pConfig == NULL) {
32233 return MA_INVALID_ARGS;
32234 }
32235
32236 bqConfig = ma_notch2__get_biquad_config(pConfig);
32237 result = ma_biquad_init(&bqConfig, &pFilter->bq);
32238 if (result != MA_SUCCESS) {
32239 return result;
32240 }
32241
32242 return MA_SUCCESS;
32243 }
32244
32245 MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter)
32246 {
32247 ma_result result;
32248 ma_biquad_config bqConfig;
32249
32250 if (pFilter == NULL || pConfig == NULL) {
32251 return MA_INVALID_ARGS;
32252 }
32253
32254 bqConfig = ma_notch2__get_biquad_config(pConfig);
32255 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
32256 if (result != MA_SUCCESS) {
32257 return result;
32258 }
32259
32260 return MA_SUCCESS;
32261 }
32262
32263 static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
32264 {
32265 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
32266 }
32267
32268 static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn)
32269 {
32270 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
32271 }
32272
32273 MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
32274 {
32275 if (pFilter == NULL) {
32276 return MA_INVALID_ARGS;
32277 }
32278
32279 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
32280 }
32281
32282 MA_API ma_uint32 ma_notch2_get_latency(ma_notch2* pFilter)
32283 {
32284 if (pFilter == NULL) {
32285 return 0;
32286 }
32287
32288 return ma_biquad_get_latency(&pFilter->bq);
32289 }
32290
32291
32292
32293 /**************************************************************************************************************************************************************
32294
32295 Peaking EQ Filter
32296
32297 **************************************************************************************************************************************************************/
32298 MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
32299 {
32300 ma_peak2_config config;
32301
32302 MA_ZERO_OBJECT(&config);
32303 config.format = format;
32304 config.channels = channels;
32305 config.sampleRate = sampleRate;
32306 config.gainDB = gainDB;
32307 config.q = q;
32308 config.frequency = frequency;
32309
32310 if (config.q == 0) {
32311 config.q = 0.707107;
32312 }
32313
32314 return config;
32315 }
32316
32317
32318 static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig)
32319 {
32320 ma_biquad_config bqConfig;
32321 double q;
32322 double w;
32323 double s;
32324 double c;
32325 double a;
32326 double A;
32327
32328 MA_ASSERT(pConfig != NULL);
32329
32330 q = pConfig->q;
32331 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
32332 s = ma_sin(w);
32333 c = ma_cos(w);
32334 a = s / (2*q);
32335 A = ma_pow(10, (pConfig->gainDB / 40));
32336
32337 bqConfig.b0 = 1 + (a * A);
32338 bqConfig.b1 = -2 * c;
32339 bqConfig.b2 = 1 - (a * A);
32340 bqConfig.a0 = 1 + (a / A);
32341 bqConfig.a1 = -2 * c;
32342 bqConfig.a2 = 1 - (a / A);
32343
32344 bqConfig.format = pConfig->format;
32345 bqConfig.channels = pConfig->channels;
32346
32347 return bqConfig;
32348 }
32349
32350 MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter)
32351 {
32352 ma_result result;
32353 ma_biquad_config bqConfig;
32354
32355 if (pFilter == NULL) {
32356 return MA_INVALID_ARGS;
32357 }
32358
32359 MA_ZERO_OBJECT(pFilter);
32360
32361 if (pConfig == NULL) {
32362 return MA_INVALID_ARGS;
32363 }
32364
32365 bqConfig = ma_peak2__get_biquad_config(pConfig);
32366 result = ma_biquad_init(&bqConfig, &pFilter->bq);
32367 if (result != MA_SUCCESS) {
32368 return result;
32369 }
32370
32371 return MA_SUCCESS;
32372 }
32373
32374 MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter)
32375 {
32376 ma_result result;
32377 ma_biquad_config bqConfig;
32378
32379 if (pFilter == NULL || pConfig == NULL) {
32380 return MA_INVALID_ARGS;
32381 }
32382
32383 bqConfig = ma_peak2__get_biquad_config(pConfig);
32384 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
32385 if (result != MA_SUCCESS) {
32386 return result;
32387 }
32388
32389 return MA_SUCCESS;
32390 }
32391
32392 static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
32393 {
32394 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
32395 }
32396
32397 static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn)
32398 {
32399 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
32400 }
32401
32402 MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
32403 {
32404 if (pFilter == NULL) {
32405 return MA_INVALID_ARGS;
32406 }
32407
32408 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
32409 }
32410
32411 MA_API ma_uint32 ma_peak2_get_latency(ma_peak2* pFilter)
32412 {
32413 if (pFilter == NULL) {
32414 return 0;
32415 }
32416
32417 return ma_biquad_get_latency(&pFilter->bq);
32418 }
32419
32420
32421 /**************************************************************************************************************************************************************
32422
32423 Low Shelf Filter
32424
32425 **************************************************************************************************************************************************************/
32426 MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
32427 {
32428 ma_loshelf2_config config;
32429
32430 MA_ZERO_OBJECT(&config);
32431 config.format = format;
32432 config.channels = channels;
32433 config.sampleRate = sampleRate;
32434 config.gainDB = gainDB;
32435 config.shelfSlope = shelfSlope;
32436 config.frequency = frequency;
32437
32438 return config;
32439 }
32440
32441
32442 static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig)
32443 {
32444 ma_biquad_config bqConfig;
32445 double w;
32446 double s;
32447 double c;
32448 double A;
32449 double S;
32450 double a;
32451 double sqrtA;
32452
32453 MA_ASSERT(pConfig != NULL);
32454
32455 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
32456 s = ma_sin(w);
32457 c = ma_cos(w);
32458 A = ma_pow(10, (pConfig->gainDB / 40));
32459 S = pConfig->shelfSlope;
32460 a = s/2 * ma_sqrt((A + 1/A) * (1/S - 1) + 2);
32461 sqrtA = 2*ma_sqrt(A)*a;
32462
32463 bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA);
32464 bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c);
32465 bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA);
32466 bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA;
32467 bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c);
32468 bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA;
32469
32470 bqConfig.format = pConfig->format;
32471 bqConfig.channels = pConfig->channels;
32472
32473 return bqConfig;
32474 }
32475
32476 MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter)
32477 {
32478 ma_result result;
32479 ma_biquad_config bqConfig;
32480
32481 if (pFilter == NULL) {
32482 return MA_INVALID_ARGS;
32483 }
32484
32485 MA_ZERO_OBJECT(pFilter);
32486
32487 if (pConfig == NULL) {
32488 return MA_INVALID_ARGS;
32489 }
32490
32491 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
32492 result = ma_biquad_init(&bqConfig, &pFilter->bq);
32493 if (result != MA_SUCCESS) {
32494 return result;
32495 }
32496
32497 return MA_SUCCESS;
32498 }
32499
32500 MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter)
32501 {
32502 ma_result result;
32503 ma_biquad_config bqConfig;
32504
32505 if (pFilter == NULL || pConfig == NULL) {
32506 return MA_INVALID_ARGS;
32507 }
32508
32509 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
32510 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
32511 if (result != MA_SUCCESS) {
32512 return result;
32513 }
32514
32515 return MA_SUCCESS;
32516 }
32517
32518 static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
32519 {
32520 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
32521 }
32522
32523 static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn)
32524 {
32525 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
32526 }
32527
32528 MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
32529 {
32530 if (pFilter == NULL) {
32531 return MA_INVALID_ARGS;
32532 }
32533
32534 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
32535 }
32536
32537 MA_API ma_uint32 ma_loshelf2_get_latency(ma_loshelf2* pFilter)
32538 {
32539 if (pFilter == NULL) {
32540 return 0;
32541 }
32542
32543 return ma_biquad_get_latency(&pFilter->bq);
32544 }
32545
32546
32547 /**************************************************************************************************************************************************************
32548
32549 High Shelf Filter
32550
32551 **************************************************************************************************************************************************************/
32552 MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
32553 {
32554 ma_hishelf2_config config;
32555
32556 MA_ZERO_OBJECT(&config);
32557 config.format = format;
32558 config.channels = channels;
32559 config.sampleRate = sampleRate;
32560 config.gainDB = gainDB;
32561 config.shelfSlope = shelfSlope;
32562 config.frequency = frequency;
32563
32564 return config;
32565 }
32566
32567
32568 static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig)
32569 {
32570 ma_biquad_config bqConfig;
32571 double w;
32572 double s;
32573 double c;
32574 double A;
32575 double S;
32576 double a;
32577 double sqrtA;
32578
32579 MA_ASSERT(pConfig != NULL);
32580
32581 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
32582 s = ma_sin(w);
32583 c = ma_cos(w);
32584 A = ma_pow(10, (pConfig->gainDB / 40));
32585 S = pConfig->shelfSlope;
32586 a = s/2 * ma_sqrt((A + 1/A) * (1/S - 1) + 2);
32587 sqrtA = 2*ma_sqrt(A)*a;
32588
32589 bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA);
32590 bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c);
32591 bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA);
32592 bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA;
32593 bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c);
32594 bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA;
32595
32596 bqConfig.format = pConfig->format;
32597 bqConfig.channels = pConfig->channels;
32598
32599 return bqConfig;
32600 }
32601
32602 MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter)
32603 {
32604 ma_result result;
32605 ma_biquad_config bqConfig;
32606
32607 if (pFilter == NULL) {
32608 return MA_INVALID_ARGS;
32609 }
32610
32611 MA_ZERO_OBJECT(pFilter);
32612
32613 if (pConfig == NULL) {
32614 return MA_INVALID_ARGS;
32615 }
32616
32617 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
32618 result = ma_biquad_init(&bqConfig, &pFilter->bq);
32619 if (result != MA_SUCCESS) {
32620 return result;
32621 }
32622
32623 return MA_SUCCESS;
32624 }
32625
32626 MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter)
32627 {
32628 ma_result result;
32629 ma_biquad_config bqConfig;
32630
32631 if (pFilter == NULL || pConfig == NULL) {
32632 return MA_INVALID_ARGS;
32633 }
32634
32635 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
32636 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
32637 if (result != MA_SUCCESS) {
32638 return result;
32639 }
32640
32641 return MA_SUCCESS;
32642 }
32643
32644 static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
32645 {
32646 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
32647 }
32648
32649 static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn)
32650 {
32651 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
32652 }
32653
32654 MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
32655 {
32656 if (pFilter == NULL) {
32657 return MA_INVALID_ARGS;
32658 }
32659
32660 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
32661 }
32662
32663 MA_API ma_uint32 ma_hishelf2_get_latency(ma_hishelf2* pFilter)
32664 {
32665 if (pFilter == NULL) {
32666 return 0;
32667 }
32668
32669 return ma_biquad_get_latency(&pFilter->bq);
32670 }
32671
32672
32673
32674 /**************************************************************************************************************************************************************
32675
32676 Resampling
32677
32678 **************************************************************************************************************************************************************/
32679 MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
32680 {
32681 ma_linear_resampler_config config;
32682 MA_ZERO_OBJECT(&config);
32683 config.format = format;
32684 config.channels = channels;
32685 config.sampleRateIn = sampleRateIn;
32686 config.sampleRateOut = sampleRateOut;
32687 config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
32688 config.lpfNyquistFactor = 1;
32689
32690 return config;
32691 }
32692
32693 static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized)
32694 {
32695 ma_uint32 gcf;
32696
32697 if (pResampler == NULL) {
32698 return MA_INVALID_ARGS;
32699 }
32700
32701 if (sampleRateIn == 0 || sampleRateOut == 0) {
32702 return MA_INVALID_ARGS;
32703 }
32704
32705 pResampler->config.sampleRateIn = sampleRateIn;
32706 pResampler->config.sampleRateOut = sampleRateOut;
32707
32708 /* Simplify the sample rate. */
32709 gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut);
32710 pResampler->config.sampleRateIn /= gcf;
32711 pResampler->config.sampleRateOut /= gcf;
32712
32713 if (pResampler->config.lpfOrder > 0) {
32714 ma_result result;
32715 ma_uint32 lpfSampleRate;
32716 double lpfCutoffFrequency;
32717 ma_lpf_config lpfConfig;
32718
32719 if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) {
32720 return MA_INVALID_ARGS;
32721 }
32722
32723 lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut));
32724 lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor);
32725
32726 lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder);
32727
32728 /*
32729 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
32730 getting cleared. Instead we re-initialize the filter which will maintain any cached frames.
32731 */
32732 if (isResamplerAlreadyInitialized) {
32733 result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf);
32734 } else {
32735 result = ma_lpf_init(&lpfConfig, &pResampler->lpf);
32736 }
32737
32738 if (result != MA_SUCCESS) {
32739 return result;
32740 }
32741 }
32742
32743 pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
32744 pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
32745
32746 /* Make sure the fractional part is less than the output sample rate. */
32747 pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut;
32748 pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut;
32749
32750 return MA_SUCCESS;
32751 }
32752
32753 MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler)
32754 {
32755 ma_result result;
32756
32757 if (pResampler == NULL) {
32758 return MA_INVALID_ARGS;
32759 }
32760
32761 MA_ZERO_OBJECT(pResampler);
32762
32763 if (pConfig == NULL) {
32764 return MA_INVALID_ARGS;
32765 }
32766
32767 pResampler->config = *pConfig;
32768
32769 /* Setting the rate will set up the filter and time advances for us. */
32770 result = ma_linear_resampler_set_rate_internal(pResampler, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE);
32771 if (result != MA_SUCCESS) {
32772 return result;
32773 }
32774
32775 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
32776 pResampler->inTimeFrac = 0;
32777
32778 return MA_SUCCESS;
32779 }
32780
32781 MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler)
32782 {
32783 if (pResampler == NULL) {
32784 return;
32785 }
32786 }
32787
32788 static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)
32789 {
32790 ma_int32 b;
32791 ma_int32 c;
32792 ma_int32 r;
32793
32794 MA_ASSERT(a <= (1<<shift));
32795
32796 b = x * ((1<<shift) - a);
32797 c = y * a;
32798 r = b + c;
32799
32800 return (ma_int16)(r >> shift);
32801 }
32802
32803 static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* pFrameOut)
32804 {
32805 ma_uint32 c;
32806 ma_uint32 a;
32807 const ma_uint32 shift = 12;
32808
32809 MA_ASSERT(pResampler != NULL);
32810 MA_ASSERT(pFrameOut != NULL);
32811
32812 a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut;
32813
32814 for (c = 0; c < pResampler->config.channels; c += 1) {
32815 ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);
32816 pFrameOut[c] = s;
32817 }
32818 }
32819
32820
32821 static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* pFrameOut)
32822 {
32823 ma_uint32 c;
32824 float a;
32825
32826 MA_ASSERT(pResampler != NULL);
32827 MA_ASSERT(pFrameOut != NULL);
32828
32829 a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut;
32830
32831 for (c = 0; c < pResampler->config.channels; c += 1) {
32832 float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a);
32833 pFrameOut[c] = s;
32834 }
32835 }
32836
32837 static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
32838 {
32839 const ma_int16* pFramesInS16;
32840 /* */ ma_int16* pFramesOutS16;
32841 ma_uint64 frameCountIn;
32842 ma_uint64 frameCountOut;
32843 ma_uint64 framesProcessedIn;
32844 ma_uint64 framesProcessedOut;
32845
32846 MA_ASSERT(pResampler != NULL);
32847 MA_ASSERT(pFrameCountIn != NULL);
32848 MA_ASSERT(pFrameCountOut != NULL);
32849
32850 pFramesInS16 = (const ma_int16*)pFramesIn;
32851 pFramesOutS16 = ( ma_int16*)pFramesOut;
32852 frameCountIn = *pFrameCountIn;
32853 frameCountOut = *pFrameCountOut;
32854 framesProcessedIn = 0;
32855 framesProcessedOut = 0;
32856
32857 for (;;) {
32858 if (framesProcessedOut >= frameCountOut) {
32859 break;
32860 }
32861
32862 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
32863 while (pResampler->inTimeInt > 0 && frameCountIn > 0) {
32864 ma_uint32 iChannel;
32865
32866 if (pFramesInS16 != NULL) {
32867 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
32868 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
32869 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
32870 }
32871 pFramesInS16 += pResampler->config.channels;
32872 } else {
32873 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
32874 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
32875 pResampler->x1.s16[iChannel] = 0;
32876 }
32877 }
32878
32879 /* Filter. */
32880 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16);
32881
32882 frameCountIn -= 1;
32883 framesProcessedIn += 1;
32884 pResampler->inTimeInt -= 1;
32885 }
32886
32887 if (pResampler->inTimeInt > 0) {
32888 break; /* Ran out of input data. */
32889 }
32890
32891 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
32892 if (pFramesOutS16 != NULL) {
32893 MA_ASSERT(pResampler->inTimeInt == 0);
32894 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
32895
32896 pFramesOutS16 += pResampler->config.channels;
32897 }
32898
32899 framesProcessedOut += 1;
32900
32901 /* Advance time forward. */
32902 pResampler->inTimeInt += pResampler->inAdvanceInt;
32903 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
32904 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
32905 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
32906 pResampler->inTimeInt += 1;
32907 }
32908 }
32909
32910 *pFrameCountIn = framesProcessedIn;
32911 *pFrameCountOut = framesProcessedOut;
32912
32913 return MA_SUCCESS;
32914 }
32915
32916 static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
32917 {
32918 const ma_int16* pFramesInS16;
32919 /* */ ma_int16* pFramesOutS16;
32920 ma_uint64 frameCountIn;
32921 ma_uint64 frameCountOut;
32922 ma_uint64 framesProcessedIn;
32923 ma_uint64 framesProcessedOut;
32924
32925 MA_ASSERT(pResampler != NULL);
32926 MA_ASSERT(pFrameCountIn != NULL);
32927 MA_ASSERT(pFrameCountOut != NULL);
32928
32929 pFramesInS16 = (const ma_int16*)pFramesIn;
32930 pFramesOutS16 = ( ma_int16*)pFramesOut;
32931 frameCountIn = *pFrameCountIn;
32932 frameCountOut = *pFrameCountOut;
32933 framesProcessedIn = 0;
32934 framesProcessedOut = 0;
32935
32936 for (;;) {
32937 if (framesProcessedOut >= frameCountOut) {
32938 break;
32939 }
32940
32941 /* Before interpolating we need to load the buffers. */
32942 while (pResampler->inTimeInt > 0 && frameCountIn > 0) {
32943 ma_uint32 iChannel;
32944
32945 if (pFramesInS16 != NULL) {
32946 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
32947 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
32948 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
32949 }
32950 pFramesInS16 += pResampler->config.channels;
32951 } else {
32952 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
32953 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
32954 pResampler->x1.s16[iChannel] = 0;
32955 }
32956 }
32957
32958 frameCountIn -= 1;
32959 framesProcessedIn += 1;
32960 pResampler->inTimeInt -= 1;
32961 }
32962
32963 if (pResampler->inTimeInt > 0) {
32964 break; /* Ran out of input data. */
32965 }
32966
32967 /* Getting here means the frames have been loaded and we can generate the next output frame. */
32968 if (pFramesOutS16 != NULL) {
32969 MA_ASSERT(pResampler->inTimeInt == 0);
32970 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
32971
32972 /* Filter. */
32973 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16);
32974
32975 pFramesOutS16 += pResampler->config.channels;
32976 }
32977
32978 framesProcessedOut += 1;
32979
32980 /* Advance time forward. */
32981 pResampler->inTimeInt += pResampler->inAdvanceInt;
32982 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
32983 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
32984 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
32985 pResampler->inTimeInt += 1;
32986 }
32987 }
32988
32989 *pFrameCountIn = framesProcessedIn;
32990 *pFrameCountOut = framesProcessedOut;
32991
32992 return MA_SUCCESS;
32993 }
32994
32995 static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
32996 {
32997 MA_ASSERT(pResampler != NULL);
32998
32999 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
33000 return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
33001 } else {
33002 return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
33003 }
33004 }
33005
33006
33007 static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
33008 {
33009 const float* pFramesInF32;
33010 /* */ float* pFramesOutF32;
33011 ma_uint64 frameCountIn;
33012 ma_uint64 frameCountOut;
33013 ma_uint64 framesProcessedIn;
33014 ma_uint64 framesProcessedOut;
33015
33016 MA_ASSERT(pResampler != NULL);
33017 MA_ASSERT(pFrameCountIn != NULL);
33018 MA_ASSERT(pFrameCountOut != NULL);
33019
33020 pFramesInF32 = (const float*)pFramesIn;
33021 pFramesOutF32 = ( float*)pFramesOut;
33022 frameCountIn = *pFrameCountIn;
33023 frameCountOut = *pFrameCountOut;
33024 framesProcessedIn = 0;
33025 framesProcessedOut = 0;
33026
33027 for (;;) {
33028 if (framesProcessedOut >= frameCountOut) {
33029 break;
33030 }
33031
33032 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
33033 while (pResampler->inTimeInt > 0 && frameCountIn > 0) {
33034 ma_uint32 iChannel;
33035
33036 if (pFramesInF32 != NULL) {
33037 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
33038 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
33039 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
33040 }
33041 pFramesInF32 += pResampler->config.channels;
33042 } else {
33043 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
33044 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
33045 pResampler->x1.f32[iChannel] = 0;
33046 }
33047 }
33048
33049 /* Filter. */
33050 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32);
33051
33052 frameCountIn -= 1;
33053 framesProcessedIn += 1;
33054 pResampler->inTimeInt -= 1;
33055 }
33056
33057 if (pResampler->inTimeInt > 0) {
33058 break; /* Ran out of input data. */
33059 }
33060
33061 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
33062 if (pFramesOutF32 != NULL) {
33063 MA_ASSERT(pResampler->inTimeInt == 0);
33064 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
33065
33066 pFramesOutF32 += pResampler->config.channels;
33067 }
33068
33069 framesProcessedOut += 1;
33070
33071 /* Advance time forward. */
33072 pResampler->inTimeInt += pResampler->inAdvanceInt;
33073 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
33074 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
33075 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
33076 pResampler->inTimeInt += 1;
33077 }
33078 }
33079
33080 *pFrameCountIn = framesProcessedIn;
33081 *pFrameCountOut = framesProcessedOut;
33082
33083 return MA_SUCCESS;
33084 }
33085
33086 static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
33087 {
33088 const float* pFramesInF32;
33089 /* */ float* pFramesOutF32;
33090 ma_uint64 frameCountIn;
33091 ma_uint64 frameCountOut;
33092 ma_uint64 framesProcessedIn;
33093 ma_uint64 framesProcessedOut;
33094
33095 MA_ASSERT(pResampler != NULL);
33096 MA_ASSERT(pFrameCountIn != NULL);
33097 MA_ASSERT(pFrameCountOut != NULL);
33098
33099 pFramesInF32 = (const float*)pFramesIn;
33100 pFramesOutF32 = ( float*)pFramesOut;
33101 frameCountIn = *pFrameCountIn;
33102 frameCountOut = *pFrameCountOut;
33103 framesProcessedIn = 0;
33104 framesProcessedOut = 0;
33105
33106 for (;;) {
33107 if (framesProcessedOut >= frameCountOut) {
33108 break;
33109 }
33110
33111 /* Before interpolating we need to load the buffers. */
33112 while (pResampler->inTimeInt > 0 && frameCountIn > 0) {
33113 ma_uint32 iChannel;
33114
33115 if (pFramesInF32 != NULL) {
33116 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
33117 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
33118 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
33119 }
33120 pFramesInF32 += pResampler->config.channels;
33121 } else {
33122 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
33123 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
33124 pResampler->x1.f32[iChannel] = 0;
33125 }
33126 }
33127
33128 frameCountIn -= 1;
33129 framesProcessedIn += 1;
33130 pResampler->inTimeInt -= 1;
33131 }
33132
33133 if (pResampler->inTimeInt > 0) {
33134 break; /* Ran out of input data. */
33135 }
33136
33137 /* Getting here means the frames have been loaded and we can generate the next output frame. */
33138 if (pFramesOutF32 != NULL) {
33139 MA_ASSERT(pResampler->inTimeInt == 0);
33140 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
33141
33142 /* Filter. */
33143 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32);
33144
33145 pFramesOutF32 += pResampler->config.channels;
33146 }
33147
33148 framesProcessedOut += 1;
33149
33150 /* Advance time forward. */
33151 pResampler->inTimeInt += pResampler->inAdvanceInt;
33152 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
33153 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
33154 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
33155 pResampler->inTimeInt += 1;
33156 }
33157 }
33158
33159 *pFrameCountIn = framesProcessedIn;
33160 *pFrameCountOut = framesProcessedOut;
33161
33162 return MA_SUCCESS;
33163 }
33164
33165 static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
33166 {
33167 MA_ASSERT(pResampler != NULL);
33168
33169 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
33170 return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
33171 } else {
33172 return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
33173 }
33174 }
33175
33176
33177 MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
33178 {
33179 if (pResampler == NULL) {
33180 return MA_INVALID_ARGS;
33181 }
33182
33183 /* */ if (pResampler->config.format == ma_format_s16) {
33184 return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
33185 } else if (pResampler->config.format == ma_format_f32) {
33186 return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
33187 } else {
33188 /* 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(). */
33189 MA_ASSERT(MA_FALSE);
33190 return MA_INVALID_ARGS;
33191 }
33192 }
33193
33194
33195 MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
33196 {
33197 return ma_linear_resampler_set_rate_internal(pResampler, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE);
33198 }
33199
33200 MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut)
33201 {
33202 ma_uint32 n;
33203 ma_uint32 d;
33204
33205 d = 1000000; /* We use up to 6 decimal places. */
33206 n = (ma_uint32)(ratioInOut * d);
33207
33208 if (n == 0) {
33209 return MA_INVALID_ARGS; /* Ratio too small. */
33210 }
33211
33212 MA_ASSERT(n != 0);
33213
33214 return ma_linear_resampler_set_rate(pResampler, n, d);
33215 }
33216
33217
33218 MA_API ma_uint64 ma_linear_resampler_get_required_input_frame_count(ma_linear_resampler* pResampler, ma_uint64 outputFrameCount)
33219 {
33220 ma_uint64 count;
33221
33222 if (pResampler == NULL) {
33223 return 0;
33224 }
33225
33226 if (outputFrameCount == 0) {
33227 return 0;
33228 }
33229
33230 /* Any whole input frames are consumed before the first output frame is generated. */
33231 count = pResampler->inTimeInt;
33232 outputFrameCount -= 1;
33233
33234 /* The rest of the output frames can be calculated in constant time. */
33235 count += outputFrameCount * pResampler->inAdvanceInt;
33236 count += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut;
33237
33238 return count;
33239 }
33240
33241 MA_API ma_uint64 ma_linear_resampler_get_expected_output_frame_count(ma_linear_resampler* pResampler, ma_uint64 inputFrameCount)
33242 {
33243 ma_uint64 outputFrameCount;
33244 ma_uint64 inTimeInt;
33245 ma_uint64 inTimeFrac;
33246
33247 if (pResampler == NULL) {
33248 return 0;
33249 }
33250
33251 /* TODO: Try making this run in constant time. */
33252
33253 outputFrameCount = 0;
33254 inTimeInt = pResampler->inTimeInt;
33255 inTimeFrac = pResampler->inTimeFrac;
33256
33257 for (;;) {
33258 while (inTimeInt > 0 && inputFrameCount > 0) {
33259 inputFrameCount -= 1;
33260 inTimeInt -= 1;
33261 }
33262
33263 if (inTimeInt > 0) {
33264 break;
33265 }
33266
33267 outputFrameCount += 1;
33268
33269 /* Advance time forward. */
33270 inTimeInt += pResampler->inAdvanceInt;
33271 inTimeFrac += pResampler->inAdvanceFrac;
33272 if (inTimeFrac >= pResampler->config.sampleRateOut) {
33273 inTimeFrac -= pResampler->config.sampleRateOut;
33274 inTimeInt += 1;
33275 }
33276 }
33277
33278 return outputFrameCount;
33279 }
33280
33281 MA_API ma_uint64 ma_linear_resampler_get_input_latency(ma_linear_resampler* pResampler)
33282 {
33283 if (pResampler == NULL) {
33284 return 0;
33285 }
33286
33287 return 1 + ma_lpf_get_latency(&pResampler->lpf);
33288 }
33289
33290 MA_API ma_uint64 ma_linear_resampler_get_output_latency(ma_linear_resampler* pResampler)
33291 {
33292 if (pResampler == NULL) {
33293 return 0;
33294 }
33295
33296 return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn;
33297 }
33298
33299
33300 #if defined(ma_speex_resampler_h)
33301 #define MA_HAS_SPEEX_RESAMPLER
33302
33303 static ma_result ma_result_from_speex_err(int err)
33304 {
33305 switch (err)
33306 {
33307 case RESAMPLER_ERR_SUCCESS: return MA_SUCCESS;
33308 case RESAMPLER_ERR_ALLOC_FAILED: return MA_OUT_OF_MEMORY;
33309 case RESAMPLER_ERR_BAD_STATE: return MA_ERROR;
33310 case RESAMPLER_ERR_INVALID_ARG: return MA_INVALID_ARGS;
33311 case RESAMPLER_ERR_PTR_OVERLAP: return MA_INVALID_ARGS;
33312 case RESAMPLER_ERR_OVERFLOW: return MA_ERROR;
33313 default: return MA_ERROR;
33314 }
33315 }
33316 #endif /* ma_speex_resampler_h */
33317
33318 MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm)
33319 {
33320 ma_resampler_config config;
33321
33322 MA_ZERO_OBJECT(&config);
33323 config.format = format;
33324 config.channels = channels;
33325 config.sampleRateIn = sampleRateIn;
33326 config.sampleRateOut = sampleRateOut;
33327 config.algorithm = algorithm;
33328
33329 /* Linear. */
33330 config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
33331 config.linear.lpfNyquistFactor = 1;
33332
33333 /* Speex. */
33334 config.speex.quality = 3; /* Cannot leave this as 0 as that is actually a valid value for Speex resampling quality. */
33335
33336 return config;
33337 }
33338
33339 MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler)
33340 {
33341 ma_result result;
33342
33343 if (pResampler == NULL) {
33344 return MA_INVALID_ARGS;
33345 }
33346
33347 MA_ZERO_OBJECT(pResampler);
33348
33349 if (pConfig == NULL) {
33350 return MA_INVALID_ARGS;
33351 }
33352
33353 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
33354 return MA_INVALID_ARGS;
33355 }
33356
33357 pResampler->config = *pConfig;
33358
33359 switch (pConfig->algorithm)
33360 {
33361 case ma_resample_algorithm_linear:
33362 {
33363 ma_linear_resampler_config linearConfig;
33364 linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
33365 linearConfig.lpfOrder = pConfig->linear.lpfOrder;
33366 linearConfig.lpfNyquistFactor = pConfig->linear.lpfNyquistFactor;
33367
33368 result = ma_linear_resampler_init(&linearConfig, &pResampler->state.linear);
33369 if (result != MA_SUCCESS) {
33370 return result;
33371 }
33372 } break;
33373
33374 case ma_resample_algorithm_speex:
33375 {
33376 #if defined(MA_HAS_SPEEX_RESAMPLER)
33377 int speexErr;
33378 pResampler->state.speex.pSpeexResamplerState = speex_resampler_init(pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->speex.quality, &speexErr);
33379 if (pResampler->state.speex.pSpeexResamplerState == NULL) {
33380 return ma_result_from_speex_err(speexErr);
33381 }
33382 #else
33383 /* Speex resampler not available. */
33384 return MA_NO_BACKEND;
33385 #endif
33386 } break;
33387
33388 default: return MA_INVALID_ARGS;
33389 }
33390
33391 return MA_SUCCESS;
33392 }
33393
33394 MA_API void ma_resampler_uninit(ma_resampler* pResampler)
33395 {
33396 if (pResampler == NULL) {
33397 return;
33398 }
33399
33400 if (pResampler->config.algorithm == ma_resample_algorithm_linear) {
33401 ma_linear_resampler_uninit(&pResampler->state.linear);
33402 }
33403
33404 #if defined(MA_HAS_SPEEX_RESAMPLER)
33405 if (pResampler->config.algorithm == ma_resample_algorithm_speex) {
33406 speex_resampler_destroy((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
33407 }
33408 #endif
33409 }
33410
33411 static ma_result ma_resampler_process_pcm_frames__read__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
33412 {
33413 return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
33414 }
33415
33416 #if defined(MA_HAS_SPEEX_RESAMPLER)
33417 static ma_result ma_resampler_process_pcm_frames__read__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
33418 {
33419 int speexErr;
33420 ma_uint64 frameCountOut;
33421 ma_uint64 frameCountIn;
33422 ma_uint64 framesProcessedOut;
33423 ma_uint64 framesProcessedIn;
33424 unsigned int framesPerIteration = UINT_MAX;
33425
33426 MA_ASSERT(pResampler != NULL);
33427 MA_ASSERT(pFramesOut != NULL);
33428 MA_ASSERT(pFrameCountOut != NULL);
33429 MA_ASSERT(pFrameCountIn != NULL);
33430
33431 /*
33432 Reading from the Speex resampler requires a bit of dancing around for a few reasons. The first thing is that it's frame counts
33433 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
33434 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
33435 do this is for ma_resampler_get_required_input_frame_count() and ma_resampler_get_expected_output_frame_count().
33436 */
33437 frameCountOut = *pFrameCountOut;
33438 frameCountIn = *pFrameCountIn;
33439 framesProcessedOut = 0;
33440 framesProcessedIn = 0;
33441
33442 while (framesProcessedOut < frameCountOut && framesProcessedIn < frameCountIn) {
33443 unsigned int frameCountInThisIteration;
33444 unsigned int frameCountOutThisIteration;
33445 const void* pFramesInThisIteration;
33446 void* pFramesOutThisIteration;
33447
33448 frameCountInThisIteration = framesPerIteration;
33449 if ((ma_uint64)frameCountInThisIteration > (frameCountIn - framesProcessedIn)) {
33450 frameCountInThisIteration = (unsigned int)(frameCountIn - framesProcessedIn);
33451 }
33452
33453 frameCountOutThisIteration = framesPerIteration;
33454 if ((ma_uint64)frameCountOutThisIteration > (frameCountOut - framesProcessedOut)) {
33455 frameCountOutThisIteration = (unsigned int)(frameCountOut - framesProcessedOut);
33456 }
33457
33458 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels));
33459 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels));
33460
33461 if (pResampler->config.format == ma_format_f32) {
33462 speexErr = speex_resampler_process_interleaved_float((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const float*)pFramesInThisIteration, &frameCountInThisIteration, (float*)pFramesOutThisIteration, &frameCountOutThisIteration);
33463 } else if (pResampler->config.format == ma_format_s16) {
33464 speexErr = speex_resampler_process_interleaved_int((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const spx_int16_t*)pFramesInThisIteration, &frameCountInThisIteration, (spx_int16_t*)pFramesOutThisIteration, &frameCountOutThisIteration);
33465 } else {
33466 /* Format not supported. Should never get here. */
33467 MA_ASSERT(MA_FALSE);
33468 return MA_INVALID_OPERATION;
33469 }
33470
33471 if (speexErr != RESAMPLER_ERR_SUCCESS) {
33472 return ma_result_from_speex_err(speexErr);
33473 }
33474
33475 framesProcessedIn += frameCountInThisIteration;
33476 framesProcessedOut += frameCountOutThisIteration;
33477 }
33478
33479 *pFrameCountOut = framesProcessedOut;
33480 *pFrameCountIn = framesProcessedIn;
33481
33482 return MA_SUCCESS;
33483 }
33484 #endif
33485
33486 static ma_result ma_resampler_process_pcm_frames__read(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
33487 {
33488 MA_ASSERT(pResampler != NULL);
33489 MA_ASSERT(pFramesOut != NULL);
33490
33491 /* pFramesOut is not NULL, which means we must have a capacity. */
33492 if (pFrameCountOut == NULL) {
33493 return MA_INVALID_ARGS;
33494 }
33495
33496 /* It doesn't make sense to not have any input frames to process. */
33497 if (pFrameCountIn == NULL || pFramesIn == NULL) {
33498 return MA_INVALID_ARGS;
33499 }
33500
33501 switch (pResampler->config.algorithm)
33502 {
33503 case ma_resample_algorithm_linear:
33504 {
33505 return ma_resampler_process_pcm_frames__read__linear(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
33506 }
33507
33508 case ma_resample_algorithm_speex:
33509 {
33510 #if defined(MA_HAS_SPEEX_RESAMPLER)
33511 return ma_resampler_process_pcm_frames__read__speex(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
33512 #else
33513 break;
33514 #endif
33515 }
33516
33517 default: break;
33518 }
33519
33520 /* Should never get here. */
33521 MA_ASSERT(MA_FALSE);
33522 return MA_INVALID_ARGS;
33523 }
33524
33525
33526 static ma_result ma_resampler_process_pcm_frames__seek__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
33527 {
33528 MA_ASSERT(pResampler != NULL);
33529
33530 /* Seeking is supported natively by the linear resampler. */
33531 return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, NULL, pFrameCountOut);
33532 }
33533
33534 #if defined(MA_HAS_SPEEX_RESAMPLER)
33535 static ma_result ma_resampler_process_pcm_frames__seek__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
33536 {
33537 /* The generic seek method is implemented in on top of ma_resampler_process_pcm_frames__read() by just processing into a dummy buffer. */
33538 float devnull[8192];
33539 ma_uint64 totalOutputFramesToProcess;
33540 ma_uint64 totalOutputFramesProcessed;
33541 ma_uint64 totalInputFramesProcessed;
33542 ma_uint32 bpf;
33543 ma_result result;
33544
33545 MA_ASSERT(pResampler != NULL);
33546
33547 totalOutputFramesProcessed = 0;
33548 totalInputFramesProcessed = 0;
33549 bpf = ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels);
33550
33551 if (pFrameCountOut != NULL) {
33552 /* Seek by output frames. */
33553 totalOutputFramesToProcess = *pFrameCountOut;
33554 } else {
33555 /* Seek by input frames. */
33556 MA_ASSERT(pFrameCountIn != NULL);
33557 totalOutputFramesToProcess = ma_resampler_get_expected_output_frame_count(pResampler, *pFrameCountIn);
33558 }
33559
33560 if (pFramesIn != NULL) {
33561 /* Process input data. */
33562 MA_ASSERT(pFrameCountIn != NULL);
33563 while (totalOutputFramesProcessed < totalOutputFramesToProcess && totalInputFramesProcessed < *pFrameCountIn) {
33564 ma_uint64 inputFramesToProcessThisIteration = (*pFrameCountIn - totalInputFramesProcessed);
33565 ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed);
33566 if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) {
33567 outputFramesToProcessThisIteration = sizeof(devnull) / bpf;
33568 }
33569
33570 result = ma_resampler_process_pcm_frames__read(pResampler, ma_offset_ptr(pFramesIn, totalInputFramesProcessed*bpf), &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration);
33571 if (result != MA_SUCCESS) {
33572 return result;
33573 }
33574
33575 totalOutputFramesProcessed += outputFramesToProcessThisIteration;
33576 totalInputFramesProcessed += inputFramesToProcessThisIteration;
33577 }
33578 } else {
33579 /* Don't process input data - just update timing and filter state as if zeroes were passed in. */
33580 while (totalOutputFramesProcessed < totalOutputFramesToProcess) {
33581 ma_uint64 inputFramesToProcessThisIteration = 16384;
33582 ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed);
33583 if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) {
33584 outputFramesToProcessThisIteration = sizeof(devnull) / bpf;
33585 }
33586
33587 result = ma_resampler_process_pcm_frames__read(pResampler, NULL, &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration);
33588 if (result != MA_SUCCESS) {
33589 return result;
33590 }
33591
33592 totalOutputFramesProcessed += outputFramesToProcessThisIteration;
33593 totalInputFramesProcessed += inputFramesToProcessThisIteration;
33594 }
33595 }
33596
33597
33598 if (pFrameCountIn != NULL) {
33599 *pFrameCountIn = totalInputFramesProcessed;
33600 }
33601 if (pFrameCountOut != NULL) {
33602 *pFrameCountOut = totalOutputFramesProcessed;
33603 }
33604
33605 return MA_SUCCESS;
33606 }
33607 #endif
33608
33609 static ma_result ma_resampler_process_pcm_frames__seek(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
33610 {
33611 MA_ASSERT(pResampler != NULL);
33612
33613 switch (pResampler->config.algorithm)
33614 {
33615 case ma_resample_algorithm_linear:
33616 {
33617 return ma_resampler_process_pcm_frames__seek__linear(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
33618 } break;
33619
33620 case ma_resample_algorithm_speex:
33621 {
33622 #if defined(MA_HAS_SPEEX_RESAMPLER)
33623 return ma_resampler_process_pcm_frames__seek__speex(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
33624 #else
33625 break;
33626 #endif
33627 };
33628
33629 default: break;
33630 }
33631
33632 /* Should never hit this. */
33633 MA_ASSERT(MA_FALSE);
33634 return MA_INVALID_ARGS;
33635 }
33636
33637
33638 MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
33639 {
33640 if (pResampler == NULL) {
33641 return MA_INVALID_ARGS;
33642 }
33643
33644 if (pFrameCountOut == NULL && pFrameCountIn == NULL) {
33645 return MA_INVALID_ARGS;
33646 }
33647
33648 if (pFramesOut != NULL) {
33649 /* Reading. */
33650 return ma_resampler_process_pcm_frames__read(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
33651 } else {
33652 /* Seeking. */
33653 return ma_resampler_process_pcm_frames__seek(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
33654 }
33655 }
33656
33657 MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
33658 {
33659 if (pResampler == NULL) {
33660 return MA_INVALID_ARGS;
33661 }
33662
33663 if (sampleRateIn == 0 || sampleRateOut == 0) {
33664 return MA_INVALID_ARGS;
33665 }
33666
33667 pResampler->config.sampleRateIn = sampleRateIn;
33668 pResampler->config.sampleRateOut = sampleRateOut;
33669
33670 switch (pResampler->config.algorithm)
33671 {
33672 case ma_resample_algorithm_linear:
33673 {
33674 return ma_linear_resampler_set_rate(&pResampler->state.linear, sampleRateIn, sampleRateOut);
33675 } break;
33676
33677 case ma_resample_algorithm_speex:
33678 {
33679 #if defined(MA_HAS_SPEEX_RESAMPLER)
33680 return ma_result_from_speex_err(speex_resampler_set_rate((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, sampleRateIn, sampleRateOut));
33681 #else
33682 break;
33683 #endif
33684 };
33685
33686 default: break;
33687 }
33688
33689 /* Should never get here. */
33690 MA_ASSERT(MA_FALSE);
33691 return MA_INVALID_OPERATION;
33692 }
33693
33694 MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio)
33695 {
33696 if (pResampler == NULL) {
33697 return MA_INVALID_ARGS;
33698 }
33699
33700 if (pResampler->config.algorithm == ma_resample_algorithm_linear) {
33701 return ma_linear_resampler_set_rate_ratio(&pResampler->state.linear, ratio);
33702 } else {
33703 /* Getting here means the backend does not have native support for setting the rate as a ratio so we just do it generically. */
33704 ma_uint32 n;
33705 ma_uint32 d;
33706
33707 d = 1000000; /* We use up to 6 decimal places. */
33708 n = (ma_uint32)(ratio * d);
33709
33710 if (n == 0) {
33711 return MA_INVALID_ARGS; /* Ratio too small. */
33712 }
33713
33714 MA_ASSERT(n != 0);
33715
33716 return ma_resampler_set_rate(pResampler, n, d);
33717 }
33718 }
33719
33720 MA_API ma_uint64 ma_resampler_get_required_input_frame_count(ma_resampler* pResampler, ma_uint64 outputFrameCount)
33721 {
33722 if (pResampler == NULL) {
33723 return 0;
33724 }
33725
33726 if (outputFrameCount == 0) {
33727 return 0;
33728 }
33729
33730 switch (pResampler->config.algorithm)
33731 {
33732 case ma_resample_algorithm_linear:
33733 {
33734 return ma_linear_resampler_get_required_input_frame_count(&pResampler->state.linear, outputFrameCount);
33735 }
33736
33737 case ma_resample_algorithm_speex:
33738 {
33739 #if defined(MA_HAS_SPEEX_RESAMPLER)
33740 ma_uint64 count;
33741 int speexErr = ma_speex_resampler_get_required_input_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, outputFrameCount, &count);
33742 if (speexErr != RESAMPLER_ERR_SUCCESS) {
33743 return 0;
33744 }
33745
33746 return count;
33747 #else
33748 break;
33749 #endif
33750 }
33751
33752 default: break;
33753 }
33754
33755 /* Should never get here. */
33756 MA_ASSERT(MA_FALSE);
33757 return 0;
33758 }
33759
33760 MA_API ma_uint64 ma_resampler_get_expected_output_frame_count(ma_resampler* pResampler, ma_uint64 inputFrameCount)
33761 {
33762 if (pResampler == NULL) {
33763 return 0; /* Invalid args. */
33764 }
33765
33766 if (inputFrameCount == 0) {
33767 return 0;
33768 }
33769
33770 switch (pResampler->config.algorithm)
33771 {
33772 case ma_resample_algorithm_linear:
33773 {
33774 return ma_linear_resampler_get_expected_output_frame_count(&pResampler->state.linear, inputFrameCount);
33775 }
33776
33777 case ma_resample_algorithm_speex:
33778 {
33779 #if defined(MA_HAS_SPEEX_RESAMPLER)
33780 ma_uint64 count;
33781 int speexErr = ma_speex_resampler_get_expected_output_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, inputFrameCount, &count);
33782 if (speexErr != RESAMPLER_ERR_SUCCESS) {
33783 return 0;
33784 }
33785
33786 return count;
33787 #else
33788 break;
33789 #endif
33790 }
33791
33792 default: break;
33793 }
33794
33795 /* Should never get here. */
33796 MA_ASSERT(MA_FALSE);
33797 return 0;
33798 }
33799
33800 MA_API ma_uint64 ma_resampler_get_input_latency(ma_resampler* pResampler)
33801 {
33802 if (pResampler == NULL) {
33803 return 0;
33804 }
33805
33806 switch (pResampler->config.algorithm)
33807 {
33808 case ma_resample_algorithm_linear:
33809 {
33810 return ma_linear_resampler_get_input_latency(&pResampler->state.linear);
33811 }
33812
33813 case ma_resample_algorithm_speex:
33814 {
33815 #if defined(MA_HAS_SPEEX_RESAMPLER)
33816 return (ma_uint64)ma_speex_resampler_get_input_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
33817 #else
33818 break;
33819 #endif
33820 }
33821
33822 default: break;
33823 }
33824
33825 /* Should never get here. */
33826 MA_ASSERT(MA_FALSE);
33827 return 0;
33828 }
33829
33830 MA_API ma_uint64 ma_resampler_get_output_latency(ma_resampler* pResampler)
33831 {
33832 if (pResampler == NULL) {
33833 return 0;
33834 }
33835
33836 switch (pResampler->config.algorithm)
33837 {
33838 case ma_resample_algorithm_linear:
33839 {
33840 return ma_linear_resampler_get_output_latency(&pResampler->state.linear);
33841 }
33842
33843 case ma_resample_algorithm_speex:
33844 {
33845 #if defined(MA_HAS_SPEEX_RESAMPLER)
33846 return (ma_uint64)ma_speex_resampler_get_output_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
33847 #else
33848 break;
33849 #endif
33850 }
33851
33852 default: break;
33853 }
33854
33855 /* Should never get here. */
33856 MA_ASSERT(MA_FALSE);
33857 return 0;
33858 }
33859
33860 /**************************************************************************************************************************************************************
33861
33862 Channel Conversion
33863
33864 **************************************************************************************************************************************************************/
33865 #ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT
33866 #define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12
33867 #endif
33868
33869 #define MA_PLANE_LEFT 0
33870 #define MA_PLANE_RIGHT 1
33871 #define MA_PLANE_FRONT 2
33872 #define MA_PLANE_BACK 3
33873 #define MA_PLANE_BOTTOM 4
33874 #define MA_PLANE_TOP 5
33875
33876 static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
33877 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */
33878 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */
33879 { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */
33880 { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */
33881 { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */
33882 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */
33883 { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */
33884 { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */
33885 { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */
33886 { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
33887 { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */
33888 { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */
33889 { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */
33890 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */
33891 { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */
33892 { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */
33893 { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */
33894 { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */
33895 { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */
33896 { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */
33897 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */
33898 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */
33899 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */
33900 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */
33901 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */
33902 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */
33903 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */
33904 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */
33905 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */
33906 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */
33907 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */
33908 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */
33909 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */
33910 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */
33911 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */
33912 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */
33913 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */
33914 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */
33915 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */
33916 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */
33917 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */
33918 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */
33919 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */
33920 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */
33921 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */
33922 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */
33923 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */
33924 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */
33925 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */
33926 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */
33927 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */
33928 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
33929 };
33930
33931 static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
33932 {
33933 /*
33934 Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
33935 the following output configuration:
33936
33937 - front/left
33938 - side/left
33939 - back/left
33940
33941 The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount
33942 of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
33943
33944 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
33945 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
33946 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
33947 receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
33948 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
33949 across 3 spatial dimensions.
33950
33951 The first thing to do is figure out how each speaker's volume is spread over each of plane:
33952 - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane
33953 - side/left: 1 plane (left only) = 1/1 = entire volume from left plane
33954 - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane
33955 - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane
33956
33957 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
33958 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
33959 taken by the other to produce the final contribution.
33960 */
33961
33962 /* Contribution = Sum(Volume to Give * Volume to Take) */
33963 float contribution =
33964 g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
33965 g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
33966 g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
33967 g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
33968 g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
33969 g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
33970
33971 return contribution;
33972 }
33973
33974 MA_API ma_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)
33975 {
33976 ma_channel_converter_config config;
33977 MA_ZERO_OBJECT(&config);
33978 config.format = format;
33979 config.channelsIn = channelsIn;
33980 config.channelsOut = channelsOut;
33981 ma_channel_map_copy(config.channelMapIn, channelMapIn, channelsIn);
33982 ma_channel_map_copy(config.channelMapOut, channelMapOut, channelsOut);
33983 config.mixingMode = mixingMode;
33984
33985 return config;
33986 }
33987
33988 static ma_int32 ma_channel_converter_float_to_fp(float x)
33989 {
33990 return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));
33991 }
33992
33993 static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
33994 {
33995 int i;
33996
33997 if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
33998 return MA_FALSE;
33999 }
34000
34001 for (i = 0; i < 6; ++i) { /* Each side of a cube. */
34002 if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
34003 return MA_TRUE;
34004 }
34005 }
34006
34007 return MA_FALSE;
34008 }
34009
34010 MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter)
34011 {
34012 ma_uint32 iChannelIn;
34013 ma_uint32 iChannelOut;
34014
34015 if (pConverter == NULL) {
34016 return MA_INVALID_ARGS;
34017 }
34018
34019 MA_ZERO_OBJECT(pConverter);
34020
34021 if (pConfig == NULL) {
34022 return MA_INVALID_ARGS;
34023 }
34024
34025 if (!ma_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) {
34026 return MA_INVALID_ARGS; /* Invalid input channel map. */
34027 }
34028 if (!ma_channel_map_valid(pConfig->channelsOut, pConfig->channelMapOut)) {
34029 return MA_INVALID_ARGS; /* Invalid output channel map. */
34030 }
34031
34032 if (pConfig->format != ma_format_s16 && pConfig->format != ma_format_f32) {
34033 return MA_INVALID_ARGS; /* Invalid format. */
34034 }
34035
34036 pConverter->format = pConfig->format;
34037 pConverter->channelsIn = pConfig->channelsIn;
34038 pConverter->channelsOut = pConfig->channelsOut;
34039 ma_channel_map_copy(pConverter->channelMapIn, pConfig->channelMapIn, pConfig->channelsIn);
34040 ma_channel_map_copy(pConverter->channelMapOut, pConfig->channelMapOut, pConfig->channelsOut);
34041 pConverter->mixingMode = pConfig->mixingMode;
34042
34043 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
34044 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
34045 if (pConverter->format == ma_format_s16) {
34046 pConverter->weights.f32[iChannelIn][iChannelOut] = pConfig->weights[iChannelIn][iChannelOut];
34047 } else {
34048 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(pConfig->weights[iChannelIn][iChannelOut]);
34049 }
34050 }
34051 }
34052
34053
34054
34055 /* If the input and output channels and channel maps are the same we should use a passthrough. */
34056 if (pConverter->channelsIn == pConverter->channelsOut) {
34057 if (ma_channel_map_equal(pConverter->channelsIn, pConverter->channelMapIn, pConverter->channelMapOut)) {
34058 pConverter->isPassthrough = MA_TRUE;
34059 }
34060 if (ma_channel_map_blank(pConverter->channelsIn, pConverter->channelMapIn) || ma_channel_map_blank(pConverter->channelsOut, pConverter->channelMapOut)) {
34061 pConverter->isPassthrough = MA_TRUE;
34062 }
34063 }
34064
34065
34066 /*
34067 We can use a simple case for expanding the mono channel. This will used when expanding a mono input into any output so long
34068 as no LFE is present in the output.
34069 */
34070 if (!pConverter->isPassthrough) {
34071 if (pConverter->channelsIn == 1 && pConverter->channelMapIn[0] == MA_CHANNEL_MONO) {
34072 /* Optimal case if no LFE is in the output channel map. */
34073 pConverter->isSimpleMonoExpansion = MA_TRUE;
34074 if (ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, MA_CHANNEL_LFE)) {
34075 pConverter->isSimpleMonoExpansion = MA_FALSE;
34076 }
34077 }
34078 }
34079
34080 /* Another optimized case is stereo to mono. */
34081 if (!pConverter->isPassthrough) {
34082 if (pConverter->channelsOut == 1 && pConverter->channelMapOut[0] == MA_CHANNEL_MONO && pConverter->channelsIn == 2) {
34083 /* Optimal case if no LFE is in the input channel map. */
34084 pConverter->isStereoToMono = MA_TRUE;
34085 if (ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, MA_CHANNEL_LFE)) {
34086 pConverter->isStereoToMono = MA_FALSE;
34087 }
34088 }
34089 }
34090
34091
34092 /*
34093 Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules:
34094
34095 1) If it's a passthrough, do nothing - it's just a simple memcpy().
34096 2) If the channel counts are the same and every channel position in the input map is present in the output map, use a
34097 simple shuffle. An example might be different 5.1 channel layouts.
34098 3) Otherwise channels are blended based on spatial locality.
34099 */
34100 if (!pConverter->isPassthrough) {
34101 if (pConverter->channelsIn == pConverter->channelsOut) {
34102 ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
34103 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34104 ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
34105 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
34106 if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) {
34107 isInputChannelPositionInOutput = MA_TRUE;
34108 break;
34109 }
34110 }
34111
34112 if (!isInputChannelPositionInOutput) {
34113 areAllChannelPositionsPresent = MA_FALSE;
34114 break;
34115 }
34116 }
34117
34118 if (areAllChannelPositionsPresent) {
34119 pConverter->isSimpleShuffle = MA_TRUE;
34120
34121 /*
34122 All the router will be doing is rearranging channels which means all we need to do is use a shuffling table which is just
34123 a mapping between the index of the input channel to the index of the output channel.
34124 */
34125 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34126 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
34127 if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) {
34128 pConverter->shuffleTable[iChannelIn] = (ma_uint8)iChannelOut;
34129 break;
34130 }
34131 }
34132 }
34133 }
34134 }
34135 }
34136
34137
34138 /*
34139 Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple
34140 shuffling. We use different algorithms for calculating weights depending on our mixing mode.
34141
34142 In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just
34143 map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel
34144 map, nothing will be heard!
34145 */
34146
34147 /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */
34148 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34149 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
34150
34151 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
34152 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
34153
34154 if (channelPosIn == channelPosOut) {
34155 if (pConverter->format == ma_format_s16) {
34156 pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT);
34157 } else {
34158 pConverter->weights.f32[iChannelIn][iChannelOut] = 1;
34159 }
34160 }
34161 }
34162 }
34163
34164 /*
34165 The mono channel is accumulated on all other channels, except LFE. Make sure in this loop we exclude output mono channels since
34166 they were handled in the pass above.
34167 */
34168 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34169 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
34170
34171 if (channelPosIn == MA_CHANNEL_MONO) {
34172 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
34173 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
34174
34175 if (channelPosOut != MA_CHANNEL_NONE && channelPosOut != MA_CHANNEL_MONO && channelPosOut != MA_CHANNEL_LFE) {
34176 if (pConverter->format == ma_format_s16) {
34177 pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT);
34178 } else {
34179 pConverter->weights.f32[iChannelIn][iChannelOut] = 1;
34180 }
34181 }
34182 }
34183 }
34184 }
34185
34186 /* The output mono channel is the average of all non-none, non-mono and non-lfe input channels. */
34187 {
34188 ma_uint32 len = 0;
34189 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34190 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
34191
34192 if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
34193 len += 1;
34194 }
34195 }
34196
34197 if (len > 0) {
34198 float monoWeight = 1.0f / len;
34199
34200 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
34201 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
34202
34203 if (channelPosOut == MA_CHANNEL_MONO) {
34204 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34205 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
34206
34207 if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
34208 if (pConverter->format == ma_format_s16) {
34209 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(monoWeight);
34210 } else {
34211 pConverter->weights.f32[iChannelIn][iChannelOut] = monoWeight;
34212 }
34213 }
34214 }
34215 }
34216 }
34217 }
34218 }
34219
34220
34221 /* Input and output channels that are not present on the other side need to be blended in based on spatial locality. */
34222 switch (pConverter->mixingMode)
34223 {
34224 case ma_channel_mix_mode_rectangular:
34225 {
34226 /* Unmapped input channels. */
34227 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34228 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
34229
34230 if (ma_is_spatial_channel_position(channelPosIn)) {
34231 if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, channelPosIn)) {
34232 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
34233 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
34234
34235 if (ma_is_spatial_channel_position(channelPosOut)) {
34236 float weight = 0;
34237 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
34238 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
34239 }
34240
34241 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
34242 if (pConverter->format == ma_format_s16) {
34243 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
34244 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(weight);
34245 }
34246 } else {
34247 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
34248 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
34249 }
34250 }
34251 }
34252 }
34253 }
34254 }
34255 }
34256
34257 /* Unmapped output channels. */
34258 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
34259 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
34260
34261 if (ma_is_spatial_channel_position(channelPosOut)) {
34262 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, channelPosOut)) {
34263 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34264 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
34265
34266 if (ma_is_spatial_channel_position(channelPosIn)) {
34267 float weight = 0;
34268 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
34269 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
34270 }
34271
34272 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
34273 if (pConverter->format == ma_format_s16) {
34274 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
34275 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(weight);
34276 }
34277 } else {
34278 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
34279 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
34280 }
34281 }
34282 }
34283 }
34284 }
34285 }
34286 }
34287 } break;
34288
34289 case ma_channel_mix_mode_custom_weights:
34290 case ma_channel_mix_mode_simple:
34291 default:
34292 {
34293 /* Fallthrough. */
34294 } break;
34295 }
34296
34297
34298 return MA_SUCCESS;
34299 }
34300
34301 MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter)
34302 {
34303 if (pConverter == NULL) {
34304 return;
34305 }
34306 }
34307
34308 static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
34309 {
34310 MA_ASSERT(pConverter != NULL);
34311 MA_ASSERT(pFramesOut != NULL);
34312 MA_ASSERT(pFramesIn != NULL);
34313
34314 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
34315 return MA_SUCCESS;
34316 }
34317
34318 static ma_result ma_channel_converter_process_pcm_frames__simple_shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
34319 {
34320 ma_uint32 iFrame;
34321 ma_uint32 iChannelIn;
34322
34323 MA_ASSERT(pConverter != NULL);
34324 MA_ASSERT(pFramesOut != NULL);
34325 MA_ASSERT(pFramesIn != NULL);
34326 MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);
34327
34328 if (pConverter->format == ma_format_s16) {
34329 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
34330 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
34331
34332 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34333 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34334 pFramesOutS16[pConverter->shuffleTable[iChannelIn]] = pFramesInS16[iChannelIn];
34335 }
34336 }
34337 } else {
34338 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
34339 const float* pFramesInF32 = (const float*)pFramesIn;
34340
34341 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34342 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34343 pFramesOutF32[pConverter->shuffleTable[iChannelIn]] = pFramesInF32[iChannelIn];
34344 }
34345 }
34346 }
34347
34348 return MA_SUCCESS;
34349 }
34350
34351 static ma_result ma_channel_converter_process_pcm_frames__simple_mono_expansion(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
34352 {
34353 ma_uint64 iFrame;
34354
34355 MA_ASSERT(pConverter != NULL);
34356 MA_ASSERT(pFramesOut != NULL);
34357 MA_ASSERT(pFramesIn != NULL);
34358
34359 if (pConverter->format == ma_format_s16) {
34360 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
34361 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
34362
34363 if (pConverter->channelsOut == 2) {
34364 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
34365 pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];
34366 pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];
34367 }
34368 } else {
34369 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
34370 ma_uint32 iChannel;
34371 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
34372 pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];
34373 }
34374 }
34375 }
34376 } else {
34377 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
34378 const float* pFramesInF32 = (const float*)pFramesIn;
34379
34380 if (pConverter->channelsOut == 2) {
34381 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
34382 pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];
34383 pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];
34384 }
34385 } else {
34386 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
34387 ma_uint32 iChannel;
34388 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
34389 pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];
34390 }
34391 }
34392 }
34393 }
34394
34395 return MA_SUCCESS;
34396 }
34397
34398 static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
34399 {
34400 ma_uint64 iFrame;
34401
34402 MA_ASSERT(pConverter != NULL);
34403 MA_ASSERT(pFramesOut != NULL);
34404 MA_ASSERT(pFramesIn != NULL);
34405 MA_ASSERT(pConverter->channelsIn == 2);
34406 MA_ASSERT(pConverter->channelsOut == 1);
34407
34408 if (pConverter->format == ma_format_s16) {
34409 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
34410 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
34411
34412 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
34413 pFramesOutS16[iFrame] = (ma_int16)(((ma_int32)pFramesInS16[iFrame*2+0] + (ma_int32)pFramesInS16[iFrame*2+1]) / 2);
34414 }
34415 } else {
34416 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
34417 const float* pFramesInF32 = (const float*)pFramesIn;
34418
34419 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
34420 pFramesOutF32[iFrame] = (pFramesInF32[iFrame*2+0] + pFramesInF32[iFrame*2+0]) * 0.5f;
34421 }
34422 }
34423
34424 return MA_SUCCESS;
34425 }
34426
34427 static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
34428 {
34429 ma_uint32 iFrame;
34430 ma_uint32 iChannelIn;
34431 ma_uint32 iChannelOut;
34432
34433 MA_ASSERT(pConverter != NULL);
34434 MA_ASSERT(pFramesOut != NULL);
34435 MA_ASSERT(pFramesIn != NULL);
34436
34437 /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
34438
34439 /* Clear. */
34440 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
34441
34442 /* Accumulate. */
34443 if (pConverter->format == ma_format_s16) {
34444 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
34445 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
34446
34447 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34448 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34449 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
34450 ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];
34451 s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
34452
34453 pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);
34454 }
34455 }
34456 }
34457 } else {
34458 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
34459 const float* pFramesInF32 = (const float*)pFramesIn;
34460
34461 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34462 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
34463 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
34464 pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];
34465 }
34466 }
34467 }
34468 }
34469
34470 return MA_SUCCESS;
34471 }
34472
34473 MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
34474 {
34475 if (pConverter == NULL) {
34476 return MA_INVALID_ARGS;
34477 }
34478
34479 if (pFramesOut == NULL) {
34480 return MA_INVALID_ARGS;
34481 }
34482
34483 if (pFramesIn == NULL) {
34484 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
34485 return MA_SUCCESS;
34486 }
34487
34488 if (pConverter->isPassthrough) {
34489 return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);
34490 } else if (pConverter->isSimpleShuffle) {
34491 return ma_channel_converter_process_pcm_frames__simple_shuffle(pConverter, pFramesOut, pFramesIn, frameCount);
34492 } else if (pConverter->isSimpleMonoExpansion) {
34493 return ma_channel_converter_process_pcm_frames__simple_mono_expansion(pConverter, pFramesOut, pFramesIn, frameCount);
34494 } else if (pConverter->isStereoToMono) {
34495 return ma_channel_converter_process_pcm_frames__stereo_to_mono(pConverter, pFramesOut, pFramesIn, frameCount);
34496 } else {
34497 return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);
34498 }
34499 }
34500
34501
34502 /**************************************************************************************************************************************************************
34503
34504 Data Conversion
34505
34506 **************************************************************************************************************************************************************/
34507 MA_API ma_data_converter_config ma_data_converter_config_init_default()
34508 {
34509 ma_data_converter_config config;
34510 MA_ZERO_OBJECT(&config);
34511
34512 config.ditherMode = ma_dither_mode_none;
34513 config.resampling.algorithm = ma_resample_algorithm_linear;
34514 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. */
34515
34516 /* Linear resampling defaults. */
34517 config.resampling.linear.lpfOrder = 1;
34518 config.resampling.linear.lpfNyquistFactor = 1;
34519
34520 /* Speex resampling defaults. */
34521 config.resampling.speex.quality = 3;
34522
34523 return config;
34524 }
34525
34526 MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
34527 {
34528 ma_data_converter_config config = ma_data_converter_config_init_default();
34529 config.formatIn = formatIn;
34530 config.formatOut = formatOut;
34531 config.channelsIn = channelsIn;
34532 config.channelsOut = channelsOut;
34533 config.sampleRateIn = sampleRateIn;
34534 config.sampleRateOut = sampleRateOut;
34535
34536 return config;
34537 }
34538
34539 MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter)
34540 {
34541 ma_result result;
34542 ma_format midFormat;
34543
34544 if (pConverter == NULL) {
34545 return MA_INVALID_ARGS;
34546 }
34547
34548 MA_ZERO_OBJECT(pConverter);
34549
34550 if (pConfig == NULL) {
34551 return MA_INVALID_ARGS;
34552 }
34553
34554 pConverter->config = *pConfig;
34555
34556 /*
34557 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
34558 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
34559 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
34560 use f32.
34561 */
34562 /* */ if (pConverter->config.formatOut == ma_format_s16 || pConverter->config.formatOut == ma_format_f32) {
34563 midFormat = pConverter->config.formatOut;
34564 } else if (pConverter->config.formatIn == ma_format_s16 || pConverter->config.formatIn == ma_format_f32) {
34565 midFormat = pConverter->config.formatIn;
34566 } else {
34567 midFormat = ma_format_f32;
34568 }
34569
34570 /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */
34571 {
34572 ma_uint32 iChannelIn;
34573 ma_uint32 iChannelOut;
34574 ma_channel_converter_config channelConverterConfig;
34575
34576 channelConverterConfig = ma_channel_converter_config_init(midFormat, pConverter->config.channelsIn, pConverter->config.channelMapIn, pConverter->config.channelsOut, pConverter->config.channelMapOut, pConverter->config.channelMixMode);
34577
34578 /* Channel weights. */
34579 for (iChannelIn = 0; iChannelIn < pConverter->config.channelsIn; iChannelIn += 1) {
34580 for (iChannelOut = 0; iChannelOut < pConverter->config.channelsOut; iChannelOut += 1) {
34581 channelConverterConfig.weights[iChannelIn][iChannelOut] = pConverter->config.channelWeights[iChannelIn][iChannelOut];
34582 }
34583 }
34584
34585 result = ma_channel_converter_init(&channelConverterConfig, &pConverter->channelConverter);
34586 if (result != MA_SUCCESS) {
34587 return result;
34588 }
34589
34590 /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */
34591 if (pConverter->channelConverter.isPassthrough == MA_FALSE) {
34592 pConverter->hasChannelConverter = MA_TRUE;
34593 }
34594 }
34595
34596
34597 /* 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. */
34598 if (pConverter->config.resampling.allowDynamicSampleRate == MA_FALSE) {
34599 pConverter->config.resampling.allowDynamicSampleRate = pConverter->config.sampleRateIn != pConverter->config.sampleRateOut;
34600 }
34601
34602 /* Resampler. */
34603 if (pConverter->config.resampling.allowDynamicSampleRate) {
34604 ma_resampler_config resamplerConfig;
34605 ma_uint32 resamplerChannels;
34606
34607 /* 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. */
34608 if (pConverter->config.channelsIn < pConverter->config.channelsOut) {
34609 resamplerChannels = pConverter->config.channelsIn;
34610 } else {
34611 resamplerChannels = pConverter->config.channelsOut;
34612 }
34613
34614 resamplerConfig = ma_resampler_config_init(midFormat, resamplerChannels, pConverter->config.sampleRateIn, pConverter->config.sampleRateOut, pConverter->config.resampling.algorithm);
34615 resamplerConfig.linear.lpfOrder = pConverter->config.resampling.linear.lpfOrder;
34616 resamplerConfig.linear.lpfNyquistFactor = pConverter->config.resampling.linear.lpfNyquistFactor;
34617 resamplerConfig.speex.quality = pConverter->config.resampling.speex.quality;
34618
34619 result = ma_resampler_init(&resamplerConfig, &pConverter->resampler);
34620 if (result != MA_SUCCESS) {
34621 return result;
34622 }
34623
34624 pConverter->hasResampler = MA_TRUE;
34625 }
34626
34627
34628 /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */
34629 if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) {
34630 /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */
34631 if (pConverter->config.formatIn == pConverter->config.formatOut) {
34632 /* The formats are the same so we can just pass through. */
34633 pConverter->hasPreFormatConversion = MA_FALSE;
34634 pConverter->hasPostFormatConversion = MA_FALSE;
34635 } else {
34636 /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */
34637 pConverter->hasPreFormatConversion = MA_FALSE;
34638 pConverter->hasPostFormatConversion = MA_TRUE;
34639 }
34640 } else {
34641 /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */
34642 if (pConverter->config.formatIn != midFormat) {
34643 pConverter->hasPreFormatConversion = MA_TRUE;
34644 }
34645 if (pConverter->config.formatOut != midFormat) {
34646 pConverter->hasPostFormatConversion = MA_TRUE;
34647 }
34648 }
34649
34650 /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */
34651 if (pConverter->hasPreFormatConversion == MA_FALSE &&
34652 pConverter->hasPostFormatConversion == MA_FALSE &&
34653 pConverter->hasChannelConverter == MA_FALSE &&
34654 pConverter->hasResampler == MA_FALSE) {
34655 pConverter->isPassthrough = MA_TRUE;
34656 }
34657
34658 return MA_SUCCESS;
34659 }
34660
34661 MA_API void ma_data_converter_uninit(ma_data_converter* pConverter)
34662 {
34663 if (pConverter == NULL) {
34664 return;
34665 }
34666
34667 if (pConverter->hasResampler) {
34668 ma_resampler_uninit(&pConverter->resampler);
34669 }
34670 }
34671
34672 static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
34673 {
34674 ma_uint64 frameCountIn;
34675 ma_uint64 frameCountOut;
34676 ma_uint64 frameCount;
34677
34678 MA_ASSERT(pConverter != NULL);
34679
34680 frameCountIn = 0;
34681 if (pFrameCountIn != NULL) {
34682 frameCountIn = *pFrameCountIn;
34683 }
34684
34685 frameCountOut = 0;
34686 if (pFrameCountOut != NULL) {
34687 frameCountOut = *pFrameCountOut;
34688 }
34689
34690 frameCount = ma_min(frameCountIn, frameCountOut);
34691
34692 if (pFramesOut != NULL) {
34693 if (pFramesIn != NULL) {
34694 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
34695 } else {
34696 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
34697 }
34698 }
34699
34700 if (pFrameCountIn != NULL) {
34701 *pFrameCountIn = frameCount;
34702 }
34703 if (pFrameCountOut != NULL) {
34704 *pFrameCountOut = frameCount;
34705 }
34706
34707 return MA_SUCCESS;
34708 }
34709
34710 static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
34711 {
34712 ma_uint64 frameCountIn;
34713 ma_uint64 frameCountOut;
34714 ma_uint64 frameCount;
34715
34716 MA_ASSERT(pConverter != NULL);
34717
34718 frameCountIn = 0;
34719 if (pFrameCountIn != NULL) {
34720 frameCountIn = *pFrameCountIn;
34721 }
34722
34723 frameCountOut = 0;
34724 if (pFrameCountOut != NULL) {
34725 frameCountOut = *pFrameCountOut;
34726 }
34727
34728 frameCount = ma_min(frameCountIn, frameCountOut);
34729
34730 if (pFramesOut != NULL) {
34731 if (pFramesIn != NULL) {
34732 ma_convert_pcm_frames_format(pFramesOut, pConverter->config.formatOut, pFramesIn, pConverter->config.formatIn, frameCount, pConverter->config.channelsIn, pConverter->config.ditherMode);
34733 } else {
34734 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
34735 }
34736 }
34737
34738 if (pFrameCountIn != NULL) {
34739 *pFrameCountIn = frameCount;
34740 }
34741 if (pFrameCountOut != NULL) {
34742 *pFrameCountOut = frameCount;
34743 }
34744
34745 return MA_SUCCESS;
34746 }
34747
34748
34749 static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
34750 {
34751 ma_result result = MA_SUCCESS;
34752 ma_uint64 frameCountIn;
34753 ma_uint64 frameCountOut;
34754 ma_uint64 framesProcessedIn;
34755 ma_uint64 framesProcessedOut;
34756
34757 MA_ASSERT(pConverter != NULL);
34758
34759 frameCountIn = 0;
34760 if (pFrameCountIn != NULL) {
34761 frameCountIn = *pFrameCountIn;
34762 }
34763
34764 frameCountOut = 0;
34765 if (pFrameCountOut != NULL) {
34766 frameCountOut = *pFrameCountOut;
34767 }
34768
34769 framesProcessedIn = 0;
34770 framesProcessedOut = 0;
34771
34772 while (framesProcessedOut < frameCountOut) {
34773 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
34774 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
34775 const void* pFramesInThisIteration;
34776 /* */ void* pFramesOutThisIteration;
34777 ma_uint64 frameCountInThisIteration;
34778 ma_uint64 frameCountOutThisIteration;
34779
34780 if (pFramesIn != NULL) {
34781 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
34782 } else {
34783 pFramesInThisIteration = NULL;
34784 }
34785
34786 if (pFramesOut != NULL) {
34787 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
34788 } else {
34789 pFramesOutThisIteration = NULL;
34790 }
34791
34792 /* Do a pre format conversion if necessary. */
34793 if (pConverter->hasPreFormatConversion) {
34794 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
34795 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
34796
34797 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
34798 if (frameCountInThisIteration > tempBufferInCap) {
34799 frameCountInThisIteration = tempBufferInCap;
34800 }
34801
34802 if (pConverter->hasPostFormatConversion) {
34803 if (frameCountInThisIteration > tempBufferOutCap) {
34804 frameCountInThisIteration = tempBufferOutCap;
34805 }
34806 }
34807
34808 if (pFramesInThisIteration != NULL) {
34809 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.config.format, pFramesInThisIteration, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
34810 } else {
34811 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
34812 }
34813
34814 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
34815
34816 if (pConverter->hasPostFormatConversion) {
34817 /* Both input and output conversion required. Output to the temp buffer. */
34818 if (frameCountOutThisIteration > tempBufferOutCap) {
34819 frameCountOutThisIteration = tempBufferOutCap;
34820 }
34821
34822 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
34823 } else {
34824 /* Only pre-format required. Output straight to the output buffer. */
34825 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration);
34826 }
34827
34828 if (result != MA_SUCCESS) {
34829 break;
34830 }
34831 } else {
34832 /* No pre-format required. Just read straight from the input buffer. */
34833 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
34834
34835 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
34836 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
34837 if (frameCountOutThisIteration > tempBufferOutCap) {
34838 frameCountOutThisIteration = tempBufferOutCap;
34839 }
34840
34841 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
34842 if (result != MA_SUCCESS) {
34843 break;
34844 }
34845 }
34846
34847 /* If we are doing a post format conversion we need to do that now. */
34848 if (pConverter->hasPostFormatConversion) {
34849 if (pFramesOutThisIteration != NULL) {
34850 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->config.formatOut, pTempBufferOut, pConverter->resampler.config.format, frameCountOutThisIteration, pConverter->resampler.config.channels, pConverter->config.ditherMode);
34851 }
34852 }
34853
34854 framesProcessedIn += frameCountInThisIteration;
34855 framesProcessedOut += frameCountOutThisIteration;
34856
34857 MA_ASSERT(framesProcessedIn <= frameCountIn);
34858 MA_ASSERT(framesProcessedOut <= frameCountOut);
34859
34860 if (frameCountOutThisIteration == 0) {
34861 break; /* Consumed all of our input data. */
34862 }
34863 }
34864
34865 if (pFrameCountIn != NULL) {
34866 *pFrameCountIn = framesProcessedIn;
34867 }
34868 if (pFrameCountOut != NULL) {
34869 *pFrameCountOut = framesProcessedOut;
34870 }
34871
34872 return result;
34873 }
34874
34875 static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
34876 {
34877 MA_ASSERT(pConverter != NULL);
34878
34879 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
34880 /* Neither pre- nor post-format required. This is simple case where only resampling is required. */
34881 return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
34882 } else {
34883 /* Format conversion required. */
34884 return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
34885 }
34886 }
34887
34888 static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
34889 {
34890 ma_result result;
34891 ma_uint64 frameCountIn;
34892 ma_uint64 frameCountOut;
34893 ma_uint64 frameCount;
34894
34895 MA_ASSERT(pConverter != NULL);
34896
34897 frameCountIn = 0;
34898 if (pFrameCountIn != NULL) {
34899 frameCountIn = *pFrameCountIn;
34900 }
34901
34902 frameCountOut = 0;
34903 if (pFrameCountOut != NULL) {
34904 frameCountOut = *pFrameCountOut;
34905 }
34906
34907 frameCount = ma_min(frameCountIn, frameCountOut);
34908
34909 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
34910 /* No format conversion required. */
34911 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount);
34912 if (result != MA_SUCCESS) {
34913 return result;
34914 }
34915 } else {
34916 /* Format conversion required. */
34917 ma_uint64 framesProcessed = 0;
34918
34919 while (framesProcessed < frameCount) {
34920 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
34921 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
34922 const void* pFramesInThisIteration;
34923 /* */ void* pFramesOutThisIteration;
34924 ma_uint64 frameCountThisIteration;
34925
34926 if (pFramesIn != NULL) {
34927 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
34928 } else {
34929 pFramesInThisIteration = NULL;
34930 }
34931
34932 if (pFramesOut != NULL) {
34933 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
34934 } else {
34935 pFramesOutThisIteration = NULL;
34936 }
34937
34938 /* Do a pre format conversion if necessary. */
34939 if (pConverter->hasPreFormatConversion) {
34940 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
34941 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
34942
34943 frameCountThisIteration = (frameCount - framesProcessed);
34944 if (frameCountThisIteration > tempBufferInCap) {
34945 frameCountThisIteration = tempBufferInCap;
34946 }
34947
34948 if (pConverter->hasPostFormatConversion) {
34949 if (frameCountThisIteration > tempBufferOutCap) {
34950 frameCountThisIteration = tempBufferOutCap;
34951 }
34952 }
34953
34954 if (pFramesInThisIteration != NULL) {
34955 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->config.formatIn, frameCountThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
34956 } else {
34957 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
34958 }
34959
34960 if (pConverter->hasPostFormatConversion) {
34961 /* Both input and output conversion required. Output to the temp buffer. */
34962 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration);
34963 } else {
34964 /* Only pre-format required. Output straight to the output buffer. */
34965 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration);
34966 }
34967
34968 if (result != MA_SUCCESS) {
34969 break;
34970 }
34971 } else {
34972 /* No pre-format required. Just read straight from the input buffer. */
34973 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
34974
34975 frameCountThisIteration = (frameCount - framesProcessed);
34976 if (frameCountThisIteration > tempBufferOutCap) {
34977 frameCountThisIteration = tempBufferOutCap;
34978 }
34979
34980 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration);
34981 if (result != MA_SUCCESS) {
34982 break;
34983 }
34984 }
34985
34986 /* If we are doing a post format conversion we need to do that now. */
34987 if (pConverter->hasPostFormatConversion) {
34988 if (pFramesOutThisIteration != NULL) {
34989 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->config.formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->config.ditherMode);
34990 }
34991 }
34992
34993 framesProcessed += frameCountThisIteration;
34994 }
34995 }
34996
34997 if (pFrameCountIn != NULL) {
34998 *pFrameCountIn = frameCount;
34999 }
35000 if (pFrameCountOut != NULL) {
35001 *pFrameCountOut = frameCount;
35002 }
35003
35004 return MA_SUCCESS;
35005 }
35006
35007 static ma_result ma_data_converter_process_pcm_frames__resampling_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
35008 {
35009 ma_result result;
35010 ma_uint64 frameCountIn;
35011 ma_uint64 frameCountOut;
35012 ma_uint64 framesProcessedIn;
35013 ma_uint64 framesProcessedOut;
35014 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
35015 ma_uint64 tempBufferInCap;
35016 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
35017 ma_uint64 tempBufferMidCap;
35018 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
35019 ma_uint64 tempBufferOutCap;
35020
35021 MA_ASSERT(pConverter != NULL);
35022 MA_ASSERT(pConverter->resampler.config.format == pConverter->channelConverter.format);
35023 MA_ASSERT(pConverter->resampler.config.channels == pConverter->channelConverter.channelsIn);
35024 MA_ASSERT(pConverter->resampler.config.channels < pConverter->channelConverter.channelsOut);
35025
35026 frameCountIn = 0;
35027 if (pFrameCountIn != NULL) {
35028 frameCountIn = *pFrameCountIn;
35029 }
35030
35031 frameCountOut = 0;
35032 if (pFrameCountOut != NULL) {
35033 frameCountOut = *pFrameCountOut;
35034 }
35035
35036 framesProcessedIn = 0;
35037 framesProcessedOut = 0;
35038
35039 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
35040 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
35041 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
35042
35043 while (framesProcessedOut < frameCountOut) {
35044 ma_uint64 frameCountInThisIteration;
35045 ma_uint64 frameCountOutThisIteration;
35046 const void* pRunningFramesIn = NULL;
35047 void* pRunningFramesOut = NULL;
35048 const void* pResampleBufferIn;
35049 void* pChannelsBufferOut;
35050
35051 if (pFramesIn != NULL) {
35052 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
35053 }
35054 if (pFramesOut != NULL) {
35055 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
35056 }
35057
35058 /* Run input data through the resampler and output it to the temporary buffer. */
35059 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
35060
35061 if (pConverter->hasPreFormatConversion) {
35062 if (frameCountInThisIteration > tempBufferInCap) {
35063 frameCountInThisIteration = tempBufferInCap;
35064 }
35065 }
35066
35067 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
35068 if (frameCountOutThisIteration > tempBufferMidCap) {
35069 frameCountOutThisIteration = tempBufferMidCap;
35070 }
35071
35072 /* We can't read more frames than can fit in the output buffer. */
35073 if (pConverter->hasPostFormatConversion) {
35074 if (frameCountOutThisIteration > tempBufferOutCap) {
35075 frameCountOutThisIteration = tempBufferOutCap;
35076 }
35077 }
35078
35079 /* 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. */
35080 {
35081 ma_uint64 requiredInputFrameCount = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration);
35082 if (frameCountInThisIteration > requiredInputFrameCount) {
35083 frameCountInThisIteration = requiredInputFrameCount;
35084 }
35085 }
35086
35087 if (pConverter->hasPreFormatConversion) {
35088 if (pFramesIn != NULL) {
35089 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.config.format, pRunningFramesIn, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
35090 pResampleBufferIn = pTempBufferIn;
35091 } else {
35092 pResampleBufferIn = NULL;
35093 }
35094 } else {
35095 pResampleBufferIn = pRunningFramesIn;
35096 }
35097
35098 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);
35099 if (result != MA_SUCCESS) {
35100 return result;
35101 }
35102
35103
35104 /*
35105 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
35106 this part if we have an output buffer.
35107 */
35108 if (pFramesOut != NULL) {
35109 if (pConverter->hasPostFormatConversion) {
35110 pChannelsBufferOut = pTempBufferOut;
35111 } else {
35112 pChannelsBufferOut = pRunningFramesOut;
35113 }
35114
35115 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);
35116 if (result != MA_SUCCESS) {
35117 return result;
35118 }
35119
35120 /* Finally we do post format conversion. */
35121 if (pConverter->hasPostFormatConversion) {
35122 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->config.formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->config.ditherMode);
35123 }
35124 }
35125
35126
35127 framesProcessedIn += frameCountInThisIteration;
35128 framesProcessedOut += frameCountOutThisIteration;
35129
35130 MA_ASSERT(framesProcessedIn <= frameCountIn);
35131 MA_ASSERT(framesProcessedOut <= frameCountOut);
35132
35133 if (frameCountOutThisIteration == 0) {
35134 break; /* Consumed all of our input data. */
35135 }
35136 }
35137
35138 if (pFrameCountIn != NULL) {
35139 *pFrameCountIn = framesProcessedIn;
35140 }
35141 if (pFrameCountOut != NULL) {
35142 *pFrameCountOut = framesProcessedOut;
35143 }
35144
35145 return MA_SUCCESS;
35146 }
35147
35148 static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
35149 {
35150 ma_result result;
35151 ma_uint64 frameCountIn;
35152 ma_uint64 frameCountOut;
35153 ma_uint64 framesProcessedIn;
35154 ma_uint64 framesProcessedOut;
35155 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
35156 ma_uint64 tempBufferInCap;
35157 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
35158 ma_uint64 tempBufferMidCap;
35159 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
35160 ma_uint64 tempBufferOutCap;
35161
35162 MA_ASSERT(pConverter != NULL);
35163 MA_ASSERT(pConverter->resampler.config.format == pConverter->channelConverter.format);
35164 MA_ASSERT(pConverter->resampler.config.channels == pConverter->channelConverter.channelsOut);
35165 MA_ASSERT(pConverter->resampler.config.channels < pConverter->channelConverter.channelsIn);
35166
35167 frameCountIn = 0;
35168 if (pFrameCountIn != NULL) {
35169 frameCountIn = *pFrameCountIn;
35170 }
35171
35172 frameCountOut = 0;
35173 if (pFrameCountOut != NULL) {
35174 frameCountOut = *pFrameCountOut;
35175 }
35176
35177 framesProcessedIn = 0;
35178 framesProcessedOut = 0;
35179
35180 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
35181 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
35182 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
35183
35184 while (framesProcessedOut < frameCountOut) {
35185 ma_uint64 frameCountInThisIteration;
35186 ma_uint64 frameCountOutThisIteration;
35187 const void* pRunningFramesIn = NULL;
35188 void* pRunningFramesOut = NULL;
35189 const void* pChannelsBufferIn;
35190 void* pResampleBufferOut;
35191
35192 if (pFramesIn != NULL) {
35193 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
35194 }
35195 if (pFramesOut != NULL) {
35196 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
35197 }
35198
35199 /* Run input data through the channel converter and output it to the temporary buffer. */
35200 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
35201
35202 if (pConverter->hasPreFormatConversion) {
35203 if (frameCountInThisIteration > tempBufferInCap) {
35204 frameCountInThisIteration = tempBufferInCap;
35205 }
35206
35207 if (pRunningFramesIn != NULL) {
35208 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
35209 pChannelsBufferIn = pTempBufferIn;
35210 } else {
35211 pChannelsBufferIn = NULL;
35212 }
35213 } else {
35214 pChannelsBufferIn = pRunningFramesIn;
35215 }
35216
35217 /*
35218 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
35219 in this case which means we should always have capacity, but I'm leaving it here just for safety for future maintenance.
35220 */
35221 if (frameCountInThisIteration > tempBufferMidCap) {
35222 frameCountInThisIteration = tempBufferMidCap;
35223 }
35224
35225 /*
35226 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
35227 input samples and will end up glitching.
35228 */
35229 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
35230 if (frameCountOutThisIteration > tempBufferMidCap) {
35231 frameCountOutThisIteration = tempBufferMidCap;
35232 }
35233
35234 if (pConverter->hasPostFormatConversion) {
35235 ma_uint64 requiredInputFrameCount;
35236
35237 if (frameCountOutThisIteration > tempBufferOutCap) {
35238 frameCountOutThisIteration = tempBufferOutCap;
35239 }
35240
35241 requiredInputFrameCount = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration);
35242 if (frameCountInThisIteration > requiredInputFrameCount) {
35243 frameCountInThisIteration = requiredInputFrameCount;
35244 }
35245 }
35246
35247 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);
35248 if (result != MA_SUCCESS) {
35249 return result;
35250 }
35251
35252
35253 /* At this point we have converted the channels to the output channel count which we now need to resample. */
35254 if (pConverter->hasPostFormatConversion) {
35255 pResampleBufferOut = pTempBufferOut;
35256 } else {
35257 pResampleBufferOut = pRunningFramesOut;
35258 }
35259
35260 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);
35261 if (result != MA_SUCCESS) {
35262 return result;
35263 }
35264
35265 /* Finally we can do the post format conversion. */
35266 if (pConverter->hasPostFormatConversion) {
35267 if (pRunningFramesOut != NULL) {
35268 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->config.formatOut, pResampleBufferOut, pConverter->resampler.config.format, frameCountOutThisIteration, pConverter->config.channelsOut, pConverter->config.ditherMode);
35269 }
35270 }
35271
35272 framesProcessedIn += frameCountInThisIteration;
35273 framesProcessedOut += frameCountOutThisIteration;
35274
35275 MA_ASSERT(framesProcessedIn <= frameCountIn);
35276 MA_ASSERT(framesProcessedOut <= frameCountOut);
35277
35278 if (frameCountOutThisIteration == 0) {
35279 break; /* Consumed all of our input data. */
35280 }
35281 }
35282
35283 if (pFrameCountIn != NULL) {
35284 *pFrameCountIn = framesProcessedIn;
35285 }
35286 if (pFrameCountOut != NULL) {
35287 *pFrameCountOut = framesProcessedOut;
35288 }
35289
35290 return MA_SUCCESS;
35291 }
35292
35293 MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
35294 {
35295 if (pConverter == NULL) {
35296 return MA_INVALID_ARGS;
35297 }
35298
35299 if (pConverter->isPassthrough) {
35300 return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
35301 }
35302
35303 /*
35304 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
35305 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
35306 that it has less work (resampling is the most expensive part of format conversion).
35307 */
35308 if (pConverter->config.channelsIn < pConverter->config.channelsOut) {
35309 /* Do resampling first, if necessary. */
35310 MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE);
35311
35312 if (pConverter->hasResampler) {
35313 /* Resampling first. */
35314 return ma_data_converter_process_pcm_frames__resampling_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
35315 } else {
35316 /* Resampling not required. */
35317 return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
35318 }
35319 } else {
35320 /* Do channel conversion first, if necessary. */
35321 if (pConverter->hasChannelConverter) {
35322 if (pConverter->hasResampler) {
35323 /* Channel routing first. */
35324 return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
35325 } else {
35326 /* Resampling not required. */
35327 return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
35328 }
35329 } else {
35330 /* Channel routing not required. */
35331 if (pConverter->hasResampler) {
35332 /* Resampling only. */
35333 return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
35334 } else {
35335 /* No channel routing nor resampling required. Just format conversion. */
35336 return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
35337 }
35338 }
35339 }
35340 }
35341
35342 MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
35343 {
35344 if (pConverter == NULL) {
35345 return MA_INVALID_ARGS;
35346 }
35347
35348 if (pConverter->hasResampler == MA_FALSE) {
35349 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
35350 }
35351
35352 return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut);
35353 }
35354
35355 MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut)
35356 {
35357 if (pConverter == NULL) {
35358 return MA_INVALID_ARGS;
35359 }
35360
35361 if (pConverter->hasResampler == MA_FALSE) {
35362 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
35363 }
35364
35365 return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut);
35366 }
35367
35368 MA_API ma_uint64 ma_data_converter_get_required_input_frame_count(ma_data_converter* pConverter, ma_uint64 outputFrameCount)
35369 {
35370 if (pConverter == NULL) {
35371 return 0;
35372 }
35373
35374 if (pConverter->hasResampler) {
35375 return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount);
35376 } else {
35377 return outputFrameCount; /* 1:1 */
35378 }
35379 }
35380
35381 MA_API ma_uint64 ma_data_converter_get_expected_output_frame_count(ma_data_converter* pConverter, ma_uint64 inputFrameCount)
35382 {
35383 if (pConverter == NULL) {
35384 return 0;
35385 }
35386
35387 if (pConverter->hasResampler) {
35388 return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount);
35389 } else {
35390 return inputFrameCount; /* 1:1 */
35391 }
35392 }
35393
35394 MA_API ma_uint64 ma_data_converter_get_input_latency(ma_data_converter* pConverter)
35395 {
35396 if (pConverter == NULL) {
35397 return 0;
35398 }
35399
35400 if (pConverter->hasResampler) {
35401 return ma_resampler_get_input_latency(&pConverter->resampler);
35402 }
35403
35404 return 0; /* No latency without a resampler. */
35405 }
35406
35407 MA_API ma_uint64 ma_data_converter_get_output_latency(ma_data_converter* pConverter)
35408 {
35409 if (pConverter == NULL) {
35410 return 0;
35411 }
35412
35413 if (pConverter->hasResampler) {
35414 return ma_resampler_get_output_latency(&pConverter->resampler);
35415 }
35416
35417 return 0; /* No latency without a resampler. */
35418 }
35419
35420
35421
35422 /**************************************************************************************************************************************************************
35423
35424 Format Conversion
35425
35426 **************************************************************************************************************************************************************/
35427
35428 static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x)
35429 {
35430 return (ma_int16)(x * 32767.0f);
35431 }
35432
35433 /* u8 */
35434 MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35435 {
35436 (void)ditherMode;
35437 ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
35438 }
35439
35440
35441 static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35442 {
35443 ma_int16* dst_s16 = (ma_int16*)dst;
35444 const ma_uint8* src_u8 = (const ma_uint8*)src;
35445
35446 ma_uint64 i;
35447 for (i = 0; i < count; i += 1) {
35448 ma_int16 x = src_u8[i];
35449 x = x - 128;
35450 x = x << 8;
35451 dst_s16[i] = x;
35452 }
35453
35454 (void)ditherMode;
35455 }
35456
35457 static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35458 {
35459 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
35460 }
35461
35462 #if defined(MA_SUPPORT_SSE2)
35463 static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35464 {
35465 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
35466 }
35467 #endif
35468 #if defined(MA_SUPPORT_AVX2)
35469 static MA_INLINE void ma_pcm_u8_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35470 {
35471 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
35472 }
35473 #endif
35474 #if defined(MA_SUPPORT_NEON)
35475 static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35476 {
35477 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
35478 }
35479 #endif
35480
35481 MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35482 {
35483 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35484 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
35485 #else
35486 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35487 if (ma_has_avx2()) {
35488 ma_pcm_u8_to_s16__avx2(dst, src, count, ditherMode);
35489 } else
35490 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35491 if (ma_has_sse2()) {
35492 ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode);
35493 } else
35494 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35495 if (ma_has_neon()) {
35496 ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode);
35497 } else
35498 #endif
35499 {
35500 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
35501 }
35502 #endif
35503 }
35504
35505
35506 static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35507 {
35508 ma_uint8* dst_s24 = (ma_uint8*)dst;
35509 const ma_uint8* src_u8 = (const ma_uint8*)src;
35510
35511 ma_uint64 i;
35512 for (i = 0; i < count; i += 1) {
35513 ma_int16 x = src_u8[i];
35514 x = x - 128;
35515
35516 dst_s24[i*3+0] = 0;
35517 dst_s24[i*3+1] = 0;
35518 dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
35519 }
35520
35521 (void)ditherMode;
35522 }
35523
35524 static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35525 {
35526 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
35527 }
35528
35529 #if defined(MA_SUPPORT_SSE2)
35530 static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35531 {
35532 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
35533 }
35534 #endif
35535 #if defined(MA_SUPPORT_AVX2)
35536 static MA_INLINE void ma_pcm_u8_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35537 {
35538 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
35539 }
35540 #endif
35541 #if defined(MA_SUPPORT_NEON)
35542 static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35543 {
35544 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
35545 }
35546 #endif
35547
35548 MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35549 {
35550 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35551 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
35552 #else
35553 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35554 if (ma_has_avx2()) {
35555 ma_pcm_u8_to_s24__avx2(dst, src, count, ditherMode);
35556 } else
35557 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35558 if (ma_has_sse2()) {
35559 ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode);
35560 } else
35561 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35562 if (ma_has_neon()) {
35563 ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode);
35564 } else
35565 #endif
35566 {
35567 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
35568 }
35569 #endif
35570 }
35571
35572
35573 static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35574 {
35575 ma_int32* dst_s32 = (ma_int32*)dst;
35576 const ma_uint8* src_u8 = (const ma_uint8*)src;
35577
35578 ma_uint64 i;
35579 for (i = 0; i < count; i += 1) {
35580 ma_int32 x = src_u8[i];
35581 x = x - 128;
35582 x = x << 24;
35583 dst_s32[i] = x;
35584 }
35585
35586 (void)ditherMode;
35587 }
35588
35589 static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35590 {
35591 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
35592 }
35593
35594 #if defined(MA_SUPPORT_SSE2)
35595 static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35596 {
35597 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
35598 }
35599 #endif
35600 #if defined(MA_SUPPORT_AVX2)
35601 static MA_INLINE void ma_pcm_u8_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35602 {
35603 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
35604 }
35605 #endif
35606 #if defined(MA_SUPPORT_NEON)
35607 static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35608 {
35609 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
35610 }
35611 #endif
35612
35613 MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35614 {
35615 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35616 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
35617 #else
35618 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35619 if (ma_has_avx2()) {
35620 ma_pcm_u8_to_s32__avx2(dst, src, count, ditherMode);
35621 } else
35622 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35623 if (ma_has_sse2()) {
35624 ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode);
35625 } else
35626 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35627 if (ma_has_neon()) {
35628 ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode);
35629 } else
35630 #endif
35631 {
35632 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
35633 }
35634 #endif
35635 }
35636
35637
35638 static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35639 {
35640 float* dst_f32 = (float*)dst;
35641 const ma_uint8* src_u8 = (const ma_uint8*)src;
35642
35643 ma_uint64 i;
35644 for (i = 0; i < count; i += 1) {
35645 float x = (float)src_u8[i];
35646 x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
35647 x = x - 1; /* 0..2 to -1..1 */
35648
35649 dst_f32[i] = x;
35650 }
35651
35652 (void)ditherMode;
35653 }
35654
35655 static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35656 {
35657 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
35658 }
35659
35660 #if defined(MA_SUPPORT_SSE2)
35661 static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35662 {
35663 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
35664 }
35665 #endif
35666 #if defined(MA_SUPPORT_AVX2)
35667 static MA_INLINE void ma_pcm_u8_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35668 {
35669 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
35670 }
35671 #endif
35672 #if defined(MA_SUPPORT_NEON)
35673 static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35674 {
35675 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
35676 }
35677 #endif
35678
35679 MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35680 {
35681 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35682 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
35683 #else
35684 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35685 if (ma_has_avx2()) {
35686 ma_pcm_u8_to_f32__avx2(dst, src, count, ditherMode);
35687 } else
35688 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35689 if (ma_has_sse2()) {
35690 ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode);
35691 } else
35692 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35693 if (ma_has_neon()) {
35694 ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode);
35695 } else
35696 #endif
35697 {
35698 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
35699 }
35700 #endif
35701 }
35702
35703
35704 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35705 static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35706 {
35707 ma_uint8* dst_u8 = (ma_uint8*)dst;
35708 const ma_uint8** src_u8 = (const ma_uint8**)src;
35709
35710 ma_uint64 iFrame;
35711 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35712 ma_uint32 iChannel;
35713 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35714 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
35715 }
35716 }
35717 }
35718 #else
35719 static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35720 {
35721 ma_uint8* dst_u8 = (ma_uint8*)dst;
35722 const ma_uint8** src_u8 = (const ma_uint8**)src;
35723
35724 if (channels == 1) {
35725 ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
35726 } else if (channels == 2) {
35727 ma_uint64 iFrame;
35728 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35729 dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
35730 dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
35731 }
35732 } else {
35733 ma_uint64 iFrame;
35734 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35735 ma_uint32 iChannel;
35736 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35737 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
35738 }
35739 }
35740 }
35741 }
35742 #endif
35743
35744 MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35745 {
35746 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35747 ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
35748 #else
35749 ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
35750 #endif
35751 }
35752
35753
35754 static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35755 {
35756 ma_uint8** dst_u8 = (ma_uint8**)dst;
35757 const ma_uint8* src_u8 = (const ma_uint8*)src;
35758
35759 ma_uint64 iFrame;
35760 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35761 ma_uint32 iChannel;
35762 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35763 dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
35764 }
35765 }
35766 }
35767
35768 static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35769 {
35770 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
35771 }
35772
35773 MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35774 {
35775 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35776 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
35777 #else
35778 ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
35779 #endif
35780 }
35781
35782
35783 /* s16 */
35784 static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35785 {
35786 ma_uint8* dst_u8 = (ma_uint8*)dst;
35787 const ma_int16* src_s16 = (const ma_int16*)src;
35788
35789 if (ditherMode == ma_dither_mode_none) {
35790 ma_uint64 i;
35791 for (i = 0; i < count; i += 1) {
35792 ma_int16 x = src_s16[i];
35793 x = x >> 8;
35794 x = x + 128;
35795 dst_u8[i] = (ma_uint8)x;
35796 }
35797 } else {
35798 ma_uint64 i;
35799 for (i = 0; i < count; i += 1) {
35800 ma_int16 x = src_s16[i];
35801
35802 /* Dither. Don't overflow. */
35803 ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
35804 if ((x + dither) <= 0x7FFF) {
35805 x = (ma_int16)(x + dither);
35806 } else {
35807 x = 0x7FFF;
35808 }
35809
35810 x = x >> 8;
35811 x = x + 128;
35812 dst_u8[i] = (ma_uint8)x;
35813 }
35814 }
35815 }
35816
35817 static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35818 {
35819 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
35820 }
35821
35822 #if defined(MA_SUPPORT_SSE2)
35823 static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35824 {
35825 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
35826 }
35827 #endif
35828 #if defined(MA_SUPPORT_AVX2)
35829 static MA_INLINE void ma_pcm_s16_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35830 {
35831 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
35832 }
35833 #endif
35834 #if defined(MA_SUPPORT_NEON)
35835 static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35836 {
35837 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
35838 }
35839 #endif
35840
35841 MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35842 {
35843 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35844 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
35845 #else
35846 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35847 if (ma_has_avx2()) {
35848 ma_pcm_s16_to_u8__avx2(dst, src, count, ditherMode);
35849 } else
35850 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35851 if (ma_has_sse2()) {
35852 ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode);
35853 } else
35854 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35855 if (ma_has_neon()) {
35856 ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode);
35857 } else
35858 #endif
35859 {
35860 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
35861 }
35862 #endif
35863 }
35864
35865
35866 MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35867 {
35868 (void)ditherMode;
35869 ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
35870 }
35871
35872
35873 static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35874 {
35875 ma_uint8* dst_s24 = (ma_uint8*)dst;
35876 const ma_int16* src_s16 = (const ma_int16*)src;
35877
35878 ma_uint64 i;
35879 for (i = 0; i < count; i += 1) {
35880 dst_s24[i*3+0] = 0;
35881 dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
35882 dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
35883 }
35884
35885 (void)ditherMode;
35886 }
35887
35888 static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35889 {
35890 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
35891 }
35892
35893 #if defined(MA_SUPPORT_SSE2)
35894 static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35895 {
35896 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
35897 }
35898 #endif
35899 #if defined(MA_SUPPORT_AVX2)
35900 static MA_INLINE void ma_pcm_s16_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35901 {
35902 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
35903 }
35904 #endif
35905 #if defined(MA_SUPPORT_NEON)
35906 static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35907 {
35908 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
35909 }
35910 #endif
35911
35912 MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35913 {
35914 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35915 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
35916 #else
35917 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35918 if (ma_has_avx2()) {
35919 ma_pcm_s16_to_s24__avx2(dst, src, count, ditherMode);
35920 } else
35921 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35922 if (ma_has_sse2()) {
35923 ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode);
35924 } else
35925 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35926 if (ma_has_neon()) {
35927 ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode);
35928 } else
35929 #endif
35930 {
35931 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
35932 }
35933 #endif
35934 }
35935
35936
35937 static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35938 {
35939 ma_int32* dst_s32 = (ma_int32*)dst;
35940 const ma_int16* src_s16 = (const ma_int16*)src;
35941
35942 ma_uint64 i;
35943 for (i = 0; i < count; i += 1) {
35944 dst_s32[i] = src_s16[i] << 16;
35945 }
35946
35947 (void)ditherMode;
35948 }
35949
35950 static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35951 {
35952 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
35953 }
35954
35955 #if defined(MA_SUPPORT_SSE2)
35956 static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35957 {
35958 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
35959 }
35960 #endif
35961 #if defined(MA_SUPPORT_AVX2)
35962 static MA_INLINE void ma_pcm_s16_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35963 {
35964 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
35965 }
35966 #endif
35967 #if defined(MA_SUPPORT_NEON)
35968 static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35969 {
35970 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
35971 }
35972 #endif
35973
35974 MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35975 {
35976 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
35977 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
35978 #else
35979 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35980 if (ma_has_avx2()) {
35981 ma_pcm_s16_to_s32__avx2(dst, src, count, ditherMode);
35982 } else
35983 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35984 if (ma_has_sse2()) {
35985 ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode);
35986 } else
35987 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35988 if (ma_has_neon()) {
35989 ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode);
35990 } else
35991 #endif
35992 {
35993 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
35994 }
35995 #endif
35996 }
35997
35998
35999 static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36000 {
36001 float* dst_f32 = (float*)dst;
36002 const ma_int16* src_s16 = (const ma_int16*)src;
36003
36004 ma_uint64 i;
36005 for (i = 0; i < count; i += 1) {
36006 float x = (float)src_s16[i];
36007
36008 #if 0
36009 /* The accurate way. */
36010 x = x + 32768.0f; /* -32768..32767 to 0..65535 */
36011 x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */
36012 x = x - 1; /* 0..2 to -1..1 */
36013 #else
36014 /* The fast way. */
36015 x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
36016 #endif
36017
36018 dst_f32[i] = x;
36019 }
36020
36021 (void)ditherMode;
36022 }
36023
36024 static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36025 {
36026 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
36027 }
36028
36029 #if defined(MA_SUPPORT_SSE2)
36030 static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36031 {
36032 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
36033 }
36034 #endif
36035 #if defined(MA_SUPPORT_AVX2)
36036 static MA_INLINE void ma_pcm_s16_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36037 {
36038 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
36039 }
36040 #endif
36041 #if defined(MA_SUPPORT_NEON)
36042 static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36043 {
36044 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
36045 }
36046 #endif
36047
36048 MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36049 {
36050 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36051 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
36052 #else
36053 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36054 if (ma_has_avx2()) {
36055 ma_pcm_s16_to_f32__avx2(dst, src, count, ditherMode);
36056 } else
36057 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36058 if (ma_has_sse2()) {
36059 ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode);
36060 } else
36061 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36062 if (ma_has_neon()) {
36063 ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode);
36064 } else
36065 #endif
36066 {
36067 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
36068 }
36069 #endif
36070 }
36071
36072
36073 static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36074 {
36075 ma_int16* dst_s16 = (ma_int16*)dst;
36076 const ma_int16** src_s16 = (const ma_int16**)src;
36077
36078 ma_uint64 iFrame;
36079 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36080 ma_uint32 iChannel;
36081 for (iChannel = 0; iChannel < channels; iChannel += 1) {
36082 dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
36083 }
36084 }
36085 }
36086
36087 static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36088 {
36089 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
36090 }
36091
36092 MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36093 {
36094 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36095 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
36096 #else
36097 ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
36098 #endif
36099 }
36100
36101
36102 static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36103 {
36104 ma_int16** dst_s16 = (ma_int16**)dst;
36105 const ma_int16* src_s16 = (const ma_int16*)src;
36106
36107 ma_uint64 iFrame;
36108 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36109 ma_uint32 iChannel;
36110 for (iChannel = 0; iChannel < channels; iChannel += 1) {
36111 dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
36112 }
36113 }
36114 }
36115
36116 static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36117 {
36118 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
36119 }
36120
36121 MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36122 {
36123 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36124 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
36125 #else
36126 ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
36127 #endif
36128 }
36129
36130
36131 /* s24 */
36132 static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36133 {
36134 ma_uint8* dst_u8 = (ma_uint8*)dst;
36135 const ma_uint8* src_s24 = (const ma_uint8*)src;
36136
36137 if (ditherMode == ma_dither_mode_none) {
36138 ma_uint64 i;
36139 for (i = 0; i < count; i += 1) {
36140 ma_int8 x = (ma_int8)src_s24[i*3 + 2] + 128;
36141 dst_u8[i] = (ma_uint8)x;
36142 }
36143 } else {
36144 ma_uint64 i;
36145 for (i = 0; i < count; i += 1) {
36146 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);
36147
36148 /* Dither. Don't overflow. */
36149 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
36150 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
36151 x = x + dither;
36152 } else {
36153 x = 0x7FFFFFFF;
36154 }
36155
36156 x = x >> 24;
36157 x = x + 128;
36158 dst_u8[i] = (ma_uint8)x;
36159 }
36160 }
36161 }
36162
36163 static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36164 {
36165 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
36166 }
36167
36168 #if defined(MA_SUPPORT_SSE2)
36169 static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36170 {
36171 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
36172 }
36173 #endif
36174 #if defined(MA_SUPPORT_AVX2)
36175 static MA_INLINE void ma_pcm_s24_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36176 {
36177 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
36178 }
36179 #endif
36180 #if defined(MA_SUPPORT_NEON)
36181 static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36182 {
36183 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
36184 }
36185 #endif
36186
36187 MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36188 {
36189 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36190 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
36191 #else
36192 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36193 if (ma_has_avx2()) {
36194 ma_pcm_s24_to_u8__avx2(dst, src, count, ditherMode);
36195 } else
36196 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36197 if (ma_has_sse2()) {
36198 ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode);
36199 } else
36200 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36201 if (ma_has_neon()) {
36202 ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode);
36203 } else
36204 #endif
36205 {
36206 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
36207 }
36208 #endif
36209 }
36210
36211
36212 static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36213 {
36214 ma_int16* dst_s16 = (ma_int16*)dst;
36215 const ma_uint8* src_s24 = (const ma_uint8*)src;
36216
36217 if (ditherMode == ma_dither_mode_none) {
36218 ma_uint64 i;
36219 for (i = 0; i < count; i += 1) {
36220 ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
36221 ma_uint16 dst_hi = ((ma_uint16)src_s24[i*3 + 2]) << 8;
36222 dst_s16[i] = (ma_int16)dst_lo | dst_hi;
36223 }
36224 } else {
36225 ma_uint64 i;
36226 for (i = 0; i < count; i += 1) {
36227 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);
36228
36229 /* Dither. Don't overflow. */
36230 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
36231 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
36232 x = x + dither;
36233 } else {
36234 x = 0x7FFFFFFF;
36235 }
36236
36237 x = x >> 16;
36238 dst_s16[i] = (ma_int16)x;
36239 }
36240 }
36241 }
36242
36243 static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36244 {
36245 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
36246 }
36247
36248 #if defined(MA_SUPPORT_SSE2)
36249 static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36250 {
36251 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
36252 }
36253 #endif
36254 #if defined(MA_SUPPORT_AVX2)
36255 static MA_INLINE void ma_pcm_s24_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36256 {
36257 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
36258 }
36259 #endif
36260 #if defined(MA_SUPPORT_NEON)
36261 static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36262 {
36263 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
36264 }
36265 #endif
36266
36267 MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36268 {
36269 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36270 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
36271 #else
36272 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36273 if (ma_has_avx2()) {
36274 ma_pcm_s24_to_s16__avx2(dst, src, count, ditherMode);
36275 } else
36276 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36277 if (ma_has_sse2()) {
36278 ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode);
36279 } else
36280 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36281 if (ma_has_neon()) {
36282 ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode);
36283 } else
36284 #endif
36285 {
36286 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
36287 }
36288 #endif
36289 }
36290
36291
36292 MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36293 {
36294 (void)ditherMode;
36295
36296 ma_copy_memory_64(dst, src, count * 3);
36297 }
36298
36299
36300 static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36301 {
36302 ma_int32* dst_s32 = (ma_int32*)dst;
36303 const ma_uint8* src_s24 = (const ma_uint8*)src;
36304
36305 ma_uint64 i;
36306 for (i = 0; i < count; i += 1) {
36307 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);
36308 }
36309
36310 (void)ditherMode;
36311 }
36312
36313 static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36314 {
36315 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
36316 }
36317
36318 #if defined(MA_SUPPORT_SSE2)
36319 static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36320 {
36321 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
36322 }
36323 #endif
36324 #if defined(MA_SUPPORT_AVX2)
36325 static MA_INLINE void ma_pcm_s24_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36326 {
36327 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
36328 }
36329 #endif
36330 #if defined(MA_SUPPORT_NEON)
36331 static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36332 {
36333 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
36334 }
36335 #endif
36336
36337 MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36338 {
36339 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36340 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
36341 #else
36342 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36343 if (ma_has_avx2()) {
36344 ma_pcm_s24_to_s32__avx2(dst, src, count, ditherMode);
36345 } else
36346 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36347 if (ma_has_sse2()) {
36348 ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode);
36349 } else
36350 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36351 if (ma_has_neon()) {
36352 ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode);
36353 } else
36354 #endif
36355 {
36356 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
36357 }
36358 #endif
36359 }
36360
36361
36362 static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36363 {
36364 float* dst_f32 = (float*)dst;
36365 const ma_uint8* src_s24 = (const ma_uint8*)src;
36366
36367 ma_uint64 i;
36368 for (i = 0; i < count; i += 1) {
36369 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);
36370
36371 #if 0
36372 /* The accurate way. */
36373 x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */
36374 x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */
36375 x = x - 1; /* 0..2 to -1..1 */
36376 #else
36377 /* The fast way. */
36378 x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */
36379 #endif
36380
36381 dst_f32[i] = x;
36382 }
36383
36384 (void)ditherMode;
36385 }
36386
36387 static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36388 {
36389 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
36390 }
36391
36392 #if defined(MA_SUPPORT_SSE2)
36393 static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36394 {
36395 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
36396 }
36397 #endif
36398 #if defined(MA_SUPPORT_AVX2)
36399 static MA_INLINE void ma_pcm_s24_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36400 {
36401 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
36402 }
36403 #endif
36404 #if defined(MA_SUPPORT_NEON)
36405 static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36406 {
36407 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
36408 }
36409 #endif
36410
36411 MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36412 {
36413 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36414 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
36415 #else
36416 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36417 if (ma_has_avx2()) {
36418 ma_pcm_s24_to_f32__avx2(dst, src, count, ditherMode);
36419 } else
36420 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36421 if (ma_has_sse2()) {
36422 ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode);
36423 } else
36424 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36425 if (ma_has_neon()) {
36426 ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode);
36427 } else
36428 #endif
36429 {
36430 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
36431 }
36432 #endif
36433 }
36434
36435
36436 static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36437 {
36438 ma_uint8* dst8 = (ma_uint8*)dst;
36439 const ma_uint8** src8 = (const ma_uint8**)src;
36440
36441 ma_uint64 iFrame;
36442 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36443 ma_uint32 iChannel;
36444 for (iChannel = 0; iChannel < channels; iChannel += 1) {
36445 dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
36446 dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
36447 dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
36448 }
36449 }
36450 }
36451
36452 static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36453 {
36454 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
36455 }
36456
36457 MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36458 {
36459 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36460 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
36461 #else
36462 ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
36463 #endif
36464 }
36465
36466
36467 static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36468 {
36469 ma_uint8** dst8 = (ma_uint8**)dst;
36470 const ma_uint8* src8 = (const ma_uint8*)src;
36471
36472 ma_uint32 iFrame;
36473 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36474 ma_uint32 iChannel;
36475 for (iChannel = 0; iChannel < channels; iChannel += 1) {
36476 dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
36477 dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
36478 dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
36479 }
36480 }
36481 }
36482
36483 static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36484 {
36485 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
36486 }
36487
36488 MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36489 {
36490 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36491 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
36492 #else
36493 ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
36494 #endif
36495 }
36496
36497
36498
36499 /* s32 */
36500 static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36501 {
36502 ma_uint8* dst_u8 = (ma_uint8*)dst;
36503 const ma_int32* src_s32 = (const ma_int32*)src;
36504
36505 if (ditherMode == ma_dither_mode_none) {
36506 ma_uint64 i;
36507 for (i = 0; i < count; i += 1) {
36508 ma_int32 x = src_s32[i];
36509 x = x >> 24;
36510 x = x + 128;
36511 dst_u8[i] = (ma_uint8)x;
36512 }
36513 } else {
36514 ma_uint64 i;
36515 for (i = 0; i < count; i += 1) {
36516 ma_int32 x = src_s32[i];
36517
36518 /* Dither. Don't overflow. */
36519 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
36520 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
36521 x = x + dither;
36522 } else {
36523 x = 0x7FFFFFFF;
36524 }
36525
36526 x = x >> 24;
36527 x = x + 128;
36528 dst_u8[i] = (ma_uint8)x;
36529 }
36530 }
36531 }
36532
36533 static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36534 {
36535 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
36536 }
36537
36538 #if defined(MA_SUPPORT_SSE2)
36539 static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36540 {
36541 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
36542 }
36543 #endif
36544 #if defined(MA_SUPPORT_AVX2)
36545 static MA_INLINE void ma_pcm_s32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36546 {
36547 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
36548 }
36549 #endif
36550 #if defined(MA_SUPPORT_NEON)
36551 static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36552 {
36553 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
36554 }
36555 #endif
36556
36557 MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36558 {
36559 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36560 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
36561 #else
36562 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36563 if (ma_has_avx2()) {
36564 ma_pcm_s32_to_u8__avx2(dst, src, count, ditherMode);
36565 } else
36566 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36567 if (ma_has_sse2()) {
36568 ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode);
36569 } else
36570 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36571 if (ma_has_neon()) {
36572 ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode);
36573 } else
36574 #endif
36575 {
36576 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
36577 }
36578 #endif
36579 }
36580
36581
36582 static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36583 {
36584 ma_int16* dst_s16 = (ma_int16*)dst;
36585 const ma_int32* src_s32 = (const ma_int32*)src;
36586
36587 if (ditherMode == ma_dither_mode_none) {
36588 ma_uint64 i;
36589 for (i = 0; i < count; i += 1) {
36590 ma_int32 x = src_s32[i];
36591 x = x >> 16;
36592 dst_s16[i] = (ma_int16)x;
36593 }
36594 } else {
36595 ma_uint64 i;
36596 for (i = 0; i < count; i += 1) {
36597 ma_int32 x = src_s32[i];
36598
36599 /* Dither. Don't overflow. */
36600 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
36601 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
36602 x = x + dither;
36603 } else {
36604 x = 0x7FFFFFFF;
36605 }
36606
36607 x = x >> 16;
36608 dst_s16[i] = (ma_int16)x;
36609 }
36610 }
36611 }
36612
36613 static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36614 {
36615 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
36616 }
36617
36618 #if defined(MA_SUPPORT_SSE2)
36619 static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36620 {
36621 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
36622 }
36623 #endif
36624 #if defined(MA_SUPPORT_AVX2)
36625 static MA_INLINE void ma_pcm_s32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36626 {
36627 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
36628 }
36629 #endif
36630 #if defined(MA_SUPPORT_NEON)
36631 static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36632 {
36633 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
36634 }
36635 #endif
36636
36637 MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36638 {
36639 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36640 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
36641 #else
36642 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36643 if (ma_has_avx2()) {
36644 ma_pcm_s32_to_s16__avx2(dst, src, count, ditherMode);
36645 } else
36646 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36647 if (ma_has_sse2()) {
36648 ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode);
36649 } else
36650 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36651 if (ma_has_neon()) {
36652 ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode);
36653 } else
36654 #endif
36655 {
36656 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
36657 }
36658 #endif
36659 }
36660
36661
36662 static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36663 {
36664 ma_uint8* dst_s24 = (ma_uint8*)dst;
36665 const ma_int32* src_s32 = (const ma_int32*)src;
36666
36667 ma_uint64 i;
36668 for (i = 0; i < count; i += 1) {
36669 ma_uint32 x = (ma_uint32)src_s32[i];
36670 dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
36671 dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
36672 dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
36673 }
36674
36675 (void)ditherMode; /* No dithering for s32 -> s24. */
36676 }
36677
36678 static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36679 {
36680 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
36681 }
36682
36683 #if defined(MA_SUPPORT_SSE2)
36684 static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36685 {
36686 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
36687 }
36688 #endif
36689 #if defined(MA_SUPPORT_AVX2)
36690 static MA_INLINE void ma_pcm_s32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36691 {
36692 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
36693 }
36694 #endif
36695 #if defined(MA_SUPPORT_NEON)
36696 static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36697 {
36698 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
36699 }
36700 #endif
36701
36702 MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36703 {
36704 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36705 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
36706 #else
36707 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36708 if (ma_has_avx2()) {
36709 ma_pcm_s32_to_s24__avx2(dst, src, count, ditherMode);
36710 } else
36711 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36712 if (ma_has_sse2()) {
36713 ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode);
36714 } else
36715 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36716 if (ma_has_neon()) {
36717 ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode);
36718 } else
36719 #endif
36720 {
36721 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
36722 }
36723 #endif
36724 }
36725
36726
36727 MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36728 {
36729 (void)ditherMode;
36730
36731 ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
36732 }
36733
36734
36735 static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36736 {
36737 float* dst_f32 = (float*)dst;
36738 const ma_int32* src_s32 = (const ma_int32*)src;
36739
36740 ma_uint64 i;
36741 for (i = 0; i < count; i += 1) {
36742 double x = src_s32[i];
36743
36744 #if 0
36745 x = x + 2147483648.0;
36746 x = x * 0.0000000004656612873077392578125;
36747 x = x - 1;
36748 #else
36749 x = x / 2147483648.0;
36750 #endif
36751
36752 dst_f32[i] = (float)x;
36753 }
36754
36755 (void)ditherMode; /* No dithering for s32 -> f32. */
36756 }
36757
36758 static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36759 {
36760 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
36761 }
36762
36763 #if defined(MA_SUPPORT_SSE2)
36764 static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36765 {
36766 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
36767 }
36768 #endif
36769 #if defined(MA_SUPPORT_AVX2)
36770 static MA_INLINE void ma_pcm_s32_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36771 {
36772 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
36773 }
36774 #endif
36775 #if defined(MA_SUPPORT_NEON)
36776 static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36777 {
36778 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
36779 }
36780 #endif
36781
36782 MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36783 {
36784 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36785 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
36786 #else
36787 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36788 if (ma_has_avx2()) {
36789 ma_pcm_s32_to_f32__avx2(dst, src, count, ditherMode);
36790 } else
36791 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36792 if (ma_has_sse2()) {
36793 ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode);
36794 } else
36795 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36796 if (ma_has_neon()) {
36797 ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode);
36798 } else
36799 #endif
36800 {
36801 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
36802 }
36803 #endif
36804 }
36805
36806
36807 static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36808 {
36809 ma_int32* dst_s32 = (ma_int32*)dst;
36810 const ma_int32** src_s32 = (const ma_int32**)src;
36811
36812 ma_uint64 iFrame;
36813 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36814 ma_uint32 iChannel;
36815 for (iChannel = 0; iChannel < channels; iChannel += 1) {
36816 dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
36817 }
36818 }
36819 }
36820
36821 static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36822 {
36823 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
36824 }
36825
36826 MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36827 {
36828 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36829 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
36830 #else
36831 ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
36832 #endif
36833 }
36834
36835
36836 static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36837 {
36838 ma_int32** dst_s32 = (ma_int32**)dst;
36839 const ma_int32* src_s32 = (const ma_int32*)src;
36840
36841 ma_uint64 iFrame;
36842 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36843 ma_uint32 iChannel;
36844 for (iChannel = 0; iChannel < channels; iChannel += 1) {
36845 dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
36846 }
36847 }
36848 }
36849
36850 static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36851 {
36852 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
36853 }
36854
36855 MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36856 {
36857 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36858 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
36859 #else
36860 ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
36861 #endif
36862 }
36863
36864
36865 /* f32 */
36866 static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36867 {
36868 ma_uint64 i;
36869
36870 ma_uint8* dst_u8 = (ma_uint8*)dst;
36871 const float* src_f32 = (const float*)src;
36872
36873 float ditherMin = 0;
36874 float ditherMax = 0;
36875 if (ditherMode != ma_dither_mode_none) {
36876 ditherMin = 1.0f / -128;
36877 ditherMax = 1.0f / 127;
36878 }
36879
36880 for (i = 0; i < count; i += 1) {
36881 float x = src_f32[i];
36882 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
36883 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
36884 x = x + 1; /* -1..1 to 0..2 */
36885 x = x * 127.5f; /* 0..2 to 0..255 */
36886
36887 dst_u8[i] = (ma_uint8)x;
36888 }
36889 }
36890
36891 static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36892 {
36893 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
36894 }
36895
36896 #if defined(MA_SUPPORT_SSE2)
36897 static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36898 {
36899 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
36900 }
36901 #endif
36902 #if defined(MA_SUPPORT_AVX2)
36903 static MA_INLINE void ma_pcm_f32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36904 {
36905 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
36906 }
36907 #endif
36908 #if defined(MA_SUPPORT_NEON)
36909 static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36910 {
36911 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
36912 }
36913 #endif
36914
36915 MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36916 {
36917 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36918 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
36919 #else
36920 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36921 if (ma_has_avx2()) {
36922 ma_pcm_f32_to_u8__avx2(dst, src, count, ditherMode);
36923 } else
36924 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36925 if (ma_has_sse2()) {
36926 ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode);
36927 } else
36928 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36929 if (ma_has_neon()) {
36930 ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode);
36931 } else
36932 #endif
36933 {
36934 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
36935 }
36936 #endif
36937 }
36938
36939 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
36940 static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36941 {
36942 ma_uint64 i;
36943
36944 ma_int16* dst_s16 = (ma_int16*)dst;
36945 const float* src_f32 = (const float*)src;
36946
36947 float ditherMin = 0;
36948 float ditherMax = 0;
36949 if (ditherMode != ma_dither_mode_none) {
36950 ditherMin = 1.0f / -32768;
36951 ditherMax = 1.0f / 32767;
36952 }
36953
36954 for (i = 0; i < count; i += 1) {
36955 float x = src_f32[i];
36956 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
36957 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
36958
36959 #if 0
36960 /* The accurate way. */
36961 x = x + 1; /* -1..1 to 0..2 */
36962 x = x * 32767.5f; /* 0..2 to 0..65535 */
36963 x = x - 32768.0f; /* 0...65535 to -32768..32767 */
36964 #else
36965 /* The fast way. */
36966 x = x * 32767.0f; /* -1..1 to -32767..32767 */
36967 #endif
36968
36969 dst_s16[i] = (ma_int16)x;
36970 }
36971 }
36972 #else
36973 static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36974 {
36975 ma_uint64 i;
36976 ma_uint64 i4;
36977 ma_uint64 count4;
36978
36979 ma_int16* dst_s16 = (ma_int16*)dst;
36980 const float* src_f32 = (const float*)src;
36981
36982 float ditherMin = 0;
36983 float ditherMax = 0;
36984 if (ditherMode != ma_dither_mode_none) {
36985 ditherMin = 1.0f / -32768;
36986 ditherMax = 1.0f / 32767;
36987 }
36988
36989 /* Unrolled. */
36990 i = 0;
36991 count4 = count >> 2;
36992 for (i4 = 0; i4 < count4; i4 += 1) {
36993 float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
36994 float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
36995 float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
36996 float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
36997
36998 float x0 = src_f32[i+0];
36999 float x1 = src_f32[i+1];
37000 float x2 = src_f32[i+2];
37001 float x3 = src_f32[i+3];
37002
37003 x0 = x0 + d0;
37004 x1 = x1 + d1;
37005 x2 = x2 + d2;
37006 x3 = x3 + d3;
37007
37008 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
37009 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
37010 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
37011 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
37012
37013 x0 = x0 * 32767.0f;
37014 x1 = x1 * 32767.0f;
37015 x2 = x2 * 32767.0f;
37016 x3 = x3 * 32767.0f;
37017
37018 dst_s16[i+0] = (ma_int16)x0;
37019 dst_s16[i+1] = (ma_int16)x1;
37020 dst_s16[i+2] = (ma_int16)x2;
37021 dst_s16[i+3] = (ma_int16)x3;
37022
37023 i += 4;
37024 }
37025
37026 /* Leftover. */
37027 for (; i < count; i += 1) {
37028 float x = src_f32[i];
37029 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
37030 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
37031 x = x * 32767.0f; /* -1..1 to -32767..32767 */
37032
37033 dst_s16[i] = (ma_int16)x;
37034 }
37035 }
37036
37037 #if defined(MA_SUPPORT_SSE2)
37038 static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37039 {
37040 ma_uint64 i;
37041 ma_uint64 i8;
37042 ma_uint64 count8;
37043 ma_int16* dst_s16;
37044 const float* src_f32;
37045 float ditherMin;
37046 float ditherMax;
37047
37048 /* Both the input and output buffers need to be aligned to 16 bytes. */
37049 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
37050 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
37051 return;
37052 }
37053
37054 dst_s16 = (ma_int16*)dst;
37055 src_f32 = (const float*)src;
37056
37057 ditherMin = 0;
37058 ditherMax = 0;
37059 if (ditherMode != ma_dither_mode_none) {
37060 ditherMin = 1.0f / -32768;
37061 ditherMax = 1.0f / 32767;
37062 }
37063
37064 i = 0;
37065
37066 /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
37067 count8 = count >> 3;
37068 for (i8 = 0; i8 < count8; i8 += 1) {
37069 __m128 d0;
37070 __m128 d1;
37071 __m128 x0;
37072 __m128 x1;
37073
37074 if (ditherMode == ma_dither_mode_none) {
37075 d0 = _mm_set1_ps(0);
37076 d1 = _mm_set1_ps(0);
37077 } else if (ditherMode == ma_dither_mode_rectangle) {
37078 d0 = _mm_set_ps(
37079 ma_dither_f32_rectangle(ditherMin, ditherMax),
37080 ma_dither_f32_rectangle(ditherMin, ditherMax),
37081 ma_dither_f32_rectangle(ditherMin, ditherMax),
37082 ma_dither_f32_rectangle(ditherMin, ditherMax)
37083 );
37084 d1 = _mm_set_ps(
37085 ma_dither_f32_rectangle(ditherMin, ditherMax),
37086 ma_dither_f32_rectangle(ditherMin, ditherMax),
37087 ma_dither_f32_rectangle(ditherMin, ditherMax),
37088 ma_dither_f32_rectangle(ditherMin, ditherMax)
37089 );
37090 } else {
37091 d0 = _mm_set_ps(
37092 ma_dither_f32_triangle(ditherMin, ditherMax),
37093 ma_dither_f32_triangle(ditherMin, ditherMax),
37094 ma_dither_f32_triangle(ditherMin, ditherMax),
37095 ma_dither_f32_triangle(ditherMin, ditherMax)
37096 );
37097 d1 = _mm_set_ps(
37098 ma_dither_f32_triangle(ditherMin, ditherMax),
37099 ma_dither_f32_triangle(ditherMin, ditherMax),
37100 ma_dither_f32_triangle(ditherMin, ditherMax),
37101 ma_dither_f32_triangle(ditherMin, ditherMax)
37102 );
37103 }
37104
37105 x0 = *((__m128*)(src_f32 + i) + 0);
37106 x1 = *((__m128*)(src_f32 + i) + 1);
37107
37108 x0 = _mm_add_ps(x0, d0);
37109 x1 = _mm_add_ps(x1, d1);
37110
37111 x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
37112 x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
37113
37114 _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
37115
37116 i += 8;
37117 }
37118
37119
37120 /* Leftover. */
37121 for (; i < count; i += 1) {
37122 float x = src_f32[i];
37123 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
37124 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
37125 x = x * 32767.0f; /* -1..1 to -32767..32767 */
37126
37127 dst_s16[i] = (ma_int16)x;
37128 }
37129 }
37130 #endif /* SSE2 */
37131
37132 #if defined(MA_SUPPORT_AVX2)
37133 static MA_INLINE void ma_pcm_f32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37134 {
37135 ma_uint64 i;
37136 ma_uint64 i16;
37137 ma_uint64 count16;
37138 ma_int16* dst_s16;
37139 const float* src_f32;
37140 float ditherMin;
37141 float ditherMax;
37142
37143 /* Both the input and output buffers need to be aligned to 32 bytes. */
37144 if ((((ma_uintptr)dst & 31) != 0) || (((ma_uintptr)src & 31) != 0)) {
37145 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
37146 return;
37147 }
37148
37149 dst_s16 = (ma_int16*)dst;
37150 src_f32 = (const float*)src;
37151
37152 ditherMin = 0;
37153 ditherMax = 0;
37154 if (ditherMode != ma_dither_mode_none) {
37155 ditherMin = 1.0f / -32768;
37156 ditherMax = 1.0f / 32767;
37157 }
37158
37159 i = 0;
37160
37161 /* AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. */
37162 count16 = count >> 4;
37163 for (i16 = 0; i16 < count16; i16 += 1) {
37164 __m256 d0;
37165 __m256 d1;
37166 __m256 x0;
37167 __m256 x1;
37168 __m256i i0;
37169 __m256i i1;
37170 __m256i p0;
37171 __m256i p1;
37172 __m256i r;
37173
37174 if (ditherMode == ma_dither_mode_none) {
37175 d0 = _mm256_set1_ps(0);
37176 d1 = _mm256_set1_ps(0);
37177 } else if (ditherMode == ma_dither_mode_rectangle) {
37178 d0 = _mm256_set_ps(
37179 ma_dither_f32_rectangle(ditherMin, ditherMax),
37180 ma_dither_f32_rectangle(ditherMin, ditherMax),
37181 ma_dither_f32_rectangle(ditherMin, ditherMax),
37182 ma_dither_f32_rectangle(ditherMin, ditherMax),
37183 ma_dither_f32_rectangle(ditherMin, ditherMax),
37184 ma_dither_f32_rectangle(ditherMin, ditherMax),
37185 ma_dither_f32_rectangle(ditherMin, ditherMax),
37186 ma_dither_f32_rectangle(ditherMin, ditherMax)
37187 );
37188 d1 = _mm256_set_ps(
37189 ma_dither_f32_rectangle(ditherMin, ditherMax),
37190 ma_dither_f32_rectangle(ditherMin, ditherMax),
37191 ma_dither_f32_rectangle(ditherMin, ditherMax),
37192 ma_dither_f32_rectangle(ditherMin, ditherMax),
37193 ma_dither_f32_rectangle(ditherMin, ditherMax),
37194 ma_dither_f32_rectangle(ditherMin, ditherMax),
37195 ma_dither_f32_rectangle(ditherMin, ditherMax),
37196 ma_dither_f32_rectangle(ditherMin, ditherMax)
37197 );
37198 } else {
37199 d0 = _mm256_set_ps(
37200 ma_dither_f32_triangle(ditherMin, ditherMax),
37201 ma_dither_f32_triangle(ditherMin, ditherMax),
37202 ma_dither_f32_triangle(ditherMin, ditherMax),
37203 ma_dither_f32_triangle(ditherMin, ditherMax),
37204 ma_dither_f32_triangle(ditherMin, ditherMax),
37205 ma_dither_f32_triangle(ditherMin, ditherMax),
37206 ma_dither_f32_triangle(ditherMin, ditherMax),
37207 ma_dither_f32_triangle(ditherMin, ditherMax)
37208 );
37209 d1 = _mm256_set_ps(
37210 ma_dither_f32_triangle(ditherMin, ditherMax),
37211 ma_dither_f32_triangle(ditherMin, ditherMax),
37212 ma_dither_f32_triangle(ditherMin, ditherMax),
37213 ma_dither_f32_triangle(ditherMin, ditherMax),
37214 ma_dither_f32_triangle(ditherMin, ditherMax),
37215 ma_dither_f32_triangle(ditherMin, ditherMax),
37216 ma_dither_f32_triangle(ditherMin, ditherMax),
37217 ma_dither_f32_triangle(ditherMin, ditherMax)
37218 );
37219 }
37220
37221 x0 = *((__m256*)(src_f32 + i) + 0);
37222 x1 = *((__m256*)(src_f32 + i) + 1);
37223
37224 x0 = _mm256_add_ps(x0, d0);
37225 x1 = _mm256_add_ps(x1, d1);
37226
37227 x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f));
37228 x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f));
37229
37230 /* Computing the final result is a little more complicated for AVX2 than SSE2. */
37231 i0 = _mm256_cvttps_epi32(x0);
37232 i1 = _mm256_cvttps_epi32(x1);
37233 p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32);
37234 p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48);
37235 r = _mm256_packs_epi32(p0, p1);
37236
37237 _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r);
37238
37239 i += 16;
37240 }
37241
37242
37243 /* Leftover. */
37244 for (; i < count; i += 1) {
37245 float x = src_f32[i];
37246 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
37247 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
37248 x = x * 32767.0f; /* -1..1 to -32767..32767 */
37249
37250 dst_s16[i] = (ma_int16)x;
37251 }
37252 }
37253 #endif /* AVX2 */
37254
37255 #if defined(MA_SUPPORT_NEON)
37256 static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37257 {
37258 ma_uint64 i;
37259 ma_uint64 i8;
37260 ma_uint64 count8;
37261 ma_int16* dst_s16;
37262 const float* src_f32;
37263 float ditherMin;
37264 float ditherMax;
37265
37266 if (!ma_has_neon()) {
37267 return ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
37268 }
37269
37270 /* Both the input and output buffers need to be aligned to 16 bytes. */
37271 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
37272 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
37273 return;
37274 }
37275
37276 dst_s16 = (ma_int16*)dst;
37277 src_f32 = (const float*)src;
37278
37279 ditherMin = 0;
37280 ditherMax = 0;
37281 if (ditherMode != ma_dither_mode_none) {
37282 ditherMin = 1.0f / -32768;
37283 ditherMax = 1.0f / 32767;
37284 }
37285
37286 i = 0;
37287
37288 /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
37289 count8 = count >> 3;
37290 for (i8 = 0; i8 < count8; i8 += 1) {
37291 float32x4_t d0;
37292 float32x4_t d1;
37293 float32x4_t x0;
37294 float32x4_t x1;
37295 int32x4_t i0;
37296 int32x4_t i1;
37297
37298 if (ditherMode == ma_dither_mode_none) {
37299 d0 = vmovq_n_f32(0);
37300 d1 = vmovq_n_f32(0);
37301 } else if (ditherMode == ma_dither_mode_rectangle) {
37302 float d0v[4];
37303 d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
37304 d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
37305 d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
37306 d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
37307 d0 = vld1q_f32(d0v);
37308
37309 float d1v[4];
37310 d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
37311 d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
37312 d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
37313 d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
37314 d1 = vld1q_f32(d1v);
37315 } else {
37316 float d0v[4];
37317 d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
37318 d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
37319 d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
37320 d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
37321 d0 = vld1q_f32(d0v);
37322
37323 float d1v[4];
37324 d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
37325 d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
37326 d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
37327 d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
37328 d1 = vld1q_f32(d1v);
37329 }
37330
37331 x0 = *((float32x4_t*)(src_f32 + i) + 0);
37332 x1 = *((float32x4_t*)(src_f32 + i) + 1);
37333
37334 x0 = vaddq_f32(x0, d0);
37335 x1 = vaddq_f32(x1, d1);
37336
37337 x0 = vmulq_n_f32(x0, 32767.0f);
37338 x1 = vmulq_n_f32(x1, 32767.0f);
37339
37340 i0 = vcvtq_s32_f32(x0);
37341 i1 = vcvtq_s32_f32(x1);
37342 *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
37343
37344 i += 8;
37345 }
37346
37347
37348 /* Leftover. */
37349 for (; i < count; i += 1) {
37350 float x = src_f32[i];
37351 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
37352 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
37353 x = x * 32767.0f; /* -1..1 to -32767..32767 */
37354
37355 dst_s16[i] = (ma_int16)x;
37356 }
37357 }
37358 #endif /* Neon */
37359 #endif /* MA_USE_REFERENCE_CONVERSION_APIS */
37360
37361 MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37362 {
37363 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
37364 ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
37365 #else
37366 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
37367 if (ma_has_avx2()) {
37368 ma_pcm_f32_to_s16__avx2(dst, src, count, ditherMode);
37369 } else
37370 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
37371 if (ma_has_sse2()) {
37372 ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode);
37373 } else
37374 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
37375 if (ma_has_neon()) {
37376 ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode);
37377 } else
37378 #endif
37379 {
37380 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
37381 }
37382 #endif
37383 }
37384
37385
37386 static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37387 {
37388 ma_uint8* dst_s24 = (ma_uint8*)dst;
37389 const float* src_f32 = (const float*)src;
37390
37391 ma_uint64 i;
37392 for (i = 0; i < count; i += 1) {
37393 ma_int32 r;
37394 float x = src_f32[i];
37395 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
37396
37397 #if 0
37398 /* The accurate way. */
37399 x = x + 1; /* -1..1 to 0..2 */
37400 x = x * 8388607.5f; /* 0..2 to 0..16777215 */
37401 x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */
37402 #else
37403 /* The fast way. */
37404 x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */
37405 #endif
37406
37407 r = (ma_int32)x;
37408 dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
37409 dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
37410 dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
37411 }
37412
37413 (void)ditherMode; /* No dithering for f32 -> s24. */
37414 }
37415
37416 static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37417 {
37418 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
37419 }
37420
37421 #if defined(MA_SUPPORT_SSE2)
37422 static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37423 {
37424 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
37425 }
37426 #endif
37427 #if defined(MA_SUPPORT_AVX2)
37428 static MA_INLINE void ma_pcm_f32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37429 {
37430 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
37431 }
37432 #endif
37433 #if defined(MA_SUPPORT_NEON)
37434 static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37435 {
37436 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
37437 }
37438 #endif
37439
37440 MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37441 {
37442 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
37443 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
37444 #else
37445 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
37446 if (ma_has_avx2()) {
37447 ma_pcm_f32_to_s24__avx2(dst, src, count, ditherMode);
37448 } else
37449 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
37450 if (ma_has_sse2()) {
37451 ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode);
37452 } else
37453 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
37454 if (ma_has_neon()) {
37455 ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode);
37456 } else
37457 #endif
37458 {
37459 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
37460 }
37461 #endif
37462 }
37463
37464
37465 static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37466 {
37467 ma_int32* dst_s32 = (ma_int32*)dst;
37468 const float* src_f32 = (const float*)src;
37469
37470 ma_uint32 i;
37471 for (i = 0; i < count; i += 1) {
37472 double x = src_f32[i];
37473 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
37474
37475 #if 0
37476 /* The accurate way. */
37477 x = x + 1; /* -1..1 to 0..2 */
37478 x = x * 2147483647.5; /* 0..2 to 0..4294967295 */
37479 x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */
37480 #else
37481 /* The fast way. */
37482 x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */
37483 #endif
37484
37485 dst_s32[i] = (ma_int32)x;
37486 }
37487
37488 (void)ditherMode; /* No dithering for f32 -> s32. */
37489 }
37490
37491 static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37492 {
37493 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
37494 }
37495
37496 #if defined(MA_SUPPORT_SSE2)
37497 static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37498 {
37499 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
37500 }
37501 #endif
37502 #if defined(MA_SUPPORT_AVX2)
37503 static MA_INLINE void ma_pcm_f32_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37504 {
37505 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
37506 }
37507 #endif
37508 #if defined(MA_SUPPORT_NEON)
37509 static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37510 {
37511 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
37512 }
37513 #endif
37514
37515 MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37516 {
37517 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
37518 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
37519 #else
37520 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
37521 if (ma_has_avx2()) {
37522 ma_pcm_f32_to_s32__avx2(dst, src, count, ditherMode);
37523 } else
37524 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
37525 if (ma_has_sse2()) {
37526 ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode);
37527 } else
37528 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
37529 if (ma_has_neon()) {
37530 ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode);
37531 } else
37532 #endif
37533 {
37534 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
37535 }
37536 #endif
37537 }
37538
37539
37540 MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
37541 {
37542 (void)ditherMode;
37543
37544 ma_copy_memory_64(dst, src, count * sizeof(float));
37545 }
37546
37547
37548 static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
37549 {
37550 float* dst_f32 = (float*)dst;
37551 const float** src_f32 = (const float**)src;
37552
37553 ma_uint64 iFrame;
37554 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
37555 ma_uint32 iChannel;
37556 for (iChannel = 0; iChannel < channels; iChannel += 1) {
37557 dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
37558 }
37559 }
37560 }
37561
37562 static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
37563 {
37564 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
37565 }
37566
37567 MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
37568 {
37569 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
37570 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
37571 #else
37572 ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
37573 #endif
37574 }
37575
37576
37577 static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
37578 {
37579 float** dst_f32 = (float**)dst;
37580 const float* src_f32 = (const float*)src;
37581
37582 ma_uint64 iFrame;
37583 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
37584 ma_uint32 iChannel;
37585 for (iChannel = 0; iChannel < channels; iChannel += 1) {
37586 dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
37587 }
37588 }
37589 }
37590
37591 static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
37592 {
37593 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
37594 }
37595
37596 MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
37597 {
37598 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
37599 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
37600 #else
37601 ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
37602 #endif
37603 }
37604
37605
37606 MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
37607 {
37608 if (formatOut == formatIn) {
37609 ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
37610 return;
37611 }
37612
37613 switch (formatIn)
37614 {
37615 case ma_format_u8:
37616 {
37617 switch (formatOut)
37618 {
37619 case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
37620 case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
37621 case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
37622 case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
37623 default: break;
37624 }
37625 } break;
37626
37627 case ma_format_s16:
37628 {
37629 switch (formatOut)
37630 {
37631 case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
37632 case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
37633 case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
37634 case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
37635 default: break;
37636 }
37637 } break;
37638
37639 case ma_format_s24:
37640 {
37641 switch (formatOut)
37642 {
37643 case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
37644 case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
37645 case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
37646 case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
37647 default: break;
37648 }
37649 } break;
37650
37651 case ma_format_s32:
37652 {
37653 switch (formatOut)
37654 {
37655 case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
37656 case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
37657 case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
37658 case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
37659 default: break;
37660 }
37661 } break;
37662
37663 case ma_format_f32:
37664 {
37665 switch (formatOut)
37666 {
37667 case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
37668 case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
37669 case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
37670 case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
37671 default: break;
37672 }
37673 } break;
37674
37675 default: break;
37676 }
37677 }
37678
37679 MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode)
37680 {
37681 ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode);
37682 }
37683
37684 MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
37685 {
37686 if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
37687 return; /* Invalid args. */
37688 }
37689
37690 /* For efficiency we do this per format. */
37691 switch (format) {
37692 case ma_format_s16:
37693 {
37694 const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
37695 ma_uint64 iPCMFrame;
37696 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
37697 ma_uint32 iChannel;
37698 for (iChannel = 0; iChannel < channels; ++iChannel) {
37699 ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
37700 pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
37701 }
37702 }
37703 } break;
37704
37705 case ma_format_f32:
37706 {
37707 const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
37708 ma_uint64 iPCMFrame;
37709 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
37710 ma_uint32 iChannel;
37711 for (iChannel = 0; iChannel < channels; ++iChannel) {
37712 float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
37713 pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
37714 }
37715 }
37716 } break;
37717
37718 default:
37719 {
37720 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
37721 ma_uint64 iPCMFrame;
37722 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
37723 ma_uint32 iChannel;
37724 for (iChannel = 0; iChannel < channels; ++iChannel) {
37725 void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
37726 const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
37727 memcpy(pDst, pSrc, sampleSizeInBytes);
37728 }
37729 }
37730 } break;
37731 }
37732 }
37733
37734 MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
37735 {
37736 switch (format)
37737 {
37738 case ma_format_s16:
37739 {
37740 ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
37741 ma_uint64 iPCMFrame;
37742 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
37743 ma_uint32 iChannel;
37744 for (iChannel = 0; iChannel < channels; ++iChannel) {
37745 const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
37746 pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
37747 }
37748 }
37749 } break;
37750
37751 case ma_format_f32:
37752 {
37753 float* pDstF32 = (float*)pInterleavedPCMFrames;
37754 ma_uint64 iPCMFrame;
37755 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
37756 ma_uint32 iChannel;
37757 for (iChannel = 0; iChannel < channels; ++iChannel) {
37758 const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
37759 pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
37760 }
37761 }
37762 } break;
37763
37764 default:
37765 {
37766 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
37767 ma_uint64 iPCMFrame;
37768 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
37769 ma_uint32 iChannel;
37770 for (iChannel = 0; iChannel < channels; ++iChannel) {
37771 void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
37772 const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
37773 memcpy(pDst, pSrc, sampleSizeInBytes);
37774 }
37775 }
37776 } break;
37777 }
37778 }
37779
37780
37781
37782 /**************************************************************************************************************************************************************
37783
37784 Channel Maps
37785
37786 **************************************************************************************************************************************************************/
37787 static void ma_get_standard_channel_map_microsoft(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
37788 {
37789 /* Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */
37790 switch (channels)
37791 {
37792 case 1:
37793 {
37794 channelMap[0] = MA_CHANNEL_MONO;
37795 } break;
37796
37797 case 2:
37798 {
37799 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37800 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37801 } break;
37802
37803 case 3: /* Not defined, but best guess. */
37804 {
37805 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37806 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37807 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
37808 } break;
37809
37810 case 4:
37811 {
37812 #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
37813 /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
37814 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37815 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37816 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
37817 channelMap[3] = MA_CHANNEL_BACK_CENTER;
37818 #else
37819 /* Quad. */
37820 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37821 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37822 channelMap[2] = MA_CHANNEL_BACK_LEFT;
37823 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
37824 #endif
37825 } break;
37826
37827 case 5: /* Not defined, but best guess. */
37828 {
37829 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37830 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37831 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
37832 channelMap[3] = MA_CHANNEL_BACK_LEFT;
37833 channelMap[4] = MA_CHANNEL_BACK_RIGHT;
37834 } break;
37835
37836 case 6:
37837 {
37838 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37839 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37840 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
37841 channelMap[3] = MA_CHANNEL_LFE;
37842 channelMap[4] = MA_CHANNEL_SIDE_LEFT;
37843 channelMap[5] = MA_CHANNEL_SIDE_RIGHT;
37844 } break;
37845
37846 case 7: /* Not defined, but best guess. */
37847 {
37848 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37849 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37850 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
37851 channelMap[3] = MA_CHANNEL_LFE;
37852 channelMap[4] = MA_CHANNEL_BACK_CENTER;
37853 channelMap[5] = MA_CHANNEL_SIDE_LEFT;
37854 channelMap[6] = MA_CHANNEL_SIDE_RIGHT;
37855 } break;
37856
37857 case 8:
37858 default:
37859 {
37860 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37861 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37862 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
37863 channelMap[3] = MA_CHANNEL_LFE;
37864 channelMap[4] = MA_CHANNEL_BACK_LEFT;
37865 channelMap[5] = MA_CHANNEL_BACK_RIGHT;
37866 channelMap[6] = MA_CHANNEL_SIDE_LEFT;
37867 channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
37868 } break;
37869 }
37870
37871 /* Remainder. */
37872 if (channels > 8) {
37873 ma_uint32 iChannel;
37874 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
37875 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
37876 }
37877 }
37878 }
37879
37880 static void ma_get_standard_channel_map_alsa(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
37881 {
37882 switch (channels)
37883 {
37884 case 1:
37885 {
37886 channelMap[0] = MA_CHANNEL_MONO;
37887 } break;
37888
37889 case 2:
37890 {
37891 channelMap[0] = MA_CHANNEL_LEFT;
37892 channelMap[1] = MA_CHANNEL_RIGHT;
37893 } break;
37894
37895 case 3:
37896 {
37897 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37898 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37899 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
37900 } break;
37901
37902 case 4:
37903 {
37904 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37905 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37906 channelMap[2] = MA_CHANNEL_BACK_LEFT;
37907 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
37908 } break;
37909
37910 case 5:
37911 {
37912 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37913 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37914 channelMap[2] = MA_CHANNEL_BACK_LEFT;
37915 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
37916 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
37917 } break;
37918
37919 case 6:
37920 {
37921 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37922 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37923 channelMap[2] = MA_CHANNEL_BACK_LEFT;
37924 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
37925 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
37926 channelMap[5] = MA_CHANNEL_LFE;
37927 } break;
37928
37929 case 7:
37930 {
37931 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37932 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37933 channelMap[2] = MA_CHANNEL_BACK_LEFT;
37934 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
37935 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
37936 channelMap[5] = MA_CHANNEL_LFE;
37937 channelMap[6] = MA_CHANNEL_BACK_CENTER;
37938 } break;
37939
37940 case 8:
37941 default:
37942 {
37943 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37944 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37945 channelMap[2] = MA_CHANNEL_BACK_LEFT;
37946 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
37947 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
37948 channelMap[5] = MA_CHANNEL_LFE;
37949 channelMap[6] = MA_CHANNEL_SIDE_LEFT;
37950 channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
37951 } break;
37952 }
37953
37954 /* Remainder. */
37955 if (channels > 8) {
37956 ma_uint32 iChannel;
37957 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
37958 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
37959 }
37960 }
37961 }
37962
37963 static void ma_get_standard_channel_map_rfc3551(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
37964 {
37965 switch (channels)
37966 {
37967 case 1:
37968 {
37969 channelMap[0] = MA_CHANNEL_MONO;
37970 } break;
37971
37972 case 2:
37973 {
37974 channelMap[0] = MA_CHANNEL_LEFT;
37975 channelMap[1] = MA_CHANNEL_RIGHT;
37976 } break;
37977
37978 case 3:
37979 {
37980 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37981 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37982 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
37983 } break;
37984
37985 case 4:
37986 {
37987 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37988 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
37989 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
37990 channelMap[3] = MA_CHANNEL_BACK_CENTER;
37991 } break;
37992
37993 case 5:
37994 {
37995 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
37996 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
37997 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
37998 channelMap[3] = MA_CHANNEL_BACK_LEFT;
37999 channelMap[4] = MA_CHANNEL_BACK_RIGHT;
38000 } break;
38001
38002 case 6:
38003 {
38004 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38005 channelMap[1] = MA_CHANNEL_SIDE_LEFT;
38006 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
38007 channelMap[3] = MA_CHANNEL_FRONT_RIGHT;
38008 channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
38009 channelMap[5] = MA_CHANNEL_BACK_CENTER;
38010 } break;
38011 }
38012
38013 /* Remainder. */
38014 if (channels > 8) {
38015 ma_uint32 iChannel;
38016 for (iChannel = 6; iChannel < MA_MAX_CHANNELS; ++iChannel) {
38017 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
38018 }
38019 }
38020 }
38021
38022 static void ma_get_standard_channel_map_flac(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
38023 {
38024 switch (channels)
38025 {
38026 case 1:
38027 {
38028 channelMap[0] = MA_CHANNEL_MONO;
38029 } break;
38030
38031 case 2:
38032 {
38033 channelMap[0] = MA_CHANNEL_LEFT;
38034 channelMap[1] = MA_CHANNEL_RIGHT;
38035 } break;
38036
38037 case 3:
38038 {
38039 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38040 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38041 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
38042 } break;
38043
38044 case 4:
38045 {
38046 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38047 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38048 channelMap[2] = MA_CHANNEL_BACK_LEFT;
38049 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
38050 } break;
38051
38052 case 5:
38053 {
38054 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38055 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38056 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
38057 channelMap[3] = MA_CHANNEL_BACK_LEFT;
38058 channelMap[4] = MA_CHANNEL_BACK_RIGHT;
38059 } break;
38060
38061 case 6:
38062 {
38063 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38064 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38065 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
38066 channelMap[3] = MA_CHANNEL_LFE;
38067 channelMap[4] = MA_CHANNEL_BACK_LEFT;
38068 channelMap[5] = MA_CHANNEL_BACK_RIGHT;
38069 } break;
38070
38071 case 7:
38072 {
38073 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38074 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38075 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
38076 channelMap[3] = MA_CHANNEL_LFE;
38077 channelMap[4] = MA_CHANNEL_BACK_CENTER;
38078 channelMap[5] = MA_CHANNEL_SIDE_LEFT;
38079 channelMap[6] = MA_CHANNEL_SIDE_RIGHT;
38080 } break;
38081
38082 case 8:
38083 default:
38084 {
38085 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38086 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38087 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
38088 channelMap[3] = MA_CHANNEL_LFE;
38089 channelMap[4] = MA_CHANNEL_BACK_LEFT;
38090 channelMap[5] = MA_CHANNEL_BACK_RIGHT;
38091 channelMap[6] = MA_CHANNEL_SIDE_LEFT;
38092 channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
38093 } break;
38094 }
38095
38096 /* Remainder. */
38097 if (channels > 8) {
38098 ma_uint32 iChannel;
38099 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
38100 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
38101 }
38102 }
38103 }
38104
38105 static void ma_get_standard_channel_map_vorbis(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
38106 {
38107 /* 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?! */
38108 switch (channels)
38109 {
38110 case 1:
38111 {
38112 channelMap[0] = MA_CHANNEL_MONO;
38113 } break;
38114
38115 case 2:
38116 {
38117 channelMap[0] = MA_CHANNEL_LEFT;
38118 channelMap[1] = MA_CHANNEL_RIGHT;
38119 } break;
38120
38121 case 3:
38122 {
38123 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38124 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
38125 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
38126 } break;
38127
38128 case 4:
38129 {
38130 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38131 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38132 channelMap[2] = MA_CHANNEL_BACK_LEFT;
38133 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
38134 } break;
38135
38136 case 5:
38137 {
38138 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38139 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
38140 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
38141 channelMap[3] = MA_CHANNEL_BACK_LEFT;
38142 channelMap[4] = MA_CHANNEL_BACK_RIGHT;
38143 } break;
38144
38145 case 6:
38146 {
38147 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38148 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
38149 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
38150 channelMap[3] = MA_CHANNEL_BACK_LEFT;
38151 channelMap[4] = MA_CHANNEL_BACK_RIGHT;
38152 channelMap[5] = MA_CHANNEL_LFE;
38153 } break;
38154
38155 case 7:
38156 {
38157 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38158 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
38159 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
38160 channelMap[3] = MA_CHANNEL_SIDE_LEFT;
38161 channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
38162 channelMap[5] = MA_CHANNEL_BACK_CENTER;
38163 channelMap[6] = MA_CHANNEL_LFE;
38164 } break;
38165
38166 case 8:
38167 default:
38168 {
38169 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38170 channelMap[1] = MA_CHANNEL_FRONT_CENTER;
38171 channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
38172 channelMap[3] = MA_CHANNEL_SIDE_LEFT;
38173 channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
38174 channelMap[5] = MA_CHANNEL_BACK_LEFT;
38175 channelMap[6] = MA_CHANNEL_BACK_RIGHT;
38176 channelMap[7] = MA_CHANNEL_LFE;
38177 } break;
38178 }
38179
38180 /* Remainder. */
38181 if (channels > 8) {
38182 ma_uint32 iChannel;
38183 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
38184 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
38185 }
38186 }
38187 }
38188
38189 static void ma_get_standard_channel_map_sound4(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
38190 {
38191 switch (channels)
38192 {
38193 case 1:
38194 {
38195 channelMap[0] = MA_CHANNEL_MONO;
38196 } break;
38197
38198 case 2:
38199 {
38200 channelMap[0] = MA_CHANNEL_LEFT;
38201 channelMap[1] = MA_CHANNEL_RIGHT;
38202 } break;
38203
38204 case 3:
38205 {
38206 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38207 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38208 channelMap[2] = MA_CHANNEL_BACK_CENTER;
38209 } break;
38210
38211 case 4:
38212 {
38213 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38214 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38215 channelMap[2] = MA_CHANNEL_BACK_LEFT;
38216 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
38217 } break;
38218
38219 case 5:
38220 {
38221 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38222 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38223 channelMap[2] = MA_CHANNEL_BACK_LEFT;
38224 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
38225 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
38226 } break;
38227
38228 case 6:
38229 {
38230 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38231 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38232 channelMap[2] = MA_CHANNEL_BACK_LEFT;
38233 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
38234 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
38235 channelMap[5] = MA_CHANNEL_LFE;
38236 } break;
38237
38238 case 7:
38239 {
38240 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38241 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38242 channelMap[2] = MA_CHANNEL_BACK_LEFT;
38243 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
38244 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
38245 channelMap[5] = MA_CHANNEL_BACK_CENTER;
38246 channelMap[6] = MA_CHANNEL_LFE;
38247 } break;
38248
38249 case 8:
38250 default:
38251 {
38252 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38253 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38254 channelMap[2] = MA_CHANNEL_BACK_LEFT;
38255 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
38256 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
38257 channelMap[5] = MA_CHANNEL_LFE;
38258 channelMap[6] = MA_CHANNEL_SIDE_LEFT;
38259 channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
38260 } break;
38261 }
38262
38263 /* Remainder. */
38264 if (channels > 8) {
38265 ma_uint32 iChannel;
38266 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
38267 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
38268 }
38269 }
38270 }
38271
38272 static void ma_get_standard_channel_map_sndio(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
38273 {
38274 switch (channels)
38275 {
38276 case 1:
38277 {
38278 channelMap[0] = MA_CHANNEL_MONO;
38279 } break;
38280
38281 case 2:
38282 {
38283 channelMap[0] = MA_CHANNEL_LEFT;
38284 channelMap[1] = MA_CHANNEL_RIGHT;
38285 } break;
38286
38287 case 3:
38288 {
38289 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38290 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38291 channelMap[2] = MA_CHANNEL_FRONT_CENTER;
38292 } break;
38293
38294 case 4:
38295 {
38296 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38297 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38298 channelMap[2] = MA_CHANNEL_BACK_LEFT;
38299 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
38300 } break;
38301
38302 case 5:
38303 {
38304 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38305 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38306 channelMap[2] = MA_CHANNEL_BACK_LEFT;
38307 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
38308 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
38309 } break;
38310
38311 case 6:
38312 default:
38313 {
38314 channelMap[0] = MA_CHANNEL_FRONT_LEFT;
38315 channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38316 channelMap[2] = MA_CHANNEL_BACK_LEFT;
38317 channelMap[3] = MA_CHANNEL_BACK_RIGHT;
38318 channelMap[4] = MA_CHANNEL_FRONT_CENTER;
38319 channelMap[5] = MA_CHANNEL_LFE;
38320 } break;
38321 }
38322
38323 /* Remainder. */
38324 if (channels > 6) {
38325 ma_uint32 iChannel;
38326 for (iChannel = 6; iChannel < MA_MAX_CHANNELS; ++iChannel) {
38327 channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
38328 }
38329 }
38330 }
38331
38332 MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
38333 {
38334 switch (standardChannelMap)
38335 {
38336 case ma_standard_channel_map_alsa:
38337 {
38338 ma_get_standard_channel_map_alsa(channels, channelMap);
38339 } break;
38340
38341 case ma_standard_channel_map_rfc3551:
38342 {
38343 ma_get_standard_channel_map_rfc3551(channels, channelMap);
38344 } break;
38345
38346 case ma_standard_channel_map_flac:
38347 {
38348 ma_get_standard_channel_map_flac(channels, channelMap);
38349 } break;
38350
38351 case ma_standard_channel_map_vorbis:
38352 {
38353 ma_get_standard_channel_map_vorbis(channels, channelMap);
38354 } break;
38355
38356 case ma_standard_channel_map_sound4:
38357 {
38358 ma_get_standard_channel_map_sound4(channels, channelMap);
38359 } break;
38360
38361 case ma_standard_channel_map_sndio:
38362 {
38363 ma_get_standard_channel_map_sndio(channels, channelMap);
38364 } break;
38365
38366 case ma_standard_channel_map_microsoft:
38367 default:
38368 {
38369 ma_get_standard_channel_map_microsoft(channels, channelMap);
38370 } break;
38371 }
38372 }
38373
38374 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
38375 {
38376 if (pOut != NULL && pIn != NULL && channels > 0) {
38377 MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels);
38378 }
38379 }
38380
38381 MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS])
38382 {
38383 if (channelMap == NULL) {
38384 return MA_FALSE;
38385 }
38386
38387 /* A channel count of 0 is invalid. */
38388 if (channels == 0) {
38389 return MA_FALSE;
38390 }
38391
38392 /* It does not make sense to have a mono channel when there is more than 1 channel. */
38393 if (channels > 1) {
38394 ma_uint32 iChannel;
38395 for (iChannel = 0; iChannel < channels; ++iChannel) {
38396 if (channelMap[iChannel] == MA_CHANNEL_MONO) {
38397 return MA_FALSE;
38398 }
38399 }
38400 }
38401
38402 return MA_TRUE;
38403 }
38404
38405 MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel channelMapA[MA_MAX_CHANNELS], const ma_channel channelMapB[MA_MAX_CHANNELS])
38406 {
38407 ma_uint32 iChannel;
38408
38409 if (channelMapA == channelMapB) {
38410 return MA_FALSE;
38411 }
38412
38413 if (channels == 0 || channels > MA_MAX_CHANNELS) {
38414 return MA_FALSE;
38415 }
38416
38417 for (iChannel = 0; iChannel < channels; ++iChannel) {
38418 if (channelMapA[iChannel] != channelMapB[iChannel]) {
38419 return MA_FALSE;
38420 }
38421 }
38422
38423 return MA_TRUE;
38424 }
38425
38426 MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS])
38427 {
38428 ma_uint32 iChannel;
38429
38430 for (iChannel = 0; iChannel < channels; ++iChannel) {
38431 if (channelMap[iChannel] != MA_CHANNEL_NONE) {
38432 return MA_FALSE;
38433 }
38434 }
38435
38436 return MA_TRUE;
38437 }
38438
38439 MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS], ma_channel channelPosition)
38440 {
38441 ma_uint32 iChannel;
38442 for (iChannel = 0; iChannel < channels; ++iChannel) {
38443 if (channelMap[iChannel] == channelPosition) {
38444 return MA_TRUE;
38445 }
38446 }
38447
38448 return MA_FALSE;
38449 }
38450
38451
38452
38453 /**************************************************************************************************************************************************************
38454
38455 Conversion Helpers
38456
38457 **************************************************************************************************************************************************************/
38458 MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn)
38459 {
38460 ma_data_converter_config config;
38461
38462 config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut);
38463 ma_get_standard_channel_map(ma_standard_channel_map_default, channelsOut, config.channelMapOut);
38464 ma_get_standard_channel_map(ma_standard_channel_map_default, channelsIn, config.channelMapIn);
38465 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
38466
38467 return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config);
38468 }
38469
38470 MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig)
38471 {
38472 ma_result result;
38473 ma_data_converter converter;
38474
38475 if (frameCountIn == 0 || pConfig == NULL) {
38476 return 0;
38477 }
38478
38479 result = ma_data_converter_init(pConfig, &converter);
38480 if (result != MA_SUCCESS) {
38481 return 0; /* Failed to initialize the data converter. */
38482 }
38483
38484 if (pOut == NULL) {
38485 frameCountOut = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn);
38486 } else {
38487 result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut);
38488 if (result != MA_SUCCESS) {
38489 frameCountOut = 0;
38490 }
38491 }
38492
38493 ma_data_converter_uninit(&converter);
38494 return frameCountOut;
38495 }
38496
38497
38498 /**************************************************************************************************************************************************************
38499
38500 Ring Buffer
38501
38502 **************************************************************************************************************************************************************/
38503 static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
38504 {
38505 return encodedOffset & 0x7FFFFFFF;
38506 }
38507
38508 static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
38509 {
38510 return encodedOffset & 0x80000000;
38511 }
38512
38513 static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
38514 {
38515 MA_ASSERT(pRB != NULL);
38516 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(pRB->encodedReadOffset));
38517 }
38518
38519 static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
38520 {
38521 MA_ASSERT(pRB != NULL);
38522 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(pRB->encodedWriteOffset));
38523 }
38524
38525 static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
38526 {
38527 return offsetLoopFlag | offsetInBytes;
38528 }
38529
38530 static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
38531 {
38532 MA_ASSERT(pOffsetInBytes != NULL);
38533 MA_ASSERT(pOffsetLoopFlag != NULL);
38534
38535 *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
38536 *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
38537 }
38538
38539
38540 MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
38541 {
38542 ma_result result;
38543 const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
38544
38545 if (pRB == NULL) {
38546 return MA_INVALID_ARGS;
38547 }
38548
38549 if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
38550 return MA_INVALID_ARGS;
38551 }
38552
38553 if (subbufferSizeInBytes > maxSubBufferSize) {
38554 return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */
38555 }
38556
38557
38558 MA_ZERO_OBJECT(pRB);
38559
38560 result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks);
38561 if (result != MA_SUCCESS) {
38562 return result;
38563 }
38564
38565 pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
38566 pRB->subbufferCount = (ma_uint32)subbufferCount;
38567
38568 if (pOptionalPreallocatedBuffer != NULL) {
38569 pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
38570 pRB->pBuffer = pOptionalPreallocatedBuffer;
38571 } else {
38572 size_t bufferSizeInBytes;
38573
38574 /*
38575 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
38576 we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.
38577 */
38578 pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT;
38579
38580 bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
38581 pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks);
38582 if (pRB->pBuffer == NULL) {
38583 return MA_OUT_OF_MEMORY;
38584 }
38585
38586 MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes);
38587 pRB->ownsBuffer = MA_TRUE;
38588 }
38589
38590 return MA_SUCCESS;
38591 }
38592
38593 MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
38594 {
38595 return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
38596 }
38597
38598 MA_API void ma_rb_uninit(ma_rb* pRB)
38599 {
38600 if (pRB == NULL) {
38601 return;
38602 }
38603
38604 if (pRB->ownsBuffer) {
38605 ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks);
38606 }
38607 }
38608
38609 MA_API void ma_rb_reset(ma_rb* pRB)
38610 {
38611 if (pRB == NULL) {
38612 return;
38613 }
38614
38615 pRB->encodedReadOffset = 0;
38616 pRB->encodedWriteOffset = 0;
38617 }
38618
38619 MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
38620 {
38621 ma_uint32 writeOffset;
38622 ma_uint32 writeOffsetInBytes;
38623 ma_uint32 writeOffsetLoopFlag;
38624 ma_uint32 readOffset;
38625 ma_uint32 readOffsetInBytes;
38626 ma_uint32 readOffsetLoopFlag;
38627 size_t bytesAvailable;
38628 size_t bytesRequested;
38629
38630 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
38631 return MA_INVALID_ARGS;
38632 }
38633
38634 /* The returned buffer should never move ahead of the write pointer. */
38635 writeOffset = pRB->encodedWriteOffset;
38636 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
38637
38638 readOffset = pRB->encodedReadOffset;
38639 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
38640
38641 /*
38642 The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we
38643 can only read up to the write pointer. If not, we can only read up to the end of the buffer.
38644 */
38645 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
38646 bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
38647 } else {
38648 bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
38649 }
38650
38651 bytesRequested = *pSizeInBytes;
38652 if (bytesRequested > bytesAvailable) {
38653 bytesRequested = bytesAvailable;
38654 }
38655
38656 *pSizeInBytes = bytesRequested;
38657 (*ppBufferOut) = ma_rb__get_read_ptr(pRB);
38658
38659 return MA_SUCCESS;
38660 }
38661
38662 MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
38663 {
38664 ma_uint32 readOffset;
38665 ma_uint32 readOffsetInBytes;
38666 ma_uint32 readOffsetLoopFlag;
38667 ma_uint32 newReadOffsetInBytes;
38668 ma_uint32 newReadOffsetLoopFlag;
38669
38670 if (pRB == NULL) {
38671 return MA_INVALID_ARGS;
38672 }
38673
38674 /* Validate the buffer. */
38675 if (pBufferOut != ma_rb__get_read_ptr(pRB)) {
38676 return MA_INVALID_ARGS;
38677 }
38678
38679 readOffset = pRB->encodedReadOffset;
38680 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
38681
38682 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
38683 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
38684 if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
38685 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
38686 }
38687
38688 /* Move the read pointer back to the start if necessary. */
38689 newReadOffsetLoopFlag = readOffsetLoopFlag;
38690 if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
38691 newReadOffsetInBytes = 0;
38692 newReadOffsetLoopFlag ^= 0x80000000;
38693 }
38694
38695 ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));
38696 return MA_SUCCESS;
38697 }
38698
38699 MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
38700 {
38701 ma_uint32 readOffset;
38702 ma_uint32 readOffsetInBytes;
38703 ma_uint32 readOffsetLoopFlag;
38704 ma_uint32 writeOffset;
38705 ma_uint32 writeOffsetInBytes;
38706 ma_uint32 writeOffsetLoopFlag;
38707 size_t bytesAvailable;
38708 size_t bytesRequested;
38709
38710 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
38711 return MA_INVALID_ARGS;
38712 }
38713
38714 /* The returned buffer should never overtake the read buffer. */
38715 readOffset = pRB->encodedReadOffset;
38716 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
38717
38718 writeOffset = pRB->encodedWriteOffset;
38719 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
38720
38721 /*
38722 In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only
38723 write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should
38724 never overtake the read pointer.
38725 */
38726 if (writeOffsetLoopFlag == readOffsetLoopFlag) {
38727 bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
38728 } else {
38729 bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
38730 }
38731
38732 bytesRequested = *pSizeInBytes;
38733 if (bytesRequested > bytesAvailable) {
38734 bytesRequested = bytesAvailable;
38735 }
38736
38737 *pSizeInBytes = bytesRequested;
38738 *ppBufferOut = ma_rb__get_write_ptr(pRB);
38739
38740 /* Clear the buffer if desired. */
38741 if (pRB->clearOnWriteAcquire) {
38742 MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes);
38743 }
38744
38745 return MA_SUCCESS;
38746 }
38747
38748 MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
38749 {
38750 ma_uint32 writeOffset;
38751 ma_uint32 writeOffsetInBytes;
38752 ma_uint32 writeOffsetLoopFlag;
38753 ma_uint32 newWriteOffsetInBytes;
38754 ma_uint32 newWriteOffsetLoopFlag;
38755
38756 if (pRB == NULL) {
38757 return MA_INVALID_ARGS;
38758 }
38759
38760 /* Validate the buffer. */
38761 if (pBufferOut != ma_rb__get_write_ptr(pRB)) {
38762 return MA_INVALID_ARGS;
38763 }
38764
38765 writeOffset = pRB->encodedWriteOffset;
38766 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
38767
38768 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
38769 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
38770 if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
38771 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
38772 }
38773
38774 /* Move the read pointer back to the start if necessary. */
38775 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
38776 if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
38777 newWriteOffsetInBytes = 0;
38778 newWriteOffsetLoopFlag ^= 0x80000000;
38779 }
38780
38781 ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));
38782 return MA_SUCCESS;
38783 }
38784
38785 MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
38786 {
38787 ma_uint32 readOffset;
38788 ma_uint32 readOffsetInBytes;
38789 ma_uint32 readOffsetLoopFlag;
38790 ma_uint32 writeOffset;
38791 ma_uint32 writeOffsetInBytes;
38792 ma_uint32 writeOffsetLoopFlag;
38793 ma_uint32 newReadOffsetInBytes;
38794 ma_uint32 newReadOffsetLoopFlag;
38795
38796 if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
38797 return MA_INVALID_ARGS;
38798 }
38799
38800 readOffset = pRB->encodedReadOffset;
38801 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
38802
38803 writeOffset = pRB->encodedWriteOffset;
38804 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
38805
38806 newReadOffsetInBytes = readOffsetInBytes;
38807 newReadOffsetLoopFlag = readOffsetLoopFlag;
38808
38809 /* We cannot go past the write buffer. */
38810 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
38811 if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
38812 newReadOffsetInBytes = writeOffsetInBytes;
38813 } else {
38814 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
38815 }
38816 } else {
38817 /* May end up looping. */
38818 if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
38819 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
38820 newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
38821 } else {
38822 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
38823 }
38824 }
38825
38826 ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
38827 return MA_SUCCESS;
38828 }
38829
38830 MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
38831 {
38832 ma_uint32 readOffset;
38833 ma_uint32 readOffsetInBytes;
38834 ma_uint32 readOffsetLoopFlag;
38835 ma_uint32 writeOffset;
38836 ma_uint32 writeOffsetInBytes;
38837 ma_uint32 writeOffsetLoopFlag;
38838 ma_uint32 newWriteOffsetInBytes;
38839 ma_uint32 newWriteOffsetLoopFlag;
38840
38841 if (pRB == NULL) {
38842 return MA_INVALID_ARGS;
38843 }
38844
38845 readOffset = pRB->encodedReadOffset;
38846 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
38847
38848 writeOffset = pRB->encodedWriteOffset;
38849 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
38850
38851 newWriteOffsetInBytes = writeOffsetInBytes;
38852 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
38853
38854 /* We cannot go past the write buffer. */
38855 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
38856 /* May end up looping. */
38857 if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
38858 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
38859 newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
38860 } else {
38861 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
38862 }
38863 } else {
38864 if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
38865 newWriteOffsetInBytes = readOffsetInBytes;
38866 } else {
38867 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
38868 }
38869 }
38870
38871 ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
38872 return MA_SUCCESS;
38873 }
38874
38875 MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB)
38876 {
38877 ma_uint32 readOffset;
38878 ma_uint32 readOffsetInBytes;
38879 ma_uint32 readOffsetLoopFlag;
38880 ma_uint32 writeOffset;
38881 ma_uint32 writeOffsetInBytes;
38882 ma_uint32 writeOffsetLoopFlag;
38883
38884 if (pRB == NULL) {
38885 return 0;
38886 }
38887
38888 readOffset = pRB->encodedReadOffset;
38889 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
38890
38891 writeOffset = pRB->encodedWriteOffset;
38892 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
38893
38894 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
38895 return writeOffsetInBytes - readOffsetInBytes;
38896 } else {
38897 return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
38898 }
38899 }
38900
38901 MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB)
38902 {
38903 ma_int32 dist;
38904
38905 if (pRB == NULL) {
38906 return 0;
38907 }
38908
38909 dist = ma_rb_pointer_distance(pRB);
38910 if (dist < 0) {
38911 return 0;
38912 }
38913
38914 return dist;
38915 }
38916
38917 MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB)
38918 {
38919 if (pRB == NULL) {
38920 return 0;
38921 }
38922
38923 return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB));
38924 }
38925
38926 MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB)
38927 {
38928 if (pRB == NULL) {
38929 return 0;
38930 }
38931
38932 return pRB->subbufferSizeInBytes;
38933 }
38934
38935 MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)
38936 {
38937 if (pRB == NULL) {
38938 return 0;
38939 }
38940
38941 if (pRB->subbufferStrideInBytes == 0) {
38942 return (size_t)pRB->subbufferSizeInBytes;
38943 }
38944
38945 return (size_t)pRB->subbufferStrideInBytes;
38946 }
38947
38948 MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
38949 {
38950 if (pRB == NULL) {
38951 return 0;
38952 }
38953
38954 return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
38955 }
38956
38957 MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
38958 {
38959 if (pRB == NULL) {
38960 return NULL;
38961 }
38962
38963 return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
38964 }
38965
38966
38967 static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
38968 {
38969 MA_ASSERT(pRB != NULL);
38970
38971 return ma_get_bytes_per_frame(pRB->format, pRB->channels);
38972 }
38973
38974 MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB)
38975 {
38976 ma_uint32 bpf;
38977 ma_result result;
38978
38979 if (pRB == NULL) {
38980 return MA_INVALID_ARGS;
38981 }
38982
38983 MA_ZERO_OBJECT(pRB);
38984
38985 bpf = ma_get_bytes_per_frame(format, channels);
38986 if (bpf == 0) {
38987 return MA_INVALID_ARGS;
38988 }
38989
38990 result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb);
38991 if (result != MA_SUCCESS) {
38992 return result;
38993 }
38994
38995 pRB->format = format;
38996 pRB->channels = channels;
38997
38998 return MA_SUCCESS;
38999 }
39000
39001 MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB)
39002 {
39003 return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
39004 }
39005
39006 MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB)
39007 {
39008 if (pRB == NULL) {
39009 return;
39010 }
39011
39012 ma_rb_uninit(&pRB->rb);
39013 }
39014
39015 MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB)
39016 {
39017 if (pRB == NULL) {
39018 return;
39019 }
39020
39021 ma_rb_reset(&pRB->rb);
39022 }
39023
39024 MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
39025 {
39026 size_t sizeInBytes;
39027 ma_result result;
39028
39029 if (pRB == NULL || pSizeInFrames == NULL) {
39030 return MA_INVALID_ARGS;
39031 }
39032
39033 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
39034
39035 result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
39036 if (result != MA_SUCCESS) {
39037 return result;
39038 }
39039
39040 *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
39041 return MA_SUCCESS;
39042 }
39043
39044 MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
39045 {
39046 if (pRB == NULL) {
39047 return MA_INVALID_ARGS;
39048 }
39049
39050 return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
39051 }
39052
39053 MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
39054 {
39055 size_t sizeInBytes;
39056 ma_result result;
39057
39058 if (pRB == NULL) {
39059 return MA_INVALID_ARGS;
39060 }
39061
39062 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
39063
39064 result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
39065 if (result != MA_SUCCESS) {
39066 return result;
39067 }
39068
39069 *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
39070 return MA_SUCCESS;
39071 }
39072
39073 MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
39074 {
39075 if (pRB == NULL) {
39076 return MA_INVALID_ARGS;
39077 }
39078
39079 return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
39080 }
39081
39082 MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
39083 {
39084 if (pRB == NULL) {
39085 return MA_INVALID_ARGS;
39086 }
39087
39088 return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
39089 }
39090
39091 MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
39092 {
39093 if (pRB == NULL) {
39094 return MA_INVALID_ARGS;
39095 }
39096
39097 return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
39098 }
39099
39100 MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB)
39101 {
39102 if (pRB == NULL) {
39103 return 0;
39104 }
39105
39106 return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
39107 }
39108
39109 MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB)
39110 {
39111 if (pRB == NULL) {
39112 return 0;
39113 }
39114
39115 return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
39116 }
39117
39118 MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB)
39119 {
39120 if (pRB == NULL) {
39121 return 0;
39122 }
39123
39124 return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
39125 }
39126
39127 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB)
39128 {
39129 if (pRB == NULL) {
39130 return 0;
39131 }
39132
39133 return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
39134 }
39135
39136 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB)
39137 {
39138 if (pRB == NULL) {
39139 return 0;
39140 }
39141
39142 return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
39143 }
39144
39145 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex)
39146 {
39147 if (pRB == NULL) {
39148 return 0;
39149 }
39150
39151 return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
39152 }
39153
39154 MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
39155 {
39156 if (pRB == NULL) {
39157 return NULL;
39158 }
39159
39160 return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
39161 }
39162
39163
39164
39165 /**************************************************************************************************************************************************************
39166
39167 Miscellaneous Helpers
39168
39169 **************************************************************************************************************************************************************/
39170 MA_API const char* ma_result_description(ma_result result)
39171 {
39172 switch (result)
39173 {
39174 case MA_SUCCESS: return "No error";
39175 case MA_ERROR: return "Unknown error";
39176 case MA_INVALID_ARGS: return "Invalid argument";
39177 case MA_INVALID_OPERATION: return "Invalid operation";
39178 case MA_OUT_OF_MEMORY: return "Out of memory";
39179 case MA_OUT_OF_RANGE: return "Out of range";
39180 case MA_ACCESS_DENIED: return "Permission denied";
39181 case MA_DOES_NOT_EXIST: return "Resource does not exist";
39182 case MA_ALREADY_EXISTS: return "Resource already exists";
39183 case MA_TOO_MANY_OPEN_FILES: return "Too many open files";
39184 case MA_INVALID_FILE: return "Invalid file";
39185 case MA_TOO_BIG: return "Too large";
39186 case MA_PATH_TOO_LONG: return "Path too long";
39187 case MA_NAME_TOO_LONG: return "Name too long";
39188 case MA_NOT_DIRECTORY: return "Not a directory";
39189 case MA_IS_DIRECTORY: return "Is a directory";
39190 case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty";
39191 case MA_END_OF_FILE: return "End of file";
39192 case MA_NO_SPACE: return "No space available";
39193 case MA_BUSY: return "Device or resource busy";
39194 case MA_IO_ERROR: return "Input/output error";
39195 case MA_INTERRUPT: return "Interrupted";
39196 case MA_UNAVAILABLE: return "Resource unavailable";
39197 case MA_ALREADY_IN_USE: return "Resource already in use";
39198 case MA_BAD_ADDRESS: return "Bad address";
39199 case MA_BAD_SEEK: return "Illegal seek";
39200 case MA_BAD_PIPE: return "Broken pipe";
39201 case MA_DEADLOCK: return "Deadlock";
39202 case MA_TOO_MANY_LINKS: return "Too many links";
39203 case MA_NOT_IMPLEMENTED: return "Not implemented";
39204 case MA_NO_MESSAGE: return "No message of desired type";
39205 case MA_BAD_MESSAGE: return "Invalid message";
39206 case MA_NO_DATA_AVAILABLE: return "No data available";
39207 case MA_INVALID_DATA: return "Invalid data";
39208 case MA_TIMEOUT: return "Timeout";
39209 case MA_NO_NETWORK: return "Network unavailable";
39210 case MA_NOT_UNIQUE: return "Not unique";
39211 case MA_NOT_SOCKET: return "Socket operation on non-socket";
39212 case MA_NO_ADDRESS: return "Destination address required";
39213 case MA_BAD_PROTOCOL: return "Protocol wrong type for socket";
39214 case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available";
39215 case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
39216 case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
39217 case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
39218 case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
39219 case MA_CONNECTION_RESET: return "Connection reset";
39220 case MA_ALREADY_CONNECTED: return "Already connected";
39221 case MA_NOT_CONNECTED: return "Not connected";
39222 case MA_CONNECTION_REFUSED: return "Connection refused";
39223 case MA_NO_HOST: return "No host";
39224 case MA_IN_PROGRESS: return "Operation in progress";
39225 case MA_CANCELLED: return "Operation cancelled";
39226 case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
39227 case MA_AT_END: return "Reached end of collection";
39228
39229 case MA_FORMAT_NOT_SUPPORTED: return "Format not supported";
39230 case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported";
39231 case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported";
39232 case MA_NO_BACKEND: return "No backend";
39233 case MA_NO_DEVICE: return "No device";
39234 case MA_API_NOT_FOUND: return "API not found";
39235 case MA_INVALID_DEVICE_CONFIG: return "Invalid device config";
39236
39237 case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized";
39238 case MA_DEVICE_NOT_STARTED: return "Device not started";
39239
39240 case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend";
39241 case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device";
39242 case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device";
39243 case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device";
39244
39245 default: return "Unknown error";
39246 }
39247 }
39248
39249 MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
39250 {
39251 if (pAllocationCallbacks != NULL) {
39252 return ma__malloc_from_callbacks(sz, pAllocationCallbacks);
39253 } else {
39254 return ma__malloc_default(sz, NULL);
39255 }
39256 }
39257
39258 MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
39259 {
39260 if (pAllocationCallbacks != NULL) {
39261 if (pAllocationCallbacks->onRealloc != NULL) {
39262 return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);
39263 } else {
39264 return NULL; /* This requires a native implementation of realloc(). */
39265 }
39266 } else {
39267 return ma__realloc_default(p, sz, NULL);
39268 }
39269 }
39270
39271 MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
39272 {
39273 if (pAllocationCallbacks != NULL) {
39274 ma__free_from_callbacks(p, pAllocationCallbacks);
39275 } else {
39276 ma__free_default(p, NULL);
39277 }
39278 }
39279
39280 MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks)
39281 {
39282 size_t extraBytes;
39283 void* pUnaligned;
39284 void* pAligned;
39285
39286 if (alignment == 0) {
39287 return 0;
39288 }
39289
39290 extraBytes = alignment-1 + sizeof(void*);
39291
39292 pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks);
39293 if (pUnaligned == NULL) {
39294 return NULL;
39295 }
39296
39297 pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
39298 ((void**)pAligned)[-1] = pUnaligned;
39299
39300 return pAligned;
39301 }
39302
39303 MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
39304 {
39305 ma_free(((void**)p)[-1], pAllocationCallbacks);
39306 }
39307
39308 MA_API const char* ma_get_format_name(ma_format format)
39309 {
39310 switch (format)
39311 {
39312 case ma_format_unknown: return "Unknown";
39313 case ma_format_u8: return "8-bit Unsigned Integer";
39314 case ma_format_s16: return "16-bit Signed Integer";
39315 case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
39316 case ma_format_s32: return "32-bit Signed Integer";
39317 case ma_format_f32: return "32-bit IEEE Floating Point";
39318 default: return "Invalid";
39319 }
39320 }
39321
39322 MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
39323 {
39324 ma_uint32 i;
39325 for (i = 0; i < channels; ++i) {
39326 pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
39327 }
39328 }
39329
39330
39331 MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format)
39332 {
39333 ma_uint32 sizes[] = {
39334 0, /* unknown */
39335 1, /* u8 */
39336 2, /* s16 */
39337 3, /* s24 */
39338 4, /* s32 */
39339 4, /* f32 */
39340 };
39341 return sizes[format];
39342 }
39343
39344
39345 /**************************************************************************************************************************************************************
39346
39347 Decoding
39348
39349 **************************************************************************************************************************************************************/
39350 #ifndef MA_NO_DECODING
39351
39352 static size_t ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
39353 {
39354 size_t bytesRead;
39355
39356 MA_ASSERT(pDecoder != NULL);
39357 MA_ASSERT(pBufferOut != NULL);
39358
39359 bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
39360 pDecoder->readPointer += bytesRead;
39361
39362 return bytesRead;
39363 }
39364
39365 static ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
39366 {
39367 ma_bool32 wasSuccessful;
39368
39369 MA_ASSERT(pDecoder != NULL);
39370
39371 wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
39372 if (wasSuccessful) {
39373 if (origin == ma_seek_origin_start) {
39374 pDecoder->readPointer = (ma_uint64)byteOffset;
39375 } else {
39376 pDecoder->readPointer += byteOffset;
39377 }
39378 }
39379
39380 return wasSuccessful;
39381 }
39382
39383
39384 MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
39385 {
39386 ma_decoder_config config;
39387 MA_ZERO_OBJECT(&config);
39388 config.format = outputFormat;
39389 config.channels = outputChannels;
39390 config.sampleRate = outputSampleRate;
39391 config.resampling.algorithm = ma_resample_algorithm_linear;
39392 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
39393 config.resampling.speex.quality = 3;
39394
39395 /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */
39396
39397 return config;
39398 }
39399
39400 MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
39401 {
39402 ma_decoder_config config;
39403 if (pConfig != NULL) {
39404 config = *pConfig;
39405 } else {
39406 MA_ZERO_OBJECT(&config);
39407 }
39408
39409 return config;
39410 }
39411
39412 static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
39413 {
39414 ma_data_converter_config converterConfig;
39415
39416 MA_ASSERT(pDecoder != NULL);
39417
39418 /* Output format. */
39419 if (pConfig->format == ma_format_unknown) {
39420 pDecoder->outputFormat = pDecoder->internalFormat;
39421 } else {
39422 pDecoder->outputFormat = pConfig->format;
39423 }
39424
39425 if (pConfig->channels == 0) {
39426 pDecoder->outputChannels = pDecoder->internalChannels;
39427 } else {
39428 pDecoder->outputChannels = pConfig->channels;
39429 }
39430
39431 if (pConfig->sampleRate == 0) {
39432 pDecoder->outputSampleRate = pDecoder->internalSampleRate;
39433 } else {
39434 pDecoder->outputSampleRate = pConfig->sampleRate;
39435 }
39436
39437 if (ma_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) {
39438 ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->outputChannels, pDecoder->outputChannelMap);
39439 } else {
39440 MA_COPY_MEMORY(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap));
39441 }
39442
39443
39444 converterConfig = ma_data_converter_config_init(
39445 pDecoder->internalFormat, pDecoder->outputFormat,
39446 pDecoder->internalChannels, pDecoder->outputChannels,
39447 pDecoder->internalSampleRate, pDecoder->outputSampleRate
39448 );
39449 ma_channel_map_copy(converterConfig.channelMapIn, pDecoder->internalChannelMap, pDecoder->internalChannels);
39450 ma_channel_map_copy(converterConfig.channelMapOut, pDecoder->outputChannelMap, pDecoder->outputChannels);
39451 converterConfig.channelMixMode = pConfig->channelMixMode;
39452 converterConfig.ditherMode = pConfig->ditherMode;
39453 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
39454 converterConfig.resampling.algorithm = pConfig->resampling.algorithm;
39455 converterConfig.resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
39456 converterConfig.resampling.speex.quality = pConfig->resampling.speex.quality;
39457
39458 return ma_data_converter_init(&converterConfig, &pDecoder->converter);
39459 }
39460
39461 /* WAV */
39462 #ifdef dr_wav_h
39463 #define MA_HAS_WAV
39464
39465 static size_t ma_decoder_internal_on_read__wav(void* pUserData, void* pBufferOut, size_t bytesToRead)
39466 {
39467 ma_decoder* pDecoder = (ma_decoder*)pUserData;
39468 MA_ASSERT(pDecoder != NULL);
39469
39470 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
39471 }
39472
39473 static drwav_bool32 ma_decoder_internal_on_seek__wav(void* pUserData, int offset, drwav_seek_origin origin)
39474 {
39475 ma_decoder* pDecoder = (ma_decoder*)pUserData;
39476 MA_ASSERT(pDecoder != NULL);
39477
39478 return ma_decoder_seek_bytes(pDecoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
39479 }
39480
39481 static ma_uint64 ma_decoder_internal_on_read_pcm_frames__wav(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
39482 {
39483 drwav* pWav;
39484
39485 MA_ASSERT(pDecoder != NULL);
39486 MA_ASSERT(pFramesOut != NULL);
39487
39488 pWav = (drwav*)pDecoder->pInternalDecoder;
39489 MA_ASSERT(pWav != NULL);
39490
39491 switch (pDecoder->internalFormat) {
39492 case ma_format_s16: return drwav_read_pcm_frames_s16(pWav, frameCount, (drwav_int16*)pFramesOut);
39493 case ma_format_s32: return drwav_read_pcm_frames_s32(pWav, frameCount, (drwav_int32*)pFramesOut);
39494 case ma_format_f32: return drwav_read_pcm_frames_f32(pWav, frameCount, (float*)pFramesOut);
39495 default: break;
39496 }
39497
39498 /* Should never get here. If we do, it means the internal format was not set correctly at initialization time. */
39499 MA_ASSERT(MA_FALSE);
39500 return 0;
39501 }
39502
39503 static ma_result ma_decoder_internal_on_seek_to_pcm_frame__wav(ma_decoder* pDecoder, ma_uint64 frameIndex)
39504 {
39505 drwav* pWav;
39506 drwav_bool32 result;
39507
39508 pWav = (drwav*)pDecoder->pInternalDecoder;
39509 MA_ASSERT(pWav != NULL);
39510
39511 result = drwav_seek_to_pcm_frame(pWav, frameIndex);
39512 if (result) {
39513 return MA_SUCCESS;
39514 } else {
39515 return MA_ERROR;
39516 }
39517 }
39518
39519 static ma_result ma_decoder_internal_on_uninit__wav(ma_decoder* pDecoder)
39520 {
39521 drwav_uninit((drwav*)pDecoder->pInternalDecoder);
39522 ma__free_from_callbacks(pDecoder->pInternalDecoder, &pDecoder->allocationCallbacks);
39523 return MA_SUCCESS;
39524 }
39525
39526 static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__wav(ma_decoder* pDecoder)
39527 {
39528 return ((drwav*)pDecoder->pInternalDecoder)->totalPCMFrameCount;
39529 }
39530
39531 static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
39532 {
39533 drwav* pWav;
39534 drwav_allocation_callbacks allocationCallbacks;
39535
39536 MA_ASSERT(pConfig != NULL);
39537 MA_ASSERT(pDecoder != NULL);
39538
39539 pWav = (drwav*)ma__malloc_from_callbacks(sizeof(*pWav), &pDecoder->allocationCallbacks);
39540 if (pWav == NULL) {
39541 return MA_OUT_OF_MEMORY;
39542 }
39543
39544 allocationCallbacks.pUserData = pDecoder->allocationCallbacks.pUserData;
39545 allocationCallbacks.onMalloc = pDecoder->allocationCallbacks.onMalloc;
39546 allocationCallbacks.onRealloc = pDecoder->allocationCallbacks.onRealloc;
39547 allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree;
39548
39549 /* Try opening the decoder first. */
39550 if (!drwav_init(pWav, ma_decoder_internal_on_read__wav, ma_decoder_internal_on_seek__wav, pDecoder, &allocationCallbacks)) {
39551 ma__free_from_callbacks(pWav, &pDecoder->allocationCallbacks);
39552 return MA_ERROR;
39553 }
39554
39555 /* If we get here it means we successfully initialized the WAV decoder. We can now initialize the rest of the ma_decoder. */
39556 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__wav;
39557 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__wav;
39558 pDecoder->onUninit = ma_decoder_internal_on_uninit__wav;
39559 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__wav;
39560 pDecoder->pInternalDecoder = pWav;
39561
39562 /* Try to be as optimal as possible for the internal format. If miniaudio does not support a format we will fall back to f32. */
39563 pDecoder->internalFormat = ma_format_unknown;
39564 switch (pWav->translatedFormatTag) {
39565 case DR_WAVE_FORMAT_PCM:
39566 {
39567 if (pWav->bitsPerSample == 8) {
39568 pDecoder->internalFormat = ma_format_s16;
39569 } else if (pWav->bitsPerSample == 16) {
39570 pDecoder->internalFormat = ma_format_s16;
39571 } else if (pWav->bitsPerSample == 32) {
39572 pDecoder->internalFormat = ma_format_s32;
39573 }
39574 } break;
39575
39576 case DR_WAVE_FORMAT_IEEE_FLOAT:
39577 {
39578 if (pWav->bitsPerSample == 32) {
39579 pDecoder->internalFormat = ma_format_f32;
39580 }
39581 } break;
39582
39583 case DR_WAVE_FORMAT_ALAW:
39584 case DR_WAVE_FORMAT_MULAW:
39585 case DR_WAVE_FORMAT_ADPCM:
39586 case DR_WAVE_FORMAT_DVI_ADPCM:
39587 {
39588 pDecoder->internalFormat = ma_format_s16;
39589 } break;
39590 }
39591
39592 if (pDecoder->internalFormat == ma_format_unknown) {
39593 pDecoder->internalFormat = ma_format_f32;
39594 }
39595
39596 pDecoder->internalChannels = pWav->channels;
39597 pDecoder->internalSampleRate = pWav->sampleRate;
39598 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDecoder->internalChannels, pDecoder->internalChannelMap);
39599
39600 return MA_SUCCESS;
39601 }
39602 #endif /* dr_wav_h */
39603
39604 /* FLAC */
39605 #ifdef dr_flac_h
39606 #define MA_HAS_FLAC
39607
39608 static size_t ma_decoder_internal_on_read__flac(void* pUserData, void* pBufferOut, size_t bytesToRead)
39609 {
39610 ma_decoder* pDecoder = (ma_decoder*)pUserData;
39611 MA_ASSERT(pDecoder != NULL);
39612
39613 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
39614 }
39615
39616 static drflac_bool32 ma_decoder_internal_on_seek__flac(void* pUserData, int offset, drflac_seek_origin origin)
39617 {
39618 ma_decoder* pDecoder = (ma_decoder*)pUserData;
39619 MA_ASSERT(pDecoder != NULL);
39620
39621 return ma_decoder_seek_bytes(pDecoder, offset, (origin == drflac_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
39622 }
39623
39624 static ma_uint64 ma_decoder_internal_on_read_pcm_frames__flac(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
39625 {
39626 drflac* pFlac;
39627
39628 MA_ASSERT(pDecoder != NULL);
39629 MA_ASSERT(pFramesOut != NULL);
39630
39631 pFlac = (drflac*)pDecoder->pInternalDecoder;
39632 MA_ASSERT(pFlac != NULL);
39633
39634 switch (pDecoder->internalFormat) {
39635 case ma_format_s16: return drflac_read_pcm_frames_s16(pFlac, frameCount, (drflac_int16*)pFramesOut);
39636 case ma_format_s32: return drflac_read_pcm_frames_s32(pFlac, frameCount, (drflac_int32*)pFramesOut);
39637 case ma_format_f32: return drflac_read_pcm_frames_f32(pFlac, frameCount, (float*)pFramesOut);
39638 default: break;
39639 }
39640
39641 /* Should never get here. If we do, it means the internal format was not set correctly at initialization time. */
39642 MA_ASSERT(MA_FALSE);
39643 return 0;
39644 }
39645
39646 static ma_result ma_decoder_internal_on_seek_to_pcm_frame__flac(ma_decoder* pDecoder, ma_uint64 frameIndex)
39647 {
39648 drflac* pFlac;
39649 drflac_bool32 result;
39650
39651 pFlac = (drflac*)pDecoder->pInternalDecoder;
39652 MA_ASSERT(pFlac != NULL);
39653
39654 result = drflac_seek_to_pcm_frame(pFlac, frameIndex);
39655 if (result) {
39656 return MA_SUCCESS;
39657 } else {
39658 return MA_ERROR;
39659 }
39660 }
39661
39662 static ma_result ma_decoder_internal_on_uninit__flac(ma_decoder* pDecoder)
39663 {
39664 drflac_close((drflac*)pDecoder->pInternalDecoder);
39665 return MA_SUCCESS;
39666 }
39667
39668 static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__flac(ma_decoder* pDecoder)
39669 {
39670 return ((drflac*)pDecoder->pInternalDecoder)->totalPCMFrameCount;
39671 }
39672
39673 static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
39674 {
39675 drflac* pFlac;
39676 drflac_allocation_callbacks allocationCallbacks;
39677
39678 MA_ASSERT(pConfig != NULL);
39679 MA_ASSERT(pDecoder != NULL);
39680
39681 allocationCallbacks.pUserData = pDecoder->allocationCallbacks.pUserData;
39682 allocationCallbacks.onMalloc = pDecoder->allocationCallbacks.onMalloc;
39683 allocationCallbacks.onRealloc = pDecoder->allocationCallbacks.onRealloc;
39684 allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree;
39685
39686 /* Try opening the decoder first. */
39687 pFlac = drflac_open(ma_decoder_internal_on_read__flac, ma_decoder_internal_on_seek__flac, pDecoder, &allocationCallbacks);
39688 if (pFlac == NULL) {
39689 return MA_ERROR;
39690 }
39691
39692 /* If we get here it means we successfully initialized the FLAC decoder. We can now initialize the rest of the ma_decoder. */
39693 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__flac;
39694 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__flac;
39695 pDecoder->onUninit = ma_decoder_internal_on_uninit__flac;
39696 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__flac;
39697 pDecoder->pInternalDecoder = pFlac;
39698
39699 /*
39700 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
39701 since it's the only one that's truly lossless.
39702 */
39703 pDecoder->internalFormat = ma_format_s32;
39704 if (pConfig->format == ma_format_s16) {
39705 pDecoder->internalFormat = ma_format_s16;
39706 } else if (pConfig->format == ma_format_f32) {
39707 pDecoder->internalFormat = ma_format_f32;
39708 }
39709
39710 pDecoder->internalChannels = pFlac->channels;
39711 pDecoder->internalSampleRate = pFlac->sampleRate;
39712 ma_get_standard_channel_map(ma_standard_channel_map_flac, pDecoder->internalChannels, pDecoder->internalChannelMap);
39713
39714 return MA_SUCCESS;
39715 }
39716 #endif /* dr_flac_h */
39717
39718 /* Vorbis */
39719 #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
39720 #define MA_HAS_VORBIS
39721
39722 /* The size in bytes of each chunk of data to read from the Vorbis stream. */
39723 #define MA_VORBIS_DATA_CHUNK_SIZE 4096
39724
39725 typedef struct
39726 {
39727 stb_vorbis* pInternalVorbis;
39728 ma_uint8* pData;
39729 size_t dataSize;
39730 size_t dataCapacity;
39731 ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
39732 ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
39733 float** ppPacketData;
39734 } ma_vorbis_decoder;
39735
39736 static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
39737 {
39738 float* pFramesOutF;
39739 ma_uint64 totalFramesRead;
39740
39741 MA_ASSERT(pVorbis != NULL);
39742 MA_ASSERT(pDecoder != NULL);
39743
39744 pFramesOutF = (float*)pFramesOut;
39745
39746 totalFramesRead = 0;
39747 while (frameCount > 0) {
39748 /* Read from the in-memory buffer first. */
39749 while (pVorbis->framesRemaining > 0 && frameCount > 0) {
39750 ma_uint32 iChannel;
39751 for (iChannel = 0; iChannel < pDecoder->internalChannels; ++iChannel) {
39752 pFramesOutF[0] = pVorbis->ppPacketData[iChannel][pVorbis->framesConsumed];
39753 pFramesOutF += 1;
39754 }
39755
39756 pVorbis->framesConsumed += 1;
39757 pVorbis->framesRemaining -= 1;
39758 frameCount -= 1;
39759 totalFramesRead += 1;
39760 }
39761
39762 if (frameCount == 0) {
39763 break;
39764 }
39765
39766 MA_ASSERT(pVorbis->framesRemaining == 0);
39767
39768 /* We've run out of cached frames, so decode the next packet and continue iteration. */
39769 do
39770 {
39771 int samplesRead;
39772 int consumedDataSize;
39773
39774 if (pVorbis->dataSize > INT_MAX) {
39775 break; /* Too big. */
39776 }
39777
39778 samplesRead = 0;
39779 consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->pInternalVorbis, pVorbis->pData, (int)pVorbis->dataSize, NULL, (float***)&pVorbis->ppPacketData, &samplesRead);
39780 if (consumedDataSize != 0) {
39781 size_t leftoverDataSize = (pVorbis->dataSize - (size_t)consumedDataSize);
39782 size_t i;
39783 for (i = 0; i < leftoverDataSize; ++i) {
39784 pVorbis->pData[i] = pVorbis->pData[i + consumedDataSize];
39785 }
39786
39787 pVorbis->dataSize = leftoverDataSize;
39788 pVorbis->framesConsumed = 0;
39789 pVorbis->framesRemaining = samplesRead;
39790 break;
39791 } else {
39792 /* Need more data. If there's any room in the existing buffer allocation fill that first. Otherwise expand. */
39793 size_t bytesRead;
39794 if (pVorbis->dataCapacity == pVorbis->dataSize) {
39795 /* No room. Expand. */
39796 size_t oldCap = pVorbis->dataCapacity;
39797 size_t newCap = pVorbis->dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
39798 ma_uint8* pNewData;
39799
39800 pNewData = (ma_uint8*)ma__realloc_from_callbacks(pVorbis->pData, newCap, oldCap, &pDecoder->allocationCallbacks);
39801 if (pNewData == NULL) {
39802 return totalFramesRead; /* Out of memory. */
39803 }
39804
39805 pVorbis->pData = pNewData;
39806 pVorbis->dataCapacity = newCap;
39807 }
39808
39809 /* Fill in a chunk. */
39810 bytesRead = ma_decoder_read_bytes(pDecoder, pVorbis->pData + pVorbis->dataSize, (pVorbis->dataCapacity - pVorbis->dataSize));
39811 if (bytesRead == 0) {
39812 return totalFramesRead; /* Error reading more data. */
39813 }
39814
39815 pVorbis->dataSize += bytesRead;
39816 }
39817 } while (MA_TRUE);
39818 }
39819
39820 return totalFramesRead;
39821 }
39822
39823 static ma_result ma_vorbis_decoder_seek_to_pcm_frame(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, ma_uint64 frameIndex)
39824 {
39825 float buffer[4096];
39826
39827 MA_ASSERT(pVorbis != NULL);
39828 MA_ASSERT(pDecoder != NULL);
39829
39830 /*
39831 This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs
39832 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
39833 find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis.
39834 */
39835 if (!ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start)) {
39836 return MA_ERROR;
39837 }
39838
39839 stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis);
39840 pVorbis->framesConsumed = 0;
39841 pVorbis->framesRemaining = 0;
39842 pVorbis->dataSize = 0;
39843
39844 while (frameIndex > 0) {
39845 ma_uint32 framesRead;
39846 ma_uint32 framesToRead = ma_countof(buffer)/pDecoder->internalChannels;
39847 if (framesToRead > frameIndex) {
39848 framesToRead = (ma_uint32)frameIndex;
39849 }
39850
39851 framesRead = (ma_uint32)ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, buffer, framesToRead);
39852 if (framesRead == 0) {
39853 return MA_ERROR;
39854 }
39855
39856 frameIndex -= framesRead;
39857 }
39858
39859 return MA_SUCCESS;
39860 }
39861
39862
39863 static ma_result ma_decoder_internal_on_seek_to_pcm_frame__vorbis(ma_decoder* pDecoder, ma_uint64 frameIndex)
39864 {
39865 ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
39866 MA_ASSERT(pVorbis != NULL);
39867
39868 return ma_vorbis_decoder_seek_to_pcm_frame(pVorbis, pDecoder, frameIndex);
39869 }
39870
39871 static ma_result ma_decoder_internal_on_uninit__vorbis(ma_decoder* pDecoder)
39872 {
39873 ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
39874 MA_ASSERT(pVorbis != NULL);
39875
39876 stb_vorbis_close(pVorbis->pInternalVorbis);
39877 ma__free_from_callbacks(pVorbis->pData, &pDecoder->allocationCallbacks);
39878 ma__free_from_callbacks(pVorbis, &pDecoder->allocationCallbacks);
39879
39880 return MA_SUCCESS;
39881 }
39882
39883 static ma_uint64 ma_decoder_internal_on_read_pcm_frames__vorbis(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
39884 {
39885 ma_vorbis_decoder* pVorbis;
39886
39887 MA_ASSERT(pDecoder != NULL);
39888 MA_ASSERT(pFramesOut != NULL);
39889 MA_ASSERT(pDecoder->internalFormat == ma_format_f32);
39890
39891 pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
39892 MA_ASSERT(pVorbis != NULL);
39893
39894 return ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, pFramesOut, frameCount);
39895 }
39896
39897 static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__vorbis(ma_decoder* pDecoder)
39898 {
39899 /* No good way to do this with Vorbis. */
39900 (void)pDecoder;
39901 return 0;
39902 }
39903
39904 static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
39905 {
39906 stb_vorbis* pInternalVorbis = NULL;
39907 size_t dataSize = 0;
39908 size_t dataCapacity = 0;
39909 ma_uint8* pData = NULL;
39910 stb_vorbis_info vorbisInfo;
39911 size_t vorbisDataSize;
39912 ma_vorbis_decoder* pVorbis;
39913
39914 MA_ASSERT(pConfig != NULL);
39915 MA_ASSERT(pDecoder != NULL);
39916
39917 /* We grow the buffer in chunks. */
39918 do
39919 {
39920 /* Allocate memory for a new chunk. */
39921 ma_uint8* pNewData;
39922 size_t bytesRead;
39923 int vorbisError = 0;
39924 int consumedDataSize = 0;
39925 size_t oldCapacity = dataCapacity;
39926
39927 dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
39928 pNewData = (ma_uint8*)ma__realloc_from_callbacks(pData, dataCapacity, oldCapacity, &pDecoder->allocationCallbacks);
39929 if (pNewData == NULL) {
39930 ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
39931 return MA_OUT_OF_MEMORY;
39932 }
39933
39934 pData = pNewData;
39935
39936 /* Fill in a chunk. */
39937 bytesRead = ma_decoder_read_bytes(pDecoder, pData + dataSize, (dataCapacity - dataSize));
39938 if (bytesRead == 0) {
39939 return MA_ERROR;
39940 }
39941
39942 dataSize += bytesRead;
39943 if (dataSize > INT_MAX) {
39944 return MA_ERROR; /* Too big. */
39945 }
39946
39947 pInternalVorbis = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
39948 if (pInternalVorbis != NULL) {
39949 /*
39950 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
39951 we need to move those bytes down to the front of the buffer since they'll be needed for future decoding.
39952 */
39953 size_t leftoverDataSize = (dataSize - (size_t)consumedDataSize);
39954 size_t i;
39955 for (i = 0; i < leftoverDataSize; ++i) {
39956 pData[i] = pData[i + consumedDataSize];
39957 }
39958
39959 dataSize = leftoverDataSize;
39960 break; /* Success. */
39961 } else {
39962 if (vorbisError == VORBIS_need_more_data) {
39963 continue;
39964 } else {
39965 return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
39966 }
39967 }
39968 } while (MA_TRUE);
39969
39970
39971 /* If we get here it means we successfully opened the Vorbis decoder. */
39972 vorbisInfo = stb_vorbis_get_info(pInternalVorbis);
39973
39974 /* Don't allow more than MA_MAX_CHANNELS channels. */
39975 if (vorbisInfo.channels > MA_MAX_CHANNELS) {
39976 stb_vorbis_close(pInternalVorbis);
39977 ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
39978 return MA_ERROR; /* Too many channels. */
39979 }
39980
39981 vorbisDataSize = sizeof(ma_vorbis_decoder) + sizeof(float)*vorbisInfo.max_frame_size;
39982 pVorbis = (ma_vorbis_decoder*)ma__malloc_from_callbacks(vorbisDataSize, &pDecoder->allocationCallbacks);
39983 if (pVorbis == NULL) {
39984 stb_vorbis_close(pInternalVorbis);
39985 ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
39986 return MA_OUT_OF_MEMORY;
39987 }
39988
39989 MA_ZERO_MEMORY(pVorbis, vorbisDataSize);
39990 pVorbis->pInternalVorbis = pInternalVorbis;
39991 pVorbis->pData = pData;
39992 pVorbis->dataSize = dataSize;
39993 pVorbis->dataCapacity = dataCapacity;
39994
39995 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__vorbis;
39996 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__vorbis;
39997 pDecoder->onUninit = ma_decoder_internal_on_uninit__vorbis;
39998 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__vorbis;
39999 pDecoder->pInternalDecoder = pVorbis;
40000
40001 /* The internal format is always f32. */
40002 pDecoder->internalFormat = ma_format_f32;
40003 pDecoder->internalChannels = vorbisInfo.channels;
40004 pDecoder->internalSampleRate = vorbisInfo.sample_rate;
40005 ma_get_standard_channel_map(ma_standard_channel_map_vorbis, pDecoder->internalChannels, pDecoder->internalChannelMap);
40006
40007 return MA_SUCCESS;
40008 }
40009 #endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */
40010
40011 /* MP3 */
40012 #ifdef dr_mp3_h
40013 #define MA_HAS_MP3
40014
40015 static size_t ma_decoder_internal_on_read__mp3(void* pUserData, void* pBufferOut, size_t bytesToRead)
40016 {
40017 ma_decoder* pDecoder = (ma_decoder*)pUserData;
40018 MA_ASSERT(pDecoder != NULL);
40019
40020 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
40021 }
40022
40023 static drmp3_bool32 ma_decoder_internal_on_seek__mp3(void* pUserData, int offset, drmp3_seek_origin origin)
40024 {
40025 ma_decoder* pDecoder = (ma_decoder*)pUserData;
40026 MA_ASSERT(pDecoder != NULL);
40027
40028 return ma_decoder_seek_bytes(pDecoder, offset, (origin == drmp3_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
40029 }
40030
40031 static ma_uint64 ma_decoder_internal_on_read_pcm_frames__mp3(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
40032 {
40033 drmp3* pMP3;
40034
40035 MA_ASSERT(pDecoder != NULL);
40036 MA_ASSERT(pFramesOut != NULL);
40037
40038 pMP3 = (drmp3*)pDecoder->pInternalDecoder;
40039 MA_ASSERT(pMP3 != NULL);
40040
40041 #if defined(DR_MP3_FLOAT_OUTPUT)
40042 MA_ASSERT(pDecoder->internalFormat == ma_format_f32);
40043 return drmp3_read_pcm_frames_f32(pMP3, frameCount, (float*)pFramesOut);
40044 #else
40045 MA_ASSERT(pDecoder->internalFormat == ma_format_s16);
40046 return drmp3_read_pcm_frames_s16(pMP3, frameCount, (drmp3_int16*)pFramesOut);
40047 #endif
40048 }
40049
40050 static ma_result ma_decoder_internal_on_seek_to_pcm_frame__mp3(ma_decoder* pDecoder, ma_uint64 frameIndex)
40051 {
40052 drmp3* pMP3;
40053 drmp3_bool32 result;
40054
40055 pMP3 = (drmp3*)pDecoder->pInternalDecoder;
40056 MA_ASSERT(pMP3 != NULL);
40057
40058 result = drmp3_seek_to_pcm_frame(pMP3, frameIndex);
40059 if (result) {
40060 return MA_SUCCESS;
40061 } else {
40062 return MA_ERROR;
40063 }
40064 }
40065
40066 static ma_result ma_decoder_internal_on_uninit__mp3(ma_decoder* pDecoder)
40067 {
40068 drmp3_uninit((drmp3*)pDecoder->pInternalDecoder);
40069 ma__free_from_callbacks(pDecoder->pInternalDecoder, &pDecoder->allocationCallbacks);
40070 return MA_SUCCESS;
40071 }
40072
40073 static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__mp3(ma_decoder* pDecoder)
40074 {
40075 return drmp3_get_pcm_frame_count((drmp3*)pDecoder->pInternalDecoder);
40076 }
40077
40078 static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40079 {
40080 drmp3* pMP3;
40081 drmp3_allocation_callbacks allocationCallbacks;
40082
40083 MA_ASSERT(pConfig != NULL);
40084 MA_ASSERT(pDecoder != NULL);
40085
40086 pMP3 = (drmp3*)ma__malloc_from_callbacks(sizeof(*pMP3), &pDecoder->allocationCallbacks);
40087 if (pMP3 == NULL) {
40088 return MA_OUT_OF_MEMORY;
40089 }
40090
40091 allocationCallbacks.pUserData = pDecoder->allocationCallbacks.pUserData;
40092 allocationCallbacks.onMalloc = pDecoder->allocationCallbacks.onMalloc;
40093 allocationCallbacks.onRealloc = pDecoder->allocationCallbacks.onRealloc;
40094 allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree;
40095
40096 /*
40097 Try opening the decoder first. We always use whatever dr_mp3 reports for channel count and sample rate. The format is determined by
40098 the presence of DR_MP3_FLOAT_OUTPUT.
40099 */
40100 if (!drmp3_init(pMP3, ma_decoder_internal_on_read__mp3, ma_decoder_internal_on_seek__mp3, pDecoder, &allocationCallbacks)) {
40101 ma__free_from_callbacks(pMP3, &pDecoder->allocationCallbacks);
40102 return MA_ERROR;
40103 }
40104
40105 /* If we get here it means we successfully initialized the MP3 decoder. We can now initialize the rest of the ma_decoder. */
40106 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__mp3;
40107 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__mp3;
40108 pDecoder->onUninit = ma_decoder_internal_on_uninit__mp3;
40109 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__mp3;
40110 pDecoder->pInternalDecoder = pMP3;
40111
40112 /* Internal format. */
40113 #if defined(DR_MP3_FLOAT_OUTPUT)
40114 pDecoder->internalFormat = ma_format_f32;
40115 #else
40116 pDecoder->internalFormat = ma_format_s16;
40117 #endif
40118 pDecoder->internalChannels = pMP3->channels;
40119 pDecoder->internalSampleRate = pMP3->sampleRate;
40120 ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->internalChannels, pDecoder->internalChannelMap);
40121
40122 return MA_SUCCESS;
40123 }
40124 #endif /* dr_mp3_h */
40125
40126 /* Raw */
40127 static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
40128 {
40129 ma_uint32 bpf;
40130 ma_uint64 totalFramesRead;
40131 void* pRunningFramesOut;
40132
40133
40134 MA_ASSERT(pDecoder != NULL);
40135 MA_ASSERT(pFramesOut != NULL);
40136
40137 /* For raw decoding we just read directly from the decoder's callbacks. */
40138 bpf = ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
40139
40140 totalFramesRead = 0;
40141 pRunningFramesOut = pFramesOut;
40142
40143 while (totalFramesRead < frameCount) {
40144 ma_uint64 framesReadThisIteration;
40145 ma_uint64 framesToReadThisIteration = (frameCount - totalFramesRead);
40146 if (framesToReadThisIteration > MA_SIZE_MAX) {
40147 framesToReadThisIteration = MA_SIZE_MAX;
40148 }
40149
40150 framesReadThisIteration = ma_decoder_read_bytes(pDecoder, pRunningFramesOut, (size_t)framesToReadThisIteration * bpf) / bpf; /* Safe cast to size_t. */
40151
40152 totalFramesRead += framesReadThisIteration;
40153 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIteration * bpf);
40154
40155 if (framesReadThisIteration < framesToReadThisIteration) {
40156 break; /* Done. */
40157 }
40158 }
40159
40160 return totalFramesRead;
40161 }
40162
40163 static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDecoder, ma_uint64 frameIndex)
40164 {
40165 ma_bool32 result = MA_FALSE;
40166 ma_uint64 totalBytesToSeek;
40167
40168 MA_ASSERT(pDecoder != NULL);
40169
40170 if (pDecoder->onSeek == NULL) {
40171 return MA_ERROR;
40172 }
40173
40174 /* 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. */
40175 totalBytesToSeek = frameIndex * ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
40176 if (totalBytesToSeek < 0x7FFFFFFF) {
40177 /* Simple case. */
40178 result = ma_decoder_seek_bytes(pDecoder, (int)(frameIndex * ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels)), ma_seek_origin_start);
40179 } else {
40180 /* Complex case. Start by doing a seek relative to the start. Then keep looping using offset seeking. */
40181 result = ma_decoder_seek_bytes(pDecoder, 0x7FFFFFFF, ma_seek_origin_start);
40182 if (result == MA_TRUE) {
40183 totalBytesToSeek -= 0x7FFFFFFF;
40184
40185 while (totalBytesToSeek > 0) {
40186 ma_uint64 bytesToSeekThisIteration = totalBytesToSeek;
40187 if (bytesToSeekThisIteration > 0x7FFFFFFF) {
40188 bytesToSeekThisIteration = 0x7FFFFFFF;
40189 }
40190
40191 result = ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_current);
40192 if (result != MA_TRUE) {
40193 break;
40194 }
40195
40196 totalBytesToSeek -= bytesToSeekThisIteration;
40197 }
40198 }
40199 }
40200
40201 if (result) {
40202 return MA_SUCCESS;
40203 } else {
40204 return MA_ERROR;
40205 }
40206 }
40207
40208 static ma_result ma_decoder_internal_on_uninit__raw(ma_decoder* pDecoder)
40209 {
40210 (void)pDecoder;
40211 return MA_SUCCESS;
40212 }
40213
40214 static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__raw(ma_decoder* pDecoder)
40215 {
40216 (void)pDecoder;
40217 return 0;
40218 }
40219
40220 static ma_result ma_decoder_init_raw__internal(const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
40221 {
40222 MA_ASSERT(pConfigIn != NULL);
40223 MA_ASSERT(pConfigOut != NULL);
40224 MA_ASSERT(pDecoder != NULL);
40225
40226 pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__raw;
40227 pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__raw;
40228 pDecoder->onUninit = ma_decoder_internal_on_uninit__raw;
40229 pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__raw;
40230
40231 /* Internal format. */
40232 pDecoder->internalFormat = pConfigIn->format;
40233 pDecoder->internalChannels = pConfigIn->channels;
40234 pDecoder->internalSampleRate = pConfigIn->sampleRate;
40235 ma_channel_map_copy(pDecoder->internalChannelMap, pConfigIn->channelMap, pConfigIn->channels);
40236
40237 return MA_SUCCESS;
40238 }
40239
40240 static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40241 {
40242 MA_ASSERT(pDecoder != NULL);
40243
40244 if (pConfig != NULL) {
40245 return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks);
40246 } else {
40247 pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default();
40248 return MA_SUCCESS;
40249 }
40250 }
40251
40252 static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40253 {
40254 ma_result result;
40255
40256 MA_ASSERT(pConfig != NULL);
40257
40258 if (pDecoder == NULL) {
40259 return MA_INVALID_ARGS;
40260 }
40261
40262 MA_ZERO_OBJECT(pDecoder);
40263
40264 if (onRead == NULL || onSeek == NULL) {
40265 return MA_INVALID_ARGS;
40266 }
40267
40268 pDecoder->onRead = onRead;
40269 pDecoder->onSeek = onSeek;
40270 pDecoder->pUserData = pUserData;
40271
40272 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
40273 if (result != MA_SUCCESS) {
40274 return result;
40275 }
40276
40277 return MA_SUCCESS;
40278 }
40279
40280 static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40281 {
40282 ma_result result;
40283
40284 result = ma_decoder__init_data_converter(pDecoder, pConfig);
40285 if (result != MA_SUCCESS) {
40286 return result;
40287 }
40288
40289 return result;
40290 }
40291
40292 MA_API ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40293 {
40294 ma_decoder_config config;
40295 ma_result result;
40296
40297 config = ma_decoder_config_init_copy(pConfig);
40298
40299 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
40300 if (result != MA_SUCCESS) {
40301 return result;
40302 }
40303
40304 #ifdef MA_HAS_WAV
40305 result = ma_decoder_init_wav__internal(&config, pDecoder);
40306 #else
40307 result = MA_NO_BACKEND;
40308 #endif
40309 if (result != MA_SUCCESS) {
40310 return result;
40311 }
40312
40313 return ma_decoder__postinit(&config, pDecoder);
40314 }
40315
40316 MA_API ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40317 {
40318 ma_decoder_config config;
40319 ma_result result;
40320
40321 config = ma_decoder_config_init_copy(pConfig);
40322
40323 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
40324 if (result != MA_SUCCESS) {
40325 return result;
40326 }
40327
40328 #ifdef MA_HAS_FLAC
40329 result = ma_decoder_init_flac__internal(&config, pDecoder);
40330 #else
40331 result = MA_NO_BACKEND;
40332 #endif
40333 if (result != MA_SUCCESS) {
40334 return result;
40335 }
40336
40337 return ma_decoder__postinit(&config, pDecoder);
40338 }
40339
40340 MA_API ma_result ma_decoder_init_vorbis(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40341 {
40342 ma_decoder_config config;
40343 ma_result result;
40344
40345 config = ma_decoder_config_init_copy(pConfig);
40346
40347 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
40348 if (result != MA_SUCCESS) {
40349 return result;
40350 }
40351
40352 #ifdef MA_HAS_VORBIS
40353 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
40354 #else
40355 result = MA_NO_BACKEND;
40356 #endif
40357 if (result != MA_SUCCESS) {
40358 return result;
40359 }
40360
40361 return ma_decoder__postinit(&config, pDecoder);
40362 }
40363
40364 MA_API ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40365 {
40366 ma_decoder_config config;
40367 ma_result result;
40368
40369 config = ma_decoder_config_init_copy(pConfig);
40370
40371 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
40372 if (result != MA_SUCCESS) {
40373 return result;
40374 }
40375
40376 #ifdef MA_HAS_MP3
40377 result = ma_decoder_init_mp3__internal(&config, pDecoder);
40378 #else
40379 result = MA_NO_BACKEND;
40380 #endif
40381 if (result != MA_SUCCESS) {
40382 return result;
40383 }
40384
40385 return ma_decoder__postinit(&config, pDecoder);
40386 }
40387
40388 MA_API ma_result ma_decoder_init_raw(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
40389 {
40390 ma_decoder_config config;
40391 ma_result result;
40392
40393 config = ma_decoder_config_init_copy(pConfigOut);
40394
40395 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
40396 if (result != MA_SUCCESS) {
40397 return result;
40398 }
40399
40400 result = ma_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
40401 if (result != MA_SUCCESS) {
40402 return result;
40403 }
40404
40405 return ma_decoder__postinit(&config, pDecoder);
40406 }
40407
40408 static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40409 {
40410 ma_result result = MA_NO_BACKEND;
40411
40412 MA_ASSERT(pConfig != NULL);
40413 MA_ASSERT(pDecoder != NULL);
40414
40415 /* Silence some warnings in the case that we don't have any decoder backends enabled. */
40416 (void)onRead;
40417 (void)onSeek;
40418 (void)pUserData;
40419 (void)pConfig;
40420 (void)pDecoder;
40421
40422 /* We use trial and error to open a decoder. */
40423
40424 #ifdef MA_HAS_WAV
40425 if (result != MA_SUCCESS) {
40426 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
40427 if (result != MA_SUCCESS) {
40428 onSeek(pDecoder, 0, ma_seek_origin_start);
40429 }
40430 }
40431 #endif
40432 #ifdef MA_HAS_FLAC
40433 if (result != MA_SUCCESS) {
40434 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
40435 if (result != MA_SUCCESS) {
40436 onSeek(pDecoder, 0, ma_seek_origin_start);
40437 }
40438 }
40439 #endif
40440 #ifdef MA_HAS_VORBIS
40441 if (result != MA_SUCCESS) {
40442 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
40443 if (result != MA_SUCCESS) {
40444 onSeek(pDecoder, 0, ma_seek_origin_start);
40445 }
40446 }
40447 #endif
40448 #ifdef MA_HAS_MP3
40449 if (result != MA_SUCCESS) {
40450 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
40451 if (result != MA_SUCCESS) {
40452 onSeek(pDecoder, 0, ma_seek_origin_start);
40453 }
40454 }
40455 #endif
40456
40457 if (result != MA_SUCCESS) {
40458 return result;
40459 }
40460
40461 return ma_decoder__postinit(pConfig, pDecoder);
40462 }
40463
40464 MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40465 {
40466 ma_decoder_config config;
40467 ma_result result;
40468
40469 config = ma_decoder_config_init_copy(pConfig);
40470
40471 result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
40472 if (result != MA_SUCCESS) {
40473 return result;
40474 }
40475
40476 return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
40477 }
40478
40479
40480 static size_t ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
40481 {
40482 size_t bytesRemaining;
40483
40484 MA_ASSERT(pDecoder->memory.dataSize >= pDecoder->memory.currentReadPos);
40485
40486 bytesRemaining = pDecoder->memory.dataSize - pDecoder->memory.currentReadPos;
40487 if (bytesToRead > bytesRemaining) {
40488 bytesToRead = bytesRemaining;
40489 }
40490
40491 if (bytesToRead > 0) {
40492 MA_COPY_MEMORY(pBufferOut, pDecoder->memory.pData + pDecoder->memory.currentReadPos, bytesToRead);
40493 pDecoder->memory.currentReadPos += bytesToRead;
40494 }
40495
40496 return bytesToRead;
40497 }
40498
40499 static ma_bool32 ma_decoder__on_seek_memory(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
40500 {
40501 if (origin == ma_seek_origin_current) {
40502 if (byteOffset > 0) {
40503 if (pDecoder->memory.currentReadPos + byteOffset > pDecoder->memory.dataSize) {
40504 byteOffset = (int)(pDecoder->memory.dataSize - pDecoder->memory.currentReadPos); /* Trying to seek too far forward. */
40505 }
40506 } else {
40507 if (pDecoder->memory.currentReadPos < (size_t)-byteOffset) {
40508 byteOffset = -(int)pDecoder->memory.currentReadPos; /* Trying to seek too far backwards. */
40509 }
40510 }
40511
40512 /* This will never underflow thanks to the clamps above. */
40513 pDecoder->memory.currentReadPos += byteOffset;
40514 } else {
40515 if ((ma_uint32)byteOffset <= pDecoder->memory.dataSize) {
40516 pDecoder->memory.currentReadPos = byteOffset;
40517 } else {
40518 pDecoder->memory.currentReadPos = pDecoder->memory.dataSize; /* Trying to seek too far forward. */
40519 }
40520 }
40521
40522 return MA_TRUE;
40523 }
40524
40525 static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40526 {
40527 ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, pConfig, pDecoder);
40528 if (result != MA_SUCCESS) {
40529 return result;
40530 }
40531
40532 if (pData == NULL || dataSize == 0) {
40533 return MA_INVALID_ARGS;
40534 }
40535
40536 pDecoder->memory.pData = (const ma_uint8*)pData;
40537 pDecoder->memory.dataSize = dataSize;
40538 pDecoder->memory.currentReadPos = 0;
40539
40540 (void)pConfig;
40541 return MA_SUCCESS;
40542 }
40543
40544 MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40545 {
40546 ma_decoder_config config;
40547 ma_result result;
40548
40549 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
40550
40551 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
40552 if (result != MA_SUCCESS) {
40553 return result;
40554 }
40555
40556 return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
40557 }
40558
40559 MA_API ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40560 {
40561 ma_decoder_config config;
40562 ma_result result;
40563
40564 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
40565
40566 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
40567 if (result != MA_SUCCESS) {
40568 return result;
40569 }
40570
40571 #ifdef MA_HAS_WAV
40572 result = ma_decoder_init_wav__internal(&config, pDecoder);
40573 #else
40574 result = MA_NO_BACKEND;
40575 #endif
40576 if (result != MA_SUCCESS) {
40577 return result;
40578 }
40579
40580 return ma_decoder__postinit(&config, pDecoder);
40581 }
40582
40583 MA_API ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40584 {
40585 ma_decoder_config config;
40586 ma_result result;
40587
40588 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
40589
40590 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
40591 if (result != MA_SUCCESS) {
40592 return result;
40593 }
40594
40595 #ifdef MA_HAS_FLAC
40596 result = ma_decoder_init_flac__internal(&config, pDecoder);
40597 #else
40598 result = MA_NO_BACKEND;
40599 #endif
40600 if (result != MA_SUCCESS) {
40601 return result;
40602 }
40603
40604 return ma_decoder__postinit(&config, pDecoder);
40605 }
40606
40607 MA_API ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40608 {
40609 ma_decoder_config config;
40610 ma_result result;
40611
40612 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
40613
40614 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
40615 if (result != MA_SUCCESS) {
40616 return result;
40617 }
40618
40619 #ifdef MA_HAS_VORBIS
40620 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
40621 #else
40622 result = MA_NO_BACKEND;
40623 #endif
40624 if (result != MA_SUCCESS) {
40625 return result;
40626 }
40627
40628 return ma_decoder__postinit(&config, pDecoder);
40629 }
40630
40631 MA_API ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40632 {
40633 ma_decoder_config config;
40634 ma_result result;
40635
40636 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
40637
40638 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
40639 if (result != MA_SUCCESS) {
40640 return result;
40641 }
40642
40643 #ifdef MA_HAS_MP3
40644 result = ma_decoder_init_mp3__internal(&config, pDecoder);
40645 #else
40646 result = MA_NO_BACKEND;
40647 #endif
40648 if (result != MA_SUCCESS) {
40649 return result;
40650 }
40651
40652 return ma_decoder__postinit(&config, pDecoder);
40653 }
40654
40655 MA_API ma_result ma_decoder_init_memory_raw(const void* pData, size_t dataSize, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
40656 {
40657 ma_decoder_config config;
40658 ma_result result;
40659
40660 config = ma_decoder_config_init_copy(pConfigOut); /* Make sure the config is not NULL. */
40661
40662 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
40663 if (result != MA_SUCCESS) {
40664 return result;
40665 }
40666
40667 result = ma_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
40668 if (result != MA_SUCCESS) {
40669 return result;
40670 }
40671
40672 return ma_decoder__postinit(&config, pDecoder);
40673 }
40674
40675 static const char* ma_path_file_name(const char* path)
40676 {
40677 const char* fileName;
40678
40679 if (path == NULL) {
40680 return NULL;
40681 }
40682
40683 fileName = path;
40684
40685 /* We just loop through the path until we find the last slash. */
40686 while (path[0] != '\0') {
40687 if (path[0] == '/' || path[0] == '\\') {
40688 fileName = path;
40689 }
40690
40691 path += 1;
40692 }
40693
40694 /* At this point the file name is sitting on a slash, so just move forward. */
40695 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
40696 fileName += 1;
40697 }
40698
40699 return fileName;
40700 }
40701
40702 static const wchar_t* ma_path_file_name_w(const wchar_t* path)
40703 {
40704 const wchar_t* fileName;
40705
40706 if (path == NULL) {
40707 return NULL;
40708 }
40709
40710 fileName = path;
40711
40712 /* We just loop through the path until we find the last slash. */
40713 while (path[0] != '\0') {
40714 if (path[0] == '/' || path[0] == '\\') {
40715 fileName = path;
40716 }
40717
40718 path += 1;
40719 }
40720
40721 /* At this point the file name is sitting on a slash, so just move forward. */
40722 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
40723 fileName += 1;
40724 }
40725
40726 return fileName;
40727 }
40728
40729
40730 static const char* ma_path_extension(const char* path)
40731 {
40732 const char* extension;
40733 const char* lastOccurance;
40734
40735 if (path == NULL) {
40736 path = "";
40737 }
40738
40739 extension = ma_path_file_name(path);
40740 lastOccurance = NULL;
40741
40742 /* Just find the last '.' and return. */
40743 while (extension[0] != '\0') {
40744 if (extension[0] == '.') {
40745 extension += 1;
40746 lastOccurance = extension;
40747 }
40748
40749 extension += 1;
40750 }
40751
40752 return (lastOccurance != NULL) ? lastOccurance : extension;
40753 }
40754
40755 static const wchar_t* ma_path_extension_w(const wchar_t* path)
40756 {
40757 const wchar_t* extension;
40758 const wchar_t* lastOccurance;
40759
40760 if (path == NULL) {
40761 path = L"";
40762 }
40763
40764 extension = ma_path_file_name_w(path);
40765 lastOccurance = NULL;
40766
40767 /* Just find the last '.' and return. */
40768 while (extension[0] != '\0') {
40769 if (extension[0] == '.') {
40770 extension += 1;
40771 lastOccurance = extension;
40772 }
40773
40774 extension += 1;
40775 }
40776
40777 return (lastOccurance != NULL) ? lastOccurance : extension;
40778 }
40779
40780
40781 static ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
40782 {
40783 const char* ext1;
40784 const char* ext2;
40785
40786 if (path == NULL || extension == NULL) {
40787 return MA_FALSE;
40788 }
40789
40790 ext1 = extension;
40791 ext2 = ma_path_extension(path);
40792
40793 #if defined(_MSC_VER) || defined(__DMC__)
40794 return _stricmp(ext1, ext2) == 0;
40795 #else
40796 return strcasecmp(ext1, ext2) == 0;
40797 #endif
40798 }
40799
40800 static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)
40801 {
40802 const wchar_t* ext1;
40803 const wchar_t* ext2;
40804
40805 if (path == NULL || extension == NULL) {
40806 return MA_FALSE;
40807 }
40808
40809 ext1 = extension;
40810 ext2 = ma_path_extension_w(path);
40811
40812 #if defined(_MSC_VER) || defined(__DMC__)
40813 return _wcsicmp(ext1, ext2) == 0;
40814 #else
40815 /*
40816 I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This
40817 isn't the most efficient way to do it, but it should work OK.
40818 */
40819 {
40820 char ext1MB[4096];
40821 char ext2MB[4096];
40822 const wchar_t* pext1 = ext1;
40823 const wchar_t* pext2 = ext2;
40824 mbstate_t mbs1;
40825 mbstate_t mbs2;
40826
40827 MA_ZERO_OBJECT(&mbs1);
40828 MA_ZERO_OBJECT(&mbs2);
40829
40830 if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {
40831 return MA_FALSE;
40832 }
40833 if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {
40834 return MA_FALSE;
40835 }
40836
40837 return strcasecmp(ext1MB, ext2MB) == 0;
40838 }
40839 #endif
40840 }
40841
40842
40843 static size_t ma_decoder__on_read_stdio(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
40844 {
40845 return fread(pBufferOut, 1, bytesToRead, (FILE*)pDecoder->pUserData);
40846 }
40847
40848 static ma_bool32 ma_decoder__on_seek_stdio(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
40849 {
40850 return fseek((FILE*)pDecoder->pUserData, byteOffset, (origin == ma_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
40851 }
40852
40853 static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40854 {
40855 ma_result result;
40856 FILE* pFile;
40857
40858 if (pDecoder == NULL) {
40859 return MA_INVALID_ARGS;
40860 }
40861
40862 MA_ZERO_OBJECT(pDecoder);
40863
40864 if (pFilePath == NULL || pFilePath[0] == '\0') {
40865 return MA_INVALID_ARGS;
40866 }
40867
40868 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
40869 if (result != MA_SUCCESS) {
40870 return result;
40871 }
40872
40873 result = ma_fopen(&pFile, pFilePath, "rb");
40874 if (pFile == NULL) {
40875 return result;
40876 }
40877
40878 /* We need to manually set the user data so the calls to ma_decoder__on_seek_stdio() succeed. */
40879 pDecoder->pUserData = pFile;
40880
40881 return MA_SUCCESS;
40882 }
40883
40884 static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40885 {
40886 ma_result result;
40887 FILE* pFile;
40888
40889 if (pDecoder == NULL) {
40890 return MA_INVALID_ARGS;
40891 }
40892
40893 MA_ZERO_OBJECT(pDecoder);
40894
40895 if (pFilePath == NULL || pFilePath[0] == '\0') {
40896 return MA_INVALID_ARGS;
40897 }
40898
40899 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
40900 if (result != MA_SUCCESS) {
40901 return result;
40902 }
40903
40904 result = ma_wfopen(&pFile, pFilePath, L"rb", &pDecoder->allocationCallbacks);
40905 if (pFile == NULL) {
40906 return result;
40907 }
40908
40909 /* We need to manually set the user data so the calls to ma_decoder__on_seek_stdio() succeed. */
40910 pDecoder->pUserData = pFile;
40911
40912 (void)pConfig;
40913 return MA_SUCCESS;
40914 }
40915
40916 MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40917 {
40918 ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder); /* This sets pDecoder->pUserData to a FILE*. */
40919 if (result != MA_SUCCESS) {
40920 return result;
40921 }
40922
40923 /* WAV */
40924 if (ma_path_extension_equal(pFilePath, "wav")) {
40925 result = ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
40926 if (result == MA_SUCCESS) {
40927 return MA_SUCCESS;
40928 }
40929
40930 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
40931 }
40932
40933 /* FLAC */
40934 if (ma_path_extension_equal(pFilePath, "flac")) {
40935 result = ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
40936 if (result == MA_SUCCESS) {
40937 return MA_SUCCESS;
40938 }
40939
40940 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
40941 }
40942
40943 /* MP3 */
40944 if (ma_path_extension_equal(pFilePath, "mp3")) {
40945 result = ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
40946 if (result == MA_SUCCESS) {
40947 return MA_SUCCESS;
40948 }
40949
40950 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
40951 }
40952
40953 /* Trial and error. */
40954 return ma_decoder_init(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
40955 }
40956
40957 MA_API ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40958 {
40959 ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
40960 if (result != MA_SUCCESS) {
40961 return result;
40962 }
40963
40964 return ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
40965 }
40966
40967 MA_API ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40968 {
40969 ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
40970 if (result != MA_SUCCESS) {
40971 return result;
40972 }
40973
40974 return ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
40975 }
40976
40977 MA_API ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40978 {
40979 ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
40980 if (result != MA_SUCCESS) {
40981 return result;
40982 }
40983
40984 return ma_decoder_init_vorbis(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
40985 }
40986
40987 MA_API ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40988 {
40989 ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
40990 if (result != MA_SUCCESS) {
40991 return result;
40992 }
40993
40994 return ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
40995 }
40996
40997
40998 MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
40999 {
41000 ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder); /* This sets pDecoder->pUserData to a FILE*. */
41001 if (result != MA_SUCCESS) {
41002 return result;
41003 }
41004
41005 /* WAV */
41006 if (ma_path_extension_equal_w(pFilePath, L"wav")) {
41007 result = ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
41008 if (result == MA_SUCCESS) {
41009 return MA_SUCCESS;
41010 }
41011
41012 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
41013 }
41014
41015 /* FLAC */
41016 if (ma_path_extension_equal_w(pFilePath, L"flac")) {
41017 result = ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
41018 if (result == MA_SUCCESS) {
41019 return MA_SUCCESS;
41020 }
41021
41022 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
41023 }
41024
41025 /* MP3 */
41026 if (ma_path_extension_equal_w(pFilePath, L"mp3")) {
41027 result = ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
41028 if (result == MA_SUCCESS) {
41029 return MA_SUCCESS;
41030 }
41031
41032 ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
41033 }
41034
41035 /* Trial and error. */
41036 return ma_decoder_init(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
41037 }
41038
41039 MA_API ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
41040 {
41041 ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
41042 if (result != MA_SUCCESS) {
41043 return result;
41044 }
41045
41046 return ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
41047 }
41048
41049 MA_API ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
41050 {
41051 ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
41052 if (result != MA_SUCCESS) {
41053 return result;
41054 }
41055
41056 return ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
41057 }
41058
41059 MA_API ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
41060 {
41061 ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
41062 if (result != MA_SUCCESS) {
41063 return result;
41064 }
41065
41066 return ma_decoder_init_vorbis(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
41067 }
41068
41069 MA_API ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
41070 {
41071 ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
41072 if (result != MA_SUCCESS) {
41073 return result;
41074 }
41075
41076 return ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
41077 }
41078
41079 MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder)
41080 {
41081 if (pDecoder == NULL) {
41082 return MA_INVALID_ARGS;
41083 }
41084
41085 if (pDecoder->onUninit) {
41086 pDecoder->onUninit(pDecoder);
41087 }
41088
41089 /* If we have a file handle, close it. */
41090 if (pDecoder->onRead == ma_decoder__on_read_stdio) {
41091 fclose((FILE*)pDecoder->pUserData);
41092 }
41093
41094 ma_data_converter_uninit(&pDecoder->converter);
41095
41096 return MA_SUCCESS;
41097 }
41098
41099 MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder)
41100 {
41101 if (pDecoder == NULL) {
41102 return 0;
41103 }
41104
41105 if (pDecoder->onGetLengthInPCMFrames) {
41106 ma_uint64 nativeLengthInPCMFrames = pDecoder->onGetLengthInPCMFrames(pDecoder);
41107 if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) {
41108 return nativeLengthInPCMFrames;
41109 } else {
41110 return ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, pDecoder->internalSampleRate, nativeLengthInPCMFrames);
41111 }
41112 }
41113
41114 return 0;
41115 }
41116
41117 MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
41118 {
41119 ma_result result;
41120 ma_uint64 totalFramesReadOut;
41121 ma_uint64 totalFramesReadIn;
41122 void* pRunningFramesOut;
41123
41124 if (pDecoder == NULL) {
41125 return 0;
41126 }
41127
41128 if (pDecoder->onReadPCMFrames == NULL) {
41129 return 0;
41130 }
41131
41132 /* Fast path. */
41133 if (pDecoder->converter.isPassthrough) {
41134 return pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount);
41135 }
41136
41137 /* Getting here means we need to do data conversion. */
41138 totalFramesReadOut = 0;
41139 totalFramesReadIn = 0;
41140 pRunningFramesOut = pFramesOut;
41141
41142 while (totalFramesReadOut < frameCount) {
41143 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */
41144 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
41145 ma_uint64 framesToReadThisIterationIn;
41146 ma_uint64 framesReadThisIterationIn;
41147 ma_uint64 framesToReadThisIterationOut;
41148 ma_uint64 framesReadThisIterationOut;
41149 ma_uint64 requiredInputFrameCount;
41150
41151 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
41152 framesToReadThisIterationIn = framesToReadThisIterationOut;
41153 if (framesToReadThisIterationIn > intermediaryBufferCap) {
41154 framesToReadThisIterationIn = intermediaryBufferCap;
41155 }
41156
41157 requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut);
41158 if (framesToReadThisIterationIn > requiredInputFrameCount) {
41159 framesToReadThisIterationIn = requiredInputFrameCount;
41160 }
41161
41162 if (requiredInputFrameCount > 0) {
41163 framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn);
41164 totalFramesReadIn += framesReadThisIterationIn;
41165 }
41166
41167 /*
41168 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
41169 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
41170 */
41171 framesReadThisIterationOut = framesToReadThisIterationOut;
41172 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
41173 if (result != MA_SUCCESS) {
41174 break;
41175 }
41176
41177 totalFramesReadOut += framesReadThisIterationOut;
41178 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
41179
41180 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
41181 break; /* We're done. */
41182 }
41183 }
41184
41185 return totalFramesReadOut;
41186 }
41187
41188 MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex)
41189 {
41190 if (pDecoder == NULL) {
41191 return 0;
41192 }
41193
41194 if (pDecoder->onSeekToPCMFrame) {
41195 return pDecoder->onSeekToPCMFrame(pDecoder, frameIndex);
41196 }
41197
41198 /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
41199 return MA_INVALID_ARGS;
41200 }
41201
41202
41203 static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
41204 {
41205 ma_uint64 totalFrameCount;
41206 ma_uint64 bpf;
41207 ma_uint64 dataCapInFrames;
41208 void* pPCMFramesOut;
41209
41210 MA_ASSERT(pDecoder != NULL);
41211
41212 totalFrameCount = 0;
41213 bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
41214
41215 /* The frame count is unknown until we try reading. Thus, we just run in a loop. */
41216 dataCapInFrames = 0;
41217 pPCMFramesOut = NULL;
41218 for (;;) {
41219 ma_uint64 frameCountToTryReading;
41220 ma_uint64 framesJustRead;
41221
41222 /* Make room if there's not enough. */
41223 if (totalFrameCount == dataCapInFrames) {
41224 void* pNewPCMFramesOut;
41225 ma_uint64 oldDataCapInFrames = dataCapInFrames;
41226 ma_uint64 newDataCapInFrames = dataCapInFrames*2;
41227 if (newDataCapInFrames == 0) {
41228 newDataCapInFrames = 4096;
41229 }
41230
41231 if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
41232 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
41233 return MA_TOO_BIG;
41234 }
41235
41236
41237 pNewPCMFramesOut = (void*)ma__realloc_from_callbacks(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), (size_t)(oldDataCapInFrames * bpf), &pDecoder->allocationCallbacks);
41238 if (pNewPCMFramesOut == NULL) {
41239 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
41240 return MA_OUT_OF_MEMORY;
41241 }
41242
41243 dataCapInFrames = newDataCapInFrames;
41244 pPCMFramesOut = pNewPCMFramesOut;
41245 }
41246
41247 frameCountToTryReading = dataCapInFrames - totalFrameCount;
41248 MA_ASSERT(frameCountToTryReading > 0);
41249
41250 framesJustRead = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading);
41251 totalFrameCount += framesJustRead;
41252
41253 if (framesJustRead < frameCountToTryReading) {
41254 break;
41255 }
41256 }
41257
41258
41259 if (pConfigOut != NULL) {
41260 pConfigOut->format = pDecoder->outputFormat;
41261 pConfigOut->channels = pDecoder->outputChannels;
41262 pConfigOut->sampleRate = pDecoder->outputSampleRate;
41263 ma_channel_map_copy(pConfigOut->channelMap, pDecoder->outputChannelMap, pDecoder->outputChannels);
41264 }
41265
41266 if (ppPCMFramesOut != NULL) {
41267 *ppPCMFramesOut = pPCMFramesOut;
41268 } else {
41269 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
41270 }
41271
41272 if (pFrameCountOut != NULL) {
41273 *pFrameCountOut = totalFrameCount;
41274 }
41275
41276 ma_decoder_uninit(pDecoder);
41277 return MA_SUCCESS;
41278 }
41279
41280 MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
41281 {
41282 ma_decoder_config config;
41283 ma_decoder decoder;
41284 ma_result result;
41285
41286 if (pFrameCountOut != NULL) {
41287 *pFrameCountOut = 0;
41288 }
41289 if (ppPCMFramesOut != NULL) {
41290 *ppPCMFramesOut = NULL;
41291 }
41292
41293 if (pFilePath == NULL) {
41294 return MA_INVALID_ARGS;
41295 }
41296
41297 config = ma_decoder_config_init_copy(pConfig);
41298
41299 result = ma_decoder_init_file(pFilePath, &config, &decoder);
41300 if (result != MA_SUCCESS) {
41301 return result;
41302 }
41303
41304 return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
41305 }
41306
41307 MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
41308 {
41309 ma_decoder_config config;
41310 ma_decoder decoder;
41311 ma_result result;
41312
41313 if (pFrameCountOut != NULL) {
41314 *pFrameCountOut = 0;
41315 }
41316 if (ppPCMFramesOut != NULL) {
41317 *ppPCMFramesOut = NULL;
41318 }
41319
41320 if (pData == NULL || dataSize == 0) {
41321 return MA_INVALID_ARGS;
41322 }
41323
41324 config = ma_decoder_config_init_copy(pConfig);
41325
41326 result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
41327 if (result != MA_SUCCESS) {
41328 return result;
41329 }
41330
41331 return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
41332 }
41333 #endif /* MA_NO_DECODING */
41334
41335
41336 #ifndef MA_NO_ENCODING
41337
41338 #if defined(MA_HAS_WAV)
41339 static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite)
41340 {
41341 ma_encoder* pEncoder = (ma_encoder*)pUserData;
41342 MA_ASSERT(pEncoder != NULL);
41343
41344 return pEncoder->onWrite(pEncoder, pData, bytesToWrite);
41345 }
41346
41347 static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin)
41348 {
41349 ma_encoder* pEncoder = (ma_encoder*)pUserData;
41350 MA_ASSERT(pEncoder != NULL);
41351
41352 return pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
41353 }
41354
41355 static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder)
41356 {
41357 drwav_data_format wavFormat;
41358 drwav_allocation_callbacks allocationCallbacks;
41359 drwav* pWav;
41360
41361 MA_ASSERT(pEncoder != NULL);
41362
41363 pWav = (drwav*)ma__malloc_from_callbacks(sizeof(*pWav), &pEncoder->config.allocationCallbacks);
41364 if (pWav == NULL) {
41365 return MA_OUT_OF_MEMORY;
41366 }
41367
41368 wavFormat.container = drwav_container_riff;
41369 wavFormat.channels = pEncoder->config.channels;
41370 wavFormat.sampleRate = pEncoder->config.sampleRate;
41371 wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8;
41372 if (pEncoder->config.format == ma_format_f32) {
41373 wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT;
41374 } else {
41375 wavFormat.format = DR_WAVE_FORMAT_PCM;
41376 }
41377
41378 allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData;
41379 allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc;
41380 allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc;
41381 allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree;
41382
41383 if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) {
41384 return MA_ERROR;
41385 }
41386
41387 pEncoder->pInternalEncoder = pWav;
41388
41389 return MA_SUCCESS;
41390 }
41391
41392 static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder)
41393 {
41394 drwav* pWav;
41395
41396 MA_ASSERT(pEncoder != NULL);
41397
41398 pWav = (drwav*)pEncoder->pInternalEncoder;
41399 MA_ASSERT(pWav != NULL);
41400
41401 drwav_uninit(pWav);
41402 ma__free_from_callbacks(pWav, &pEncoder->config.allocationCallbacks);
41403 }
41404
41405 static ma_uint64 ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount)
41406 {
41407 drwav* pWav;
41408
41409 MA_ASSERT(pEncoder != NULL);
41410
41411 pWav = (drwav*)pEncoder->pInternalEncoder;
41412 MA_ASSERT(pWav != NULL);
41413
41414 return drwav_write_pcm_frames(pWav, frameCount, pFramesIn);
41415 }
41416 #endif
41417
41418 MA_API ma_encoder_config ma_encoder_config_init(ma_resource_format resourceFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
41419 {
41420 ma_encoder_config config;
41421
41422 MA_ZERO_OBJECT(&config);
41423 config.resourceFormat = resourceFormat;
41424 config.format = format;
41425 config.channels = channels;
41426 config.sampleRate = sampleRate;
41427
41428 return config;
41429 }
41430
41431 MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder)
41432 {
41433 ma_result result;
41434
41435 if (pEncoder == NULL) {
41436 return MA_INVALID_ARGS;
41437 }
41438
41439 MA_ZERO_OBJECT(pEncoder);
41440
41441 if (pConfig == NULL) {
41442 return MA_INVALID_ARGS;
41443 }
41444
41445 if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) {
41446 return MA_INVALID_ARGS;
41447 }
41448
41449 pEncoder->config = *pConfig;
41450
41451 result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks);
41452 if (result != MA_SUCCESS) {
41453 return result;
41454 }
41455
41456 return MA_SUCCESS;
41457 }
41458
41459 MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder)
41460 {
41461 ma_result result = MA_SUCCESS;
41462
41463 /* This assumes ma_encoder_preinit() has been called prior. */
41464 MA_ASSERT(pEncoder != NULL);
41465
41466 if (onWrite == NULL || onSeek == NULL) {
41467 return MA_INVALID_ARGS;
41468 }
41469
41470 pEncoder->onWrite = onWrite;
41471 pEncoder->onSeek = onSeek;
41472 pEncoder->pUserData = pUserData;
41473
41474 switch (pEncoder->config.resourceFormat)
41475 {
41476 case ma_resource_format_wav:
41477 {
41478 #if defined(MA_HAS_WAV)
41479 pEncoder->onInit = ma_encoder__on_init_wav;
41480 pEncoder->onUninit = ma_encoder__on_uninit_wav;
41481 pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav;
41482 #else
41483 result = MA_NO_BACKEND;
41484 #endif
41485 } break;
41486
41487 default:
41488 {
41489 result = MA_INVALID_ARGS;
41490 } break;
41491 }
41492
41493 /* Getting here means we should have our backend callbacks set up. */
41494 if (result == MA_SUCCESS) {
41495 result = pEncoder->onInit(pEncoder);
41496 if (result != MA_SUCCESS) {
41497 return result;
41498 }
41499 }
41500
41501 return MA_SUCCESS;
41502 }
41503
41504 MA_API size_t ma_encoder__on_write_stdio(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite)
41505 {
41506 return fwrite(pBufferIn, 1, bytesToWrite, (FILE*)pEncoder->pFile);
41507 }
41508
41509 MA_API ma_bool32 ma_encoder__on_seek_stdio(ma_encoder* pEncoder, int byteOffset, ma_seek_origin origin)
41510 {
41511 return fseek((FILE*)pEncoder->pFile, byteOffset, (origin == ma_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
41512 }
41513
41514 MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
41515 {
41516 ma_result result;
41517 FILE* pFile;
41518
41519 result = ma_encoder_preinit(pConfig, pEncoder);
41520 if (result != MA_SUCCESS) {
41521 return result;
41522 }
41523
41524 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
41525 result = ma_fopen(&pFile, pFilePath, "wb");
41526 if (pFile == NULL) {
41527 return result;
41528 }
41529
41530 pEncoder->pFile = pFile;
41531
41532 return ma_encoder_init__internal(ma_encoder__on_write_stdio, ma_encoder__on_seek_stdio, NULL, pEncoder);
41533 }
41534
41535 MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
41536 {
41537 ma_result result;
41538 FILE* pFile;
41539
41540 result = ma_encoder_preinit(pConfig, pEncoder);
41541 if (result != MA_SUCCESS) {
41542 return result;
41543 }
41544
41545 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
41546 result = ma_wfopen(&pFile, pFilePath, L"wb", &pEncoder->config.allocationCallbacks);
41547 if (pFile != NULL) {
41548 return result;
41549 }
41550
41551 pEncoder->pFile = pFile;
41552
41553 return ma_encoder_init__internal(ma_encoder__on_write_stdio, ma_encoder__on_seek_stdio, NULL, pEncoder);
41554 }
41555
41556 MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
41557 {
41558 ma_result result;
41559
41560 result = ma_encoder_preinit(pConfig, pEncoder);
41561 if (result != MA_SUCCESS) {
41562 return result;
41563 }
41564
41565 return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder);
41566 }
41567
41568
41569 MA_API void ma_encoder_uninit(ma_encoder* pEncoder)
41570 {
41571 if (pEncoder == NULL) {
41572 return;
41573 }
41574
41575 if (pEncoder->onUninit) {
41576 pEncoder->onUninit(pEncoder);
41577 }
41578
41579 /* If we have a file handle, close it. */
41580 if (pEncoder->onWrite == ma_encoder__on_write_stdio) {
41581 fclose((FILE*)pEncoder->pFile);
41582 }
41583 }
41584
41585
41586 MA_API ma_uint64 ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount)
41587 {
41588 if (pEncoder == NULL || pFramesIn == NULL) {
41589 return 0;
41590 }
41591
41592 return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount);
41593 }
41594 #endif /* MA_NO_ENCODING */
41595
41596
41597
41598 /**************************************************************************************************************************************************************
41599
41600 Generation
41601
41602 **************************************************************************************************************************************************************/
41603 MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency)
41604 {
41605 ma_waveform_config config;
41606
41607 MA_ZERO_OBJECT(&config);
41608 config.format = format;
41609 config.channels = channels;
41610 config.sampleRate = sampleRate;
41611 config.type = type;
41612 config.amplitude = amplitude;
41613 config.frequency = frequency;
41614
41615 return config;
41616 }
41617
41618 MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform)
41619 {
41620 if (pWaveform == NULL) {
41621 return MA_INVALID_ARGS;
41622 }
41623
41624 MA_ZERO_OBJECT(pWaveform);
41625 pWaveform->config = *pConfig;
41626 pWaveform->advance = 1.0 / pWaveform->config.sampleRate;
41627 pWaveform->time = 0;
41628
41629 return MA_SUCCESS;
41630 }
41631
41632 MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude)
41633 {
41634 if (pWaveform == NULL) {
41635 return MA_INVALID_ARGS;
41636 }
41637
41638 pWaveform->config.amplitude = amplitude;
41639 return MA_SUCCESS;
41640 }
41641
41642 MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency)
41643 {
41644 if (pWaveform == NULL) {
41645 return MA_INVALID_ARGS;
41646 }
41647
41648 pWaveform->config.frequency = frequency;
41649 return MA_SUCCESS;
41650 }
41651
41652 MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate)
41653 {
41654 if (pWaveform == NULL) {
41655 return MA_INVALID_ARGS;
41656 }
41657
41658 pWaveform->advance = 1.0 / sampleRate;
41659 return MA_SUCCESS;
41660 }
41661
41662 static float ma_waveform_sine_f32(double time, double frequency, double amplitude)
41663 {
41664 return (float)(ma_sin(MA_TAU_D * time * frequency) * amplitude);
41665 }
41666
41667 static ma_int16 ma_waveform_sine_s16(double time, double frequency, double amplitude)
41668 {
41669 return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, frequency, amplitude));
41670 }
41671
41672 static float ma_waveform_square_f32(double time, double frequency, double amplitude)
41673 {
41674 double t = time * frequency;
41675 double f = t - (ma_int64)t;
41676 double r;
41677
41678 if (f < 0.5) {
41679 r = amplitude;
41680 } else {
41681 r = -amplitude;
41682 }
41683
41684 return (float)r;
41685 }
41686
41687 static ma_int16 ma_waveform_square_s16(double time, double frequency, double amplitude)
41688 {
41689 return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, frequency, amplitude));
41690 }
41691
41692 static float ma_waveform_triangle_f32(double time, double frequency, double amplitude)
41693 {
41694 double t = time * frequency;
41695 double f = t - (ma_int64)t;
41696 double r;
41697
41698 r = 2 * ma_abs(2 * (f - 0.5)) - 1;
41699
41700 return (float)(r * amplitude);
41701 }
41702
41703 static ma_int16 ma_waveform_triangle_s16(double time, double frequency, double amplitude)
41704 {
41705 return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, frequency, amplitude));
41706 }
41707
41708 static float ma_waveform_sawtooth_f32(double time, double frequency, double amplitude)
41709 {
41710 double t = time * frequency;
41711 double f = t - (ma_int64)t;
41712 double r;
41713
41714 r = 2 * (f - 0.5);
41715
41716 return (float)(r * amplitude);
41717 }
41718
41719 static ma_int16 ma_waveform_sawtooth_s16(double time, double frequency, double amplitude)
41720 {
41721 return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, frequency, amplitude));
41722 }
41723
41724 static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
41725 {
41726 ma_uint64 iFrame;
41727 ma_uint64 iChannel;
41728 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
41729 ma_uint32 bpf = bps * pWaveform->config.channels;
41730
41731 MA_ASSERT(pWaveform != NULL);
41732 MA_ASSERT(pFramesOut != NULL);
41733
41734 if (pWaveform->config.format == ma_format_f32) {
41735 float* pFramesOutF32 = (float*)pFramesOut;
41736 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41737 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41738 pWaveform->time += pWaveform->advance;
41739
41740 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41741 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
41742 }
41743 }
41744 } else if (pWaveform->config.format == ma_format_s16) {
41745 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
41746 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41747 ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41748 pWaveform->time += pWaveform->advance;
41749
41750 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41751 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
41752 }
41753 }
41754 } else {
41755 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41756 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41757 pWaveform->time += pWaveform->advance;
41758
41759 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41760 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
41761 }
41762 }
41763 }
41764 }
41765
41766 static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
41767 {
41768 ma_uint64 iFrame;
41769 ma_uint64 iChannel;
41770 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
41771 ma_uint32 bpf = bps * pWaveform->config.channels;
41772
41773 MA_ASSERT(pWaveform != NULL);
41774 MA_ASSERT(pFramesOut != NULL);
41775
41776 if (pWaveform->config.format == ma_format_f32) {
41777 float* pFramesOutF32 = (float*)pFramesOut;
41778 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41779 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41780 pWaveform->time += pWaveform->advance;
41781
41782 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41783 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
41784 }
41785 }
41786 } else if (pWaveform->config.format == ma_format_s16) {
41787 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
41788 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41789 ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41790 pWaveform->time += pWaveform->advance;
41791
41792 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41793 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
41794 }
41795 }
41796 } else {
41797 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41798 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41799 pWaveform->time += pWaveform->advance;
41800
41801 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41802 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
41803 }
41804 }
41805 }
41806 }
41807
41808 static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
41809 {
41810 ma_uint64 iFrame;
41811 ma_uint64 iChannel;
41812 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
41813 ma_uint32 bpf = bps * pWaveform->config.channels;
41814
41815 MA_ASSERT(pWaveform != NULL);
41816 MA_ASSERT(pFramesOut != NULL);
41817
41818 if (pWaveform->config.format == ma_format_f32) {
41819 float* pFramesOutF32 = (float*)pFramesOut;
41820 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41821 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41822 pWaveform->time += pWaveform->advance;
41823
41824 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41825 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
41826 }
41827 }
41828 } else if (pWaveform->config.format == ma_format_s16) {
41829 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
41830 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41831 ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41832 pWaveform->time += pWaveform->advance;
41833
41834 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41835 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
41836 }
41837 }
41838 } else {
41839 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41840 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41841 pWaveform->time += pWaveform->advance;
41842
41843 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41844 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
41845 }
41846 }
41847 }
41848 }
41849
41850 static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
41851 {
41852 ma_uint64 iFrame;
41853 ma_uint64 iChannel;
41854 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
41855 ma_uint32 bpf = bps * pWaveform->config.channels;
41856
41857 MA_ASSERT(pWaveform != NULL);
41858 MA_ASSERT(pFramesOut != NULL);
41859
41860 if (pWaveform->config.format == ma_format_f32) {
41861 float* pFramesOutF32 = (float*)pFramesOut;
41862 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41863 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41864 pWaveform->time += pWaveform->advance;
41865
41866 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41867 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
41868 }
41869 }
41870 } else if (pWaveform->config.format == ma_format_s16) {
41871 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
41872 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41873 ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41874 pWaveform->time += pWaveform->advance;
41875
41876 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41877 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
41878 }
41879 }
41880 } else {
41881 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41882 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.frequency, pWaveform->config.amplitude);
41883 pWaveform->time += pWaveform->advance;
41884
41885 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
41886 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
41887 }
41888 }
41889 }
41890 }
41891
41892 MA_API ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
41893 {
41894 if (pWaveform == NULL) {
41895 return 0;
41896 }
41897
41898 if (pFramesOut != NULL) {
41899 switch (pWaveform->config.type)
41900 {
41901 case ma_waveform_type_sine:
41902 {
41903 ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount);
41904 } break;
41905
41906 case ma_waveform_type_square:
41907 {
41908 ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount);
41909 } break;
41910
41911 case ma_waveform_type_triangle:
41912 {
41913 ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount);
41914 } break;
41915
41916 case ma_waveform_type_sawtooth:
41917 {
41918 ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount);
41919 } break;
41920
41921 default: return 0;
41922 }
41923 } else {
41924 pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */
41925 }
41926
41927 return frameCount;
41928 }
41929
41930
41931 MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude)
41932 {
41933 ma_noise_config config;
41934 MA_ZERO_OBJECT(&config);
41935
41936 config.format = format;
41937 config.channels = channels;
41938 config.type = type;
41939 config.seed = seed;
41940 config.amplitude = amplitude;
41941
41942 if (config.seed == 0) {
41943 config.seed = MA_DEFAULT_LCG_SEED;
41944 }
41945
41946 return config;
41947 }
41948
41949 MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise)
41950 {
41951 if (pNoise == NULL) {
41952 return MA_INVALID_ARGS;
41953 }
41954
41955 MA_ZERO_OBJECT(pNoise);
41956
41957 if (pConfig == NULL) {
41958 return MA_INVALID_ARGS;
41959 }
41960
41961 pNoise->config = *pConfig;
41962 ma_lcg_seed(&pNoise->lcg, pConfig->seed);
41963
41964 if (pNoise->config.type == ma_noise_type_pink) {
41965 ma_uint32 iChannel;
41966 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
41967 pNoise->state.pink.accumulation[iChannel] = 0;
41968 pNoise->state.pink.counter[iChannel] = 1;
41969 }
41970 }
41971
41972 if (pNoise->config.type == ma_noise_type_brownian) {
41973 ma_uint32 iChannel;
41974 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
41975 pNoise->state.brownian.accumulation[iChannel] = 0;
41976 }
41977 }
41978
41979 return MA_SUCCESS;
41980 }
41981
41982 static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise)
41983 {
41984 return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude);
41985 }
41986
41987 static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise)
41988 {
41989 return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise));
41990 }
41991
41992 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
41993 {
41994 ma_uint64 iFrame;
41995 ma_uint32 iChannel;
41996
41997 if (pNoise->config.format == ma_format_f32) {
41998 float* pFramesOutF32 = (float*)pFramesOut;
41999 if (pNoise->config.duplicateChannels) {
42000 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42001 float s = ma_noise_f32_white(pNoise);
42002 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42003 pFramesOutF32[iFrame*pNoise->config.channels + iChannel] = s;
42004 }
42005 }
42006 } else {
42007 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42008 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42009 pFramesOutF32[iFrame*pNoise->config.channels + iChannel] = ma_noise_f32_white(pNoise);
42010 }
42011 }
42012 }
42013 } else if (pNoise->config.format == ma_format_s16) {
42014 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
42015 if (pNoise->config.duplicateChannels) {
42016 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42017 ma_int16 s = ma_noise_s16_white(pNoise);
42018 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42019 pFramesOutS16[iFrame*pNoise->config.channels + iChannel] = s;
42020 }
42021 }
42022 } else {
42023 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42024 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42025 pFramesOutS16[iFrame*pNoise->config.channels + iChannel] = ma_noise_s16_white(pNoise);
42026 }
42027 }
42028 }
42029 } else {
42030 ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
42031 ma_uint32 bpf = bps * pNoise->config.channels;
42032
42033 if (pNoise->config.duplicateChannels) {
42034 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42035 float s = ma_noise_f32_white(pNoise);
42036 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42037 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
42038 }
42039 }
42040 } else {
42041 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42042 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42043 float s = ma_noise_f32_white(pNoise);
42044 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
42045 }
42046 }
42047 }
42048 }
42049
42050 return frameCount;
42051 }
42052
42053
42054 static MA_INLINE unsigned int ma_tzcnt32(unsigned int x)
42055 {
42056 unsigned int n;
42057
42058 /* Special case for odd numbers since they should happen about half the time. */
42059 if (x & 0x1) {
42060 return 0;
42061 }
42062
42063 if (x == 0) {
42064 return sizeof(x) << 3;
42065 }
42066
42067 n = 1;
42068 if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; }
42069 if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; }
42070 if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; }
42071 if ((x & 0x00000003) == 0) { x >>= 2; n += 2; }
42072 n -= x & 0x00000001;
42073
42074 return n;
42075 }
42076
42077 /*
42078 Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h
42079
42080 This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/
42081 */
42082 static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel)
42083 {
42084 double result;
42085 double binPrev;
42086 double binNext;
42087 unsigned int ibin;
42088
42089 ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (ma_countof(pNoise->state.pink.bin[0]) - 1);
42090
42091 binPrev = pNoise->state.pink.bin[iChannel][ibin];
42092 binNext = ma_lcg_rand_f64(&pNoise->lcg);
42093 pNoise->state.pink.bin[iChannel][ibin] = binNext;
42094
42095 pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev);
42096 pNoise->state.pink.counter[iChannel] += 1;
42097
42098 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]);
42099 result /= 10;
42100
42101 return (float)(result * pNoise->config.amplitude);
42102 }
42103
42104 static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel)
42105 {
42106 return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel));
42107 }
42108
42109 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
42110 {
42111 ma_uint64 iFrame;
42112 ma_uint32 iChannel;
42113
42114 if (pNoise->config.format == ma_format_f32) {
42115 float* pFramesOutF32 = (float*)pFramesOut;
42116 if (pNoise->config.duplicateChannels) {
42117 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42118 float s = ma_noise_f32_pink(pNoise, 0);
42119 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42120 pFramesOutF32[iFrame*pNoise->config.channels + iChannel] = s;
42121 }
42122 }
42123 } else {
42124 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42125 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42126 pFramesOutF32[iFrame*pNoise->config.channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel);
42127 }
42128 }
42129 }
42130 } else if (pNoise->config.format == ma_format_s16) {
42131 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
42132 if (pNoise->config.duplicateChannels) {
42133 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42134 ma_int16 s = ma_noise_s16_pink(pNoise, 0);
42135 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42136 pFramesOutS16[iFrame*pNoise->config.channels + iChannel] = s;
42137 }
42138 }
42139 } else {
42140 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42141 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42142 pFramesOutS16[iFrame*pNoise->config.channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel);
42143 }
42144 }
42145 }
42146 } else {
42147 ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
42148 ma_uint32 bpf = bps * pNoise->config.channels;
42149
42150 if (pNoise->config.duplicateChannels) {
42151 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42152 float s = ma_noise_f32_pink(pNoise, 0);
42153 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42154 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
42155 }
42156 }
42157 } else {
42158 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42159 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42160 float s = ma_noise_f32_pink(pNoise, iChannel);
42161 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
42162 }
42163 }
42164 }
42165 }
42166
42167 return frameCount;
42168 }
42169
42170
42171 static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)
42172 {
42173 double result;
42174
42175 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);
42176 result /= 1.005; /* Don't escape the -1..1 range on average. */
42177
42178 pNoise->state.brownian.accumulation[iChannel] = result;
42179 result /= 20;
42180
42181 return (float)(result * pNoise->config.amplitude);
42182 }
42183
42184 static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)
42185 {
42186 return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));
42187 }
42188
42189 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
42190 {
42191 ma_uint64 iFrame;
42192 ma_uint32 iChannel;
42193
42194 if (pNoise->config.format == ma_format_f32) {
42195 float* pFramesOutF32 = (float*)pFramesOut;
42196 if (pNoise->config.duplicateChannels) {
42197 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42198 float s = ma_noise_f32_brownian(pNoise, 0);
42199 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42200 pFramesOutF32[iFrame*pNoise->config.channels + iChannel] = s;
42201 }
42202 }
42203 } else {
42204 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42205 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42206 pFramesOutF32[iFrame*pNoise->config.channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel);
42207 }
42208 }
42209 }
42210 } else if (pNoise->config.format == ma_format_s16) {
42211 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
42212 if (pNoise->config.duplicateChannels) {
42213 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42214 ma_int16 s = ma_noise_s16_brownian(pNoise, 0);
42215 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42216 pFramesOutS16[iFrame*pNoise->config.channels + iChannel] = s;
42217 }
42218 }
42219 } else {
42220 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42221 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42222 pFramesOutS16[iFrame*pNoise->config.channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel);
42223 }
42224 }
42225 }
42226 } else {
42227 ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
42228 ma_uint32 bpf = bps * pNoise->config.channels;
42229
42230 if (pNoise->config.duplicateChannels) {
42231 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42232 float s = ma_noise_f32_brownian(pNoise, 0);
42233 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42234 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
42235 }
42236 }
42237 } else {
42238 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42239 for (iChannel = 0; iChannel < pNoise->config.channels; iChannel += 1) {
42240 float s = ma_noise_f32_brownian(pNoise, iChannel);
42241 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
42242 }
42243 }
42244 }
42245 }
42246
42247 return frameCount;
42248 }
42249
42250 MA_API ma_uint64 ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
42251 {
42252 if (pNoise == NULL) {
42253 return 0;
42254 }
42255
42256 if (pNoise->config.type == ma_noise_type_white) {
42257 return ma_noise_read_pcm_frames__white(pNoise, pFramesOut, frameCount);
42258 }
42259
42260 if (pNoise->config.type == ma_noise_type_pink) {
42261 return ma_noise_read_pcm_frames__pink(pNoise, pFramesOut, frameCount);
42262 }
42263
42264 if (pNoise->config.type == ma_noise_type_brownian) {
42265 return ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount);
42266 }
42267
42268 /* Should never get here. */
42269 MA_ASSERT(MA_FALSE);
42270 return 0;
42271 }
42272
42273 /* End globally disabled warnings. */
42274 #if defined(_MSC_VER)
42275 #pragma warning(pop)
42276 #endif
42277
42278 #endif /* MINIAUDIO_IMPLEMENTATION */
42279
42280 /*
42281 MAJOR CHANGES IN VERSION 0.9
42282 ============================
42283 Version 0.9 includes major API changes, centered mostly around full-duplex and the rebrand to "miniaudio". Before I go into
42284 detail about the major changes I would like to apologize. I know it's annoying dealing with breaking API changes, but I think
42285 it's best to get these changes out of the way now while the library is still relatively young and unknown.
42286
42287 There's been a lot of refactoring with this release so there's a good chance a few bugs have been introduced. I apologize in
42288 advance 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
42289 you, and you don't need full-duplex support, you can avoid upgrading (though you won't be getting future bug fixes).
42290
42291
42292 Rebranding to "miniaudio"
42293 -------------------------
42294 The decision was made to rename mini_al to miniaudio. Don't worry, it's the same project. The reason for this is simple:
42295
42296 1) Having the word "audio" in the title makes it immediately clear that the library is related to audio; and
42297 2) I don't like the look of the underscore.
42298
42299 This rebrand has necessitated a change in namespace from "mal" to "ma". I know this is annoying, and I apologize, but it's
42300 better to get this out of the road now rather than later. Also, since there are necessary API changes for full-duplex support
42301 I 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
42302 this will be the last of the major API changes. Fingers crossed!
42303
42304 The implementation define is now "#define MINIAUDIO_IMPLEMENTATION". You can also use "#define MA_IMPLEMENTATION" if that's
42305 your preference.
42306
42307
42308 Full-Duplex Support
42309 -------------------
42310 The major feature added to version 0.9 is full-duplex. This has necessitated a few API changes.
42311
42312 1) The data callback has now changed. Previously there was one type of callback for playback and another for capture. I wanted
42313 to avoid a third callback just for full-duplex so the decision was made to break this API and unify the callbacks. Now,
42314 there is just one callback which is the same for all three modes (playback, capture, duplex). The new callback looks like
42315 the following:
42316
42317 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
42318
42319 This callback allows you to move data straight out of the input buffer and into the output buffer in full-duplex mode. In
42320 playback-only mode, pInput will be null. Likewise, pOutput will be null in capture-only mode. The sample count is no longer
42321 returned from the callback since it's not necessary for miniaudio anymore.
42322
42323 2) The device config needed to change in order to support full-duplex. Full-duplex requires the ability to allow the client
42324 to choose a different PCM format for the playback and capture sides. The old ma_device_config object simply did not allow
42325 this and needed to change. With these changes you now specify the device ID, format, channels, channel map and share mode
42326 on a per-playback and per-capture basis (see example below). The sample rate must be the same for playback and capture.
42327
42328 Since the device config API has changed I have also decided to take the opportunity to simplify device initialization. Now,
42329 the device ID, device type and callback user data are set in the config. ma_device_init() is now simplified down to taking
42330 just the context, device config and a pointer to the device object being initialized. The rationale for this change is that
42331 it just makes more sense to me that these are set as part of the config like everything else.
42332
42333 Example device initialization:
42334
42335 ma_device_config config = ma_device_config_init(ma_device_type_duplex); // Or ma_device_type_playback or ma_device_type_capture.
42336 config.playback.pDeviceID = &myPlaybackDeviceID; // Or NULL for the default playback device.
42337 config.playback.format = ma_format_f32;
42338 config.playback.channels = 2;
42339 config.capture.pDeviceID = &myCaptureDeviceID; // Or NULL for the default capture device.
42340 config.capture.format = ma_format_s16;
42341 config.capture.channels = 1;
42342 config.sampleRate = 44100;
42343 config.dataCallback = data_callback;
42344 config.pUserData = &myUserData;
42345
42346 result = ma_device_init(&myContext, &config, &device);
42347 if (result != MA_SUCCESS) {
42348 ... handle error ...
42349 }
42350
42351 Note that the "onDataCallback" member of ma_device_config has been renamed to "dataCallback". Also, "onStopCallback" has
42352 been renamed to "stopCallback".
42353
42354 This is the first pass for full-duplex and there is a known bug. You will hear crackling on the following backends when sample
42355 rate conversion is required for the playback device:
42356 - Core Audio
42357 - JACK
42358 - AAudio
42359 - OpenSL
42360 - WebAudio
42361
42362 In addition to the above, not all platforms have been absolutely thoroughly tested simply because I lack the hardware for such
42363 thorough testing. If you experience a bug, an issue report on GitHub or an email would be greatly appreciated (and a sample
42364 program that reproduces the issue if possible).
42365
42366
42367 Other API Changes
42368 -----------------
42369 In addition to the above, the following API changes have been made:
42370
42371 - The log callback is no longer passed to ma_context_config_init(). Instead you need to set it manually after initialization.
42372 - The onLogCallback member of ma_context_config has been renamed to "logCallback".
42373 - 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)
42374 - You can use ma_log_level_to_string() to convert the logLevel to human readable text if you want to log it.
42375 - Some APIs have been renamed:
42376 - mal_decoder_read() -> ma_decoder_read_pcm_frames()
42377 - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
42378 - mal_sine_wave_read() -> ma_sine_wave_read_f32()
42379 - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
42380 - Some APIs have been removed:
42381 - mal_device_get_buffer_size_in_bytes()
42382 - mal_device_set_recv_callback()
42383 - mal_device_set_send_callback()
42384 - mal_src_set_input_sample_rate()
42385 - mal_src_set_output_sample_rate()
42386 - Error codes have been rearranged. If you're a binding maintainer you will need to update.
42387 - The ma_backend enums have been rearranged to priority order. The rationale for this is to simplify automatic backend selection
42388 and to make it easier to see the priority. If you're a binding maintainer you will need to update.
42389 - ma_dsp has been renamed to ma_pcm_converter. The rationale for this change is that I'm expecting "ma_dsp" to conflict with
42390 some future planned high-level APIs.
42391 - For functions that take a pointer/count combo, such as ma_decoder_read_pcm_frames(), the parameter order has changed so that
42392 the pointer comes before the count. The rationale for this is to keep it consistent with things like memcpy().
42393
42394
42395 Miscellaneous Changes
42396 ---------------------
42397 The following miscellaneous changes have also been made.
42398
42399 - The AAudio backend has been added for Android 8 and above. This is Android's new "High-Performance Audio" API. (For the
42400 record, this is one of the nicest audio APIs out there, just behind the BSD audio APIs).
42401 - The WebAudio backend has been added. This is based on ScriptProcessorNode. This removes the need for SDL.
42402 - The SDL and OpenAL backends have been removed. These were originally implemented to add support for platforms for which miniaudio
42403 was not explicitly supported. These are no longer needed and have therefore been removed.
42404 - Device initialization now fails if the requested share mode is not supported. If you ask for exclusive mode, you either get an
42405 exclusive mode device, or an error. The rationale for this change is to give the client more control over how to handle cases
42406 when the desired shared mode is unavailable.
42407 - A lock-free ring buffer API has been added. There are two varients of this. "ma_rb" operates on bytes, whereas "ma_pcm_rb"
42408 operates on PCM frames.
42409 - The library is now licensed as a choice of Public Domain (Unlicense) _or_ MIT-0 (No Attribution) which is the same as MIT, but
42410 removes the attribution requirement. The rationale for this is to support countries that don't recognize public domain.
42411 */
42412
42413 /*
42414 REVISION HISTORY
42415 ================
42416 v0.10.4 - 2020-04-12
42417 - Fix a data conversion bug when converting from the client format to the native device format.
42418
42419 v0.10.3 - 2020-04-07
42420 - Bring up to date with breaking changes to dr_mp3.
42421 - Remove MA_NO_STDIO. This was causing compilation errors and the maintenance cost versus practical benefit is no longer worthwhile.
42422 - Fix a bug with data conversion where it was unnecessarily converting to s16 or f32 and then straight back to the original format.
42423 - Fix compilation errors and warnings with Visual Studio 2005.
42424 - ALSA: Disable ALSA's automatic data conversion by default and add configuration options to the device config:
42425 - alsa.noAutoFormat
42426 - alsa.noAutoChannels
42427 - alsa.noAutoResample
42428 - WASAPI: Add some overrun recovery for ma_device_type_capture devices.
42429
42430 v0.10.2 - 2020-03-22
42431 - Decorate some APIs with MA_API which were missed in the previous version.
42432 - Fix a bug in ma_linear_resampler_set_rate() and ma_linear_resampler_set_rate_ratio().
42433
42434 v0.10.1 - 2020-03-17
42435 - Add MA_API decoration. This can be customized by defining it before including miniaudio.h.
42436 - Fix a bug where opening a file would return a success code when in fact it failed.
42437 - Fix compilation errors with Visual Studio 6 and 2003.
42438 - Fix warnings on macOS.
42439
42440 v0.10.0 - 2020-03-07
42441 - API CHANGE: Refactor data conversion APIs
42442 - ma_format_converter has been removed. Use ma_convert_pcm_frames_format() instead.
42443 - ma_channel_router has been replaced with ma_channel_converter.
42444 - ma_src has been replaced with ma_resampler
42445 - ma_pcm_converter has been replaced with ma_data_converter
42446 - API CHANGE: Add support for custom memory allocation callbacks. The following APIs have been updated to take an extra parameter for the allocation
42447 callbacks:
42448 - ma_malloc()
42449 - ma_realloc()
42450 - ma_free()
42451 - ma_aligned_malloc()
42452 - ma_aligned_free()
42453 - ma_rb_init() / ma_rb_init_ex()
42454 - ma_pcm_rb_init() / ma_pcm_rb_init_ex()
42455 - API CHANGE: Simplify latency specification in device configurations. The bufferSizeInFrames and bufferSizeInMilliseconds parameters have been replaced with
42456 periodSizeInFrames and periodSizeInMilliseconds respectively. The previous variables defined the size of the entire buffer, whereas the new ones define the
42457 size of a period. The following APIs have been removed since they are no longer relevant:
42458 - ma_get_default_buffer_size_in_milliseconds()
42459 - ma_get_default_buffer_size_in_frames()
42460 - API CHANGE: ma_device_set_stop_callback() has been removed. If you require a stop callback, you must now set it via the device config just like the data
42461 callback.
42462 - API CHANGE: The ma_sine_wave API has been replaced with ma_waveform. The following APIs have been removed:
42463 - ma_sine_wave_init()
42464 - ma_sine_wave_read_f32()
42465 - ma_sine_wave_read_f32_ex()
42466 - API CHANGE: ma_convert_frames() has been updated to take an extra parameter which is the size of the output buffer in PCM frames. Parameters have also been
42467 reordered.
42468 - API CHANGE: ma_convert_frames_ex() has been changed to take a pointer to a ma_data_converter_config object to specify the input and output formats to
42469 convert between.
42470 - API CHANGE: ma_calculate_frame_count_after_src() has been renamed to ma_calculate_frame_count_after_resampling().
42471 - Add support for the following filters:
42472 - Biquad (ma_biquad)
42473 - First order low-pass (ma_lpf1)
42474 - Second order low-pass (ma_lpf2)
42475 - Low-pass with configurable order (ma_lpf)
42476 - First order high-pass (ma_hpf1)
42477 - Second order high-pass (ma_hpf2)
42478 - High-pass with configurable order (ma_hpf)
42479 - Second order band-pass (ma_bpf2)
42480 - Band-pass with configurable order (ma_bpf)
42481 - Second order peaking EQ (ma_peak2)
42482 - Second order notching (ma_notch2)
42483 - Second order low shelf (ma_loshelf2)
42484 - Second order high shelf (ma_hishelf2)
42485 - Add waveform generation API (ma_waveform) with support for the following:
42486 - Sine
42487 - Square
42488 - Triangle
42489 - Sawtooth
42490 - Add noise generation API (ma_noise) with support for the following:
42491 - White
42492 - Pink
42493 - Brownian
42494 - Add encoding API (ma_encoder). This only supports outputting to WAV files via dr_wav.
42495 - Add ma_result_description() which is used to retrieve a human readable description of a given result code.
42496 - Result codes have been changed. Binding maintainers will need to update their result code constants.
42497 - More meaningful result codes are now returned when a file fails to open.
42498 - Internal functions have all been made static where possible.
42499 - Fix potential crash when ma_device object's are not aligned to MA_SIMD_ALIGNMENT.
42500 - Fix a bug in ma_decoder_get_length_in_pcm_frames() where it was returning the length based on the internal sample rate rather than the output sample rate.
42501 - Fix bugs in some backends where the device is not drained properly in ma_device_stop().
42502 - Improvements to documentation.
42503
42504 v0.9.10 - 2020-01-15
42505 - Fix compilation errors due to #if/#endif mismatches.
42506 - WASAPI: Fix a bug where automatic stream routing is being performed for devices that are initialized with an explicit device ID.
42507 - iOS: Fix a crash on device uninitialization.
42508
42509 v0.9.9 - 2020-01-09
42510 - Fix compilation errors with MinGW.
42511 - Fix compilation errors when compiling on Apple platforms.
42512 - WASAPI: Add support for disabling hardware offloading.
42513 - WASAPI: Add support for disabling automatic stream routing.
42514 - Core Audio: Fix bugs in the case where the internal device uses deinterleaved buffers.
42515 - Core Audio: Add support for controlling the session category (AVAudioSessionCategory) and options (AVAudioSessionCategoryOptions).
42516 - JACK: Fix bug where incorrect ports are connected.
42517
42518 v0.9.8 - 2019-10-07
42519 - WASAPI: Fix a potential deadlock when starting a full-duplex device.
42520 - WASAPI: Enable automatic resampling by default. Disable with config.wasapi.noAutoConvertSRC.
42521 - Core Audio: Fix bugs with automatic stream routing.
42522 - Add support for controlling whether or not the content of the output buffer passed in to the data callback is pre-initialized
42523 to zero. By default it will be initialized to zero, but this can be changed by setting noPreZeroedOutputBuffer in the device
42524 config. Setting noPreZeroedOutputBuffer to true will leave the contents undefined.
42525 - Add support for clipping samples after the data callback has returned. This only applies when the playback sample format is
42526 configured as ma_format_f32. If you are doing clipping yourself, you can disable this overhead by setting noClip to true in
42527 the device config.
42528 - Add support for master volume control for devices.
42529 - 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.
42530 - Use ma_device_set_master_gain_db() to set the volume in decibels where 0 is full volume and < 0 reduces the volume.
42531 - Fix warnings emitted by GCC when `__inline__` is undefined or defined as nothing.
42532
42533 v0.9.7 - 2019-08-28
42534 - Add support for loopback mode (WASAPI only).
42535 - To use this, set the device type to ma_device_type_loopback, and then fill out the capture section of the device config.
42536 - If you need to capture from a specific output device, set the capture device ID to that of a playback device.
42537 - Fix a crash when an error is posted in ma_device_init().
42538 - Fix a compilation error when compiling for ARM architectures.
42539 - Fix a bug with the audio(4) backend where the device is incorrectly being opened in non-blocking mode.
42540 - Fix memory leaks in the Core Audio backend.
42541 - Minor refactoring to the WinMM, ALSA, PulseAudio, OSS, audio(4), sndio and null backends.
42542
42543 v0.9.6 - 2019-08-04
42544 - Add support for loading decoders using a wchar_t string for file paths.
42545 - Don't trigger an assert when ma_device_start() is called on a device that is already started. This will now log a warning
42546 and return MA_INVALID_OPERATION. The same applies for ma_device_stop().
42547 - Try fixing an issue with PulseAudio taking a long time to start playback.
42548 - Fix a bug in ma_convert_frames() and ma_convert_frames_ex().
42549 - Fix memory leaks in the WASAPI backend.
42550 - Fix a compilation error with Visual Studio 2010.
42551
42552 v0.9.5 - 2019-05-21
42553 - Add logging to ma_dlopen() and ma_dlsym().
42554 - Add ma_decoder_get_length_in_pcm_frames().
42555 - Fix a bug with capture on the OpenSL|ES backend.
42556 - Fix a bug with the ALSA backend where a device would not restart after being stopped.
42557
42558 v0.9.4 - 2019-05-06
42559 - Add support for C89. With this change, miniaudio should compile clean with GCC/Clang with "-std=c89 -ansi -pedantic" and
42560 Microsoft compilers back to VC6. Other compilers should also work, but have not been tested.
42561
42562 v0.9.3 - 2019-04-19
42563 - Fix compiler errors on GCC when compiling with -std=c99.
42564
42565 v0.9.2 - 2019-04-08
42566 - Add support for per-context user data.
42567 - Fix a potential bug with context configs.
42568 - Fix some bugs with PulseAudio.
42569
42570 v0.9.1 - 2019-03-17
42571 - Fix a bug where the output buffer is not getting zeroed out before calling the data callback. This happens when
42572 the device is running in passthrough mode (not doing any data conversion).
42573 - Fix an issue where the data callback is getting called too frequently on the WASAPI and DirectSound backends.
42574 - Fix error on the UWP build.
42575 - Fix a build error on Apple platforms.
42576
42577 v0.9 - 2019-03-06
42578 - Rebranded to "miniaudio". All namespaces have been renamed from "mal" to "ma".
42579 - API CHANGE: ma_device_init() and ma_device_config_init() have changed significantly:
42580 - The device type, device ID and user data pointer have moved from ma_device_init() to the config.
42581 - All variations of ma_device_config_init_*() have been removed in favor of just ma_device_config_init().
42582 - ma_device_config_init() now takes only one parameter which is the device type. All other properties need
42583 to be set on the returned object directly.
42584 - The onDataCallback and onStopCallback members of ma_device_config have been renamed to "dataCallback"
42585 and "stopCallback".
42586 - The ID of the physical device is now split into two: one for the playback device and the other for the
42587 capture device. This is required for full-duplex. These are named "pPlaybackDeviceID" and "pCaptureDeviceID".
42588 - API CHANGE: The data callback has changed. It now uses a unified callback for all device types rather than
42589 being separate for each. It now takes two pointers - one containing input data and the other output data. This
42590 design in required for full-duplex. The return value is now void instead of the number of frames written. The
42591 new callback looks like the following:
42592 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
42593 - API CHANGE: Remove the log callback parameter from ma_context_config_init(). With this change,
42594 ma_context_config_init() now takes no parameters and the log callback is set via the structure directly. The
42595 new policy for config initialization is that only mandatory settings are passed in to *_config_init(). The
42596 "onLog" member of ma_context_config has been renamed to "logCallback".
42597 - API CHANGE: Remove ma_device_get_buffer_size_in_bytes().
42598 - API CHANGE: Rename decoding APIs to "pcm_frames" convention.
42599 - mal_decoder_read() -> ma_decoder_read_pcm_frames()
42600 - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
42601 - API CHANGE: Rename sine wave reading APIs to f32 convention.
42602 - mal_sine_wave_read() -> ma_sine_wave_read_f32()
42603 - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
42604 - API CHANGE: Remove some deprecated APIs
42605 - mal_device_set_recv_callback()
42606 - mal_device_set_send_callback()
42607 - mal_src_set_input_sample_rate()
42608 - mal_src_set_output_sample_rate()
42609 - API CHANGE: Add log level to the log callback. New signature:
42610 - void on_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
42611 - API CHANGE: Changes to result codes. Constants have changed and unused codes have been removed. If you're
42612 a binding mainainer you will need to update your result code constants.
42613 - API CHANGE: Change the order of the ma_backend enums to priority order. If you are a binding maintainer, you
42614 will need to update.
42615 - API CHANGE: Rename mal_dsp to ma_pcm_converter. All functions have been renamed from mal_dsp_*() to
42616 ma_pcm_converter_*(). All structures have been renamed from mal_dsp* to ma_pcm_converter*.
42617 - API CHANGE: Reorder parameters of ma_decoder_read_pcm_frames() to be consistent with the new parameter order scheme.
42618 - The resampling algorithm has been changed from sinc to linear. The rationale for this is that the sinc implementation
42619 is too inefficient right now. This will hopefully be improved at a later date.
42620 - Device initialization will no longer fall back to shared mode if exclusive mode is requested but is unusable.
42621 With this change, if you request an device in exclusive mode, but exclusive mode is not supported, it will not
42622 automatically fall back to shared mode. The client will need to reinitialize the device in shared mode if that's
42623 what they want.
42624 - Add ring buffer API. This is ma_rb and ma_pcm_rb, the difference being that ma_rb operates on bytes and
42625 ma_pcm_rb operates on PCM frames.
42626 - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously
42627 used for web support, will be removed in a future version.
42628 - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts
42629 with Android 8. OpenSL|ES is used as a fallback for older versions of Android.
42630 - Remove OpenAL and SDL backends.
42631 - Fix a possible deadlock when rapidly stopping the device after it has started.
42632 - Update documentation.
42633 - Change licensing to a choice of public domain _or_ MIT-0 (No Attribution).
42634
42635 v0.8.14 - 2018-12-16
42636 - Core Audio: Fix a bug where the device state is not set correctly after stopping.
42637 - Add support for custom weights to the channel router.
42638 - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav.
42639
42640 v0.8.13 - 2018-12-04
42641 - Core Audio: Fix a bug with channel mapping.
42642 - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight.
42643
42644 v0.8.12 - 2018-11-27
42645 - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2".
42646 - Fix a linking error with ALSA.
42647 - Fix a bug on iOS where the device name is not set correctly.
42648
42649 v0.8.11 - 2018-11-21
42650 - iOS bug fixes.
42651 - Minor tweaks to PulseAudio.
42652
42653 v0.8.10 - 2018-10-21
42654 - Core Audio: Fix a hang when uninitializing a device.
42655 - Fix a bug where an incorrect value is returned from mal_device_stop().
42656
42657 v0.8.9 - 2018-09-28
42658 - Fix a bug with the SDL backend where device initialization fails.
42659
42660 v0.8.8 - 2018-09-14
42661 - Fix Linux build with the ALSA backend.
42662 - Minor documentation fix.
42663
42664 v0.8.7 - 2018-09-12
42665 - Fix a bug with UWP detection.
42666
42667 v0.8.6 - 2018-08-26
42668 - Automatically switch the internal device when the default device is unplugged. Note that this is still in the
42669 early stages and not all backends handle this the same way. As of this version, this will not detect a default
42670 device switch when changed from the operating system's audio preferences (unless the backend itself handles
42671 this automatically). This is not supported in exclusive mode.
42672 - WASAPI and Core Audio: Add support for stream routing. When the application is using a default device and the
42673 user switches the default device via the operating system's audio preferences, miniaudio will automatically switch
42674 the internal device to the new default. This is not supported in exclusive mode.
42675 - WASAPI: Add support for hardware offloading via IAudioClient2. Only supported on Windows 8 and newer.
42676 - WASAPI: Add support for low-latency shared mode via IAudioClient3. Only supported on Windows 10 and newer.
42677 - Add support for compiling the UWP build as C.
42678 - mal_device_set_recv_callback() and mal_device_set_send_callback() have been deprecated. You must now set this
42679 when the device is initialized with mal_device_init*(). These will be removed in version 0.9.0.
42680
42681 v0.8.5 - 2018-08-12
42682 - Add support for specifying the size of a device's buffer in milliseconds. You can still set the buffer size in
42683 frames if that suits you. When bufferSizeInFrames is 0, bufferSizeInMilliseconds will be used. If both are non-0
42684 then bufferSizeInFrames will take priority. If both are set to 0 the default buffer size is used.
42685 - Add support for the audio(4) backend to OpenBSD.
42686 - Fix a bug with the ALSA backend that was causing problems on Raspberry Pi. This significantly improves the
42687 Raspberry Pi experience.
42688 - Fix a bug where an incorrect number of samples is returned from sinc resampling.
42689 - Add support for setting the value to be passed to internal calls to CoInitializeEx().
42690 - WASAPI and WinMM: Stop the device when it is unplugged.
42691
42692 v0.8.4 - 2018-08-06
42693 - Add sndio backend for OpenBSD.
42694 - Add audio(4) backend for NetBSD.
42695 - Drop support for the OSS backend on everything except FreeBSD and DragonFly BSD.
42696 - Formats are now native-endian (were previously little-endian).
42697 - Mark some APIs as deprecated:
42698 - mal_src_set_input_sample_rate() and mal_src_set_output_sample_rate() are replaced with mal_src_set_sample_rate().
42699 - mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() are replaced with mal_dsp_set_sample_rate().
42700 - Fix a bug when capturing using the WASAPI backend.
42701 - Fix some aliasing issues with resampling, specifically when increasing the sample rate.
42702 - Fix warnings.
42703
42704 v0.8.3 - 2018-07-15
42705 - Fix a crackling bug when resampling in capture mode.
42706 - Core Audio: Fix a bug where capture does not work.
42707 - ALSA: Fix a bug where the worker thread can get stuck in an infinite loop.
42708 - PulseAudio: Fix a bug where mal_context_init() succeeds when PulseAudio is unusable.
42709 - JACK: Fix a bug where mal_context_init() succeeds when JACK is unusable.
42710
42711 v0.8.2 - 2018-07-07
42712 - Fix a bug on macOS with Core Audio where the internal callback is not called.
42713
42714 v0.8.1 - 2018-07-06
42715 - Fix compilation errors and warnings.
42716
42717 v0.8 - 2018-07-05
42718 - Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old
42719 way is still supported for now, but you should update as it may be removed in the future.
42720 - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with
42721 mal_context_get_devices(). An additional low-level device enumration API has been introduced called
42722 mal_context_enumerate_devices() which uses a callback to report devices.
42723 - API CHANGE: Rename mal_get_sample_size_in_bytes() to mal_get_bytes_per_sample() and add
42724 mal_get_bytes_per_frame().
42725 - API CHANGE: Replace mal_device_config.preferExclusiveMode with mal_device_config.shareMode.
42726 - This new config can be set to mal_share_mode_shared (default) or mal_share_mode_exclusive.
42727 - API CHANGE: Remove excludeNullDevice from mal_context_config.alsa.
42728 - API CHANGE: Rename MAL_MAX_SAMPLE_SIZE_IN_BYTES to MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES.
42729 - API CHANGE: Change the default channel mapping to the standard Microsoft mapping.
42730 - API CHANGE: Remove backend-specific result codes.
42731 - API CHANGE: Changes to the format conversion APIs (mal_pcm_f32_to_s16(), etc.)
42732 - Add support for Core Audio (Apple).
42733 - Add support for PulseAudio.
42734 - This is the highest priority backend on Linux (higher priority than ALSA) since it is commonly
42735 installed by default on many of the popular distros and offer's more seamless integration on
42736 platforms where PulseAudio is used. In addition, if PulseAudio is installed and running (which
42737 is extremely common), it's better to just use PulseAudio directly rather than going through the
42738 "pulse" ALSA plugin (which is what the "default" ALSA device is likely set to).
42739 - Add support for JACK.
42740 - Remove dependency on asound.h for the ALSA backend. This means the ALSA development packages are no
42741 longer required to build miniaudio.
42742 - Remove dependency on dsound.h for the DirectSound backend. This fixes build issues with some
42743 distributions of MinGW.
42744 - Remove dependency on audioclient.h for the WASAPI backend. This fixes build issues with some
42745 distributions of MinGW.
42746 - Add support for dithering to format conversion.
42747 - Add support for configuring the priority of the worker thread.
42748 - Add a sine wave generator.
42749 - Improve efficiency of sample rate conversion.
42750 - Introduce the notion of standard channel maps. Use mal_get_standard_channel_map().
42751 - Introduce the notion of default device configurations. A default config uses the same configuration
42752 as the backend's internal device, and as such results in a pass-through data transmission pipeline.
42753 - Add support for passing in NULL for the device config in mal_device_init(), which uses a default
42754 config. This requires manually calling mal_device_set_send/recv_callback().
42755 - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.)
42756 - Make mal_device_init_ex() more robust.
42757 - Make some APIs more const-correct.
42758 - Fix errors with SDL detection on Apple platforms.
42759 - Fix errors with OpenAL detection.
42760 - Fix some memory leaks.
42761 - Fix a bug with opening decoders from memory.
42762 - Early work on SSE2, AVX2 and NEON optimizations.
42763 - Miscellaneous bug fixes.
42764 - Documentation updates.
42765
42766 v0.7 - 2018-02-25
42767 - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts.
42768 - Add decoder APIs for loading WAV, FLAC, Vorbis and MP3 files.
42769 - Allow opening of devices without a context.
42770 - In this case the context is created and managed internally by the device.
42771 - Change the default channel mapping to the same as that used by FLAC.
42772 - Fix build errors with macOS.
42773
42774 v0.6c - 2018-02-12
42775 - Fix build errors with BSD/OSS.
42776
42777 v0.6b - 2018-02-03
42778 - Fix some warnings when compiling with Visual C++.
42779
42780 v0.6a - 2018-01-26
42781 - Fix errors with channel mixing when increasing the channel count.
42782 - Improvements to the build system for the OpenAL backend.
42783 - Documentation fixes.
42784
42785 v0.6 - 2017-12-08
42786 - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll
42787 need to update.
42788 - API CHANGE: SRC and DSP callbacks now take a pointer to a mal_src and mal_dsp object respectively.
42789 - API CHANGE: Improvements to event and thread APIs. These changes make these APIs more consistent.
42790 - Add support for SDL and Emscripten.
42791 - Simplify the build system further for when development packages for various backends are not installed.
42792 With this change, when the compiler supports __has_include, backends without the relevant development
42793 packages installed will be ignored. This fixes the build for old versions of MinGW.
42794 - Fixes to the Android build.
42795 - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of
42796 audio data to a different format.
42797 - Improvements to f32 -> u8/s16/s24/s32 conversion routines.
42798 - Fix a bug where the wrong value is returned from mal_device_start() for the OpenSL backend.
42799 - Fixes and improvements for Raspberry Pi.
42800 - Warning fixes.
42801
42802 v0.5 - 2017-11-11
42803 - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for
42804 configuring the context. The works in the same kind of way as the device config. The rationale for this
42805 change is to give applications better control over context-level properties, add support for backend-
42806 specific configurations, and support extensibility without breaking the API.
42807 - API CHANGE: The alsa.preferPlugHW device config variable has been removed since it's not really useful for
42808 anything anymore.
42809 - ALSA: By default, device enumeration will now only enumerate over unique card/device pairs. Applications
42810 can enable verbose device enumeration by setting the alsa.useVerboseDeviceEnumeration context config
42811 variable.
42812 - ALSA: When opening a device in shared mode (the default), the dmix/dsnoop plugin will be prioritized. If
42813 this fails it will fall back to the hw plugin. With this change the preferExclusiveMode config is now
42814 honored. Note that this does not happen when alsa.useVerboseDeviceEnumeration is set to true (see above)
42815 which is by design.
42816 - ALSA: Add support for excluding the "null" device using the alsa.excludeNullDevice context config variable.
42817 - ALSA: Fix a bug with channel mapping which causes an assertion to fail.
42818 - Fix errors with enumeration when pInfo is set to NULL.
42819 - OSS: Fix a bug when starting a device when the client sends 0 samples for the initial buffer fill.
42820
42821 v0.4 - 2017-11-05
42822 - API CHANGE: The log callback is now per-context rather than per-device and as is thus now passed to
42823 mal_context_init(). The rationale for this change is that it allows applications to capture diagnostic
42824 messages at the context level. Previously this was only available at the device level.
42825 - API CHANGE: The device config passed to mal_device_init() is now const.
42826 - Added support for OSS which enables support on BSD platforms.
42827 - Added support for WinMM (waveOut/waveIn).
42828 - Added support for UWP (Universal Windows Platform) applications. Currently C++ only.
42829 - Added support for exclusive mode for selected backends. Currently supported on WASAPI.
42830 - POSIX builds no longer require explicit linking to libpthread (-lpthread).
42831 - ALSA: Explicit linking to libasound (-lasound) is no longer required.
42832 - ALSA: Latency improvements.
42833 - ALSA: Use MMAP mode where available. This can be disabled with the alsa.noMMap config.
42834 - ALSA: Use "hw" devices instead of "plughw" devices by default. This can be disabled with the
42835 alsa.preferPlugHW config.
42836 - WASAPI is now the highest priority backend on Windows platforms.
42837 - Fixed an error with sample rate conversion which was causing crackling when capturing.
42838 - Improved error handling.
42839 - Improved compiler support.
42840 - Miscellaneous bug fixes.
42841
42842 v0.3 - 2017-06-19
42843 - API CHANGE: Introduced the notion of a context. The context is the highest level object and is required for
42844 enumerating and creating devices. Now, applications must first create a context, and then use that to
42845 enumerate and create devices. The reason for this change is to ensure device enumeration and creation is
42846 tied to the same backend. In addition, some backends are better suited to this design.
42847 - API CHANGE: Removed the rewinding APIs because they're too inconsistent across the different backends, hard
42848 to test and maintain, and just generally unreliable.
42849 - Added helper APIs for initializing mal_device_config objects.
42850 - Null Backend: Fixed a crash when recording.
42851 - Fixed build for UWP.
42852 - Added support for f32 formats to the OpenSL|ES backend.
42853 - Added initial implementation of the WASAPI backend.
42854 - Added initial implementation of the OpenAL backend.
42855 - Added support for low quality linear sample rate conversion.
42856 - Added early support for basic channel mapping.
42857
42858 v0.2 - 2016-10-28
42859 - API CHANGE: Add user data pointer as the last parameter to mal_device_init(). The rationale for this
42860 change is to ensure the logging callback has access to the user data during initialization.
42861 - API CHANGE: Have device configuration properties be passed to mal_device_init() via a structure. Rationale:
42862 1) The number of parameters is just getting too much.
42863 2) It makes it a bit easier to add new configuration properties in the future. In particular, there's a
42864 chance there will be support added for backend-specific properties.
42865 - Dropped support for f64, A-law and Mu-law formats since they just aren't common enough to justify the
42866 added maintenance cost.
42867 - DirectSound: Increased the default buffer size for capture devices.
42868 - Added initial implementation of the OpenSL|ES backend.
42869
42870 v0.1 - 2016-10-21
42871 - Initial versioned release.
42872 */
42873
42874
42875 /*
42876 This software is available as a choice of the following licenses. Choose
42877 whichever you prefer.
42878
42879 ===============================================================================
42880 ALTERNATIVE 1 - Public Domain (www.unlicense.org)
42881 ===============================================================================
42882 This is free and unencumbered software released into the public domain.
42883
42884 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
42885 software, either in source code form or as a compiled binary, for any purpose,
42886 commercial or non-commercial, and by any means.
42887
42888 In jurisdictions that recognize copyright laws, the author or authors of this
42889 software dedicate any and all copyright interest in the software to the public
42890 domain. We make this dedication for the benefit of the public at large and to
42891 the detriment of our heirs and successors. We intend this dedication to be an
42892 overt act of relinquishment in perpetuity of all present and future rights to
42893 this software under copyright law.
42894
42895 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42896 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42897 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
42898 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
42899 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
42900 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
42901
42902 For more information, please refer to <http://unlicense.org/>
42903
42904 ===============================================================================
42905 ALTERNATIVE 2 - MIT No Attribution
42906 ===============================================================================
42907 Copyright 2020 David Reid
42908
42909 Permission is hereby granted, free of charge, to any person obtaining a copy of
42910 this software and associated documentation files (the "Software"), to deal in
42911 the Software without restriction, including without limitation the rights to
42912 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
42913 of the Software, and to permit persons to whom the Software is furnished to do
42914 so.
42915
42916 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42917 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42918 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
42919 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42920 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42921 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
42922 SOFTWARE.
42923 */