vkQuake2 doxygen  1.0 dev
miniaudio.h
Go to the documentation of this file.
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 (formerly mini_al) - v0.9.5 - 2019-05-21
4 
5 David Reid - davidreidsoftware@gmail.com
6 */
7 
8 /*
9 MAJOR CHANGES IN VERSION 0.9
10 ============================
11 Version 0.9 includes major API changes, centered mostly around full-duplex and the rebrand to "miniaudio". Before I go into
12 detail about the major changes I would like to apologize. I know it's annoying dealing with breaking API changes, but I think
13 it's best to get these changes out of the way now while the library is still relatively young and unknown.
14 
15 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
16 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
17 you, and you don't need full-duplex support, you can avoid upgrading (though you won't be getting future bug fixes).
18 
19 
20 Rebranding to "miniaudio"
21 -------------------------
22 The decision was made to rename mini_al to miniaudio. Don't worry, it's the same project. The reason for this is simple:
23 
24 1) Having the word "audio" in the title makes it immediately clear that the library is related to audio; and
25 2) I don't like the look of the underscore.
26 
27 This rebrand has necessitated a change in namespace from "mal" to "ma". I know this is annoying, and I apologize, but it's
28 better to get this out of the road now rather than later. Also, since there are necessary API changes for full-duplex support
29 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
30 this will be the last of the major API changes. Fingers crossed!
31 
32 The implementation define is now "#define MINIAUDIO_IMPLEMENTATION". You can also use "#define MA_IMPLEMENTATION" if that's
33 your preference.
34 
35 
36 Full-Duplex Support
37 -------------------
38 The major feature added to version 0.9 is full-duplex. This has necessitated a few API changes.
39 
40 1) The data callback has now changed. Previously there was one type of callback for playback and another for capture. I wanted
41  to avoid a third callback just for full-duplex so the decision was made to break this API and unify the callbacks. Now,
42  there is just one callback which is the same for all three modes (playback, capture, duplex). The new callback looks like
43  the following:
44 
45  void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
46 
47  This callback allows you to move data straight out of the input buffer and into the output buffer in full-duplex mode. In
48  playback-only mode, pInput will be null. Likewise, pOutput will be null in capture-only mode. The sample count is no longer
49  returned from the callback since it's not necessary for miniaudio anymore.
50 
51 2) The device config needed to change in order to support full-duplex. Full-duplex requires the ability to allow the client
52  to choose a different PCM format for the playback and capture sides. The old ma_device_config object simply did not allow
53  this and needed to change. With these changes you now specify the device ID, format, channels, channel map and share mode
54  on a per-playback and per-capture basis (see example below). The sample rate must be the same for playback and capture.
55 
56  Since the device config API has changed I have also decided to take the opportunity to simplify device initialization. Now,
57  the device ID, device type and callback user data are set in the config. ma_device_init() is now simplified down to taking
58  just the context, device config and a pointer to the device object being initialized. The rationale for this change is that
59  it just makes more sense to me that these are set as part of the config like everything else.
60 
61  Example device initialization:
62 
63  ma_device_config config = ma_device_config_init(ma_device_type_duplex); // Or ma_device_type_playback or ma_device_type_capture.
64  config.playback.pDeviceID = &myPlaybackDeviceID; // Or NULL for the default playback device.
65  config.playback.format = ma_format_f32;
66  config.playback.channels = 2;
67  config.capture.pDeviceID = &myCaptureDeviceID; // Or NULL for the default capture device.
68  config.capture.format = ma_format_s16;
69  config.capture.channels = 1;
70  config.sampleRate = 44100;
71  config.dataCallback = data_callback;
72  config.pUserData = &myUserData;
73 
74  result = ma_device_init(&myContext, &config, &device);
75  if (result != MA_SUCCESS) {
76  ... handle error ...
77  }
78 
79  Note that the "onDataCallback" member of ma_device_config has been renamed to "dataCallback". Also, "onStopCallback" has
80  been renamed to "stopCallback".
81 
82 This is the first pass for full-duplex and there is a known bug. You will hear crackling on the following backends when sample
83 rate conversion is required for the playback device:
84  - Core Audio
85  - JACK
86  - AAudio
87  - OpenSL
88  - WebAudio
89 
90 In addition to the above, not all platforms have been absolutely thoroughly tested simply because I lack the hardware for such
91 thorough testing. If you experience a bug, an issue report on GitHub or an email would be greatly appreciated (and a sample
92 program that reproduces the issue if possible).
93 
94 
95 Other API Changes
96 -----------------
97 In addition to the above, the following API changes have been made:
98 
99 - The log callback is no longer passed to ma_context_config_init(). Instead you need to set it manually after initialization.
100 - The onLogCallback member of ma_context_config has been renamed to "logCallback".
101 - 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)
102  - You can use ma_log_level_to_string() to convert the logLevel to human readable text if you want to log it.
103 - Some APIs have been renamed:
104  - mal_decoder_read() -> ma_decoder_read_pcm_frames()
105  - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
106  - mal_sine_wave_read() -> ma_sine_wave_read_f32()
107  - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
108 - Some APIs have been removed:
109  - mal_device_get_buffer_size_in_bytes()
110  - mal_device_set_recv_callback()
111  - mal_device_set_send_callback()
112  - mal_src_set_input_sample_rate()
113  - mal_src_set_output_sample_rate()
114 - Error codes have been rearranged. If you're a binding maintainer you will need to update.
115 - The ma_backend enums have been rearranged to priority order. The rationale for this is to simplify automatic backend selection
116  and to make it easier to see the priority. If you're a binding maintainer you will need to update.
117 - ma_dsp has been renamed to ma_pcm_converter. The rationale for this change is that I'm expecting "ma_dsp" to conflict with
118  some future planned high-level APIs.
119 - For functions that take a pointer/count combo, such as ma_decoder_read_pcm_frames(), the parameter order has changed so that
120  the pointer comes before the count. The rationale for this is to keep it consistent with things like memcpy().
121 
122 
123 Miscellaneous Changes
124 ---------------------
125 The following miscellaneous changes have also been made.
126 
127 - The AAudio backend has been added for Android 8 and above. This is Android's new "High-Performance Audio" API. (For the
128  record, this is one of the nicest audio APIs out there, just behind the BSD audio APIs).
129 - The WebAudio backend has been added. This is based on ScriptProcessorNode. This removes the need for SDL.
130 - The SDL and OpenAL backends have been removed. These were originally implemented to add support for platforms for which miniaudio
131  was not explicitly supported. These are no longer needed and have therefore been removed.
132 - Device initialization now fails if the requested share mode is not supported. If you ask for exclusive mode, you either get an
133  exclusive mode device, or an error. The rationale for this change is to give the client more control over how to handle cases
134  when the desired shared mode is unavailable.
135 - A lock-free ring buffer API has been added. There are two varients of this. "ma_rb" operates on bytes, whereas "ma_pcm_rb"
136  operates on PCM frames.
137 - The library is now licensed as a choice of Public Domain (Unlicense) _or_ MIT-0 (No Attribution) which is the same as MIT, but
138  removes the attribution requirement. The rationale for this is to support countries that don't recognize public domain.
139 */
140 
141 /*
142 ABOUT
143 =====
144 miniaudio is a single file library for audio playback and capture. It's written in C (compilable as
145 C++) and released into the public domain.
146 
147 Supported Backends:
148  - WASAPI
149  - DirectSound
150  - WinMM
151  - Core Audio (Apple)
152  - ALSA
153  - PulseAudio
154  - JACK
155  - sndio (OpenBSD)
156  - audio(4) (NetBSD and OpenBSD)
157  - OSS (FreeBSD)
158  - AAudio (Android 8.0+)
159  - OpenSL|ES (Android only)
160  - Web Audio (Emscripten)
161  - Null (Silence)
162 
163 Supported Formats:
164  - Unsigned 8-bit PCM
165  - Signed 16-bit PCM
166  - Signed 24-bit PCM (tightly packed)
167  - Signed 32-bit PCM
168  - IEEE 32-bit floating point PCM
169 
170 
171 USAGE
172 =====
173 miniaudio is a single-file library. To use it, do something like the following in one .c file.
174  #define MINIAUDIO_IMPLEMENTATION
175  #include "miniaudio.h"
176 
177 You can then #include this file in other parts of the program as you would with any other header file.
178 
179 miniaudio uses an asynchronous, callback based API. You initialize a device with a configuration (sample rate,
180 channel count, etc.) which includes the callback you want to use to handle data transmission to/from the
181 device. In the callback you either read from a data pointer in the case of playback or write to it in the case
182 of capture.
183 
184 Playback Example
185 ----------------
186  void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
187  {
188  ma_decoder* pDecoder = (ma_decoder*)pDevice->pUserData;
189  if (pDecoder == NULL) {
190  return;
191  }
192 
193  ma_decoder_read_pcm_frames(pDecoder, frameCount, pOutput);
194  }
195 
196  ...
197 
198  ma_device_config config = ma_device_config_init(ma_device_type_playback);
199  config.playback.format = decoder.outputFormat;
200  config.playback.channels = decoder.outputChannels;
201  config.sampleRate = decoder.outputSampleRate;
202  config.dataCallback = data_callback;
203  config.pUserData = &decoder;
204 
205  ma_device device;
206  if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
207  ... An error occurred ...
208  }
209 
210  ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually.
211 
212  ...
213 
214  ma_device_uninit(&device); // This will stop the device so no need to do that manually.
215 
216 
217 BUILDING
218 ========
219 miniaudio should Just Work by adding it to your project's source tree. You do not need to download or install
220 any dependencies. See below for platform-specific details.
221 
222 If you want to disable a specific backend, #define the appropriate MA_NO_* option before the implementation.
223 
224 Note that GCC and Clang requires "-msse2", "-mavx2", etc. for SIMD optimizations.
225 
226 
227 Building for Windows
228 --------------------
229 The Windows build should compile clean on all popular compilers without the need to configure any include paths
230 nor link to any libraries.
231 
232 Building for macOS and iOS
233 --------------------------
234 The macOS build should compile clean without the need to download any dependencies or link to any libraries or
235 frameworks. The iOS build needs to be compiled as Objective-C (sorry) and will need to link the relevant frameworks
236 but should Just Work with Xcode.
237 
238 Building for Linux
239 ------------------
240 The Linux build only requires linking to -ldl, -lpthread and -lm. You do not need any development packages.
241 
242 Building for BSD
243 ----------------
244 The BSD build only requires linking to -ldl, -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and
245 FreeBSD uses OSS.
246 
247 Building for Android
248 --------------------
249 AAudio is the highest priority backend on Android. This should work out out of the box without needing any kind of
250 compiler configuration. Support for AAudio starts with Android 8 which means older versions will fall back to
251 OpenSL|ES which requires API level 16+.
252 
253 Building for Emscripten
254 -----------------------
255 The Emscripten build emits Web Audio JavaScript directly and should Just Work without any configuration.
256 
257 
258 NOTES
259 =====
260 - This library uses an asynchronous API for delivering and requesting audio data. Each device will have
261  it's own worker thread which is managed by the library.
262 - If ma_device_init() is called with a device that's not aligned to the platform's natural alignment
263  boundary (4 bytes on 32-bit, 8 bytes on 64-bit), it will _not_ be thread-safe. The reason for this
264  is that it depends on members of ma_device being correctly aligned for atomic assignments.
265 - Sample data is always native-endian and interleaved. For example, ma_format_s16 means signed 16-bit
266  integer samples, interleaved. Let me know if you need non-interleaved and I'll look into it.
267 - The sndio backend is currently only enabled on OpenBSD builds.
268 - The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it.
269 - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI
270  and Core Audio, however other backends such as PulseAudio may naturally support it, though not all have
271  been tested.
272 
273 
274 BACKEND NUANCES
275 ===============
276 
277 PulseAudio
278 ----------
279 - If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
280  https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling
281  Alternatively, consider using a different backend such as ALSA.
282 
283 Android
284 -------
285 - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
286  <uses-permission android:name="android.permission.RECORD_AUDIO" />
287 - With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a limitation with OpenSL|ES.
288 - With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration API (devices are
289  enumerated through Java). You can however perform your own device enumeration through Java and then set the ID in the
290  ma_device_id structure (ma_device_id.aaudio) and pass it to ma_device_init().
291 - The backend API will perform resampling where possible. The reason for this as opposed to using miniaudio's built-in
292  resampler is to take advantage of any potential device-specific optimizations the driver may implement.
293 
294 UWP
295 ---
296 - UWP only supports default playback and capture devices.
297 - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):
298  <Package ...>
299  ...
300  <Capabilities>
301  <DeviceCapability Name="microphone" />
302  </Capabilities>
303  </Package>
304 
305 Web Audio / Emscripten
306 ----------------------
307 - The first time a context is initialized it will create a global object called "mal" whose primary purpose is to act
308  as a factory for device objects.
309 - Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated.
310 - Google is implementing a policy in their browsers that prevent automatic media output without first receiving some kind
311  of user input. See here for details: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting
312  the device may fail if you try to start playback without first handling some kind of user input.
313 
314 
315 OPTIONS
316 =======
317 #define these options before including this file.
318 
319 #define MA_NO_WASAPI
320  Disables the WASAPI backend.
321 
322 #define MA_NO_DSOUND
323  Disables the DirectSound backend.
324 
325 #define MA_NO_WINMM
326  Disables the WinMM backend.
327 
328 #define MA_NO_ALSA
329  Disables the ALSA backend.
330 
331 #define MA_NO_PULSEAUDIO
332  Disables the PulseAudio backend.
333 
334 #define MA_NO_JACK
335  Disables the JACK backend.
336 
337 #define MA_NO_COREAUDIO
338  Disables the Core Audio backend.
339 
340 #define MA_NO_SNDIO
341  Disables the sndio backend.
342 
343 #define MA_NO_AUDIO4
344  Disables the audio(4) backend.
345 
346 #define MA_NO_OSS
347  Disables the OSS backend.
348 
349 #define MA_NO_AAUDIO
350  Disables the AAudio backend.
351 
352 #define MA_NO_OPENSL
353  Disables the OpenSL|ES backend.
354 
355 #define MA_NO_WEBAUDIO
356  Disables the Web Audio backend.
357 
358 #define MA_NO_NULL
359  Disables the null backend.
360 
361 #define MA_DEFAULT_PERIODS
362  When a period count of 0 is specified when a device is initialized, it will default to this.
363 
364 #define MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
365 #define MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
366  When a buffer size of 0 is specified when a device is initialized it will default to a buffer of this size, depending
367  on the chosen performance profile. These can be increased or decreased depending on your specific requirements.
368 
369 #define MA_NO_DECODING
370  Disables the decoding APIs.
371 
372 #define MA_NO_DEVICE_IO
373  Disables playback and recording. This will disable ma_context and ma_device APIs. This is useful if you only want to
374  use miniaudio's data conversion and/or decoding APIs.
375 
376 #define MA_NO_STDIO
377  Disables file IO APIs.
378 
379 #define MA_NO_SSE2
380  Disables SSE2 optimizations.
381 
382 #define MA_NO_AVX2
383  Disables AVX2 optimizations.
384 
385 #define MA_NO_AVX512
386  Disables AVX-512 optimizations.
387 
388 #define MA_NO_NEON
389  Disables NEON optimizations.
390 
391 #define MA_LOG_LEVEL <Level>
392  Sets the logging level. Set level to one of the following:
393  MA_LOG_LEVEL_VERBOSE
394  MA_LOG_LEVEL_INFO
395  MA_LOG_LEVEL_WARNING
396  MA_LOG_LEVEL_ERROR
397 
398 #define MA_DEBUG_OUTPUT
399  Enable printf() debug output.
400 
401 #define MA_COINIT_VALUE
402  Windows only. The value to pass to internal calls to CoInitializeEx(). Defaults to COINIT_MULTITHREADED.
403 
404 
405 DEFINITIONS
406 ===========
407 This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms
408 throughout the audio space, so this section is intended to clarify how miniaudio uses each term.
409 
410 Sample
411 ------
412 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.
413 
414 Frame / PCM Frame
415 -----------------
416 A frame is a groups of samples equal to the number of channels. For a stereo stream a frame is 2 samples, a mono frame
417 is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" and "PCM frame" are the same thing in
418 miniaudio. Note that this is different to a compressed frame. If ever miniaudio needs to refer to a compressed frame, such
419 as a FLAC frame, it will always clarify what it's referring to with something like "FLAC frame" or whatnot.
420 
421 Channel
422 -------
423 A stream of monaural audio that is emitted from an individual speaker in a speaker system, or received from an individual
424 microphone in a microphone system. A stereo stream has two channels (a left channel, and a right channel), a 5.1 surround
425 sound system has 6 channels, etc. Some audio systems refer to a channel as a complex audio stream that's mixed with other
426 channels to produce the final mix - this is completely different to miniaudio's use of the term "channel" and should not be
427 confused.
428 
429 Sample Rate
430 -----------
431 The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number of PCM frames that are
432 processed per second.
433 
434 Formats
435 -------
436 Throughout miniaudio you will see references to different sample formats:
437  u8 - Unsigned 8-bit integer
438  s16 - Signed 16-bit integer
439  s24 - Signed 24-bit integer (tightly packed).
440  s32 - Signed 32-bit integer
441  f32 - 32-bit floating point
442 */
443 
444 #ifndef miniaudio_h
445 #define miniaudio_h
446 
447 #ifdef __cplusplus
448 extern "C" {
449 #endif
450 
451 #if defined(_MSC_VER)
452  #pragma warning(push)
453  #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
454  #pragma warning(disable:4324) /* structure was padded due to alignment specifier */
455 #else
456  #pragma GCC diagnostic push
457  #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
458 #endif
459 
460 /* Platform/backend detection. */
461 #ifdef _WIN32
462  #define MA_WIN32
463  #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
464  #define MA_WIN32_UWP
465  #else
466  #define MA_WIN32_DESKTOP
467  #endif
468 #else
469  #define MA_POSIX
470  #include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
471 
472  #ifdef __unix__
473  #define MA_UNIX
474  #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
475  #define MA_BSD
476  #endif
477  #endif
478  #ifdef __linux__
479  #define MA_LINUX
480  #endif
481  #ifdef __APPLE__
482  #define MA_APPLE
483  #endif
484  #ifdef __ANDROID__
485  #define MA_ANDROID
486  #endif
487  #ifdef __EMSCRIPTEN__
488  #define MA_EMSCRIPTEN
489  #endif
490 #endif
491 
492 #include <stddef.h> /* For size_t. */
493 
494 /* Sized types. Prefer built-in types. Fall back to stdint. */
495 #ifdef _MSC_VER
496  #if defined(__clang__)
497  #pragma GCC diagnostic push
498  #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
499  #pragma GCC diagnostic ignored "-Wc++11-long-long"
500  #endif
501  typedef signed __int8 ma_int8;
502  typedef unsigned __int8 ma_uint8;
503  typedef signed __int16 ma_int16;
504  typedef unsigned __int16 ma_uint16;
505  typedef signed __int32 ma_int32;
506  typedef unsigned __int32 ma_uint32;
507  typedef signed __int64 ma_int64;
508  typedef unsigned __int64 ma_uint64;
509  #if defined(__clang__)
510  #pragma GCC diagnostic pop
511  #endif
512 #else
513  #define MA_HAS_STDINT
514  #include <stdint.h>
515  typedef int8_t ma_int8;
516  typedef uint8_t ma_uint8;
517  typedef int16_t ma_int16;
518  typedef uint16_t ma_uint16;
519  typedef int32_t ma_int32;
520  typedef uint32_t ma_uint32;
521  typedef int64_t ma_int64;
522  typedef uint64_t ma_uint64;
523 #endif
524 
525 #ifdef MA_HAS_STDINT
526  typedef uintptr_t ma_uintptr;
527 #else
528  #if defined(_WIN32)
529  #if defined(_WIN64)
530  typedef ma_uint64 ma_uintptr;
531  #else
532  typedef ma_uint32 ma_uintptr;
533  #endif
534  #elif defined(__GNUC__)
535  #if defined(__LP64__)
536  typedef ma_uint64 ma_uintptr;
537  #else
538  typedef ma_uint32 ma_uintptr;
539  #endif
540  #else
541  typedef ma_uint64 ma_uintptr; /* Fallback. */
542  #endif
543 #endif
544 
547 #define MA_TRUE 1
548 #define MA_FALSE 0
549 
550 typedef void* ma_handle;
551 typedef void* ma_ptr;
552 typedef void (* ma_proc)(void);
553 
554 #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
555 typedef ma_uint16 wchar_t;
556 #endif
557 
558 /* Define NULL for some compilers. */
559 #ifndef NULL
560 #define NULL 0
561 #endif
562 
563 #if defined(SIZE_MAX)
564  #define MA_SIZE_MAX SIZE_MAX
565 #else
566  #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
567 #endif
568 
569 
570 #ifdef _MSC_VER
571 #define MA_INLINE __forceinline
572 #else
573 #ifdef __GNUC__
574 #define MA_INLINE __inline__ __attribute__((always_inline))
575 #else
576 #define MA_INLINE
577 #endif
578 #endif
579 
580 #if defined(_MSC_VER)
581  #if _MSC_VER >= 1400
582  #define MA_ALIGN(alignment) __declspec(align(alignment))
583  #endif
584 #elif !defined(__DMC__)
585  #define MA_ALIGN(alignment) __attribute__((aligned(alignment)))
586 #endif
587 #ifndef MA_ALIGN
588  #define MA_ALIGN(alignment)
589 #endif
590 
591 #ifdef _MSC_VER
592 #define MA_ALIGNED_STRUCT(alignment) MA_ALIGN(alignment) struct
593 #else
594 #define MA_ALIGNED_STRUCT(alignment) struct MA_ALIGN(alignment)
595 #endif
596 
597 /* SIMD alignment in bytes. Currently set to 64 bytes in preparation for future AVX-512 optimizations. */
598 #define MA_SIMD_ALIGNMENT 64
599 
600 
601 /* Logging levels */
602 #define MA_LOG_LEVEL_VERBOSE 4
603 #define MA_LOG_LEVEL_INFO 3
604 #define MA_LOG_LEVEL_WARNING 2
605 #define MA_LOG_LEVEL_ERROR 1
606 
607 #ifndef MA_LOG_LEVEL
608 #define MA_LOG_LEVEL MA_LOG_LEVEL_ERROR
609 #endif
610 
611 typedef struct ma_context ma_context;
612 typedef struct ma_device ma_device;
613 
615 #define MA_CHANNEL_NONE 0
616 #define MA_CHANNEL_MONO 1
617 #define MA_CHANNEL_FRONT_LEFT 2
618 #define MA_CHANNEL_FRONT_RIGHT 3
619 #define MA_CHANNEL_FRONT_CENTER 4
620 #define MA_CHANNEL_LFE 5
621 #define MA_CHANNEL_BACK_LEFT 6
622 #define MA_CHANNEL_BACK_RIGHT 7
623 #define MA_CHANNEL_FRONT_LEFT_CENTER 8
624 #define MA_CHANNEL_FRONT_RIGHT_CENTER 9
625 #define MA_CHANNEL_BACK_CENTER 10
626 #define MA_CHANNEL_SIDE_LEFT 11
627 #define MA_CHANNEL_SIDE_RIGHT 12
628 #define MA_CHANNEL_TOP_CENTER 13
629 #define MA_CHANNEL_TOP_FRONT_LEFT 14
630 #define MA_CHANNEL_TOP_FRONT_CENTER 15
631 #define MA_CHANNEL_TOP_FRONT_RIGHT 16
632 #define MA_CHANNEL_TOP_BACK_LEFT 17
633 #define MA_CHANNEL_TOP_BACK_CENTER 18
634 #define MA_CHANNEL_TOP_BACK_RIGHT 19
635 #define MA_CHANNEL_AUX_0 20
636 #define MA_CHANNEL_AUX_1 21
637 #define MA_CHANNEL_AUX_2 22
638 #define MA_CHANNEL_AUX_3 23
639 #define MA_CHANNEL_AUX_4 24
640 #define MA_CHANNEL_AUX_5 25
641 #define MA_CHANNEL_AUX_6 26
642 #define MA_CHANNEL_AUX_7 27
643 #define MA_CHANNEL_AUX_8 28
644 #define MA_CHANNEL_AUX_9 29
645 #define MA_CHANNEL_AUX_10 30
646 #define MA_CHANNEL_AUX_11 31
647 #define MA_CHANNEL_AUX_12 32
648 #define MA_CHANNEL_AUX_13 33
649 #define MA_CHANNEL_AUX_14 34
650 #define MA_CHANNEL_AUX_15 35
651 #define MA_CHANNEL_AUX_16 36
652 #define MA_CHANNEL_AUX_17 37
653 #define MA_CHANNEL_AUX_18 38
654 #define MA_CHANNEL_AUX_19 39
655 #define MA_CHANNEL_AUX_20 40
656 #define MA_CHANNEL_AUX_21 41
657 #define MA_CHANNEL_AUX_22 42
658 #define MA_CHANNEL_AUX_23 43
659 #define MA_CHANNEL_AUX_24 44
660 #define MA_CHANNEL_AUX_25 45
661 #define MA_CHANNEL_AUX_26 46
662 #define MA_CHANNEL_AUX_27 47
663 #define MA_CHANNEL_AUX_28 48
664 #define MA_CHANNEL_AUX_29 49
665 #define MA_CHANNEL_AUX_30 50
666 #define MA_CHANNEL_AUX_31 51
667 #define MA_CHANNEL_LEFT MA_CHANNEL_FRONT_LEFT
668 #define MA_CHANNEL_RIGHT MA_CHANNEL_FRONT_RIGHT
669 #define MA_CHANNEL_POSITION_COUNT MA_CHANNEL_AUX_31 + 1
670 
671 
672 typedef int ma_result;
673 #define MA_SUCCESS 0
674 
675 /* General errors. */
676 #define MA_ERROR -1 /* A generic error. */
677 #define MA_INVALID_ARGS -2
678 #define MA_INVALID_OPERATION -3
679 #define MA_OUT_OF_MEMORY -4
680 #define MA_ACCESS_DENIED -5
681 #define MA_TOO_LARGE -6
682 #define MA_TIMEOUT -7
683 
684 /* General miniaudio-specific errors. */
685 #define MA_FORMAT_NOT_SUPPORTED -100
686 #define MA_DEVICE_TYPE_NOT_SUPPORTED -101
687 #define MA_SHARE_MODE_NOT_SUPPORTED -102
688 #define MA_NO_BACKEND -103
689 #define MA_NO_DEVICE -104
690 #define MA_API_NOT_FOUND -105
691 #define MA_INVALID_DEVICE_CONFIG -106
692 
693 /* State errors. */
694 #define MA_DEVICE_BUSY -200
695 #define MA_DEVICE_NOT_INITIALIZED -201
696 #define MA_DEVICE_NOT_STARTED -202
697 #define MA_DEVICE_UNAVAILABLE -203
698 
699 /* Operation errors. */
700 #define MA_FAILED_TO_MAP_DEVICE_BUFFER -300
701 #define MA_FAILED_TO_UNMAP_DEVICE_BUFFER -301
702 #define MA_FAILED_TO_INIT_BACKEND -302
703 #define MA_FAILED_TO_READ_DATA_FROM_CLIENT -303
704 #define MA_FAILED_TO_READ_DATA_FROM_DEVICE -304
705 #define MA_FAILED_TO_SEND_DATA_TO_CLIENT -305
706 #define MA_FAILED_TO_SEND_DATA_TO_DEVICE -306
707 #define MA_FAILED_TO_OPEN_BACKEND_DEVICE -307
708 #define MA_FAILED_TO_START_BACKEND_DEVICE -308
709 #define MA_FAILED_TO_STOP_BACKEND_DEVICE -309
710 #define MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE -310
711 #define MA_FAILED_TO_CREATE_MUTEX -311
712 #define MA_FAILED_TO_CREATE_EVENT -312
713 #define MA_FAILED_TO_CREATE_THREAD -313
714 
715 
716 /* Standard sample rates. */
717 #define MA_SAMPLE_RATE_8000 8000
718 #define MA_SAMPLE_RATE_11025 11025
719 #define MA_SAMPLE_RATE_16000 16000
720 #define MA_SAMPLE_RATE_22050 22050
721 #define MA_SAMPLE_RATE_24000 24000
722 #define MA_SAMPLE_RATE_32000 32000
723 #define MA_SAMPLE_RATE_44100 44100
724 #define MA_SAMPLE_RATE_48000 48000
725 #define MA_SAMPLE_RATE_88200 88200
726 #define MA_SAMPLE_RATE_96000 96000
727 #define MA_SAMPLE_RATE_176400 176400
728 #define MA_SAMPLE_RATE_192000 192000
729 #define MA_SAMPLE_RATE_352800 352800
730 #define MA_SAMPLE_RATE_384000 384000
731 
732 #define MA_MIN_PCM_SAMPLE_SIZE_IN_BYTES 1 /* For simplicity, miniaudio does not support PCM samples that are not byte aligned. */
733 #define MA_MAX_PCM_SAMPLE_SIZE_IN_BYTES 8
734 #define MA_MIN_CHANNELS 1
735 #define MA_MAX_CHANNELS 32
736 #define MA_MIN_SAMPLE_RATE MA_SAMPLE_RATE_8000
737 #define MA_MAX_SAMPLE_RATE MA_SAMPLE_RATE_384000
738 #define MA_SRC_SINC_MIN_WINDOW_WIDTH 2
739 #define MA_SRC_SINC_MAX_WINDOW_WIDTH 32
740 #define MA_SRC_SINC_DEFAULT_WINDOW_WIDTH 32
741 #define MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION 8
742 #define MA_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES 256
743 
744 typedef enum
745 {
748 
749 typedef enum
750 {
754 
755 typedef enum
756 {
761 
762 typedef enum
763 {
764  /*
765  I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
766  added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().
767  */
768  ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */
770  ma_format_s16 = 2, /* Seems to be the most widely supported format. */
771  ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */
775 } ma_format;
776 
777 typedef enum
778 {
779  ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */
780  ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */
781  ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */
785 
786 typedef enum
787 {
790  ma_standard_channel_map_rfc3551, /* Based off AIFF. */
793  ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */
794  ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */
795  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. */
798 
799 typedef enum
800 {
804 
805 
807 typedef ma_uint32 (* ma_format_converter_read_proc) (ma_format_converter* pConverter, ma_uint32 frameCount, void* pFramesOut, void* pUserData);
808 typedef ma_uint32 (* ma_format_converter_read_deinterleaved_proc)(ma_format_converter* pConverter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData);
809 
810 typedef struct
811 {
824  void* pUserData;
826 
828 {
834  void (* onConvertPCM)(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode);
835  void (* onInterleavePCM)(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels);
836  void (* onDeinterleavePCM)(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels);
837 };
838 
839 
840 
842 typedef ma_uint32 (* ma_channel_router_read_deinterleaved_proc)(ma_channel_router* pRouter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData);
843 
844 typedef struct
845 {
849  ma_channel channelMapOut[MA_MAX_CHANNELS];
851  float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
857  void* pUserData;
859 
861 {
870 };
871 
872 
873 
874 typedef struct ma_src ma_src;
875 typedef ma_uint32 (* ma_src_read_deinterleaved_proc)(ma_src* pSRC, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData); /* Returns the number of frames that were read. */
876 
877 typedef enum
878 {
884 
885 typedef enum
886 {
891 
892 typedef struct
893 {
897 
898 typedef struct
899 {
910  void* pUserData;
911  union
912  {
914  };
915 } ma_src_config;
916 
918 {
919  union
920  {
921  struct
922  {
924  float timeIn;
925  ma_uint32 leftoverFrames;
926  } linear;
927 
928  struct
929  {
931  float timeIn;
932  ma_uint32 inputFrameCount; /* The number of frames sitting in the input buffer, not including the first half of the window. */
933  ma_uint32 windowPosInSamples; /* An offset of <input>. */
934  float table[MA_SRC_SINC_MAX_WINDOW_WIDTH*1 * MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION]; /* Precomputed lookup table. The +1 is used to avoid the need for an overflow check. */
935  } sinc;
936  };
937 
938  ma_src_config config;
939  ma_bool32 isEndOfInputLoaded : 1;
940  ma_bool32 useSSE2 : 1;
941  ma_bool32 useAVX2 : 1;
942  ma_bool32 useAVX512 : 1;
943  ma_bool32 useNEON : 1;
944 };
945 
947 typedef ma_uint32 (* ma_pcm_converter_read_proc)(ma_pcm_converter* pDSP, void* pSamplesOut, ma_uint32 frameCount, void* pUserData);
948 
949 typedef struct
950 {
958  ma_channel channelMapOut[MA_MAX_CHANNELS];
963  ma_bool32 neverConsumeEndOfInput : 1; /* <-- For SRC. */
969  void* pUserData;
970  union
971  {
973  };
975 
977 {
979  void* pUserData;
980  ma_format_converter formatConverterIn; /* For converting data to f32 in preparation for further processing. */
981  ma_format_converter formatConverterOut; /* For converting data to the requested output format. Used as the final step in the processing pipeline. */
982  ma_channel_router channelRouter; /* For channel conversion. */
983  ma_src src; /* For sample rate conversion. */
984  ma_bool32 isDynamicSampleRateAllowed : 1; /* ma_pcm_converter_set_input_sample_rate() and ma_pcm_converter_set_output_sample_rate() will fail if this is set to false. */
985  ma_bool32 isPreFormatConversionRequired : 1;
986  ma_bool32 isPostFormatConversionRequired : 1;
987  ma_bool32 isChannelRoutingRequired : 1;
988  ma_bool32 isSRCRequired : 1;
989  ma_bool32 isChannelRoutingAtStart : 1;
990  ma_bool32 isPassthrough : 1; /* <-- Will be set to true when the DSP pipeline is an optimized passthrough. */
991 };
992 
993 
994 /************************************************************************************************************************************************************
995 *************************************************************************************************************************************************************
996 
997 DATA CONVERSION
998 ===============
999 
1000 This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.
1001 
1002 *************************************************************************************************************************************************************
1003 ************************************************************************************************************************************************************/
1004 
1005 /************************************************************************************************************************************************************
1006 
1007 Channel Maps
1008 ============
1009 
1010 Below is the channel map used by ma_standard_channel_map_default:
1011 
1012 |---------------|------------------------------|
1013 | Channel Count | Mapping |
1014 |---------------|------------------------------|
1015 | 1 (Mono) | 0: MA_CHANNEL_MONO |
1016 |---------------|------------------------------|
1017 | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT |
1018 | | 1: MA_CHANNEL_FRONT_RIGHT |
1019 |---------------|------------------------------|
1020 | 3 | 0: MA_CHANNEL_FRONT_LEFT |
1021 | | 1: MA_CHANNEL_FRONT_RIGHT |
1022 | | 2: MA_CHANNEL_FRONT_CENTER |
1023 |---------------|------------------------------|
1024 | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT |
1025 | | 1: MA_CHANNEL_FRONT_RIGHT |
1026 | | 2: MA_CHANNEL_FRONT_CENTER |
1027 | | 3: MA_CHANNEL_BACK_CENTER |
1028 |---------------|------------------------------|
1029 | 5 | 0: MA_CHANNEL_FRONT_LEFT |
1030 | | 1: MA_CHANNEL_FRONT_RIGHT |
1031 | | 2: MA_CHANNEL_FRONT_CENTER |
1032 | | 3: MA_CHANNEL_BACK_LEFT |
1033 | | 4: MA_CHANNEL_BACK_RIGHT |
1034 |---------------|------------------------------|
1035 | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT |
1036 | | 1: MA_CHANNEL_FRONT_RIGHT |
1037 | | 2: MA_CHANNEL_FRONT_CENTER |
1038 | | 3: MA_CHANNEL_LFE |
1039 | | 4: MA_CHANNEL_SIDE_LEFT |
1040 | | 5: MA_CHANNEL_SIDE_RIGHT |
1041 |---------------|------------------------------|
1042 | 7 | 0: MA_CHANNEL_FRONT_LEFT |
1043 | | 1: MA_CHANNEL_FRONT_RIGHT |
1044 | | 2: MA_CHANNEL_FRONT_CENTER |
1045 | | 3: MA_CHANNEL_LFE |
1046 | | 4: MA_CHANNEL_BACK_CENTER |
1047 | | 4: MA_CHANNEL_SIDE_LEFT |
1048 | | 5: MA_CHANNEL_SIDE_RIGHT |
1049 |---------------|------------------------------|
1050 | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT |
1051 | | 1: MA_CHANNEL_FRONT_RIGHT |
1052 | | 2: MA_CHANNEL_FRONT_CENTER |
1053 | | 3: MA_CHANNEL_LFE |
1054 | | 4: MA_CHANNEL_BACK_LEFT |
1055 | | 5: MA_CHANNEL_BACK_RIGHT |
1056 | | 6: MA_CHANNEL_SIDE_LEFT |
1057 | | 7: MA_CHANNEL_SIDE_RIGHT |
1058 |---------------|------------------------------|
1059 | Other | All channels set to 0. This |
1060 | | is equivalent to the same |
1061 | | mapping as the device. |
1062 |---------------|------------------------------|
1063 
1064 ************************************************************************************************************************************************************/
1065 
1066 /*
1067 Helper for retrieving a standard channel map.
1068 */
1070 
1071 /*
1072 Copies a channel map.
1073 */
1075 
1076 
1077 /*
1078 Determines whether or not a channel map is valid.
1079 
1080 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
1081 is usually treated as a passthrough.
1082 
1083 Invalid channel maps:
1084  - A channel map with no channels
1085  - A channel map with more than one channel and a mono channel
1086 */
1088 
1089 /*
1090 Helper for comparing two channel maps for equality.
1091 
1092 This assumes the channel count is the same between the two.
1093 */
1095 
1096 /*
1097 Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
1098 */
1100 
1101 /*
1102 Helper for determining whether or not a channel is present in the given channel map.
1103 */
1105 
1106 
1107 /************************************************************************************************************************************************************
1108 
1109 Format Conversion
1110 =================
1111 The format converter serves two purposes:
1112  1) Conversion between data formats (u8 to f32, etc.)
1113  2) Interleaving and deinterleaving
1114 
1115 When initializing a converter, you specify the input and output formats (u8, s16, etc.) and read callbacks. There are two read callbacks - one for
1116 interleaved input data (onRead) and another for deinterleaved input data (onReadDeinterleaved). You implement whichever is most convenient for you. You
1117 can implement both, but it's not recommended as it just introduces unnecessary complexity.
1118 
1119 To read data as interleaved samples, use ma_format_converter_read(). Otherwise use ma_format_converter_read_deinterleaved().
1120 
1121 Dithering
1122 ---------
1123 The format converter also supports dithering. Dithering can be set using ditherMode variable in the config, like so.
1124 
1125  pConfig->ditherMode = ma_dither_mode_rectangle;
1126 
1127 The different dithering modes include the following, in order of efficiency:
1128  - None: ma_dither_mode_none
1129  - Rectangle: ma_dither_mode_rectangle
1130  - Triangle: ma_dither_mode_triangle
1131 
1132 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.
1133 Dithering is available for the following conversions:
1134  - s16 -> u8
1135  - s24 -> u8
1136  - s32 -> u8
1137  - f32 -> u8
1138  - s24 -> s16
1139  - s32 -> s16
1140  - f32 -> s16
1141 
1142 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.
1143 
1144 ************************************************************************************************************************************************************/
1145 
1146 /*
1147 Initializes a format converter.
1148 */
1150 
1151 /*
1152 Reads data from the format converter as interleaved channels.
1153 */
1154 ma_uint64 ma_format_converter_read(ma_format_converter* pConverter, ma_uint64 frameCount, void* pFramesOut, void* pUserData);
1155 
1156 /*
1157 Reads data from the format converter as deinterleaved channels.
1158 */
1159 ma_uint64 ma_format_converter_read_deinterleaved(ma_format_converter* pConverter, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
1160 
1161 /*
1162 Helper for initializing a format converter config.
1163 */
1167 
1168 
1169 
1170 /************************************************************************************************************************************************************
1171 
1172 Channel Routing
1173 ===============
1174 There are two main things you can do with the channel router:
1175  1) Rearrange channels
1176  2) Convert from one channel count to another
1177 
1178 Channel Rearrangement
1179 ---------------------
1180 A simple example of channel rearrangement may be swapping the left and right channels in a stereo stream. To do this you just pass in the same channel
1181 count for both the input and output with channel maps that contain the same channels (in a different order).
1182 
1183 Channel Conversion
1184 ------------------
1185 The channel router can also convert from one channel count to another, such as converting a 5.1 stream to stero. When changing the channel count, the
1186 router will first perform a 1:1 mapping of channel positions that are present in both the input and output channel maps. The second thing it will do
1187 is distribute the input mono channel (if any) across all output channels, excluding any None and LFE channels. If there is an output mono channel, all
1188 input channels will be averaged, excluding any None and LFE channels.
1189 
1190 The last case to consider is when a channel position in the input channel map is not present in the output channel map, and vice versa. In this case the
1191 channel router will perform a blend of other related channels to produce an audible channel. There are several blending modes.
1192  1) Simple
1193  Unmatched channels are silenced.
1194  2) Planar Blending
1195  Channels are blended based on a set of planes that each speaker emits audio from.
1196 
1197 Rectangular / Planar Blending
1198 -----------------------------
1199 In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker.
1200 This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left
1201 of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map
1202 contains only the front/left channel position, but the output channel map contains both the front/left and front/center channel. When deciding on the audio
1203 data to send to the front/center speaker (which has no 1:1 mapping with an input channel) we need to use some logic based on our available input channel
1204 positions.
1205 
1206 As mentioned earlier, our front/left speaker is, conceptually speaking, emitting audio from the front _and_ the left planes. Similarly, the front/center
1207 speaker is emitting audio from _only_ the front plane. What these two channels have in common is that they are both emitting audio from the front plane.
1208 Thus, it makes sense that the front/center speaker should receive some contribution from the front/left channel. How much contribution depends on their
1209 planar relationship (thus the name of this blending technique).
1210 
1211 Because the front/left channel is emitting audio from two planes (front and left), you can think of it as though it's willing to dedicate 50% of it's total
1212 volume to each of it's planes (a channel position emitting from 1 plane would be willing to given 100% of it's total volume to that plane, and a channel
1213 position emitting from 3 planes would be willing to given 33% of it's total volume to each plane). Similarly, the front/center speaker is emitting audio
1214 from only one plane so you can think of it as though it's willing to _take_ 100% of it's volume from front plane emissions. Now, since the front/left
1215 channel is willing to _give_ 50% of it's total volume to the front plane, and the front/center speaker is willing to _take_ 100% of it's total volume
1216 from the front, you can imagine that 50% of the front/left speaker will be given to the front/center speaker.
1217 
1218 Usage
1219 -----
1220 To use the channel router you need to specify three things:
1221  1) The input channel count and channel map
1222  2) The output channel count and channel map
1223  3) The mixing mode to use in the case where a 1:1 mapping is unavailable
1224 
1225 Note that input and output data is always deinterleaved 32-bit floating point.
1226 
1227 Initialize the channel router with ma_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration,
1228 mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing. Note
1229 that the mixing mode is only used when a 1:1 mapping is unavailable. This includes the custom weights mode.
1230 
1231 Read data from the channel router with ma_channel_router_read_deinterleaved(). Output data is always 32-bit floating point.
1232 
1233 ************************************************************************************************************************************************************/
1234 
1235 /*
1236 Initializes a channel router where it is assumed that the input data is non-interleaved.
1237 */
1239 
1240 /*
1241 Reads data from the channel router as deinterleaved channels.
1242 */
1243 ma_uint64 ma_channel_router_read_deinterleaved(ma_channel_router* pRouter, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
1244 
1245 /*
1246 Helper for initializing a channel router config.
1247 */
1248 ma_channel_router_config ma_channel_router_config_init(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, ma_channel_router_read_deinterleaved_proc onRead, void* pUserData);
1249 
1250 
1251 /************************************************************************************************************************************************************
1252 
1253 Sample Rate Conversion
1254 ======================
1255 
1256 ************************************************************************************************************************************************************/
1257 
1258 /*
1259 Initializes a sample rate conversion object.
1260 */
1261 ma_result ma_src_init(const ma_src_config* pConfig, ma_src* pSRC);
1262 
1263 /*
1264 Dynamically adjusts the sample rate.
1265 
1266 This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
1267 is not acceptable you will need to use your own algorithm.
1268 */
1269 ma_result ma_src_set_sample_rate(ma_src* pSRC, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
1270 
1271 /*
1272 Reads a number of frames.
1273 
1274 Returns the number of frames actually read.
1275 */
1276 ma_uint64 ma_src_read_deinterleaved(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
1277 
1278 /*
1279 Helper for creating a sample rate conversion config.
1280 */
1282 ma_src_config ma_src_config_init(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint32 channels, ma_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData);
1283 
1284 
1285 /************************************************************************************************************************************************************
1286 
1287 Conversion
1288 
1289 ************************************************************************************************************************************************************/
1290 
1291 /*
1292 Initializes a DSP object.
1293 */
1295 
1296 /*
1297 Dynamically adjusts the input sample rate.
1298 
1299 This will fail is the DSP was not initialized with allowDynamicSampleRate.
1300 
1301 DEPRECATED. Use ma_pcm_converter_set_sample_rate() instead.
1302 */
1304 
1305 /*
1306 Dynamically adjusts the output sample rate.
1307 
1308 This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
1309 is not acceptable you will need to use your own algorithm.
1310 
1311 This will fail is the DSP was not initialized with allowDynamicSampleRate.
1312 
1313 DEPRECATED. Use ma_pcm_converter_set_sample_rate() instead.
1314 */
1316 
1317 /*
1318 Dynamically adjusts the output sample rate.
1319 
1320 This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
1321 is not acceptable you will need to use your own algorithm.
1322 
1323 This will fail if the DSP was not initialized with allowDynamicSampleRate.
1324 */
1326 
1327 /*
1328 Reads a number of frames and runs them through the DSP processor.
1329 */
1330 ma_uint64 ma_pcm_converter_read(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint64 frameCount);
1331 
1332 /*
1333 Helper for initializing a ma_pcm_converter_config object.
1334 */
1336 ma_pcm_converter_config ma_pcm_converter_config_init(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_pcm_converter_read_proc onRead, void* pUserData);
1337 ma_pcm_converter_config ma_pcm_converter_config_init_ex(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], ma_pcm_converter_read_proc onRead, void* pUserData);
1338 
1339 /*
1340 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
1341 determine the required size of the output buffer.
1342 
1343 A return value of 0 indicates an error.
1344 
1345 This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead.
1346 */
1347 ma_uint64 ma_convert_frames(void* pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_uint64 frameCount);
1348 ma_uint64 ma_convert_frames_ex(void* pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], const void* pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint64 frameCount);
1349 
1350 
1351 /************************************************************************************************************************************************************
1352 
1353 Ring Buffer
1354 ===========
1355 
1356 Features
1357 --------
1358 - Lock free (assuming single producer, single consumer)
1359 - Support for interleaved and deinterleaved streams
1360 - Allows the caller to allocate their own block of memory
1361 
1362 Usage
1363 -----
1364 - Call ma_rb_init() to initialize a simple buffer, with an optional pre-allocated buffer. If you pass in NULL
1365  for the pre-allocated buffer, it will be allocated for you and free()'d in ma_rb_uninit(). If you pass in
1366  your own pre-allocated buffer, free()-ing is left to you.
1367 
1368 - Call ma_rb_init_ex() if you need a deinterleaved buffer. The data for each sub-buffer is offset from each
1369  other based on the stride. Use ma_rb_get_subbuffer_stride(), ma_rb_get_subbuffer_offset() and
1370  ma_rb_get_subbuffer_ptr() to manage your sub-buffers.
1371 
1372 - Use ma_rb_acquire_read() and ma_rb_acquire_write() to retrieve a pointer to a section of the ring buffer.
1373  You specify the number of bytes you need, and on output it will set to what was actually acquired. If the
1374  read or write pointer is positioned such that the number of bytes requested will require a loop, it will be
1375  clamped to the end of the buffer. Therefore, the number of bytes you're given may be less than the number
1376  you requested.
1377 
1378 - After calling ma_rb_acquire_read/write(), you do your work on the buffer and then "commit" it with
1379  ma_rb_commit_read/write(). This is where the read/write pointers are updated. When you commit you need to
1380  pass in the buffer that was returned by the earlier call to ma_rb_acquire_read/write() and is only used
1381  for validation. The number of bytes passed to ma_rb_commit_read/write() is what's used to increment the
1382  pointers.
1383 
1384 - If you want to correct for drift between the write pointer and the read pointer you can use a combination
1385  of ma_rb_pointer_distance(), ma_rb_seek_read() and ma_rb_seek_write(). Note that you can only move the
1386  pointers forward, and you should only move the read pointer forward via the consumer thread, and the write
1387  pointer forward by the producer thread. If there is too much space between the pointers, move the read
1388  pointer forward. If there is too little space between the pointers, move the write pointer forward.
1389 
1390 
1391 Notes
1392 -----
1393 - Thread safety depends on a single producer, single consumer model. Only one thread is allowed to write, and
1394  only one thread is allowed to read. The producer is the only one allowed to move the write pointer, and the
1395  consumer is the only one allowed to move the read pointer.
1396 - Operates on bytes. Use ma_pcm_rb to operate in terms of PCM frames.
1397 - Maximum buffer size in bytes is 0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1) because of reasons.
1398 
1399 
1400 PCM Ring Buffer
1401 ===============
1402 This is the same as the regular ring buffer, except that it works on PCM frames instead of bytes.
1403 
1404 ************************************************************************************************************************************************************/
1405 typedef struct
1406 {
1407  void* pBuffer;
1408  ma_uint32 subbufferSizeInBytes;
1409  ma_uint32 subbufferCount;
1410  ma_uint32 subbufferStrideInBytes;
1411  volatile ma_uint32 encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. */
1412  volatile ma_uint32 encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. */
1413  ma_bool32 ownsBuffer : 1; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */
1414  ma_bool32 clearOnWriteAcquire : 1; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */
1415 } ma_rb;
1416 
1417 ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, ma_rb* pRB);
1418 ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, ma_rb* pRB);
1419 void ma_rb_uninit(ma_rb* pRB);
1420 ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
1421 ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
1422 ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
1423 ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
1424 ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);
1425 ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);
1426 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. */
1427 size_t ma_rb_get_subbuffer_size(ma_rb* pRB);
1428 size_t ma_rb_get_subbuffer_stride(ma_rb* pRB);
1429 size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);
1430 void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);
1431 
1432 
1433 typedef struct
1434 {
1438 } ma_pcm_rb;
1439 
1440 ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, ma_pcm_rb* pRB);
1441 ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, ma_pcm_rb* pRB);
1442 void ma_pcm_rb_uninit(ma_pcm_rb* pRB);
1443 ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
1444 ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
1445 ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
1446 ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
1447 ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
1448 ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
1449 ma_int32 ma_pcm_rb_pointer_disance(ma_pcm_rb* pRB); /* Return value is in frames. */
1453 void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);
1454 
1455 
1456 /************************************************************************************************************************************************************
1457 
1458 Miscellaneous Helpers
1459 
1460 ************************************************************************************************************************************************************/
1462 /*
1463 malloc(). Calls MA_MALLOC().
1464 */
1465 void* ma_malloc(size_t sz);
1467 /*
1468 realloc(). Calls MA_REALLOC().
1469 */
1470 void* ma_realloc(void* p, size_t sz);
1471 
1472 /*
1473 free(). Calls MA_FREE().
1474 */
1475 void ma_free(void* p);
1476 
1477 /*
1478 Performs an aligned malloc, with the assumption that the alignment is a power of 2.
1479 */
1480 void* ma_aligned_malloc(size_t sz, size_t alignment);
1481 
1482 /*
1483 Free's an aligned malloc'd buffer.
1484 */
1485 void ma_aligned_free(void* p);
1486 
1487 /*
1488 Retrieves a friendly name for a format.
1489 */
1490 const char* ma_get_format_name(ma_format format);
1491 
1492 /*
1493 Blends two frames in floating point format.
1494 */
1495 void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);
1496 
1497 /*
1498 Retrieves the size of a sample in bytes for the given format.
1499 
1500 This API is efficient and is implemented using a lookup table.
1501 
1502 Thread Safety: SAFE
1503  This API is pure.
1504 */
1507 
1508 /*
1509 Converts a log level to a string.
1510 */
1511 const char* ma_log_level_to_string(ma_uint32 logLevel);
1512 
1513 
1514 /************************************************************************************************************************************************************
1515 
1516 Format Conversion
1517 
1518 ************************************************************************************************************************************************************/
1519 void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1520 void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1521 void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1522 void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1523 void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1524 void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1525 void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1526 void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1527 void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1528 void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1529 void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1530 void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1531 void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1532 void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1533 void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1534 void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1535 void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1536 void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1537 void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1538 void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1539 void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);
1540 
1541 /*
1542 Deinterleaves an interleaved buffer.
1543 */
1544 void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
1545 
1546 /*
1547 Interleaves a group of deinterleaved buffers.
1548 */
1549 void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
1550 
1551 
1552 /************************************************************************************************************************************************************
1553 *************************************************************************************************************************************************************
1554 
1555 DEVICE I/O
1556 ==========
1557 
1558 This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.
1559 
1560 *************************************************************************************************************************************************************
1561 ************************************************************************************************************************************************************/
1562 #ifndef MA_NO_DEVICE_IO
1563 /* Some backends are only supported on certain platforms. */
1564 #if defined(MA_WIN32)
1565  #define MA_SUPPORT_WASAPI
1566  #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
1567  #define MA_SUPPORT_DSOUND
1568  #define MA_SUPPORT_WINMM
1569  #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
1570  #endif
1571 #endif
1572 #if defined(MA_UNIX)
1573  #if defined(MA_LINUX)
1574  #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */
1575  #define MA_SUPPORT_ALSA
1576  #endif
1577  #endif
1578  #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
1579  #define MA_SUPPORT_PULSEAUDIO
1580  #define MA_SUPPORT_JACK
1581  #endif
1582  #if defined(MA_ANDROID)
1583  #define MA_SUPPORT_AAUDIO
1584  #define MA_SUPPORT_OPENSL
1585  #endif
1586  #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
1587  #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
1588  #endif
1589  #if defined(__NetBSD__) || defined(__OpenBSD__)
1590  #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */
1591  #endif
1592  #if defined(__FreeBSD__) || defined(__DragonFly__)
1593  #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */
1594  #endif
1595 #endif
1596 #if defined(MA_APPLE)
1597  #define MA_SUPPORT_COREAUDIO
1598 #endif
1599 #if defined(MA_EMSCRIPTEN)
1600  #define MA_SUPPORT_WEBAUDIO
1601 #endif
1602 
1603 /* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
1604 #if !defined(MA_EMSCRIPTEN)
1605 #define MA_SUPPORT_NULL
1606 #endif
1607 
1608 
1609 #if !defined(MA_NO_WASAPI) && defined(MA_SUPPORT_WASAPI)
1610  #define MA_ENABLE_WASAPI
1611 #endif
1612 #if !defined(MA_NO_DSOUND) && defined(MA_SUPPORT_DSOUND)
1613  #define MA_ENABLE_DSOUND
1614 #endif
1615 #if !defined(MA_NO_WINMM) && defined(MA_SUPPORT_WINMM)
1616  #define MA_ENABLE_WINMM
1617 #endif
1618 #if !defined(MA_NO_ALSA) && defined(MA_SUPPORT_ALSA)
1619  #define MA_ENABLE_ALSA
1620 #endif
1621 #if !defined(MA_NO_PULSEAUDIO) && defined(MA_SUPPORT_PULSEAUDIO)
1622  #define MA_ENABLE_PULSEAUDIO
1623 #endif
1624 #if !defined(MA_NO_JACK) && defined(MA_SUPPORT_JACK)
1625  #define MA_ENABLE_JACK
1626 #endif
1627 #if !defined(MA_NO_COREAUDIO) && defined(MA_SUPPORT_COREAUDIO)
1628  #define MA_ENABLE_COREAUDIO
1629 #endif
1630 #if !defined(MA_NO_SNDIO) && defined(MA_SUPPORT_SNDIO)
1631  #define MA_ENABLE_SNDIO
1632 #endif
1633 #if !defined(MA_NO_AUDIO4) && defined(MA_SUPPORT_AUDIO4)
1634  #define MA_ENABLE_AUDIO4
1635 #endif
1636 #if !defined(MA_NO_OSS) && defined(MA_SUPPORT_OSS)
1637  #define MA_ENABLE_OSS
1638 #endif
1639 #if !defined(MA_NO_AAUDIO) && defined(MA_SUPPORT_AAUDIO)
1640  #define MA_ENABLE_AAUDIO
1641 #endif
1642 #if !defined(MA_NO_OPENSL) && defined(MA_SUPPORT_OPENSL)
1643  #define MA_ENABLE_OPENSL
1644 #endif
1645 #if !defined(MA_NO_WEBAUDIO) && defined(MA_SUPPORT_WEBAUDIO)
1646  #define MA_ENABLE_WEBAUDIO
1647 #endif
1648 #if !defined(MA_NO_NULL) && defined(MA_SUPPORT_NULL)
1649  #define MA_ENABLE_NULL
1650 #endif
1651 
1652 #ifdef MA_SUPPORT_WASAPI
1653 /* We need a IMMNotificationClient object for WASAPI. */
1654 typedef struct
1655 {
1656  void* lpVtbl;
1660 #endif
1661 
1662 /* Backend enums must be in priority order. */
1663 typedef enum
1664 {
1678  ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
1679 } ma_backend;
1680 
1681 /* Thread priorties should be ordered such that the default priority of the worker thread is 0. */
1682 typedef enum
1683 {
1693 
1694 typedef struct
1695 {
1697 
1698  union
1699  {
1700 #ifdef MA_WIN32
1701  struct
1702  {
1703  /*HANDLE*/ ma_handle hThread;
1704  } win32;
1705 #endif
1706 #ifdef MA_POSIX
1707  struct
1708  {
1709  pthread_t thread;
1710  } posix;
1711 #endif
1712  int _unused;
1713  };
1714 } ma_thread;
1715 
1716 typedef struct
1717 {
1719 
1720  union
1721  {
1722 #ifdef MA_WIN32
1723  struct
1724  {
1725  /*HANDLE*/ ma_handle hMutex;
1726  } win32;
1727 #endif
1728 #ifdef MA_POSIX
1729  struct
1730  {
1731  pthread_mutex_t mutex;
1732  } posix;
1733 #endif
1734  int _unused;
1735  };
1736 } ma_mutex;
1737 
1738 typedef struct
1739 {
1741 
1742  union
1743  {
1744 #ifdef MA_WIN32
1745  struct
1746  {
1747  /*HANDLE*/ ma_handle hEvent;
1748  } win32;
1749 #endif
1750 #ifdef MA_POSIX
1751  struct
1752  {
1753  pthread_mutex_t mutex;
1754  pthread_cond_t condition;
1755  ma_uint32 value;
1756  } posix;
1757 #endif
1758  int _unused;
1759  };
1760 } ma_event;
1761 
1762 
1763 /*
1764 The callback for processing audio data from the device.
1765 
1766 pOutput is a pointer to a buffer that will receive audio data that will later be played back through the speakers. This will be non-null
1767 for a playback or full-duplex device and null for a capture device.
1768 
1769 pInput is a pointer to a buffer containing input data from the device. This will be non-null for a capture or full-duplex device, and
1770 null for a playback device.
1771 
1772 frameCount is the number of PCM frames to process. If an output buffer is provided (pOutput is not null), applications should write out
1773 to the entire output buffer.
1774 
1775 Do _not_ call any miniaudio APIs from the callback. Attempting the stop the device can result in a deadlock. The proper way to stop the
1776 device is to call ma_device_stop() from a different thread, normally the main application thread.
1777 */
1778 typedef void (* ma_device_callback_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
1779 
1780 /*
1781 The callback for when the device has been stopped.
1782 
1783 This will be called when the device is stopped explicitly with ma_device_stop() and also called implicitly when the device is stopped
1784 through external forces such as being unplugged or an internal error occuring.
1785 
1786 Do not restart the device from the callback.
1787 */
1788 typedef void (* ma_stop_proc)(ma_device* pDevice);
1789 
1790 /*
1791 The callback for handling log messages.
1792 
1793 It is possible for pDevice to be null in which case the log originated from the context. If it is non-null you can assume the message
1794 came from the device.
1795 
1796 logLevel is one of the following:
1797  MA_LOG_LEVEL_VERBOSE
1798  MA_LOG_LEVEL_INFO
1799  MA_LOG_LEVEL_WARNING
1800  MA_LOG_LEVEL_ERROR
1801 */
1802 typedef void (* ma_log_proc)(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message);
1803 
1804 typedef enum
1805 {
1809 } ma_device_type;
1810 
1811 typedef enum
1812 {
1815 } ma_share_mode;
1816 
1817 typedef union
1818 {
1819 #ifdef MA_SUPPORT_WASAPI
1820  wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
1821 #endif
1822 #ifdef MA_SUPPORT_DSOUND
1823  ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
1824 #endif
1825 #ifdef MA_SUPPORT_WINMM
1826  /*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. */
1827 #endif
1828 #ifdef MA_SUPPORT_ALSA
1829  char alsa[256]; /* ALSA uses a name string for identification. */
1830 #endif
1831 #ifdef MA_SUPPORT_PULSEAUDIO
1832  char pulse[256]; /* PulseAudio uses a name string for identification. */
1833 #endif
1834 #ifdef MA_SUPPORT_JACK
1835  int jack; /* JACK always uses default devices. */
1836 #endif
1837 #ifdef MA_SUPPORT_COREAUDIO
1838  char coreaudio[256]; /* Core Audio uses a string for identification. */
1839 #endif
1840 #ifdef MA_SUPPORT_SNDIO
1841  char sndio[256]; /* "snd/0", etc. */
1842 #endif
1843 #ifdef MA_SUPPORT_AUDIO4
1844  char audio4[256]; /* "/dev/audio", etc. */
1845 #endif
1846 #ifdef MA_SUPPORT_OSS
1847  char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */
1848 #endif
1849 #ifdef MA_SUPPORT_AAUDIO
1850  ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
1851 #endif
1852 #ifdef MA_SUPPORT_OPENSL
1853  ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
1854 #endif
1855 #ifdef MA_SUPPORT_WEBAUDIO
1856  char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
1857 #endif
1858 #ifdef MA_SUPPORT_NULL
1859  int nullbackend; /* The null backend uses an integer for device IDs. */
1860 #endif
1861 } ma_device_id;
1862 
1863 typedef struct
1864 {
1865  /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
1867  char name[256];
1868 
1869  /*
1870  Detailed info. As much of this is filled as possible with ma_context_get_device_info(). Note that you are allowed to initialize
1871  a device with settings outside of this range, but it just means the data will be converted using miniaudio's data conversion
1872  pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided
1873  here mainly for informational purposes or in the rare case that someone might find it useful.
1874 
1875  These will be set to 0 when returned by ma_context_enumerate_devices() or ma_context_get_devices().
1876  */
1883 } ma_device_info;
1884 
1885 typedef union
1886 {
1888  double counterD;
1889 } ma_timer;
1890 
1891 typedef struct
1892 {
1901  void* pUserData;
1902  struct
1903  {
1909  } playback;
1910  struct
1911  {
1912  ma_device_id* pDeviceID;
1913  ma_format format;
1915  ma_channel channelMap[MA_MAX_CHANNELS];
1916  ma_share_mode shareMode;
1917  } capture;
1918 
1919  struct
1920  {
1921  ma_bool32 noMMap; /* Disables MMap mode. */
1922  } alsa;
1923  struct
1924  {
1925  const char* pStreamNamePlayback;
1926  const char* pStreamNameCapture;
1927  } pulse;
1929 
1930 typedef struct
1931 {
1934  void* pUserData;
1935 
1936  struct
1937  {
1939  } alsa;
1940  struct
1941  {
1942  const char* pApplicationName;
1943  const char* pServerName;
1944  ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */
1945  } pulse;
1946  struct
1947  {
1948  const char* pClientName;
1950  } jack;
1952 
1953 typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);
1954 
1956 {
1957  ma_backend backend; /* DirectSound, ALSA, etc. */
1960  void* pUserData;
1961  ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */
1962  ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */
1963  ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */
1966  ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */
1967  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. */
1968 
1969  ma_result (* onUninit )(ma_context* pContext);
1970  ma_bool32 (* onDeviceIDEqual )(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1);
1971  ma_result (* onEnumDevices )(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); /* Return false from the callback to stop enumeration. */
1972  ma_result (* onGetDeviceInfo )(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo);
1973  ma_result (* onDeviceInit )(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
1977  ma_result (* onDeviceWrite )(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount); /* Data is in internal device format. */
1978  ma_result (* onDeviceRead )(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount); /* Data is in internal device format. */
1980 
1981  union
1982  {
1983 #ifdef MA_SUPPORT_WASAPI
1984  struct
1985  {
1986  int _unused;
1987  } wasapi;
1988 #endif
1989 #ifdef MA_SUPPORT_DSOUND
1990  struct
1991  {
1997  } dsound;
1998 #endif
1999 #ifdef MA_SUPPORT_WINMM
2000  struct
2001  {
2020  } winmm;
2021 #endif
2022 #ifdef MA_SUPPORT_ALSA
2023  struct
2024  {
2025  ma_handle asoundSO;
2026  ma_proc snd_pcm_open;
2027  ma_proc snd_pcm_close;
2028  ma_proc snd_pcm_hw_params_sizeof;
2029  ma_proc snd_pcm_hw_params_any;
2030  ma_proc snd_pcm_hw_params_set_format;
2031  ma_proc snd_pcm_hw_params_set_format_first;
2032  ma_proc snd_pcm_hw_params_get_format_mask;
2033  ma_proc snd_pcm_hw_params_set_channels_near;
2034  ma_proc snd_pcm_hw_params_set_rate_resample;
2035  ma_proc snd_pcm_hw_params_set_rate_near;
2036  ma_proc snd_pcm_hw_params_set_buffer_size_near;
2037  ma_proc snd_pcm_hw_params_set_periods_near;
2038  ma_proc snd_pcm_hw_params_set_access;
2039  ma_proc snd_pcm_hw_params_get_format;
2040  ma_proc snd_pcm_hw_params_get_channels;
2041  ma_proc snd_pcm_hw_params_get_channels_min;
2042  ma_proc snd_pcm_hw_params_get_channels_max;
2043  ma_proc snd_pcm_hw_params_get_rate;
2044  ma_proc snd_pcm_hw_params_get_rate_min;
2045  ma_proc snd_pcm_hw_params_get_rate_max;
2046  ma_proc snd_pcm_hw_params_get_buffer_size;
2047  ma_proc snd_pcm_hw_params_get_periods;
2048  ma_proc snd_pcm_hw_params_get_access;
2049  ma_proc snd_pcm_hw_params;
2050  ma_proc snd_pcm_sw_params_sizeof;
2051  ma_proc snd_pcm_sw_params_current;
2052  ma_proc snd_pcm_sw_params_get_boundary;
2053  ma_proc snd_pcm_sw_params_set_avail_min;
2054  ma_proc snd_pcm_sw_params_set_start_threshold;
2055  ma_proc snd_pcm_sw_params_set_stop_threshold;
2056  ma_proc snd_pcm_sw_params;
2057  ma_proc snd_pcm_format_mask_sizeof;
2058  ma_proc snd_pcm_format_mask_test;
2059  ma_proc snd_pcm_get_chmap;
2060  ma_proc snd_pcm_state;
2061  ma_proc snd_pcm_prepare;
2062  ma_proc snd_pcm_start;
2063  ma_proc snd_pcm_drop;
2064  ma_proc snd_pcm_drain;
2065  ma_proc snd_device_name_hint;
2066  ma_proc snd_device_name_get_hint;
2067  ma_proc snd_card_get_index;
2068  ma_proc snd_device_name_free_hint;
2069  ma_proc snd_pcm_mmap_begin;
2070  ma_proc snd_pcm_mmap_commit;
2071  ma_proc snd_pcm_recover;
2072  ma_proc snd_pcm_readi;
2073  ma_proc snd_pcm_writei;
2074  ma_proc snd_pcm_avail;
2075  ma_proc snd_pcm_avail_update;
2076  ma_proc snd_pcm_wait;
2077  ma_proc snd_pcm_info;
2078  ma_proc snd_pcm_info_sizeof;
2079  ma_proc snd_pcm_info_get_name;
2080  ma_proc snd_config_update_free_global;
2081 
2082  ma_mutex internalDeviceEnumLock;
2083  ma_bool32 useVerboseDeviceEnumeration;
2084  } alsa;
2085 #endif
2086 #ifdef MA_SUPPORT_PULSEAUDIO
2087  struct
2088  {
2089  ma_handle pulseSO;
2090  ma_proc pa_mainloop_new;
2091  ma_proc pa_mainloop_free;
2092  ma_proc pa_mainloop_get_api;
2093  ma_proc pa_mainloop_iterate;
2094  ma_proc pa_mainloop_wakeup;
2095  ma_proc pa_context_new;
2096  ma_proc pa_context_unref;
2097  ma_proc pa_context_connect;
2098  ma_proc pa_context_disconnect;
2099  ma_proc pa_context_set_state_callback;
2100  ma_proc pa_context_get_state;
2101  ma_proc pa_context_get_sink_info_list;
2102  ma_proc pa_context_get_source_info_list;
2103  ma_proc pa_context_get_sink_info_by_name;
2104  ma_proc pa_context_get_source_info_by_name;
2105  ma_proc pa_operation_unref;
2106  ma_proc pa_operation_get_state;
2107  ma_proc pa_channel_map_init_extend;
2108  ma_proc pa_channel_map_valid;
2109  ma_proc pa_channel_map_compatible;
2110  ma_proc pa_stream_new;
2111  ma_proc pa_stream_unref;
2112  ma_proc pa_stream_connect_playback;
2113  ma_proc pa_stream_connect_record;
2114  ma_proc pa_stream_disconnect;
2115  ma_proc pa_stream_get_state;
2116  ma_proc pa_stream_get_sample_spec;
2117  ma_proc pa_stream_get_channel_map;
2118  ma_proc pa_stream_get_buffer_attr;
2119  ma_proc pa_stream_set_buffer_attr;
2120  ma_proc pa_stream_get_device_name;
2121  ma_proc pa_stream_set_write_callback;
2122  ma_proc pa_stream_set_read_callback;
2123  ma_proc pa_stream_flush;
2124  ma_proc pa_stream_drain;
2125  ma_proc pa_stream_is_corked;
2126  ma_proc pa_stream_cork;
2127  ma_proc pa_stream_trigger;
2128  ma_proc pa_stream_begin_write;
2129  ma_proc pa_stream_write;
2130  ma_proc pa_stream_peek;
2131  ma_proc pa_stream_drop;
2132  ma_proc pa_stream_writable_size;
2133  ma_proc pa_stream_readable_size;
2134 
2135  char* pApplicationName;
2136  char* pServerName;
2137  ma_bool32 tryAutoSpawn;
2138  } pulse;
2139 #endif
2140 #ifdef MA_SUPPORT_JACK
2141  struct
2142  {
2160 
2163  } jack;
2164 #endif
2165 #ifdef MA_SUPPORT_COREAUDIO
2166  struct
2167  {
2168  ma_handle hCoreFoundation;
2169  ma_proc CFStringGetCString;
2170 
2171  ma_handle hCoreAudio;
2172  ma_proc AudioObjectGetPropertyData;
2173  ma_proc AudioObjectGetPropertyDataSize;
2174  ma_proc AudioObjectSetPropertyData;
2175  ma_proc AudioObjectAddPropertyListener;
2176 
2177  ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */
2178  ma_proc AudioComponentFindNext;
2179  ma_proc AudioComponentInstanceDispose;
2180  ma_proc AudioComponentInstanceNew;
2181  ma_proc AudioOutputUnitStart;
2182  ma_proc AudioOutputUnitStop;
2183  ma_proc AudioUnitAddPropertyListener;
2184  ma_proc AudioUnitGetPropertyInfo;
2185  ma_proc AudioUnitGetProperty;
2186  ma_proc AudioUnitSetProperty;
2187  ma_proc AudioUnitInitialize;
2188  ma_proc AudioUnitRender;
2189 
2190  /*AudioComponent*/ ma_ptr component;
2191  } coreaudio;
2192 #endif
2193 #ifdef MA_SUPPORT_SNDIO
2194  struct
2195  {
2196  ma_handle sndioSO;
2197  ma_proc sio_open;
2198  ma_proc sio_close;
2199  ma_proc sio_setpar;
2200  ma_proc sio_getpar;
2201  ma_proc sio_getcap;
2202  ma_proc sio_start;
2203  ma_proc sio_stop;
2204  ma_proc sio_read;
2205  ma_proc sio_write;
2206  ma_proc sio_onmove;
2207  ma_proc sio_nfds;
2208  ma_proc sio_pollfd;
2209  ma_proc sio_revents;
2210  ma_proc sio_eof;
2211  ma_proc sio_setvol;
2212  ma_proc sio_onvol;
2213  ma_proc sio_initpar;
2214  } sndio;
2215 #endif
2216 #ifdef MA_SUPPORT_AUDIO4
2217  struct
2218  {
2219  int _unused;
2220  } audio4;
2221 #endif
2222 #ifdef MA_SUPPORT_OSS
2223  struct
2224  {
2225  int versionMajor;
2226  int versionMinor;
2227  } oss;
2228 #endif
2229 #ifdef MA_SUPPORT_AAUDIO
2230  struct
2231  {
2232  ma_handle hAAudio; /* libaaudio.so */
2233  ma_proc AAudio_createStreamBuilder;
2234  ma_proc AAudioStreamBuilder_delete;
2235  ma_proc AAudioStreamBuilder_setDeviceId;
2236  ma_proc AAudioStreamBuilder_setDirection;
2237  ma_proc AAudioStreamBuilder_setSharingMode;
2238  ma_proc AAudioStreamBuilder_setFormat;
2239  ma_proc AAudioStreamBuilder_setChannelCount;
2240  ma_proc AAudioStreamBuilder_setSampleRate;
2241  ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
2242  ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
2243  ma_proc AAudioStreamBuilder_setDataCallback;
2244  ma_proc AAudioStreamBuilder_setPerformanceMode;
2245  ma_proc AAudioStreamBuilder_openStream;
2246  ma_proc AAudioStream_close;
2247  ma_proc AAudioStream_getState;
2248  ma_proc AAudioStream_waitForStateChange;
2249  ma_proc AAudioStream_getFormat;
2250  ma_proc AAudioStream_getChannelCount;
2251  ma_proc AAudioStream_getSampleRate;
2252  ma_proc AAudioStream_getBufferCapacityInFrames;
2253  ma_proc AAudioStream_getFramesPerDataCallback;
2254  ma_proc AAudioStream_getFramesPerBurst;
2255  ma_proc AAudioStream_requestStart;
2256  ma_proc AAudioStream_requestStop;
2257  } aaudio;
2258 #endif
2259 #ifdef MA_SUPPORT_OPENSL
2260  struct
2261  {
2262  int _unused;
2263  } opensl;
2264 #endif
2265 #ifdef MA_SUPPORT_WEBAUDIO
2266  struct
2267  {
2268  int _unused;
2269  } webaudio;
2270 #endif
2271 #ifdef MA_SUPPORT_NULL
2272  struct
2273  {
2274  int _unused;
2275  } null_backend;
2276 #endif
2277  };
2278 
2279  union
2280  {
2281 #ifdef MA_WIN32
2282  struct
2283  {
2284  /*HMODULE*/ ma_handle hOle32DLL;
2291 
2292  /*HMODULE*/ ma_handle hUser32DLL;
2295 
2300  } win32;
2301 #endif
2302 #ifdef MA_POSIX
2303  struct
2304  {
2305  ma_handle pthreadSO;
2306  ma_proc pthread_create;
2307  ma_proc pthread_join;
2308  ma_proc pthread_mutex_init;
2309  ma_proc pthread_mutex_destroy;
2310  ma_proc pthread_mutex_lock;
2311  ma_proc pthread_mutex_unlock;
2312  ma_proc pthread_cond_init;
2313  ma_proc pthread_cond_destroy;
2314  ma_proc pthread_cond_wait;
2315  ma_proc pthread_cond_signal;
2316  ma_proc pthread_attr_init;
2317  ma_proc pthread_attr_destroy;
2318  ma_proc pthread_attr_setschedpolicy;
2319  ma_proc pthread_attr_getschedparam;
2320  ma_proc pthread_attr_setschedparam;
2321  } posix;
2322 #endif
2323  int _unused;
2324  };
2325 };
2326 
2328 {
2329  ma_context* pContext;
2331  ma_uint32 sampleRate;
2332  ma_uint32 state;
2333  ma_device_callback_proc onData;
2334  ma_stop_proc onStop;
2335  void* pUserData; /* Application defined data. */
2336  ma_mutex lock;
2337  ma_event wakeupEvent;
2338  ma_event startEvent;
2339  ma_event stopEvent;
2340  ma_thread thread;
2341  ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
2342  ma_bool32 usingDefaultSampleRate : 1;
2343  ma_bool32 usingDefaultBufferSize : 1;
2344  ma_bool32 usingDefaultPeriods : 1;
2345  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(). */
2346  struct
2347  {
2348  char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
2349  ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
2350  ma_bool32 usingDefaultFormat : 1;
2351  ma_bool32 usingDefaultChannels : 1;
2352  ma_bool32 usingDefaultChannelMap : 1;
2353  ma_format format;
2355  ma_channel channelMap[MA_MAX_CHANNELS];
2357  ma_uint32 internalChannels;
2358  ma_uint32 internalSampleRate;
2359  ma_channel internalChannelMap[MA_MAX_CHANNELS];
2360  ma_uint32 internalBufferSizeInFrames;
2361  ma_uint32 internalPeriods;
2362  ma_pcm_converter converter;
2363  ma_uint32 _dspFrameCount; /* Internal use only. Used as the data source when reading from the device. */
2364  const ma_uint8* _dspFrames; /* ^^^ AS ABOVE ^^^ */
2365  } playback;
2366  struct
2367  {
2368  char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
2369  ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
2370  ma_bool32 usingDefaultFormat : 1;
2371  ma_bool32 usingDefaultChannels : 1;
2372  ma_bool32 usingDefaultChannelMap : 1;
2373  ma_format format;
2375  ma_channel channelMap[MA_MAX_CHANNELS];
2377  ma_uint32 internalChannels;
2378  ma_uint32 internalSampleRate;
2379  ma_channel internalChannelMap[MA_MAX_CHANNELS];
2380  ma_uint32 internalBufferSizeInFrames;
2381  ma_uint32 internalPeriods;
2382  ma_pcm_converter converter;
2383  ma_uint32 _dspFrameCount; /* Internal use only. Used as the data source when reading from the device. */
2384  const ma_uint8* _dspFrames; /* ^^^ AS ABOVE ^^^ */
2385  } capture;
2386 
2387  union
2388  {
2389 #ifdef MA_SUPPORT_WASAPI
2390  struct
2391  {
2392  /*IAudioClient**/ ma_ptr pAudioClientPlayback;
2393  /*IAudioClient**/ ma_ptr pAudioClientCapture;
2394  /*IAudioRenderClient**/ ma_ptr pRenderClient;
2395  /*IAudioCaptureClient**/ ma_ptr pCaptureClient;
2396  /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
2397  ma_IMMNotificationClient notificationClient;
2398  /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
2399  /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
2400  ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalBufferSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */
2401  ma_uint32 actualBufferSizeInFramesCapture;
2402  ma_uint32 originalBufferSizeInFrames;
2403  ma_uint32 originalBufferSizeInMilliseconds;
2404  ma_uint32 originalPeriods;
2405  ma_bool32 hasDefaultPlaybackDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
2406  ma_bool32 hasDefaultCaptureDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
2407  ma_uint32 periodSizeInFramesPlayback;
2408  ma_uint32 periodSizeInFramesCapture;
2409  ma_bool32 isStartedCapture;
2410  ma_bool32 isStartedPlayback;
2411  } wasapi;
2412 #endif
2413 #ifdef MA_SUPPORT_DSOUND
2414  struct
2415  {
2416  /*LPDIRECTSOUND*/ ma_ptr pPlayback;
2417  /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
2418  /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
2419  /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
2420  /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
2421  } dsound;
2422 #endif
2423 #ifdef MA_SUPPORT_WINMM
2424  struct
2425  {
2426  /*HWAVEOUT*/ ma_handle hDevicePlayback;
2427  /*HWAVEIN*/ ma_handle hDeviceCapture;
2428  /*HANDLE*/ ma_handle hEventPlayback;
2429  /*HANDLE*/ ma_handle hEventCapture;
2430  ma_uint32 fragmentSizeInFrames;
2431  ma_uint32 fragmentSizeInBytes;
2432  ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
2433  ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
2434  ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
2435  ma_uint32 headerFramesConsumedCapture; /* ^^^ */
2436  /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
2437  /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
2438  ma_uint8* pIntermediaryBufferPlayback;
2439  ma_uint8* pIntermediaryBufferCapture;
2440  ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
2441  ma_bool32 isStarted;
2442  } winmm;
2443 #endif
2444 #ifdef MA_SUPPORT_ALSA
2445  struct
2446  {
2447  /*snd_pcm_t**/ ma_ptr pPCMPlayback;
2448  /*snd_pcm_t**/ ma_ptr pPCMCapture;
2449  ma_bool32 isUsingMMapPlayback : 1;
2450  ma_bool32 isUsingMMapCapture : 1;
2451  } alsa;
2452 #endif
2453 #ifdef MA_SUPPORT_PULSEAUDIO
2454  struct
2455  {
2456  /*pa_mainloop**/ ma_ptr pMainLoop;
2457  /*pa_mainloop_api**/ ma_ptr pAPI;
2458  /*pa_context**/ ma_ptr pPulseContext;
2459  /*pa_stream**/ ma_ptr pStreamPlayback;
2460  /*pa_stream**/ ma_ptr pStreamCapture;
2461  /*pa_context_state*/ ma_uint32 pulseContextState;
2462  void* pMappedBufferPlayback;
2463  const void* pMappedBufferCapture;
2464  ma_uint32 mappedBufferFramesRemainingPlayback;
2465  ma_uint32 mappedBufferFramesRemainingCapture;
2466  ma_uint32 mappedBufferFramesCapacityPlayback;
2467  ma_uint32 mappedBufferFramesCapacityCapture;
2468  ma_bool32 breakFromMainLoop : 1;
2469  } pulse;
2470 #endif
2471 #ifdef MA_SUPPORT_JACK
2472  struct
2473  {
2474  /*jack_client_t**/ ma_ptr pClient;
2475  /*jack_port_t**/ ma_ptr pPortsPlayback[MA_MAX_CHANNELS];
2476  /*jack_port_t**/ ma_ptr pPortsCapture[MA_MAX_CHANNELS];
2477  float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
2478  float* pIntermediaryBufferCapture;
2479  ma_pcm_rb duplexRB;
2480  } jack;
2481 #endif
2482 #ifdef MA_SUPPORT_COREAUDIO
2483  struct
2484  {
2485  ma_uint32 deviceObjectIDPlayback;
2486  ma_uint32 deviceObjectIDCapture;
2487  /*AudioUnit*/ ma_ptr audioUnitPlayback;
2488  /*AudioUnit*/ ma_ptr audioUnitCapture;
2489  /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */
2490  ma_event stopEvent;
2491  ma_uint32 originalBufferSizeInFrames;
2492  ma_uint32 originalBufferSizeInMilliseconds;
2493  ma_uint32 originalPeriods;
2494  ma_bool32 isDefaultPlaybackDevice;
2495  ma_bool32 isDefaultCaptureDevice;
2496  ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
2497  ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
2498  ma_pcm_rb duplexRB;
2499  } coreaudio;
2500 #endif
2501 #ifdef MA_SUPPORT_SNDIO
2502  struct
2503  {
2504  ma_ptr handlePlayback;
2505  ma_ptr handleCapture;
2506  ma_bool32 isStartedPlayback;
2507  ma_bool32 isStartedCapture;
2508  } sndio;
2509 #endif
2510 #ifdef MA_SUPPORT_AUDIO4
2511  struct
2512  {
2513  int fdPlayback;
2514  int fdCapture;
2515  } audio4;
2516 #endif
2517 #ifdef MA_SUPPORT_OSS
2518  struct
2519  {
2520  int fdPlayback;
2521  int fdCapture;
2522  } oss;
2523 #endif
2524 #ifdef MA_SUPPORT_AAUDIO
2525  struct
2526  {
2527  /*AAudioStream**/ ma_ptr pStreamPlayback;
2528  /*AAudioStream**/ ma_ptr pStreamCapture;
2529  ma_pcm_rb duplexRB;
2530  } aaudio;
2531 #endif
2532 #ifdef MA_SUPPORT_OPENSL
2533  struct
2534  {
2535  /*SLObjectItf*/ ma_ptr pOutputMixObj;
2536  /*SLOutputMixItf*/ ma_ptr pOutputMix;
2537  /*SLObjectItf*/ ma_ptr pAudioPlayerObj;
2538  /*SLPlayItf*/ ma_ptr pAudioPlayer;
2539  /*SLObjectItf*/ ma_ptr pAudioRecorderObj;
2540  /*SLRecordItf*/ ma_ptr pAudioRecorder;
2541  /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;
2542  /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;
2543  ma_uint32 currentBufferIndexPlayback;
2544  ma_uint32 currentBufferIndexCapture;
2545  ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
2546  ma_uint8* pBufferCapture;
2547  ma_pcm_rb duplexRB;
2548  } opensl;
2549 #endif
2550 #ifdef MA_SUPPORT_WEBAUDIO
2551  struct
2552  {
2553  int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
2554  int indexCapture;
2555  ma_pcm_rb duplexRB; /* In external capture format. */
2556  } webaudio;
2557 #endif
2558 #ifdef MA_SUPPORT_NULL
2559  struct
2560  {
2561  ma_thread deviceThread;
2562  ma_event operationEvent;
2563  ma_event operationCompletionEvent;
2564  ma_uint32 operation;
2565  ma_result operationResult;
2566  ma_timer timer;
2567  double priorRunTime;
2568  ma_uint32 currentPeriodFramesRemainingPlayback;
2569  ma_uint32 currentPeriodFramesRemainingCapture;
2570  ma_uint64 lastProcessedFramePlayback;
2571  ma_uint32 lastProcessedFrameCapture;
2572  ma_bool32 isStarted;
2573  } null_device;
2574 #endif
2575  };
2576 };
2577 #if defined(_MSC_VER)
2578  #pragma warning(pop)
2579 #else
2580  #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
2581 #endif
2582 
2583 /*
2584 Initializes a context.
2585 
2586 The context is used for selecting and initializing the relevant backends.
2587 
2588 Note that the location of the context cannot change throughout it's lifetime. Consider allocating
2589 the ma_context object with malloc() if this is an issue. The reason for this is that a pointer
2590 to the context is stored in the ma_device structure.
2591 
2592 <backends> is used to allow the application to prioritize backends depending on it's specific
2593 requirements. This can be null in which case it uses the default priority, which is as follows:
2594  - WASAPI
2595  - DirectSound
2596  - WinMM
2597  - Core Audio (Apple)
2598  - sndio
2599  - audio(4)
2600  - OSS
2601  - PulseAudio
2602  - ALSA
2603  - JACK
2604  - AAudio
2605  - OpenSL|ES
2606  - Web Audio / Emscripten
2607  - Null
2608 
2609 <pConfig> is used to configure the context. Use the logCallback config to set a callback for whenever a
2610 log message is posted. The priority of the worker thread can be set with the threadPriority config.
2611 
2612 It is recommended that only a single context is active at any given time because it's a bulky data
2613 structure which performs run-time linking for the relevant backends every time it's initialized.
2614 
2615 Return Value:
2616  MA_SUCCESS if successful; any other error code otherwise.
2617 
2618 Thread Safety: UNSAFE
2619 */
2620 ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
2621 
2622 /*
2623 Uninitializes a context.
2624 
2625 Results are undefined if you call this while any device created by this context is still active.
2626 
2627 Return Value:
2628  MA_SUCCESS if successful; any other error code otherwise.
2629 
2630 Thread Safety: UNSAFE
2631 */
2633 
2634 /*
2635 Enumerates over every device (both playback and capture).
2636 
2637 This is a lower-level enumeration function to the easier to use ma_context_get_devices(). Use
2638 ma_context_enumerate_devices() if you would rather not incur an internal heap allocation, or
2639 it simply suits your code better.
2640 
2641 Do _not_ assume the first enumerated device of a given type is the default device.
2642 
2643 Some backends and platforms may only support default playback and capture devices.
2644 
2645 Note that this only retrieves the ID and name/description of the device. The reason for only
2646 retrieving basic information is that it would otherwise require opening the backend device in
2647 order to probe it for more detailed information which can be inefficient. Consider using
2648 ma_context_get_device_info() for this, but don't call it from within the enumeration callback.
2649 
2650 In general, you should not do anything complicated from within the callback. In particular, do
2651 not try initializing a device from within the callback.
2652 
2653 Consider using ma_context_get_devices() for a simpler and safer API, albeit at the expense of
2654 an internal heap allocation.
2655 
2656 Returning false from the callback will stop enumeration. Returning true will continue enumeration.
2657 
2658 Return Value:
2659  MA_SUCCESS if successful; any other error code otherwise.
2660 
2661 Thread Safety: SAFE
2662  This is guarded using a simple mutex lock.
2663 */
2665 
2666 /*
2667 Retrieves basic information about every active playback and/or capture device.
2668 
2669 You can pass in NULL for the playback or capture lists in which case they'll be ignored.
2670 
2671 It is _not_ safe to assume the first device in the list is the default device.
2672 
2673 The returned pointers will become invalid upon the next call this this function, or when the
2674 context is uninitialized. Do not free the returned pointers.
2675 
2676 This function follows the same enumeration rules as ma_context_enumerate_devices(). See
2677 documentation for ma_context_enumerate_devices() for more information.
2678 
2679 Return Value:
2680  MA_SUCCESS if successful; any other error code otherwise.
2681 
2682 Thread Safety: SAFE
2683  Since each call to this function invalidates the pointers from the previous call, you
2684  should not be calling this simultaneously across multiple threads. Instead, you need to
2685  make a copy of the returned data with your own higher level synchronization.
2686 */
2687 ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
2688 
2689 /*
2690 Retrieves information about a device with the given ID.
2691 
2692 Do _not_ call this from within the ma_context_enumerate_devices() callback.
2693 
2694 It's possible for a device to have different information and capabilities depending on whether
2695 or not it's opened in shared or exclusive mode. For example, in shared mode, WASAPI always uses
2696 floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this
2697 function allows you to specify which share mode you want information for. Note that not all
2698 backends and devices support shared or exclusive mode, in which case this function will fail
2699 if the requested share mode is unsupported.
2700 
2701 This leaves pDeviceInfo unmodified in the result of an error.
2702 
2703 Return Value:
2704  MA_SUCCESS if successful; any other error code otherwise.
2705 
2706 Thread Safety: SAFE
2707  This is guarded using a simple mutex lock.
2708 */
2709 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);
2710 
2711 /*
2712 Initializes a device.
2713 
2714 The context can be null in which case it uses the default. This is equivalent to passing in a
2715 context that was initialized like so:
2716 
2717  ma_context_init(NULL, 0, NULL, &context);
2718 
2719 Do not pass in null for the context if you are needing to open multiple devices. You can,
2720 however, use null when initializing the first device, and then use device.pContext for the
2721 initialization of other devices.
2722 
2723 The device's configuration is controlled with pConfig. This allows you to configure the sample
2724 format, channel count, sample rate, etc. Before calling ma_device_init(), you will need to
2725 initialize a ma_device_config object using ma_device_config_init(). You must set the callback in
2726 the device config. Once initialized, the device's config is immutable. If you need to change the
2727 config you will need to initialize a new device.
2728 
2729 Passing in 0 to any property in pConfig will force the use of a default value. In the case of
2730 sample format, channel count, sample rate and channel map it will default to the values used by
2731 the backend's internal device. For the size of the buffer you can set bufferSizeInFrames or
2732 bufferSizeInMilliseconds (if both are set it will prioritize bufferSizeInFrames). If both are
2733 set to zero, it will default to MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY or
2734 MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE, depending on whether or not performanceProfile
2735 is set to ma_performance_profile_low_latency or ma_performance_profile_conservative.
2736 
2737 If you request exclusive mode and the backend does not support it an error will be returned. For
2738 robustness, you may want to first try initializing the device in exclusive mode, and then fall back
2739 to shared mode if required. Alternatively you can just request shared mode (the default if you
2740 leave it unset in the config) which is the most reliable option. Some backends do not have a
2741 practical way of choosing whether or not the device should be exclusive or not (ALSA, for example)
2742 in which case it just acts as a hint. Unless you have special requirements you should try avoiding
2743 exclusive mode as it's intrusive to the user. Starting with Windows 10, miniaudio will use low-latency
2744 shared mode where possible which may make exclusive mode unnecessary.
2745 
2746 When sending or receiving data to/from a device, miniaudio will internally perform a format
2747 conversion to convert between the format specified by pConfig and the format used internally by
2748 the backend. If you pass in NULL for pConfig or 0 for the sample format, channel count,
2749 sample rate _and_ channel map, data transmission will run on an optimized pass-through fast path.
2750 
2751 The buffer size should be treated as a hint. miniaudio will try it's best to use exactly what you
2752 ask for, but it may differ. You should not assume the number of frames specified in each call to
2753 the data callback is exactly what you originally specified.
2754 
2755 The <periods> property controls how frequently the background thread is woken to check for more
2756 data. It's tied to the buffer size, so as an example, if your buffer size is equivalent to 10
2757 milliseconds and you have 2 periods, the CPU will wake up approximately every 5 milliseconds.
2758 
2759 When compiling for UWP you must ensure you call this function on the main UI thread because the
2760 operating system may need to present the user with a message asking for permissions. Please refer
2761 to the official documentation for ActivateAudioInterfaceAsync() for more information.
2762 
2763 ALSA Specific: When initializing the default device, requesting shared mode will try using the
2764 "dmix" device for playback and the "dsnoop" device for capture. If these fail it will try falling
2765 back to the "hw" device.
2766 
2767 Return Value:
2768  MA_SUCCESS if successful; any other error code otherwise.
2769 
2770 Thread Safety: UNSAFE
2771  It is not safe to call this function simultaneously for different devices because some backends
2772  depend on and mutate global state (such as OpenSL|ES). The same applies to calling this at the
2773  same time as ma_device_uninit().
2774 */
2775 ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
2776 
2777 /*
2778 Initializes a device without a context, with extra parameters for controlling the configuration
2779 of the internal self-managed context.
2780 
2781 See ma_device_init() and ma_context_init().
2782 */
2783 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);
2784 
2785 /*
2786 Uninitializes a device.
2787 
2788 This will explicitly stop the device. You do not need to call ma_device_stop() beforehand, but it's
2789 harmless if you do.
2790 
2791 Do not call this in any callback.
2792 
2793 Return Value:
2794  MA_SUCCESS if successful; any other error code otherwise.
2795 
2796 Thread Safety: UNSAFE
2797  As soon as this API is called the device should be considered undefined. All bets are off if you
2798  try using the device at the same time as uninitializing it.
2799 */
2800 void ma_device_uninit(ma_device* pDevice);
2801 
2802 /*
2803 Sets the callback to use when the device has stopped, either explicitly or as a result of an error.
2804 
2805 Thread Safety: SAFE
2806  This API is implemented as a simple atomic assignment.
2807 */
2809 
2810 /*
2811 Activates the device. For playback devices this begins playback. For capture devices it begins
2812 recording.
2813 
2814 For a playback device, this will retrieve an initial chunk of audio data from the client before
2815 returning. The reason for this is to ensure there is valid audio data in the buffer, which needs
2816 to be done _before_ the device begins playback.
2817 
2818 This API waits until the backend device has been started for real by the worker thread. It also
2819 waits on a mutex for thread-safety.
2820 
2821 Do not call this in any callback.
2822 
2823 Return Value:
2824  MA_SUCCESS if successful; any other error code otherwise.
2825 
2826 Thread Safety: SAFE
2827 */
2829 
2830 /*
2831 Puts the device to sleep, but does not uninitialize it. Use ma_device_start() to start it up again.
2832 
2833 This API needs to wait on the worker thread to stop the backend device properly before returning. It
2834 also waits on a mutex for thread-safety. In addition, some backends need to wait for the device to
2835 finish playback/recording of the current fragment which can take some time (usually proportionate to
2836 the buffer size that was specified at initialization time).
2837 
2838 This should not drop unprocessed samples. Backends are required to either pause the stream in-place
2839 or drain the buffer if pausing is not possible. The reason for this is that stopping the device and
2840 the resuming it with ma_device_start() (which you might do when your program loses focus) may result
2841 in a situation where those samples are never output to the speakers or received from the microphone
2842 which can in turn result in de-syncs.
2843 
2844 Do not call this in any callback.
2845 
2846 Return Value:
2847  MA_SUCCESS if successful; any other error code otherwise.
2848 
2849 Thread Safety: SAFE
2850 */
2852 
2853 /*
2854 Determines whether or not the device is started.
2855 
2856 This is implemented as a simple accessor.
2857 
2858 Return Value:
2859  True if the device is started, false otherwise.
2860 
2861 Thread Safety: SAFE
2862  If another thread calls ma_device_start() or ma_device_stop() at this same time as this function
2863  is called, there's a very small chance the return value will be out of sync.
2864 */
2866 
2867 
2868 /*
2869 Helper function for initializing a ma_context_config object.
2870 */
2872 
2873 /*
2874 Initializes a device config.
2875 
2876 By default, the device config will use native device settings (format, channels, sample rate, etc.). Using native
2877 settings means you will get an optimized pass-through data transmission pipeline to and from the device, but you will
2878 need to do all format conversions manually. Normally you would want to use a known format that your program can handle
2879 natively, which you can do by specifying it after this function returns, like so:
2880 
2881  ma_device_config config = ma_device_config_init(ma_device_type_playback);
2882  config.callback = my_data_callback;
2883  config.pUserData = pMyUserData;
2884  config.format = ma_format_f32;
2885  config.channels = 2;
2886  config.sampleRate = 44100;
2887 
2888 In this case miniaudio will perform all of the necessary data conversion for you behind the scenes.
2889 
2890 Currently miniaudio only supports asynchronous, callback based data delivery which means you must specify callback. A
2891 pointer to user data can also be specified which is set in the pUserData member of the ma_device object.
2892 
2893 To specify a channel map you can use ma_get_standard_channel_map():
2894 
2895  ma_get_standard_channel_map(ma_standard_channel_map_default, config.channels, config.channelMap);
2896 
2897 Alternatively you can set the channel map manually if you need something specific or something that isn't one of miniaudio's
2898 stock channel maps.
2899 
2900 By default the system's default device will be used. Set the pDeviceID member to a pointer to a ma_device_id object to
2901 use a specific device. You can enumerate over the devices with ma_context_enumerate_devices() or ma_context_get_devices()
2902 which will give you access to the device ID. Set pDeviceID to NULL to use the default device.
2903 
2904 The device type can be one of the ma_device_type's:
2905  ma_device_type_playback
2906  ma_device_type_capture
2907  ma_device_type_duplex
2908 
2909 Thread Safety: SAFE
2910 */
2912 
2913 
2914 /************************************************************************************************************************************************************
2915 
2916 Utiltities
2917 
2918 ************************************************************************************************************************************************************/
2919 
2920 /*
2921 Creates a mutex.
2922 
2923 A mutex must be created from a valid context. A mutex is initially unlocked.
2924 */
2925 ma_result ma_mutex_init(ma_context* pContext, ma_mutex* pMutex);
2926 
2927 /*
2928 Deletes a mutex.
2929 */
2930 void ma_mutex_uninit(ma_mutex* pMutex);
2931 
2932 /*
2933 Locks a mutex with an infinite timeout.
2934 */
2935 void ma_mutex_lock(ma_mutex* pMutex);
2936 
2937 /*
2938 Unlocks a mutex.
2939 */
2940 void ma_mutex_unlock(ma_mutex* pMutex);
2941 
2942 
2943 /*
2944 Retrieves a friendly name for a backend.
2945 */
2946 const char* ma_get_backend_name(ma_backend backend);
2947 
2948 /*
2949 Adjust buffer size based on a scaling factor.
2950 
2951 This just multiplies the base size by the scaling factor, making sure it's a size of at least 1.
2952 */
2953 ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale);
2954 
2955 /*
2956 Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
2957 */
2959 
2960 /*
2961 Calculates a buffer size in frames from the specified number of milliseconds and sample rate.
2962 */
2964 
2965 /*
2966 Retrieves the default buffer size in milliseconds based on the specified performance profile.
2967 */
2969 
2970 /*
2971 Calculates a buffer size in frames for the specified performance profile and scale factor.
2972 */
2974 
2975 /*
2976 Copies silent frames into the given buffer.
2977 */
2978 void ma_zero_pcm_frames(void* p, ma_uint32 frameCount, ma_format format, ma_uint32 channels);
2979 
2980 #endif /* MA_NO_DEVICE_IO */
2981 
2982 
2983 
2984 
2985 /************************************************************************************************************************************************************
2986 
2987 Decoding
2988 
2989 ************************************************************************************************************************************************************/
2990 #ifndef MA_NO_DECODING
2991 
2992 typedef struct ma_decoder ma_decoder;
2993 
2994 typedef enum
2995 {
2998 } ma_seek_origin;
2999 
3000 typedef size_t (* ma_decoder_read_proc) (ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */
3001 typedef ma_bool32 (* ma_decoder_seek_proc) (ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin);
3005 
3006 typedef struct
3007 {
3008  ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */
3009  ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */
3010  ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */
3015  union
3016  {
3018  } src;
3020 
3022 {
3025  void* pUserData;
3026  ma_uint64 readPointer; /* Used for returning back to a previous position after analysing the stream or whatnot. */
3035  ma_pcm_converter dsp; /* <-- Format conversion is achieved by running frames through this. */
3039  void* pInternalDecoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
3040  struct
3041  {
3042  const ma_uint8* pData;
3043  size_t dataSize;
3045  } memory; /* Only used for decoders that were opened against a block of memory. */
3046 };
3047 
3048 ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
3049 
3050 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);
3051 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);
3052 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);
3053 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);
3054 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);
3055 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);
3056 
3057 ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3058 ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3059 ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3060 ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3061 ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3062 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);
3063 
3064 #ifndef MA_NO_STDIO
3065 ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3066 ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3067 #endif
3068 
3070 
3071 /*
3072 Retrieves the length of the decoder in PCM frames.
3073 
3074 Do not call this on streams of an undefined length, such as internet radio.
3075 
3076 If the length is unknown or an error occurs, 0 will be returned.
3077 
3078 This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio
3079 uses internally.
3080 
3081 This will run in linear time for MP3 decoders. Do not call this in time critical scenarios.
3082 */
3084 
3085 ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount);
3087 
3088 /*
3089 Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
3090 pConfig should be set to what you want. On output it will be set to what you got.
3091 */
3092 #ifndef MA_NO_STDIO
3093 ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppDataOut);
3094 #endif
3095 ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppDataOut);
3096 
3097 #endif /* MA_NO_DECODING */
3098 
3099 
3100 /************************************************************************************************************************************************************
3101 
3102 Generation
3103 
3104 ************************************************************************************************************************************************************/
3105 typedef struct
3106 {
3107  double amplitude;
3108  double periodsPerSecond;
3109  double delta;
3110  double time;
3113 ma_result ma_sine_wave_init(double amplitude, double period, ma_uint32 sampleRate, ma_sine_wave* pSineWave);
3115 ma_uint64 ma_sine_wave_read_f32_ex(ma_sine_wave* pSineWave, ma_uint64 frameCount, ma_uint32 channels, ma_stream_layout layout, float** ppFrames);
3116 
3117 #ifdef __cplusplus
3118 }
3119 #endif
3120 #endif /* miniaudio_h */
3121 
3122 
3123 
3124 /************************************************************************************************************************************************************
3125 *************************************************************************************************************************************************************
3126 
3127 IMPLEMENTATION
3128 
3129 *************************************************************************************************************************************************************
3130 ************************************************************************************************************************************************************/
3131 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
3132 #include <assert.h>
3133 #include <limits.h> /* For INT_MAX */
3134 #include <math.h> /* sin(), etc. */
3135 
3136 #if defined(MA_DEBUG_OUTPUT)
3137 #include <stdio.h> /* for printf() for debug output */
3138 #endif
3139 
3140 #ifdef MA_WIN32
3141 #include <windows.h>
3142 #include <objbase.h>
3143 #include <mmreg.h>
3144 #include <mmsystem.h>
3145 #else
3146 #include <stdlib.h> /* For malloc()/free() */
3147 #include <string.h> /* For memset() */
3148 #endif
3149 
3150 #if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
3151 #include <mach/mach_time.h> /* For mach_absolute_time() */
3152 #endif
3153 
3154 #ifdef MA_POSIX
3155 #include <sys/time.h>
3156 #include <sys/types.h>
3157 #include <unistd.h>
3158 #include <dlfcn.h>
3159 #endif
3160 
3161 #ifdef MA_EMSCRIPTEN
3162 #include <emscripten/emscripten.h>
3163 #endif
3164 
3165 #if !defined(MA_64BIT) && !defined(MA_32BIT)
3166 #ifdef _WIN32
3167 #ifdef _WIN64
3168 #define MA_64BIT
3169 #else
3170 #define MA_32BIT
3171 #endif
3172 #endif
3173 #endif
3174 
3175 #if !defined(MA_64BIT) && !defined(MA_32BIT)
3176 #ifdef __GNUC__
3177 #ifdef __LP64__
3178 #define MA_64BIT
3179 #else
3180 #define MA_32BIT
3181 #endif
3182 #endif
3183 #endif
3184 
3185 #if !defined(MA_64BIT) && !defined(MA_32BIT)
3186 #include <stdint.h>
3187 #if INTPTR_MAX == INT64_MAX
3188 #define MA_64BIT
3189 #else
3190 #define MA_32BIT
3191 #endif
3192 #endif
3193 
3194 /* Architecture Detection */
3195 #if defined(__x86_64__) || defined(_M_X64)
3196 #define MA_X64
3197 #elif defined(__i386) || defined(_M_IX86)
3198 #define MA_X86
3199 #elif defined(__arm__) || defined(_M_ARM)
3200 #define MA_ARM
3201 #endif
3202 
3203 /* Cannot currently support AVX-512 if AVX is disabled. */
3204 #if !defined(MA_NO_AVX512) && defined(MA_NO_AVX2)
3205 #define MA_NO_AVX512
3206 #endif
3207 
3208 /* Intrinsics Support */
3209 #if defined(MA_X64) || defined(MA_X86)
3210  #if defined(_MSC_VER) && !defined(__clang__)
3211  /* MSVC. */
3212  #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */
3213  #define MA_SUPPORT_SSE2
3214  #endif
3215  /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */
3216  /* #define MA_SUPPORT_AVX*/
3217  /*#endif*/
3218  #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */
3219  #define MA_SUPPORT_AVX2
3220  #endif
3221  #if _MSC_VER >= 1910 && !defined(MA_NO_AVX512) /* 2017 */
3222  #define MA_SUPPORT_AVX512
3223  #endif
3224  #else
3225  /* Assume GNUC-style. */
3226  #if defined(__SSE2__) && !defined(MA_NO_SSE2)
3227  #define MA_SUPPORT_SSE2
3228  #endif
3229  /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/
3230  /* #define MA_SUPPORT_AVX*/
3231  /*#endif*/
3232  #if defined(__AVX2__) && !defined(MA_NO_AVX2)
3233  #define MA_SUPPORT_AVX2
3234  #endif
3235  #if defined(__AVX512F__) && !defined(MA_NO_AVX512)
3236  #define MA_SUPPORT_AVX512
3237  #endif
3238  #endif
3239 
3240  /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
3241  #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
3242  #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>)
3243  #define MA_SUPPORT_SSE2
3244  #endif
3245  /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include(<immintrin.h>)*/
3246  /* #define MA_SUPPORT_AVX*/
3247  /*#endif*/
3248  #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>)
3249  #define MA_SUPPORT_AVX2
3250  #endif
3251  #if !defined(MA_SUPPORT_AVX512) && !defined(MA_NO_AVX512) && __has_include(<zmmintrin.h>)
3252  #define MA_SUPPORT_AVX512
3253  #endif
3254  #endif
3255 
3256  #if defined(MA_SUPPORT_AVX512)
3257  #include <immintrin.h> /* Not a mistake. Intentionally including <immintrin.h> instead of <zmmintrin.h> because otherwise the compiler will complain. */
3258  #elif defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
3259  #include <immintrin.h>
3260  #elif defined(MA_SUPPORT_SSE2)
3261  #include <emmintrin.h>
3262  #endif
3263 #endif
3264 
3265 #if defined(MA_ARM)
3266  #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
3267  #define MA_SUPPORT_NEON
3268  #endif
3269 
3270  /* Fall back to looking for the #include file. */
3271  #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
3272  #if !defined(MA_SUPPORT_NEON) && !defined(MA_NO_NEON) && __has_include(<arm_neon.h>)
3273  #define MA_SUPPORT_NEON
3274  #endif
3275  #endif
3276 
3277  #if defined(MA_SUPPORT_NEON)
3278  #include <arm_neon.h>
3279  #endif
3280 #endif
3281 
3282 #if defined(_MSC_VER)
3283  #pragma warning(push)
3284  #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */
3285 #endif
3286 
3287 #if defined(MA_X64) || defined(MA_X86)
3288  #if defined(_MSC_VER) && !defined(__clang__)
3289  #if _MSC_VER >= 1400
3290  #include <intrin.h>
3291  static MA_INLINE void ma_cpuid(int info[4], int fid)
3292  {
3293  __cpuid(info, fid);
3294  }
3295  #else
3296  #define MA_NO_CPUID
3297  #endif
3298 
3299  #if _MSC_VER >= 1600
3300  static MA_INLINE unsigned __int64 ma_xgetbv(int reg)
3301  {
3302  return _xgetbv(reg);
3303  }
3304  #else
3305  #define MA_NO_XGETBV
3306  #endif
3307  #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
3308  static MA_INLINE void ma_cpuid(int info[4], int fid)
3309  {
3310  /*
3311  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
3312  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
3313  supporting different assembly dialects.
3314 
3315  What's basically happening is that we're saving and restoring the ebx register manually.
3316  */
3317  #if defined(DRFLAC_X86) && defined(__PIC__)
3318  __asm__ __volatile__ (
3319  "xchg{l} {%%}ebx, %k1;"
3320  "cpuid;"
3321  "xchg{l} {%%}ebx, %k1;"
3322  : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
3323  );
3324  #else
3325  __asm__ __volatile__ (
3326  "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
3327  );
3328  #endif
3329  }
3330 
3331  static MA_INLINE ma_uint64 ma_xgetbv(int reg)
3332  {
3333  unsigned int hi;
3334  unsigned int lo;
3335 
3336  __asm__ __volatile__ (
3337  "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
3338  );
3339 
3340  return ((ma_uint64)hi << 32) | (ma_uint64)lo;
3341  }
3342  #else
3343  #define MA_NO_CPUID
3344  #define MA_NO_XGETBV
3345  #endif
3346 #else
3347  #define MA_NO_CPUID
3348  #define MA_NO_XGETBV
3349 #endif
3350 
3351 static MA_INLINE ma_bool32 ma_has_sse2()
3352 {
3353 #if defined(MA_SUPPORT_SSE2)
3354  #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
3355  #if defined(MA_X64)
3356  return MA_TRUE; /* 64-bit targets always support SSE2. */
3357  #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
3358  return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
3359  #else
3360  #if defined(MA_NO_CPUID)
3361  return MA_FALSE;
3362  #else
3363  int info[4];
3364  ma_cpuid(info, 1);
3365  return (info[3] & (1 << 26)) != 0;
3366  #endif
3367  #endif
3368  #else
3369  return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
3370  #endif
3371 #else
3372  return MA_FALSE; /* No compiler support. */
3373 #endif
3374 }
3375 
3376 #if 0
3377 static MA_INLINE ma_bool32 ma_has_avx()
3378 {
3379 #if defined(MA_SUPPORT_AVX)
3380  #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)
3381  #if defined(_AVX_) || defined(__AVX__)
3382  return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */
3383  #else
3384  /* AVX requires both CPU and OS support. */
3385  #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
3386  return MA_FALSE;
3387  #else
3388  int info[4];
3389  ma_cpuid(info, 1);
3390  if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
3391  ma_uint64 xrc = ma_xgetbv(0);
3392  if ((xrc & 0x06) == 0x06) {
3393  return MA_TRUE;
3394  } else {
3395  return MA_FALSE;
3396  }
3397  } else {
3398  return MA_FALSE;
3399  }
3400  #endif
3401  #endif
3402  #else
3403  return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */
3404  #endif
3405 #else
3406  return MA_FALSE; /* No compiler support. */
3407 #endif
3408 }
3409 #endif
3410 
3411 static MA_INLINE ma_bool32 ma_has_avx2()
3412 {
3413 #if defined(MA_SUPPORT_AVX2)
3414  #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)
3415  #if defined(_AVX2_) || defined(__AVX2__)
3416  return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */
3417  #else
3418  /* AVX2 requires both CPU and OS support. */
3419  #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
3420  return MA_FALSE;
3421  #else
3422  int info1[4];
3423  int info7[4];
3424  ma_cpuid(info1, 1);
3425  ma_cpuid(info7, 7);
3426  if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {
3427  ma_uint64 xrc = ma_xgetbv(0);
3428  if ((xrc & 0x06) == 0x06) {
3429  return MA_TRUE;
3430  } else {
3431  return MA_FALSE;
3432  }
3433  } else {
3434  return MA_FALSE;
3435  }
3436  #endif
3437  #endif
3438  #else
3439  return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */
3440  #endif
3441 #else
3442  return MA_FALSE; /* No compiler support. */
3443 #endif
3444 }
3445 
3446 static MA_INLINE ma_bool32 ma_has_avx512f()
3447 {
3448 #if defined(MA_SUPPORT_AVX512)
3449  #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX512)
3450  #if defined(__AVX512F__)
3451  return MA_TRUE; /* If the compiler is allowed to freely generate AVX-512F code we can assume support. */
3452  #else
3453  /* AVX-512 requires both CPU and OS support. */
3454  #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
3455  return MA_FALSE;
3456  #else
3457  int info1[4];
3458  int info7[4];
3459  ma_cpuid(info1, 1);
3460  ma_cpuid(info7, 7);
3461  if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 16)) != 0)) {
3462  ma_uint64 xrc = ma_xgetbv(0);
3463  if ((xrc & 0xE6) == 0xE6) {
3464  return MA_TRUE;
3465  } else {
3466  return MA_FALSE;
3467  }
3468  } else {
3469  return MA_FALSE;
3470  }
3471  #endif
3472  #endif
3473  #else
3474  return MA_FALSE; /* AVX-512F is only supported on x86 and x64 architectures. */
3475  #endif
3476 #else
3477  return MA_FALSE; /* No compiler support. */
3478 #endif
3479 }
3480 
3481 static MA_INLINE ma_bool32 ma_has_neon()
3482 {
3483 #if defined(MA_SUPPORT_NEON)
3484  #if defined(MA_ARM) && !defined(MA_NO_NEON)
3485  #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
3486  return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */
3487  #else
3488  /* TODO: Runtime check. */
3489  return MA_FALSE;
3490  #endif
3491  #else
3492  return MA_FALSE; /* NEON is only supported on ARM architectures. */
3493  #endif
3494 #else
3495  return MA_FALSE; /* No compiler support. */
3496 #endif
3497 }
3498 
3499 
3500 static MA_INLINE ma_bool32 ma_is_little_endian()
3501 {
3502 #if defined(MA_X86) || defined(MA_X64)
3503  return MA_TRUE;
3504 #else
3505  int n = 1;
3506  return (*(char*)&n) == 1;
3507 #endif
3508 }
3509 
3510 static MA_INLINE ma_bool32 ma_is_big_endian()
3511 {
3512  return !ma_is_little_endian();
3513 }
3514 
3515 
3516 #ifndef MA_COINIT_VALUE
3517 #define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED*/
3518 #endif
3519 
3520 
3521 
3522 #ifndef MA_PI
3523 #define MA_PI 3.14159265358979323846264f
3524 #endif
3525 #ifndef MA_PI_D
3526 #define MA_PI_D 3.14159265358979323846264
3527 #endif
3528 #ifndef MA_TAU
3529 #define MA_TAU 6.28318530717958647693f
3530 #endif
3531 #ifndef MA_TAU_D
3532 #define MA_TAU_D 6.28318530717958647693
3533 #endif
3534 
3535 
3536 /* The default format when ma_format_unknown (0) is requested when initializing a device. */
3537 #ifndef MA_DEFAULT_FORMAT
3538 #define MA_DEFAULT_FORMAT ma_format_f32
3539 #endif
3540 
3541 /* The default channel count to use when 0 is used when initializing a device. */
3542 #ifndef MA_DEFAULT_CHANNELS
3543 #define MA_DEFAULT_CHANNELS 2
3544 #endif
3545 
3546 /* The default sample rate to use when 0 is used when initializing a device. */
3547 #ifndef MA_DEFAULT_SAMPLE_RATE
3548 #define MA_DEFAULT_SAMPLE_RATE 48000
3549 #endif
3550 
3551 /* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */
3552 #ifndef MA_DEFAULT_PERIODS
3553 #define MA_DEFAULT_PERIODS 3
3554 #endif
3555 
3556 /* The base buffer size in milliseconds for low latency mode. */
3557 #ifndef MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
3558 #define MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY (10*MA_DEFAULT_PERIODS)
3559 #endif
3560 
3561 /* The base buffer size in milliseconds for conservative mode. */
3562 #ifndef MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
3563 #define MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE (100*MA_DEFAULT_PERIODS)
3564 #endif
3565 
3566 
3567 /* Standard sample rates, in order of priority. */
3568 ma_uint32 g_maStandardSampleRatePriorities[] = {
3569  MA_SAMPLE_RATE_48000, /* Most common */
3571 
3572  MA_SAMPLE_RATE_32000, /* Lows */
3575 
3576  MA_SAMPLE_RATE_88200, /* Highs */
3580 
3581  MA_SAMPLE_RATE_16000, /* Extreme lows */
3584 
3585  MA_SAMPLE_RATE_352800, /* Extreme highs */
3587 };
3588 
3589 ma_format g_maFormatPriorities[] = {
3590  ma_format_s16, /* Most common */
3591  ma_format_f32,
3592 
3593  /*ma_format_s24_32,*/ /* Clean alignment */
3594  ma_format_s32,
3595 
3596  ma_format_s24, /* Unclean alignment */
3597 
3598  ma_format_u8 /* Low quality */
3599 };
3600 
3601 
3602 
3603 /******************************************************************************
3604 
3605 Standard Library Stuff
3606 
3607 ******************************************************************************/
3608 #ifndef MA_MALLOC
3609 #ifdef MA_WIN32
3610 #define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz))
3611 #else
3612 #define MA_MALLOC(sz) malloc((sz))
3613 #endif
3614 #endif
3615 
3616 #ifndef MA_REALLOC
3617 #ifdef MA_WIN32
3618 #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)))
3619 #else
3620 #define MA_REALLOC(p, sz) realloc((p), (sz))
3621 #endif
3622 #endif
3623 
3624 #ifndef MA_FREE
3625 #ifdef MA_WIN32
3626 #define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p))
3627 #else
3628 #define MA_FREE(p) free((p))
3629 #endif
3630 #endif
3631 
3632 #ifndef MA_ZERO_MEMORY
3633 #ifdef MA_WIN32
3634 #define MA_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz))
3635 #else
3636 #define MA_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
3637 #endif
3638 #endif
3639 
3640 #ifndef MA_COPY_MEMORY
3641 #ifdef MA_WIN32
3642 #define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz))
3643 #else
3644 #define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
3645 #endif
3646 #endif
3647 
3648 #ifndef MA_ASSERT
3649 #ifdef MA_WIN32
3650 #define MA_ASSERT(condition) assert(condition)
3651 #else
3652 #define MA_ASSERT(condition) assert(condition)
3653 #endif
3654 #endif
3655 
3656 #define ma_zero_memory MA_ZERO_MEMORY
3657 #define ma_copy_memory MA_COPY_MEMORY
3658 #define ma_assert MA_ASSERT
3659 
3660 #define ma_zero_object(p) ma_zero_memory((p), sizeof(*(p)))
3661 #define ma_countof(x) (sizeof(x) / sizeof(x[0]))
3662 #define ma_max(x, y) (((x) > (y)) ? (x) : (y))
3663 #define ma_min(x, y) (((x) < (y)) ? (x) : (y))
3664 #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi)))
3665 #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
3666 
3667 #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))
3668 
3669 /*
3670 Return Values:
3671  0: Success
3672  22: EINVAL
3673  34: ERANGE
3674 
3675 Not using symbolic constants for errors because I want to avoid #including errno.h
3676 */
3677 int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
3678 {
3679  size_t i;
3680 
3681  if (dst == 0) {
3682  return 22;
3683  }
3684  if (dstSizeInBytes == 0) {
3685  return 34;
3686  }
3687  if (src == 0) {
3688  dst[0] = '\0';
3689  return 22;
3690  }
3691 
3692  for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
3693  dst[i] = src[i];
3694  }
3695 
3696  if (i < dstSizeInBytes) {
3697  dst[i] = '\0';
3698  return 0;
3699  }
3700 
3701  dst[0] = '\0';
3702  return 34;
3703 }
3704 
3705 int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
3706 {
3707  size_t maxcount;
3708  size_t i;
3709 
3710  if (dst == 0) {
3711  return 22;
3712  }
3713  if (dstSizeInBytes == 0) {
3714  return 34;
3715  }
3716  if (src == 0) {
3717  dst[0] = '\0';
3718  return 22;
3719  }
3720 
3721  maxcount = count;
3722  if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
3723  maxcount = dstSizeInBytes - 1;
3724  }
3725 
3726  for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
3727  dst[i] = src[i];
3728  }
3729 
3730  if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
3731  dst[i] = '\0';
3732  return 0;
3733  }
3734 
3735  dst[0] = '\0';
3736  return 34;
3737 }
3738 
3739 int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
3740 {
3741  char* dstorig;
3742 
3743  if (dst == 0) {
3744  return 22;
3745  }
3746  if (dstSizeInBytes == 0) {
3747  return 34;
3748  }
3749  if (src == 0) {
3750  dst[0] = '\0';
3751  return 22;
3752  }
3753 
3754  dstorig = dst;
3755 
3756  while (dstSizeInBytes > 0 && dst[0] != '\0') {
3757  dst += 1;
3758  dstSizeInBytes -= 1;
3759  }
3760 
3761  if (dstSizeInBytes == 0) {
3762  return 22; /* Unterminated. */
3763  }
3764 
3765 
3766  while (dstSizeInBytes > 0 && src[0] != '\0') {
3767  *dst++ = *src++;
3768  dstSizeInBytes -= 1;
3769  }
3770 
3771  if (dstSizeInBytes > 0) {
3772  dst[0] = '\0';
3773  } else {
3774  dstorig[0] = '\0';
3775  return 34;
3776  }
3777 
3778  return 0;
3779 }
3780 
3781 int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
3782 {
3783  char* dstorig;
3784 
3785  if (dst == 0) {
3786  return 22;
3787  }
3788  if (dstSizeInBytes == 0) {
3789  return 34;
3790  }
3791  if (src == 0) {
3792  return 22;
3793  }
3794 
3795  dstorig = dst;
3796 
3797  while (dstSizeInBytes > 0 && dst[0] != '\0') {
3798  dst += 1;
3799  dstSizeInBytes -= 1;
3800  }
3801 
3802  if (dstSizeInBytes == 0) {
3803  return 22; /* Unterminated. */
3804  }
3805 
3806 
3807  if (count == ((size_t)-1)) { /* _TRUNCATE */
3808  count = dstSizeInBytes - 1;
3809  }
3810 
3811  while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
3812  *dst++ = *src++;
3813  dstSizeInBytes -= 1;
3814  count -= 1;
3815  }
3816 
3817  if (dstSizeInBytes > 0) {
3818  dst[0] = '\0';
3819  } else {
3820  dstorig[0] = '\0';
3821  return 34;
3822  }
3823 
3824  return 0;
3825 }
3826 
3827 int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
3828 {
3829  int sign;
3830  unsigned int valueU;
3831  char* dstEnd;
3832 
3833  if (dst == NULL || dstSizeInBytes == 0) {
3834  return 22;
3835  }
3836  if (radix < 2 || radix > 36) {
3837  dst[0] = '\0';
3838  return 22;
3839  }
3840 
3841  sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */
3842 
3843  if (value < 0) {
3844  valueU = -value;
3845  } else {
3846  valueU = value;
3847  }
3848 
3849  dstEnd = dst;
3850  do
3851  {
3852  int remainder = valueU % radix;
3853  if (remainder > 9) {
3854  *dstEnd = (char)((remainder - 10) + 'a');
3855  } else {
3856  *dstEnd = (char)(remainder + '0');
3857  }
3858 
3859  dstEnd += 1;
3860  dstSizeInBytes -= 1;
3861  valueU /= radix;
3862  } while (dstSizeInBytes > 0 && valueU > 0);
3863 
3864  if (dstSizeInBytes == 0) {
3865  dst[0] = '\0';
3866  return 22; /* Ran out of room in the output buffer. */
3867  }
3868 
3869  if (sign < 0) {
3870  *dstEnd++ = '-';
3871  dstSizeInBytes -= 1;
3872  }
3873 
3874  if (dstSizeInBytes == 0) {
3875  dst[0] = '\0';
3876  return 22; /* Ran out of room in the output buffer. */
3877  }
3878 
3879  *dstEnd = '\0';
3880 
3881 
3882  /* At this point the string will be reversed. */
3883  dstEnd -= 1;
3884  while (dst < dstEnd) {
3885  char temp = *dst;
3886  *dst = *dstEnd;
3887  *dstEnd = temp;
3888 
3889  dst += 1;
3890  dstEnd -= 1;
3891  }
3892 
3893  return 0;
3894 }
3895 
3896 int ma_strcmp(const char* str1, const char* str2)
3897 {
3898  if (str1 == str2) return 0;
3899 
3900  /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */
3901  if (str1 == NULL) return -1;
3902  if (str2 == NULL) return 1;
3903 
3904  for (;;) {
3905  if (str1[0] == '\0') {
3906  break;
3907  }
3908  if (str1[0] != str2[0]) {
3909  break;
3910  }
3911 
3912  str1 += 1;
3913  str2 += 1;
3914  }
3915 
3916  return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
3917 }
3918 
3919 int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)
3920 {
3921  int result;
3922 
3923  result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);
3924  if (result != 0) {
3925  return result;
3926  }
3927 
3928  result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);
3929  if (result != 0) {
3930  return result;
3931  }
3932 
3933  return result;
3934 }
3935 
3936 char* ma_copy_string(const char* src)
3937 {
3938  size_t sz = strlen(src)+1;
3939  char* dst = (char*)ma_malloc(sz);
3940  if (dst == NULL) {
3941  return NULL;
3942  }
3943 
3944  ma_strcpy_s(dst, sz, src);
3945 
3946  return dst;
3947 }
3948 
3949 
3950 /* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
3951 static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)
3952 {
3953  x--;
3954  x |= x >> 1;
3955  x |= x >> 2;
3956  x |= x >> 4;
3957  x |= x >> 8;
3958  x |= x >> 16;
3959  x++;
3960 
3961  return x;
3962 }
3963 
3964 static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)
3965 {
3966  return ma_next_power_of_2(x) >> 1;
3967 }
3968 
3969 static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)
3970 {
3971  unsigned int prev = ma_prev_power_of_2(x);
3972  unsigned int next = ma_next_power_of_2(x);
3973  if ((next - x) > (x - prev)) {
3974  return prev;
3975  } else {
3976  return next;
3977  }
3978 }
3979 
3980 static MA_INLINE unsigned int ma_count_set_bits(unsigned int x)
3981 {
3982  unsigned int count = 0;
3983  while (x != 0) {
3984  if (x & 1) {
3985  count += 1;
3986  }
3987 
3988  x = x >> 1;
3989  }
3990 
3991  return count;
3992 }
3993 
3994 
3995 
3996 /* Clamps an f32 sample to -1..1 */
3997 static MA_INLINE float ma_clip_f32(float x)
3998 {
3999  if (x < -1) return -1;
4000  if (x > +1) return +1;
4001  return x;
4002 }
4003 
4004 static MA_INLINE float ma_mix_f32(float x, float y, float a)
4005 {
4006  return x*(1-a) + y*a;
4007 }
4008 static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
4009 {
4010  float r0 = (y - x);
4011  float r1 = r0*a;
4012  return x + r1;
4013  /*return x + (y - x)*a;*/
4014 }
4015 
4016 #if defined(MA_SUPPORT_SSE2)
4017 static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)
4018 {
4019  return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));
4020 }
4021 #endif
4022 #if defined(MA_SUPPORT_AVX2)
4023 static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)
4024 {
4025  return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));
4026 }
4027 #endif
4028 #if defined(MA_SUPPORT_AVX512)
4029 static MA_INLINE __m512 ma_mix_f32_fast__avx512(__m512 x, __m512 y, __m512 a)
4030 {
4031  return _mm512_add_ps(x, _mm512_mul_ps(_mm512_sub_ps(y, x), a));
4032 }
4033 #endif
4034 #if defined(MA_SUPPORT_NEON)
4035 static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)
4036 {
4037  return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));
4038 }
4039 #endif
4040 
4041 
4042 static MA_INLINE double ma_mix_f64(double x, double y, double a)
4043 {
4044  return x*(1-a) + y*a;
4045 }
4046 static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
4047 {
4048  return x + (y - x)*a;
4049 }
4050 
4051 static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
4052 {
4053  return lo + x*(hi-lo);
4054 }
4055 
4056 
4057 /*
4058 Random Number Generation
4059 
4060 miniaudio uses the LCG random number generation algorithm. This is good enough for audio.
4061 
4062 Note that miniaudio's LCG implementation uses global state which is _not_ thread-local. When this is called across
4063 multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough
4064 for miniaudio's purposes.
4065 */
4066 #define MA_LCG_M 2147483647
4067 #define MA_LCG_A 48271
4068 #define MA_LCG_C 0
4069 static ma_int32 g_maLCG;
4070 
4071 void ma_seed(ma_int32 seed)
4072 {
4073  g_maLCG = seed;
4074 }
4075 
4076 ma_int32 ma_rand_s32()
4077 {
4078  ma_int32 lcg = g_maLCG;
4079  ma_int32 r = (MA_LCG_A * lcg + MA_LCG_C) % MA_LCG_M;
4080  g_maLCG = r;
4081  return r;
4082 }
4083 
4084 double ma_rand_f64()
4085 {
4086  return (ma_rand_s32() + 0x80000000) / (double)0x7FFFFFFF;
4087 }
4088 
4089 float ma_rand_f32()
4090 {
4091  return (float)ma_rand_f64();
4092 }
4093 
4094 static MA_INLINE float ma_rand_range_f32(float lo, float hi)
4095 {
4096  return ma_scale_to_range_f32(ma_rand_f32(), lo, hi);
4097 }
4098 
4099 static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)
4100 {
4101  double x = ma_rand_f64();
4102  return lo + (ma_int32)(x*(hi-lo));
4103 }
4104 
4105 
4106 static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)
4107 {
4108  return ma_rand_range_f32(ditherMin, ditherMax);
4109 }
4110 
4111 static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)
4112 {
4113  float a = ma_rand_range_f32(ditherMin, 0);
4114  float b = ma_rand_range_f32(0, ditherMax);
4115  return a + b;
4116 }
4117 
4118 static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)
4119 {
4120  if (ditherMode == ma_dither_mode_rectangle) {
4121  return ma_dither_f32_rectangle(ditherMin, ditherMax);
4122  }
4123  if (ditherMode == ma_dither_mode_triangle) {
4124  return ma_dither_f32_triangle(ditherMin, ditherMax);
4125  }
4126 
4127  return 0;
4128 }
4129 
4130 static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)
4131 {
4132  if (ditherMode == ma_dither_mode_rectangle) {
4133  ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);
4134  return a;
4135  }
4136  if (ditherMode == ma_dither_mode_triangle) {
4137  ma_int32 a = ma_rand_range_s32(ditherMin, 0);
4138  ma_int32 b = ma_rand_range_s32(0, ditherMax);
4139  return a + b;
4140  }
4141 
4142  return 0;
4143 }
4144 
4145 
4146 /*
4147 Splits a buffer into parts of equal length and of the given alignment. The returned size of the split buffers will be a
4148 multiple of the alignment. The alignment must be a power of 2.
4149 */
4150 void ma_split_buffer(void* pBuffer, size_t bufferSize, size_t splitCount, size_t alignment, void** ppBuffersOut, size_t* pSplitSizeOut)
4151 {
4152  ma_uintptr pBufferUnaligned;
4153  ma_uintptr pBufferAligned;
4154  size_t unalignedBytes;
4155  size_t splitSize;
4156 
4157  if (pSplitSizeOut) {
4158  *pSplitSizeOut = 0;
4159  }
4160 
4161  if (pBuffer == NULL || bufferSize == 0 || splitCount == 0) {
4162  return;
4163  }
4164 
4165  if (alignment == 0) {
4166  alignment = 1;
4167  }
4168 
4169  pBufferUnaligned = (ma_uintptr)pBuffer;
4170  pBufferAligned = (pBufferUnaligned + (alignment-1)) & ~(alignment-1);
4171  unalignedBytes = (size_t)(pBufferAligned - pBufferUnaligned);
4172 
4173  splitSize = 0;
4174  if (bufferSize >= unalignedBytes) {
4175  splitSize = (bufferSize - unalignedBytes) / splitCount;
4176  splitSize = splitSize & ~(alignment-1);
4177  }
4178 
4179  if (ppBuffersOut != NULL) {
4180  size_t i;
4181  for (i = 0; i < splitCount; ++i) {
4182  ppBuffersOut[i] = (ma_uint8*)(pBufferAligned + (splitSize*i));
4183  }
4184  }
4185 
4186  if (pSplitSizeOut) {
4187  *pSplitSizeOut = splitSize;
4188  }
4189 }
4190 
4191 
4192 /******************************************************************************
4193 
4194 Atomics
4195 
4196 ******************************************************************************/
4197 #if defined(_WIN32) && !defined(__GNUC__)
4198 #define ma_memory_barrier() MemoryBarrier()
4199 #define ma_atomic_exchange_32(a, b) InterlockedExchange((LONG*)a, (LONG)b)
4200 #define ma_atomic_exchange_64(a, b) InterlockedExchange64((LONGLONG*)a, (LONGLONG)b)
4201 #define ma_atomic_increment_32(a) InterlockedIncrement((LONG*)a)
4202 #define ma_atomic_decrement_32(a) InterlockedDecrement((LONG*)a)
4203 #else
4204 #define ma_memory_barrier() __sync_synchronize()
4205 #define ma_atomic_exchange_32(a, b) (void)__sync_lock_test_and_set(a, b); __sync_synchronize()
4206 #define ma_atomic_exchange_64(a, b) (void)__sync_lock_test_and_set(a, b); __sync_synchronize()
4207 #define ma_atomic_increment_32(a) __sync_add_and_fetch(a, 1)
4208 #define ma_atomic_decrement_32(a) __sync_sub_and_fetch(a, 1)
4209 #endif
4210 
4211 #ifdef MA_64BIT
4212 #define ma_atomic_exchange_ptr ma_atomic_exchange_64
4213 #endif
4214 #ifdef MA_32BIT
4215 #define ma_atomic_exchange_ptr ma_atomic_exchange_32
4216 #endif
4217 
4218 
4219 ma_uint32 ma_get_standard_sample_rate_priority_index(ma_uint32 sampleRate) /* Lower = higher priority */
4220 {
4221  ma_uint32 i;
4222  for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
4223  if (g_maStandardSampleRatePriorities[i] == sampleRate) {
4224  return i;
4225  }
4226  }
4227 
4228  return (ma_uint32)-1;
4229 }
4230 
4231 ma_uint64 ma_calculate_frame_count_after_src(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)
4232 {
4233  double srcRatio = (double)sampleRateOut / sampleRateIn;
4234  double frameCountOutF = (ma_int64)frameCountIn * srcRatio; /* Cast to int64 required for VC6. */
4235  ma_uint64 frameCountOut = (ma_uint64)frameCountOutF;
4236 
4237  /* If the output frame count is fractional, make sure we add an extra frame to ensure there's enough room for that last sample. */
4238  if ((frameCountOutF - (ma_int64)frameCountOut) > 0.0) {
4239  frameCountOut += 1;
4240  }
4241 
4242  return frameCountOut;
4243 }
4244 
4245 
4246 /************************************************************************************************************************************************************
4247 *************************************************************************************************************************************************************
4248 
4249 DEVICE I/O
4250 ==========
4251 
4252 *************************************************************************************************************************************************************
4253 ************************************************************************************************************************************************************/
4254 #ifndef MA_NO_DEVICE_IO
4255 /*
4256 Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
4257 using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
4258 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
4259 disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
4260 not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
4261 */
4262 /*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/
4263 
4264 /* Disable run-time linking on certain backends. */
4265 #ifndef MA_NO_RUNTIME_LINKING
4266  #if defined(MA_ANDROID) || defined(MA_EMSCRIPTEN)
4267  #define MA_NO_RUNTIME_LINKING
4268  #endif
4269 #endif
4270 
4271 /*
4272 Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not
4273 certain unused functions and variables can be excluded from the build to avoid warnings.
4274 */
4275 #ifdef MA_ENABLE_WASAPI
4276  #define MA_HAS_WASAPI /* Every compiler should support WASAPI */
4277 #endif
4278 #ifdef MA_ENABLE_DSOUND
4279  #define MA_HAS_DSOUND /* Every compiler should support DirectSound. */
4280 #endif
4281 #ifdef MA_ENABLE_WINMM
4282  #define MA_HAS_WINMM /* Every compiler I'm aware of supports WinMM. */
4283 #endif
4284 #ifdef MA_ENABLE_ALSA
4285  #define MA_HAS_ALSA
4286  #ifdef MA_NO_RUNTIME_LINKING
4287  #ifdef __has_include
4288  #if !__has_include(<alsa/asoundlib.h>)
4289  #undef MA_HAS_ALSA
4290  #endif
4291  #endif
4292  #endif
4293 #endif
4294 #ifdef MA_ENABLE_PULSEAUDIO
4295  #define MA_HAS_PULSEAUDIO
4296  #ifdef MA_NO_RUNTIME_LINKING
4297  #ifdef __has_include
4298  #if !__has_include(<pulse/pulseaudio.h>)
4299  #undef MA_HAS_PULSEAUDIO
4300  #endif
4301  #endif
4302  #endif
4303 #endif
4304 #ifdef MA_ENABLE_JACK
4305  #define MA_HAS_JACK
4306  #ifdef MA_NO_RUNTIME_LINKING
4307  #ifdef __has_include
4308  #if !__has_include(<jack/jack.h>)
4309  #undef MA_HAS_JACK
4310  #endif
4311  #endif
4312  #endif
4313 #endif
4314 #ifdef MA_ENABLE_COREAUDIO
4315  #define MA_HAS_COREAUDIO
4316 #endif
4317 #ifdef MA_ENABLE_SNDIO
4318  #define MA_HAS_SNDIO
4319 #endif
4320 #ifdef MA_ENABLE_AUDIO4
4321  #define MA_HAS_AUDIO4
4322 #endif
4323 #ifdef MA_ENABLE_OSS
4324  #define MA_HAS_OSS
4325 #endif
4326 #ifdef MA_ENABLE_AAUDIO
4327  #define MA_HAS_AAUDIO
4328 #endif
4329 #ifdef MA_ENABLE_OPENSL
4330  #define MA_HAS_OPENSL
4331 #endif
4332 #ifdef MA_ENABLE_WEBAUDIO
4333  #define MA_HAS_WEBAUDIO
4334 #endif
4335 #ifdef MA_ENABLE_NULL
4336  #define MA_HAS_NULL /* Everything supports the null backend. */
4337 #endif
4338 
4339 const char* ma_get_backend_name(ma_backend backend)
4340 {
4341  switch (backend)
4342  {
4343  case ma_backend_wasapi: return "WASAPI";
4344  case ma_backend_dsound: return "DirectSound";
4345  case ma_backend_winmm: return "WinMM";
4346  case ma_backend_coreaudio: return "Core Audio";
4347  case ma_backend_sndio: return "sndio";
4348  case ma_backend_audio4: return "audio(4)";
4349  case ma_backend_oss: return "OSS";
4350  case ma_backend_pulseaudio: return "PulseAudio";
4351  case ma_backend_alsa: return "ALSA";
4352  case ma_backend_jack: return "JACK";
4353  case ma_backend_aaudio: return "AAudio";
4354  case ma_backend_opensl: return "OpenSL|ES";
4355  case ma_backend_webaudio: return "Web Audio";
4356  case ma_backend_null: return "Null";
4357  default: return "Unknown";
4358  }
4359 }
4360 
4361 
4362 
4363 #ifdef MA_WIN32
4364  #define MA_THREADCALL WINAPI
4365  typedef unsigned long ma_thread_result;
4366 #else
4367  #define MA_THREADCALL
4368  typedef void* ma_thread_result;
4369 #endif
4370 typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
4371 
4372 #ifdef MA_WIN32
4373 typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
4374 typedef void (WINAPI * MA_PFN_CoUninitialize)();
4375 typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
4376 typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv);
4377 typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar);
4378 typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
4379 
4380 typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)();
4381 typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)();
4382 
4383 /* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
4384 typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
4385 typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
4386 typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
4387 #endif
4388 
4389 
4390 #define MA_STATE_UNINITIALIZED 0
4391 #define MA_STATE_STOPPED 1 /* The device's default state after initialization. */
4392 #define MA_STATE_STARTED 2 /* The worker thread is in it's main loop waiting for the driver to request or deliver audio data. */
4393 #define MA_STATE_STARTING 3 /* Transitioning from a stopped state to started. */
4394 #define MA_STATE_STOPPING 4 /* Transitioning from a started state to stopped. */
4395 
4396 #define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
4397 #define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
4398 
4399 
4400 const char* ma_log_level_to_string(ma_uint32 logLevel)
4401 {
4402  switch (logLevel)
4403  {
4404  case MA_LOG_LEVEL_VERBOSE: return "";
4405  case MA_LOG_LEVEL_INFO: return "INFO";
4406  case MA_LOG_LEVEL_WARNING: return "WARNING";
4407  case MA_LOG_LEVEL_ERROR: return "ERROR";
4408  default: return "ERROR";
4409  }
4410 }
4411 
4412 /* Posts a log message. */
4413 void ma_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
4414 {
4415  if (pContext == NULL) {
4416  return;
4417  }
4418 
4419 #if defined(MA_LOG_LEVEL)
4420  if (logLevel <= MA_LOG_LEVEL) {
4421  ma_log_proc onLog;
4422 
4423  #if defined(MA_DEBUG_OUTPUT)
4424  if (logLevel <= MA_LOG_LEVEL) {
4425  printf("%s: %s\n", ma_log_level_to_string(logLevel), message);
4426  }
4427  #endif
4428 
4429  onLog = pContext->logCallback;
4430  if (onLog) {
4431  onLog(pContext, pDevice, logLevel, message);
4432  }
4433  }
4434 #endif
4435 }
4436 
4437 /* Posts an log message. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */
4438 ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
4439 {
4440  /* Derive the context from the device if necessary. */
4441  if (pContext == NULL) {
4442  if (pDevice != NULL) {
4443  pContext = pDevice->pContext;
4444  }
4445  }
4446 
4447  ma_log(pContext, pDevice, logLevel, message);
4448  return resultCode;
4449 }
4450 
4451 ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
4452 {
4453  return ma_context_post_error(NULL, pDevice, logLevel, message, resultCode);
4454 }
4455 
4456 
4457 /*******************************************************************************
4458 
4459 Timing
4460 
4461 *******************************************************************************/
4462 #ifdef MA_WIN32
4463 LARGE_INTEGER g_ma_TimerFrequency = {{0}};
4464 void ma_timer_init(ma_timer* pTimer)
4465 {
4466  LARGE_INTEGER counter;
4467 
4468  if (g_ma_TimerFrequency.QuadPart == 0) {
4469  QueryPerformanceFrequency(&g_ma_TimerFrequency);
4470  }
4471 
4472  QueryPerformanceCounter(&counter);
4473  pTimer->counter = counter.QuadPart;
4474 }
4475 
4476 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
4477 {
4478  LARGE_INTEGER counter;
4479  if (!QueryPerformanceCounter(&counter)) {
4480  return 0;
4481  }
4482 
4483  return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
4484 }
4485 #elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
4486 ma_uint64 g_ma_TimerFrequency = 0;
4487 void ma_timer_init(ma_timer* pTimer)
4488 {
4489  mach_timebase_info_data_t baseTime;
4490  mach_timebase_info(&baseTime);
4491  g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
4492 
4493  pTimer->counter = mach_absolute_time();
4494 }
4495 
4496 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
4497 {
4498  ma_uint64 newTimeCounter = mach_absolute_time();
4499  ma_uint64 oldTimeCounter = pTimer->counter;
4500 
4501  return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
4502 }
4503 #elif defined(MA_EMSCRIPTEN)
4504 void ma_timer_init(ma_timer* pTimer)
4505 {
4506  pTimer->counterD = emscripten_get_now();
4507 }
4508 
4509 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
4510 {
4511  return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
4512 }
4513 #else
4514 #if _POSIX_C_SOURCE >= 199309L
4515 #if defined(CLOCK_MONOTONIC)
4516  #define MA_CLOCK_ID CLOCK_MONOTONIC
4517 #else
4518  #define MA_CLOCK_ID CLOCK_REALTIME
4519 #endif
4520 
4521 void ma_timer_init(ma_timer* pTimer)
4522 {
4523  struct timespec newTime;
4524  clock_gettime(MA_CLOCK_ID, &newTime);
4525 
4526  pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
4527 }
4528 
4529 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
4530 {
4531  ma_uint64 newTimeCounter;
4532  ma_uint64 oldTimeCounter;
4533 
4534  struct timespec newTime;
4535  clock_gettime(MA_CLOCK_ID, &newTime);
4536 
4537  newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
4538  oldTimeCounter = pTimer->counter;
4539 
4540  return (newTimeCounter - oldTimeCounter) / 1000000000.0;
4541 }
4542 #else
4543 void ma_timer_init(ma_timer* pTimer)
4544 {
4545  struct timeval newTime;
4546  gettimeofday(&newTime, NULL);
4547 
4548  pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
4549 }
4550 
4551 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
4552 {
4553  ma_uint64 newTimeCounter;
4554  ma_uint64 oldTimeCounter;
4555 
4556  struct timeval newTime;
4557  gettimeofday(&newTime, NULL);
4558 
4559  newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
4560  oldTimeCounter = pTimer->counter;
4561 
4562  return (newTimeCounter - oldTimeCounter) / 1000000.0;
4563 }
4564 #endif
4565 #endif
4566 
4567 
4568 /*******************************************************************************
4569 
4570 Dynamic Linking
4571 
4572 *******************************************************************************/
4573 ma_handle ma_dlopen(ma_context* pContext, const char* filename)
4574 {
4575  ma_handle handle;
4576 
4577 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
4578  if (pContext != NULL) {
4579  char message[256];
4580  ma_strappend(message, sizeof(message), "Loading library: ", filename);
4581  ma_log(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
4582  }
4583 #endif
4584 
4585 #ifdef _WIN32
4586 #ifdef MA_WIN32_DESKTOP
4587  handle = (ma_handle)LoadLibraryA(filename);
4588 #else
4589  /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
4590  WCHAR filenameW[4096];
4591  if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
4592  handle = NULL;
4593  } else {
4594  handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
4595  }
4596 #endif
4597 #else
4598  handle = (ma_handle)dlopen(filename, RTLD_NOW);
4599 #endif
4600 
4601  /*
4602  I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority
4603  backend is a deliberate design choice. Instead I'm logging it as an informational message.
4604  */
4605 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_INFO
4606  if (handle == NULL) {
4607  char message[256];
4608  ma_strappend(message, sizeof(message), "Failed to load library: ", filename);
4609  ma_log(pContext, NULL, MA_LOG_LEVEL_INFO, message);
4610  }
4611 #endif
4612 
4613  (void)pContext; /* It's possible for pContext to be unused. */
4614  return handle;
4615 }
4616 
4617 void ma_dlclose(ma_context* pContext, ma_handle handle)
4618 {
4619 #ifdef _WIN32
4620  FreeLibrary((HMODULE)handle);
4621 #else
4622  dlclose((void*)handle);
4623 #endif
4624 
4625  (void)pContext;
4626 }
4627 
4628 ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol)
4629 {
4630  ma_proc proc;
4631 
4632 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
4633  if (pContext != NULL) {
4634  char message[256];
4635  ma_strappend(message, sizeof(message), "Loading symbol: ", symbol);
4636  ma_log(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
4637  }
4638 #endif
4639 
4640 #ifdef _WIN32
4641  proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
4642 #else
4643 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
4644  #pragma GCC diagnostic push
4645  #pragma GCC diagnostic ignored "-Wpedantic"
4646 #endif
4647  proc = (ma_proc)dlsym((void*)handle, symbol);
4648 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
4649  #pragma GCC diagnostic pop
4650 #endif
4651 #endif
4652 
4653 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_WARNING
4654  if (handle == NULL) {
4655  char message[256];
4656  ma_strappend(message, sizeof(message), "Failed to load symbol: ", symbol);
4657  ma_log(pContext, NULL, MA_LOG_LEVEL_WARNING, message);
4658  }
4659 #endif
4660 
4661  (void)pContext; /* It's possible for pContext to be unused. */
4662  return proc;
4663 }
4664 
4665 
4666 /*******************************************************************************
4667 
4668 Threading
4669 
4670 *******************************************************************************/
4671 #ifdef MA_WIN32
4672 int ma_thread_priority_to_win32(ma_thread_priority priority)
4673 {
4674  switch (priority) {
4675  case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
4676  case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
4677  case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
4678  case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
4679  case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
4680  case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
4681  case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
4682  default: return THREAD_PRIORITY_NORMAL;
4683  }
4684 }
4685 
4686 ma_result ma_thread_create__win32(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
4687 {
4688  pThread->win32.hThread = CreateThread(NULL, 0, entryProc, pData, 0, NULL);
4689  if (pThread->win32.hThread == NULL) {
4691  }
4692 
4693  SetThreadPriority((HANDLE)pThread->win32.hThread, ma_thread_priority_to_win32(pContext->threadPriority));
4694 
4695  return MA_SUCCESS;
4696 }
4697 
4698 void ma_thread_wait__win32(ma_thread* pThread)
4699 {
4700  WaitForSingleObject(pThread->win32.hThread, INFINITE);
4701 }
4702 
4703 void ma_sleep__win32(ma_uint32 milliseconds)
4704 {
4705  Sleep((DWORD)milliseconds);
4706 }
4707 
4708 
4709 ma_result ma_mutex_init__win32(ma_context* pContext, ma_mutex* pMutex)
4710 {
4711  (void)pContext;
4712 
4713  pMutex->win32.hMutex = CreateEventA(NULL, FALSE, TRUE, NULL);
4714  if (pMutex->win32.hMutex == NULL) {
4716  }
4717 
4718  return MA_SUCCESS;
4719 }
4720 
4721 void ma_mutex_uninit__win32(ma_mutex* pMutex)
4722 {
4723  CloseHandle(pMutex->win32.hMutex);
4724 }
4725 
4726 void ma_mutex_lock__win32(ma_mutex* pMutex)
4727 {
4728  WaitForSingleObject(pMutex->win32.hMutex, INFINITE);
4729 }
4730 
4731 void ma_mutex_unlock__win32(ma_mutex* pMutex)
4732 {
4733  SetEvent(pMutex->win32.hMutex);
4734 }
4735 
4736 
4737 ma_result ma_event_init__win32(ma_context* pContext, ma_event* pEvent)
4738 {
4739  (void)pContext;
4740 
4741  pEvent->win32.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
4742  if (pEvent->win32.hEvent == NULL) {
4744  }
4745 
4746  return MA_SUCCESS;
4747 }
4748 
4749 void ma_event_uninit__win32(ma_event* pEvent)
4750 {
4751  CloseHandle(pEvent->win32.hEvent);
4752 }
4753 
4754 ma_bool32 ma_event_wait__win32(ma_event* pEvent)
4755 {
4756  return WaitForSingleObject(pEvent->win32.hEvent, INFINITE) == WAIT_OBJECT_0;
4757 }
4758 
4759 ma_bool32 ma_event_signal__win32(ma_event* pEvent)
4760 {
4761  return SetEvent(pEvent->win32.hEvent);
4762 }
4763 #endif
4764 
4765 
4766 #ifdef MA_POSIX
4767 #include <sched.h>
4768 
4769 typedef int (* ma_pthread_create_proc)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
4770 typedef int (* ma_pthread_join_proc)(pthread_t thread, void **retval);
4771 typedef int (* ma_pthread_mutex_init_proc)(pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr);
4772 typedef int (* ma_pthread_mutex_destroy_proc)(pthread_mutex_t *__mutex);
4773 typedef int (* ma_pthread_mutex_lock_proc)(pthread_mutex_t *__mutex);
4774 typedef int (* ma_pthread_mutex_unlock_proc)(pthread_mutex_t *__mutex);
4775 typedef int (* ma_pthread_cond_init_proc)(pthread_cond_t *__restrict __cond, const pthread_condattr_t *__restrict __cond_attr);
4776 typedef int (* ma_pthread_cond_destroy_proc)(pthread_cond_t *__cond);
4777 typedef int (* ma_pthread_cond_signal_proc)(pthread_cond_t *__cond);
4778 typedef int (* ma_pthread_cond_wait_proc)(pthread_cond_t *__restrict __cond, pthread_mutex_t *__restrict __mutex);
4779 typedef int (* ma_pthread_attr_init_proc)(pthread_attr_t *attr);
4780 typedef int (* ma_pthread_attr_destroy_proc)(pthread_attr_t *attr);
4781 typedef int (* ma_pthread_attr_setschedpolicy_proc)(pthread_attr_t *attr, int policy);
4782 typedef int (* ma_pthread_attr_getschedparam_proc)(const pthread_attr_t *attr, struct sched_param *param);
4783 typedef int (* ma_pthread_attr_setschedparam_proc)(pthread_attr_t *attr, const struct sched_param *param);
4784 
4785 ma_result ma_thread_create__posix(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
4786 {
4787  int result;
4788  pthread_attr_t* pAttr = NULL;
4789 
4790 #if !defined(__EMSCRIPTEN__)
4791  /* Try setting the thread priority. It's not critical if anything fails here. */
4792  pthread_attr_t attr;
4793  if (((ma_pthread_attr_init_proc)pContext->posix.pthread_attr_init)(&attr) == 0) {
4794  int scheduler = -1;
4795  if (pContext->threadPriority == ma_thread_priority_idle) {
4796 #ifdef SCHED_IDLE
4797  if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_IDLE) == 0) {
4798  scheduler = SCHED_IDLE;
4799  }
4800 #endif
4801  } else if (pContext->threadPriority == ma_thread_priority_realtime) {
4802 #ifdef SCHED_FIFO
4803  if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_FIFO) == 0) {
4804  scheduler = SCHED_FIFO;
4805  }
4806 #endif
4807 #ifdef MA_LINUX
4808  } else {
4809  scheduler = sched_getscheduler(0);
4810 #endif
4811  }
4812 
4813  if (scheduler != -1) {
4814  int priorityMin = sched_get_priority_min(scheduler);
4815  int priorityMax = sched_get_priority_max(scheduler);
4816  int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
4817 
4818  struct sched_param sched;
4819  if (((ma_pthread_attr_getschedparam_proc)pContext->posix.pthread_attr_getschedparam)(&attr, &sched) == 0) {
4820  if (pContext->threadPriority == ma_thread_priority_idle) {
4821  sched.sched_priority = priorityMin;
4822  } else if (pContext->threadPriority == ma_thread_priority_realtime) {
4823  sched.sched_priority = priorityMax;
4824  } else {
4825  sched.sched_priority += ((int)pContext->threadPriority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
4826  if (sched.sched_priority < priorityMin) {
4827  sched.sched_priority = priorityMin;
4828  }
4829  if (sched.sched_priority > priorityMax) {
4830  sched.sched_priority = priorityMax;
4831  }
4832  }
4833 
4834  if (((ma_pthread_attr_setschedparam_proc)pContext->posix.pthread_attr_setschedparam)(&attr, &sched) == 0) {
4835  pAttr = &attr;
4836  }
4837  }
4838  }
4839 
4840  ((ma_pthread_attr_destroy_proc)pContext->posix.pthread_attr_destroy)(&attr);
4841  }
4842 #endif
4843 
4844  result = ((ma_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, pAttr, entryProc, pData);
4845  if (result != 0) {
4847  }
4848 
4849  return MA_SUCCESS;
4850 }
4851 
4852 void ma_thread_wait__posix(ma_thread* pThread)
4853 {
4854  ((ma_pthread_join_proc)pThread->pContext->posix.pthread_join)(pThread->posix.thread, NULL);
4855 }
4856 
4857 void ma_sleep__posix(ma_uint32 milliseconds)
4858 {
4859 #ifdef MA_EMSCRIPTEN
4860  (void)milliseconds;
4861  ma_assert(MA_FALSE); /* The Emscripten build should never sleep. */
4862 #else
4863  #if _POSIX_C_SOURCE >= 199309L
4864  struct timespec ts;
4865  ts.tv_sec = milliseconds / 1000000;
4866  ts.tv_nsec = milliseconds % 1000000 * 1000000;
4867  nanosleep(&ts, NULL);
4868  #else
4869  struct timeval tv;
4870  tv.tv_sec = milliseconds / 1000;
4871  tv.tv_usec = milliseconds % 1000 * 1000;
4872  select(0, NULL, NULL, NULL, &tv);
4873  #endif
4874 #endif
4875 }
4876 
4877 
4878 ma_result ma_mutex_init__posix(ma_context* pContext, ma_mutex* pMutex)
4879 {
4880  int result = ((ma_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pMutex->posix.mutex, NULL);
4881  if (result != 0) {
4883  }
4884 
4885  return MA_SUCCESS;
4886 }
4887 
4888 void ma_mutex_uninit__posix(ma_mutex* pMutex)
4889 {
4890  ((ma_pthread_mutex_destroy_proc)pMutex->pContext->posix.pthread_mutex_destroy)(&pMutex->posix.mutex);
4891 }
4892 
4893 void ma_mutex_lock__posix(ma_mutex* pMutex)
4894 {
4895  ((ma_pthread_mutex_lock_proc)pMutex->pContext->posix.pthread_mutex_lock)(&pMutex->posix.mutex);
4896 }
4897 
4898 void ma_mutex_unlock__posix(ma_mutex* pMutex)
4899 {
4900  ((ma_pthread_mutex_unlock_proc)pMutex->pContext->posix.pthread_mutex_unlock)(&pMutex->posix.mutex);
4901 }
4902 
4903 
4904 ma_result ma_event_init__posix(ma_context* pContext, ma_event* pEvent)
4905 {
4906  if (((ma_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pEvent->posix.mutex, NULL) != 0) {
4908  }
4909 
4910  if (((ma_pthread_cond_init_proc)pContext->posix.pthread_cond_init)(&pEvent->posix.condition, NULL) != 0) {
4912  }
4913 
4914  pEvent->posix.value = 0;
4915  return MA_SUCCESS;
4916 }
4917 
4918 void ma_event_uninit__posix(ma_event* pEvent)
4919 {
4920  ((ma_pthread_cond_destroy_proc)pEvent->pContext->posix.pthread_cond_destroy)(&pEvent->posix.condition);
4921  ((ma_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex);
4922 }
4923 
4924 ma_bool32 ma_event_wait__posix(ma_event* pEvent)
4925 {
4926  ((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
4927  {
4928  while (pEvent->posix.value == 0) {
4929  ((ma_pthread_cond_wait_proc)pEvent->pContext->posix.pthread_cond_wait)(&pEvent->posix.condition, &pEvent->posix.mutex);
4930  }
4931  pEvent->posix.value = 0; /* Auto-reset. */
4932  }
4933  ((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
4934 
4935  return MA_TRUE;
4936 }
4937 
4938 ma_bool32 ma_event_signal__posix(ma_event* pEvent)
4939 {
4940  ((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
4941  {
4942  pEvent->posix.value = 1;
4943  ((ma_pthread_cond_signal_proc)pEvent->pContext->posix.pthread_cond_signal)(&pEvent->posix.condition);
4944  }
4945  ((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
4946 
4947  return MA_TRUE;
4948 }
4949 #endif
4950 
4951 ma_result ma_thread_create(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
4952 {
4953  if (pContext == NULL || pThread == NULL || entryProc == NULL) {
4954  return MA_FALSE;
4955  }
4956 
4957  pThread->pContext = pContext;
4958 
4959 #ifdef MA_WIN32
4960  return ma_thread_create__win32(pContext, pThread, entryProc, pData);
4961 #endif
4962 #ifdef MA_POSIX
4963  return ma_thread_create__posix(pContext, pThread, entryProc, pData);
4964 #endif
4965 }
4966 
4967 void ma_thread_wait(ma_thread* pThread)
4968 {
4969  if (pThread == NULL) {
4970  return;
4971  }
4972 
4973 #ifdef MA_WIN32
4974  ma_thread_wait__win32(pThread);
4975 #endif
4976 #ifdef MA_POSIX
4977  ma_thread_wait__posix(pThread);
4978 #endif
4979 }
4980 
4981 void ma_sleep(ma_uint32 milliseconds)
4982 {
4983 #ifdef MA_WIN32
4984  ma_sleep__win32(milliseconds);
4985 #endif
4986 #ifdef MA_POSIX
4987  ma_sleep__posix(milliseconds);
4988 #endif
4989 }
4990 
4991 
4992 ma_result ma_mutex_init(ma_context* pContext, ma_mutex* pMutex)
4993 {
4994  if (pContext == NULL || pMutex == NULL) {
4995  return MA_INVALID_ARGS;
4996  }
4997 
4998  pMutex->pContext = pContext;
4999 
5000 #ifdef MA_WIN32
5001  return ma_mutex_init__win32(pContext, pMutex);
5002 #endif
5003 #ifdef MA_POSIX
5004  return ma_mutex_init__posix(pContext, pMutex);
5005 #endif
5006 }
5007 
5008 void ma_mutex_uninit(ma_mutex* pMutex)
5009 {
5010  if (pMutex == NULL || pMutex->pContext == NULL) {
5011  return;
5012  }
5013 
5014 #ifdef MA_WIN32
5015  ma_mutex_uninit__win32(pMutex);
5016 #endif
5017 #ifdef MA_POSIX
5018  ma_mutex_uninit__posix(pMutex);
5019 #endif
5020 }
5021 
5022 void ma_mutex_lock(ma_mutex* pMutex)
5023 {
5024  if (pMutex == NULL || pMutex->pContext == NULL) {
5025  return;
5026  }
5027 
5028 #ifdef MA_WIN32
5029  ma_mutex_lock__win32(pMutex);
5030 #endif
5031 #ifdef MA_POSIX
5032  ma_mutex_lock__posix(pMutex);
5033 #endif
5034 }
5035 
5036 void ma_mutex_unlock(ma_mutex* pMutex)
5037 {
5038  if (pMutex == NULL || pMutex->pContext == NULL) {
5039  return;
5040 }
5041 
5042 #ifdef MA_WIN32
5043  ma_mutex_unlock__win32(pMutex);
5044 #endif
5045 #ifdef MA_POSIX
5046  ma_mutex_unlock__posix(pMutex);
5047 #endif
5048 }
5049 
5050 
5051 ma_result ma_event_init(ma_context* pContext, ma_event* pEvent)
5052 {
5053  if (pContext == NULL || pEvent == NULL) {
5054  return MA_FALSE;
5055  }
5056 
5057  pEvent->pContext = pContext;
5058 
5059 #ifdef MA_WIN32
5060  return ma_event_init__win32(pContext, pEvent);
5061 #endif
5062 #ifdef MA_POSIX
5063  return ma_event_init__posix(pContext, pEvent);
5064 #endif
5065 }
5066 
5067 void ma_event_uninit(ma_event* pEvent)
5068 {
5069  if (pEvent == NULL || pEvent->pContext == NULL) {
5070  return;
5071  }
5072 
5073 #ifdef MA_WIN32
5074  ma_event_uninit__win32(pEvent);
5075 #endif
5076 #ifdef MA_POSIX
5077  ma_event_uninit__posix(pEvent);
5078 #endif
5079 }
5080 
5081 ma_bool32 ma_event_wait(ma_event* pEvent)
5082 {
5083  if (pEvent == NULL || pEvent->pContext == NULL) {
5084  return MA_FALSE;
5085  }
5086 
5087 #ifdef MA_WIN32
5088  return ma_event_wait__win32(pEvent);
5089 #endif
5090 #ifdef MA_POSIX
5091  return ma_event_wait__posix(pEvent);
5092 #endif
5093 }
5094 
5095 ma_bool32 ma_event_signal(ma_event* pEvent)
5096 {
5097  if (pEvent == NULL || pEvent->pContext == NULL) {
5098  return MA_FALSE;
5099  }
5100 
5101 #ifdef MA_WIN32
5102  return ma_event_signal__win32(pEvent);
5103 #endif
5104 #ifdef MA_POSIX
5105  return ma_event_signal__posix(pEvent);
5106 #endif
5107 }
5108 
5109 
5110 ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
5111 {
5112  /* Normalize the range in case we were given something stupid. */
5113  if (sampleRateMin < MA_MIN_SAMPLE_RATE) {
5114  sampleRateMin = MA_MIN_SAMPLE_RATE;
5115  }
5116  if (sampleRateMax > MA_MAX_SAMPLE_RATE) {
5117  sampleRateMax = MA_MAX_SAMPLE_RATE;
5118  }
5119  if (sampleRateMin > sampleRateMax) {
5120  sampleRateMin = sampleRateMax;
5121  }
5122 
5123  if (sampleRateMin == sampleRateMax) {
5124  return sampleRateMax;
5125  } else {
5126  size_t iStandardRate;
5127  for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
5128  ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
5129  if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
5130  return standardRate;
5131  }
5132  }
5133  }
5134 
5135  /* Should never get here. */
5136  ma_assert(MA_FALSE);
5137  return 0;
5138 }
5139 
5140 ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
5141 {
5142  ma_uint32 closestRate = 0;
5143  ma_uint32 closestDiff = 0xFFFFFFFF;
5144  size_t iStandardRate;
5145 
5146  for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
5147  ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
5148  ma_uint32 diff;
5149 
5150  if (sampleRateIn > standardRate) {
5151  diff = sampleRateIn - standardRate;
5152  } else {
5153  diff = standardRate - sampleRateIn;
5154  }
5155 
5156  if (diff == 0) {
5157  return standardRate; /* The input sample rate is a standard rate. */
5158  }
5159 
5160  if (closestDiff > diff) {
5161  closestDiff = diff;
5162  closestRate = standardRate;
5163  }
5164  }
5165 
5166  return closestRate;
5167 }
5168 
5169 
5170 ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale)
5171 {
5172  return ma_max(1, (ma_uint32)(baseBufferSize*scale));
5173 }
5174 
5176 {
5177  return bufferSizeInFrames / (sampleRate/1000);
5178 }
5179 
5181 {
5182  return bufferSizeInMilliseconds * (sampleRate/1000);
5183 }
5184 
5186 {
5187  if (performanceProfile == ma_performance_profile_low_latency) {
5188  return MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY;
5189  } else {
5190  return MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE;
5191  }
5192 }
5193 
5195 {
5196  ma_uint32 bufferSizeInMilliseconds;
5197  ma_uint32 sampleRateMS;
5198 
5199  bufferSizeInMilliseconds = ma_get_default_buffer_size_in_milliseconds(performanceProfile);
5200  if (bufferSizeInMilliseconds == 0) {
5201  bufferSizeInMilliseconds = 1;
5202  }
5203 
5204  sampleRateMS = (sampleRate/1000);
5205  if (sampleRateMS == 0) {
5206  sampleRateMS = 1;
5207  }
5208 
5209  return bufferSizeInMilliseconds * sampleRateMS;
5210 }
5211 
5212 ma_uint32 ma_get_fragment_size_in_bytes(ma_uint32 bufferSizeInFrames, ma_uint32 periods, ma_format format, ma_uint32 channels)
5213 {
5214  ma_uint32 fragmentSizeInFrames = bufferSizeInFrames / periods;
5215  return fragmentSizeInFrames * ma_get_bytes_per_frame(format, channels);
5216 }
5217 
5218 void ma_zero_pcm_frames(void* p, ma_uint32 frameCount, ma_format format, ma_uint32 channels)
5219 {
5220  ma_zero_memory(p, frameCount * ma_get_bytes_per_frame(format, channels));
5221 }
5222 
5223 
5224 
5225 /* The callback for reading from the client -> DSP -> device. */
5226 ma_uint32 ma_device__on_read_from_client(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
5227 {
5228  ma_device* pDevice = (ma_device*)pUserData;
5229  ma_device_callback_proc onData;
5230 
5231  ma_assert(pDevice != NULL);
5232 
5233  ma_zero_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
5234 
5235  onData = pDevice->onData;
5236  if (onData) {
5237  onData(pDevice, pFramesOut, NULL, frameCount);
5238  return frameCount;
5239  }
5240 
5241  (void)pDSP;
5242  return 0;
5243 }
5244 
5245 /* The PCM converter callback for reading from a buffer. */
5246 ma_uint32 ma_device__pcm_converter__on_read_from_buffer_capture(ma_pcm_converter* pConverter, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
5247 {
5248  ma_device* pDevice = (ma_device*)pUserData;
5249  ma_uint32 framesToRead;
5250  ma_uint32 bytesToRead;
5251 
5252  ma_assert(pDevice != NULL);
5253 
5254  if (pDevice->capture._dspFrameCount == 0) {
5255  return 0; /* Nothing left. */
5256  }
5257 
5258  framesToRead = frameCount;
5259  if (framesToRead > pDevice->capture._dspFrameCount) {
5260  framesToRead = pDevice->capture._dspFrameCount;
5261  }
5262 
5263  bytesToRead = framesToRead * ma_get_bytes_per_frame(pConverter->formatConverterIn.config.formatIn, pConverter->channelRouter.config.channelsIn);
5264  ma_copy_memory(pFramesOut, pDevice->capture._dspFrames, bytesToRead);
5265  pDevice->capture._dspFrameCount -= framesToRead;
5266  pDevice->capture._dspFrames += bytesToRead;
5267 
5268  return framesToRead;
5269 }
5270 
5271 ma_uint32 ma_device__pcm_converter__on_read_from_buffer_playback(ma_pcm_converter* pConverter, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
5272 {
5273  ma_device* pDevice = (ma_device*)pUserData;
5274  ma_uint32 framesToRead;
5275  ma_uint32 bytesToRead;
5276 
5277  ma_assert(pDevice != NULL);
5278 
5279  if (pDevice->playback._dspFrameCount == 0) {
5280  return 0; /* Nothing left. */
5281  }
5282 
5283  framesToRead = frameCount;
5284  if (framesToRead > pDevice->playback._dspFrameCount) {
5285  framesToRead = pDevice->playback._dspFrameCount;
5286  }
5287 
5288  bytesToRead = framesToRead * ma_get_bytes_per_frame(pConverter->formatConverterIn.config.formatIn, pConverter->channelRouter.config.channelsIn);
5289  ma_copy_memory(pFramesOut, pDevice->playback._dspFrames, bytesToRead);
5290  pDevice->playback._dspFrameCount -= framesToRead;
5291  pDevice->playback._dspFrames += bytesToRead;
5292 
5293  return framesToRead;
5294 }
5295 
5296 
5297 
5298 /* A helper function for reading sample data from the client. */
5299 static MA_INLINE void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pSamples)
5300 {
5301  ma_device_callback_proc onData;
5302 
5303  ma_assert(pDevice != NULL);
5304  ma_assert(frameCount > 0);
5305  ma_assert(pSamples != NULL);
5306 
5307  onData = pDevice->onData;
5308  if (onData) {
5309  if (pDevice->playback.converter.isPassthrough) {
5310  ma_zero_pcm_frames(pSamples, frameCount, pDevice->playback.format, pDevice->playback.channels);
5311  onData(pDevice, pSamples, NULL, frameCount);
5312  } else {
5313  ma_pcm_converter_read(&pDevice->playback.converter, pSamples, frameCount);
5314  }
5315  }
5316 }
5317 
5318 /* A helper for sending sample data to the client. */
5319 static MA_INLINE void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCount, const void* pSamples)
5320 {
5321  ma_device_callback_proc onData;
5322 
5323  ma_assert(pDevice != NULL);
5324  ma_assert(frameCount > 0);
5325  ma_assert(pSamples != NULL);
5326 
5327  onData = pDevice->onData;
5328  if (onData) {
5329  if (pDevice->capture.converter.isPassthrough) {
5330  onData(pDevice, NULL, pSamples, frameCount);
5331  } else {
5332  ma_uint8 chunkBuffer[4096];
5333  ma_uint32 chunkFrameCount;
5334 
5335  pDevice->capture._dspFrameCount = frameCount;
5336  pDevice->capture._dspFrames = (const ma_uint8*)pSamples;
5337 
5338  chunkFrameCount = sizeof(chunkBuffer) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
5339 
5340  for (;;) {
5341  ma_uint32 framesJustRead = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, chunkBuffer, chunkFrameCount);
5342  if (framesJustRead == 0) {
5343  break;
5344  }
5345 
5346  onData(pDevice, NULL, chunkBuffer, framesJustRead);
5347 
5348  if (framesJustRead < chunkFrameCount) {
5349  break;
5350  }
5351  }
5352  }
5353  }
5354 }
5355 
5356 static MA_INLINE ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCount, const void* pFramesInInternalFormat, ma_pcm_rb* pRB)
5357 {
5358  ma_result result;
5359 
5360  ma_assert(pDevice != NULL);
5361  ma_assert(frameCount > 0);
5362  ma_assert(pFramesInInternalFormat != NULL);
5363  ma_assert(pRB != NULL);
5364 
5365  pDevice->capture._dspFrameCount = (ma_uint32)frameCount;
5366  pDevice->capture._dspFrames = (const ma_uint8*)pFramesInInternalFormat;
5367 
5368  /* Write to the ring buffer. The ring buffer is in the external format. */
5369  for (;;) {
5370  ma_uint32 framesProcessed;
5371  ma_uint32 framesToProcess = 256;
5372  void* pFramesInExternalFormat;
5373 
5374  result = ma_pcm_rb_acquire_write(pRB, &framesToProcess, &pFramesInExternalFormat);
5375  if (result != MA_SUCCESS) {
5376  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.", result);
5377  break;
5378  }
5379 
5380  if (framesToProcess == 0) {
5382  break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
5383  }
5384  }
5385 
5386  /* Convert. */
5387  framesProcessed = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, pFramesInExternalFormat, framesToProcess);
5388 
5389  result = ma_pcm_rb_commit_write(pRB, framesProcessed, pFramesInExternalFormat);
5390  if (result != MA_SUCCESS) {
5391  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.", result);
5392  break;
5393  }
5394 
5395  if (framesProcessed < framesToProcess) {
5396  break; /* Done. */
5397  }
5398  }
5399 
5400  return MA_SUCCESS;
5401 }
5402 
5403 static MA_INLINE ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
5404 {
5405  ma_result result;
5406  ma_uint8 playbackFramesInExternalFormat[4096];
5407  ma_uint8 silentInputFrames[4096];
5408  ma_uint32 totalFramesToReadFromClient;
5409  ma_uint32 totalFramesReadFromClient;
5410 
5411  ma_assert(pDevice != NULL);
5412  ma_assert(frameCount > 0);
5413  ma_assert(pFramesInInternalFormat != NULL);
5414  ma_assert(pRB != NULL);
5415 
5416  /*
5417  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
5418  the whole frameCount frames we just use silence instead for the input data.
5419  */
5420  ma_zero_memory(silentInputFrames, sizeof(silentInputFrames));
5421 
5422  /* We need to calculate how many output frames are required to be read from the client to completely fill frameCount internal frames. */
5423  totalFramesToReadFromClient = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->playback.internalSampleRate, frameCount); /* ma_pcm_converter_get_required_input_frame_count(&pDevice->playback.converter, (ma_uint32)frameCount); */
5424  totalFramesReadFromClient = 0;
5425  while (totalFramesReadFromClient < totalFramesToReadFromClient && ma_device_is_started(pDevice)) {
5426  ma_uint32 framesRemainingFromClient;
5427  ma_uint32 framesToProcessFromClient;
5428  ma_uint32 inputFrameCount;
5429  void* pInputFrames;
5430 
5431  framesRemainingFromClient = (totalFramesToReadFromClient - totalFramesReadFromClient);
5432  framesToProcessFromClient = sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
5433  if (framesToProcessFromClient > framesRemainingFromClient) {
5434  framesToProcessFromClient = framesRemainingFromClient;
5435  }
5436 
5437  /* We need to grab captured samples before firing the callback. If there's not enough input samples we just pass silence. */
5438  inputFrameCount = framesToProcessFromClient;
5439  result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
5440  if (result == MA_SUCCESS) {
5441  if (inputFrameCount > 0) {
5442  /* Use actual input frames. */
5443  pDevice->onData(pDevice, playbackFramesInExternalFormat, pInputFrames, inputFrameCount);
5444  } else {
5445  if (ma_pcm_rb_pointer_disance(pRB) == 0) {
5446  break; /* Underrun. */
5447  }
5448  }
5449 
5450  /* We're done with the captured samples. */
5451  result = ma_pcm_rb_commit_read(pRB, inputFrameCount, pInputFrames);
5452  if (result != MA_SUCCESS) {
5453  break; /* Don't know what to do here... Just abandon ship. */
5454  }
5455  } else {
5456  /* Use silent input frames. */
5457  inputFrameCount = ma_min(
5458  sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels),
5459  sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)
5460  );
5461 
5462  pDevice->onData(pDevice, playbackFramesInExternalFormat, silentInputFrames, inputFrameCount);
5463  }
5464 
5465  /* We have samples in external format so now we need to convert to internal format and output to the device. */
5466  pDevice->playback._dspFrameCount = inputFrameCount;
5467  pDevice->playback._dspFrames = (const ma_uint8*)playbackFramesInExternalFormat;
5468  ma_pcm_converter_read(&pDevice->playback.converter, pFramesInInternalFormat, inputFrameCount);
5469 
5470  totalFramesReadFromClient += inputFrameCount;
5471  pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, inputFrameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
5472  }
5473 
5474  return MA_SUCCESS;
5475 }
5476 
5477 /* A helper for changing the state of the device. */
5478 static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_uint32 newState)
5479 {
5480  ma_atomic_exchange_32(&pDevice->state, newState);
5481 }
5482 
5483 /* A helper for getting the state of the device. */
5484 static MA_INLINE ma_uint32 ma_device__get_state(ma_device* pDevice)
5485 {
5486  return pDevice->state;
5487 }
5488 
5489 /* A helper for determining whether or not the device is running in async mode. */
5490 static MA_INLINE ma_bool32 ma_device__is_async(ma_device* pDevice)
5491 {
5492  return pDevice->onData != NULL;
5493 }
5494 
5495 
5496 #ifdef MA_WIN32
5497  GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
5498  GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
5499  /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
5500  /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
5501 #endif
5502 
5503 
5504 ma_bool32 ma_context__device_id_equal(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
5505 {
5506  ma_assert(pContext != NULL);
5507 
5508  if (pID0 == pID1) return MA_TRUE;
5509 
5510  if ((pID0 == NULL && pID1 != NULL) ||
5511  (pID0 != NULL && pID1 == NULL)) {
5512  return MA_FALSE;
5513  }
5514 
5515  if (pContext->onDeviceIDEqual) {
5516  return pContext->onDeviceIDEqual(pContext, pID0, pID1);
5517  }
5518 
5519  return MA_FALSE;
5520 }
5521 
5522 
5523 typedef struct
5524 {
5525  ma_device_type deviceType;
5526  const ma_device_id* pDeviceID;
5527  char* pName;
5528  size_t nameBufferSize;
5529  ma_bool32 foundDevice;
5530 } ma_context__try_get_device_name_by_id__enum_callback_data;
5531 
5532 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)
5533 {
5534  ma_context__try_get_device_name_by_id__enum_callback_data* pData = (ma_context__try_get_device_name_by_id__enum_callback_data*)pUserData;
5535  ma_assert(pData != NULL);
5536 
5537  if (pData->deviceType == deviceType) {
5538  if (pContext->onDeviceIDEqual(pContext, pData->pDeviceID, &pDeviceInfo->id)) {
5539  ma_strncpy_s(pData->pName, pData->nameBufferSize, pDeviceInfo->name, (size_t)-1);
5540  pData->foundDevice = MA_TRUE;
5541  }
5542  }
5543 
5544  return !pData->foundDevice;
5545 }
5546 
5547 /*
5548 Generic function for retrieving the name of a device by it's ID.
5549 
5550 This function simply enumerates every device and then retrieves the name of the first device that has the same ID.
5551 */
5552 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)
5553 {
5554  ma_result result;
5555  ma_context__try_get_device_name_by_id__enum_callback_data data;
5556 
5557  ma_assert(pContext != NULL);
5558  ma_assert(pName != NULL);
5559 
5560  if (pDeviceID == NULL) {
5561  return MA_NO_DEVICE;
5562  }
5563 
5564  data.deviceType = deviceType;
5565  data.pDeviceID = pDeviceID;
5566  data.pName = pName;
5567  data.nameBufferSize = nameBufferSize;
5568  data.foundDevice = MA_FALSE;
5569  result = ma_context_enumerate_devices(pContext, ma_context__try_get_device_name_by_id__enum_callback, &data);
5570  if (result != MA_SUCCESS) {
5571  return result;
5572  }
5573 
5574  if (!data.foundDevice) {
5575  return MA_NO_DEVICE;
5576  } else {
5577  return MA_SUCCESS;
5578  }
5579 }
5580 
5581 
5582 ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */
5583 {
5584  ma_uint32 i;
5585  for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
5586  if (g_maFormatPriorities[i] == format) {
5587  return i;
5588  }
5589  }
5590 
5591  /* Getting here means the format could not be found or is equal to ma_format_unknown. */
5592  return (ma_uint32)-1;
5593 }
5594 
5595 void ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
5596 
5597 
5598 /*******************************************************************************
5599 
5600 Null Backend
5601 
5602 *******************************************************************************/
5603 #ifdef MA_HAS_NULL
5604 
5605 #define MA_DEVICE_OP_NONE__NULL 0
5606 #define MA_DEVICE_OP_START__NULL 1
5607 #define MA_DEVICE_OP_SUSPEND__NULL 2
5608 #define MA_DEVICE_OP_KILL__NULL 3
5609 
5610 ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
5611 {
5612  ma_device* pDevice = (ma_device*)pData;
5613  ma_assert(pDevice != NULL);
5614 
5615  for (;;) { /* Keep the thread alive until the device is uninitialized. */
5616  /* Wait for an operation to be requested. */
5617  ma_event_wait(&pDevice->null_device.operationEvent);
5618 
5619  /* At this point an event should have been triggered. */
5620 
5621  /* Starting the device needs to put the thread into a loop. */
5622  if (pDevice->null_device.operation == MA_DEVICE_OP_START__NULL) {
5623  ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
5624 
5625  /* Reset the timer just in case. */
5626  ma_timer_init(&pDevice->null_device.timer);
5627 
5628  /* Keep looping until an operation has been requested. */
5629  while (pDevice->null_device.operation != MA_DEVICE_OP_NONE__NULL && pDevice->null_device.operation != MA_DEVICE_OP_START__NULL) {
5630  ma_sleep(10); /* Don't hog the CPU. */
5631  }
5632 
5633  /* Getting here means a suspend or kill operation has been requested. */
5634  ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
5635  ma_event_signal(&pDevice->null_device.operationCompletionEvent);
5636  continue;
5637  }
5638 
5639  /* Suspending the device means we need to stop the timer and just continue the loop. */
5640  if (pDevice->null_device.operation == MA_DEVICE_OP_SUSPEND__NULL) {
5641  ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
5642 
5643  /* We need to add the current run time to the prior run time, then reset the timer. */
5644  pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
5645  ma_timer_init(&pDevice->null_device.timer);
5646 
5647  /* We're done. */
5648  ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
5649  ma_event_signal(&pDevice->null_device.operationCompletionEvent);
5650  continue;
5651  }
5652 
5653  /* Killing the device means we need to get out of this loop so that this thread can terminate. */
5654  if (pDevice->null_device.operation == MA_DEVICE_OP_KILL__NULL) {
5655  ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
5656  ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
5657  ma_event_signal(&pDevice->null_device.operationCompletionEvent);
5658  break;
5659  }
5660 
5661  /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
5662  if (pDevice->null_device.operation == MA_DEVICE_OP_NONE__NULL) {
5663  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). */
5664  ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_INVALID_OPERATION);
5665  ma_event_signal(&pDevice->null_device.operationCompletionEvent);
5666  continue; /* Continue the loop. Don't terminate. */
5667  }
5668  }
5669 
5670  return (ma_thread_result)0;
5671 }
5672 
5673 ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
5674 {
5675  ma_atomic_exchange_32(&pDevice->null_device.operation, operation);
5676  if (!ma_event_signal(&pDevice->null_device.operationEvent)) {
5677  return MA_ERROR;
5678  }
5679 
5680  if (!ma_event_wait(&pDevice->null_device.operationCompletionEvent)) {
5681  return MA_ERROR;
5682  }
5683 
5684  return pDevice->null_device.operationResult;
5685 }
5686 
5687 ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
5688 {
5689  ma_uint32 internalSampleRate;
5690  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
5691  internalSampleRate = pDevice->capture.internalSampleRate;
5692  } else {
5693  internalSampleRate = pDevice->playback.internalSampleRate;
5694  }
5695 
5696 
5697  return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
5698 }
5699 
5700 ma_bool32 ma_context_is_device_id_equal__null(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
5701 {
5702  ma_assert(pContext != NULL);
5703  ma_assert(pID0 != NULL);
5704  ma_assert(pID1 != NULL);
5705  (void)pContext;
5706 
5707  return pID0->nullbackend == pID1->nullbackend;
5708 }
5709 
5710 ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
5711 {
5712  ma_bool32 cbResult = MA_TRUE;
5713 
5714  ma_assert(pContext != NULL);
5715  ma_assert(callback != NULL);
5716 
5717  /* Playback. */
5718  if (cbResult) {
5719  ma_device_info deviceInfo;
5720  ma_zero_object(&deviceInfo);
5721  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
5722  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
5723  }
5724 
5725  /* Capture. */
5726  if (cbResult) {
5727  ma_device_info deviceInfo;
5728  ma_zero_object(&deviceInfo);
5729  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
5730  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
5731  }
5732 
5733  return MA_SUCCESS;
5734 }
5735 
5736 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)
5737 {
5738  ma_uint32 iFormat;
5739 
5740  ma_assert(pContext != NULL);
5741 
5742  if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
5743  return MA_NO_DEVICE; /* Don't know the device. */
5744  }
5745 
5746  /* Name / Description */
5747  if (deviceType == ma_device_type_playback) {
5748  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
5749  } else {
5750  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
5751  }
5752 
5753  /* Support everything on the null backend. */
5754  pDeviceInfo->formatCount = ma_format_count - 1; /* Minus one because we don't want to include ma_format_unknown. */
5755  for (iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
5756  pDeviceInfo->formats[iFormat] = (ma_format)(iFormat + 1); /* +1 to skip over ma_format_unknown. */
5757  }
5758 
5759  pDeviceInfo->minChannels = 1;
5760  pDeviceInfo->maxChannels = MA_MAX_CHANNELS;
5761  pDeviceInfo->minSampleRate = MA_SAMPLE_RATE_8000;
5762  pDeviceInfo->maxSampleRate = MA_SAMPLE_RATE_384000;
5763 
5764  (void)pContext;
5765  (void)shareMode;
5766  return MA_SUCCESS;
5767 }
5768 
5769 
5770 void ma_device_uninit__null(ma_device* pDevice)
5771 {
5772  ma_assert(pDevice != NULL);
5773 
5774  /* Keep it clean and wait for the device thread to finish before returning. */
5775  ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
5776 
5777  /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */
5778  ma_event_uninit(&pDevice->null_device.operationCompletionEvent);
5779  ma_event_uninit(&pDevice->null_device.operationEvent);
5780 }
5781 
5782 ma_result ma_device_init__null(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
5783 {
5784  ma_result result;
5785  ma_uint32 bufferSizeInFrames;
5786 
5787  ma_assert(pDevice != NULL);
5788 
5789  ma_zero_object(&pDevice->null_device);
5790 
5791  bufferSizeInFrames = pConfig->bufferSizeInFrames;
5792  if (bufferSizeInFrames == 0) {
5794  }
5795 
5796  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
5797  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "NULL Capture Device", (size_t)-1);
5798  pDevice->capture.internalFormat = pConfig->capture.format;
5799  pDevice->capture.internalChannels = pConfig->capture.channels;
5800  ma_channel_map_copy(pDevice->capture.internalChannelMap, pConfig->capture.channelMap, pConfig->capture.channels);
5801  pDevice->capture.internalBufferSizeInFrames = bufferSizeInFrames;
5802  pDevice->capture.internalPeriods = pConfig->periods;
5803  }
5804  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
5805  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "NULL Playback Device", (size_t)-1);
5806  pDevice->playback.internalFormat = pConfig->playback.format;
5807  pDevice->playback.internalChannels = pConfig->playback.channels;
5808  ma_channel_map_copy(pDevice->playback.internalChannelMap, pConfig->playback.channelMap, pConfig->playback.channels);
5809  pDevice->playback.internalBufferSizeInFrames = bufferSizeInFrames;
5810  pDevice->playback.internalPeriods = pConfig->periods;
5811  }
5812 
5813  /*
5814  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
5815  first period is "written" to it, and then stopped in ma_device_stop__null().
5816  */
5817  result = ma_event_init(pContext, &pDevice->null_device.operationEvent);
5818  if (result != MA_SUCCESS) {
5819  return result;
5820  }
5821 
5822  result = ma_event_init(pContext, &pDevice->null_device.operationCompletionEvent);
5823  if (result != MA_SUCCESS) {
5824  return result;
5825  }
5826 
5827  result = ma_thread_create(pContext, &pDevice->thread, ma_device_thread__null, pDevice);
5828  if (result != MA_SUCCESS) {
5829  return result;
5830  }
5831 
5832  return MA_SUCCESS;
5833 }
5834 
5835 ma_result ma_device_start__null(ma_device* pDevice)
5836 {
5837  ma_assert(pDevice != NULL);
5838 
5839  ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
5840 
5841  ma_atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE);
5842  return MA_SUCCESS;
5843 }
5844 
5845 ma_result ma_device_stop__null(ma_device* pDevice)
5846 {
5847  ma_assert(pDevice != NULL);
5848 
5849  ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
5850 
5851  ma_atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE);
5852  return MA_SUCCESS;
5853 }
5854 
5855 ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount)
5856 {
5857  ma_result result = MA_SUCCESS;
5858  ma_uint32 totalPCMFramesProcessed;
5859  ma_bool32 wasStartedOnEntry;
5860 
5861  wasStartedOnEntry = pDevice->null_device.isStarted;
5862 
5863  /* Keep going until everything has been read. */
5864  totalPCMFramesProcessed = 0;
5865  while (totalPCMFramesProcessed < frameCount) {
5866  ma_uint64 targetFrame;
5867 
5868  /* If there are any frames remaining in the current period, consume those first. */
5869  if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) {
5870  ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
5871  ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback;
5872  if (framesToProcess > framesRemaining) {
5873  framesToProcess = framesRemaining;
5874  }
5875 
5876  /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */
5877  (void)pPCMFrames;
5878 
5879  pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
5880  totalPCMFramesProcessed += framesToProcess;
5881  }
5882 
5883  /* 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. */
5884  if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) {
5885  pDevice->null_device.currentPeriodFramesRemainingPlayback = 0;
5886 
5887  if (!pDevice->null_device.isStarted && !wasStartedOnEntry) {
5888  result = ma_device_start__null(pDevice);
5889  if (result != MA_SUCCESS) {
5890  break;
5891  }
5892  }
5893  }
5894 
5895  /* If we've consumed the whole buffer we can return now. */
5896  ma_assert(totalPCMFramesProcessed <= frameCount);
5897  if (totalPCMFramesProcessed == frameCount) {
5898  break;
5899  }
5900 
5901  /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
5902  targetFrame = pDevice->null_device.lastProcessedFramePlayback;
5903  for (;;) {
5904  ma_uint64 currentFrame;
5905 
5906  /* Stop waiting if the device has been stopped. */
5907  if (!pDevice->null_device.isStarted) {
5908  break;
5909  }
5910 
5911  currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
5912  if (currentFrame >= targetFrame) {
5913  break;
5914  }
5915 
5916  /* Getting here means we haven't yet reached the target sample, so continue waiting. */
5917  ma_sleep(10);
5918  }
5919 
5920  pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
5921  pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
5922  }
5923 
5924  return result;
5925 }
5926 
5927 ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount)
5928 {
5929  ma_result result = MA_SUCCESS;
5930  ma_uint32 totalPCMFramesProcessed;
5931 
5932  /* The device needs to be started immediately. */
5933  if (!pDevice->null_device.isStarted) {
5934  result = ma_device_start__null(pDevice);
5935  if (result != MA_SUCCESS) {
5936  return result;
5937  }
5938  }
5939 
5940  /* Keep going until everything has been read. */
5941  totalPCMFramesProcessed = 0;
5942  while (totalPCMFramesProcessed < frameCount) {
5943  ma_uint64 targetFrame;
5944 
5945  /* If there are any frames remaining in the current period, consume those first. */
5946  if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) {
5947  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
5948  ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
5949  ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture;
5950  if (framesToProcess > framesRemaining) {
5951  framesToProcess = framesRemaining;
5952  }
5953 
5954  /* We need to ensured the output buffer is zeroed. */
5955  ma_zero_memory(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);
5956 
5957  pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;
5958  totalPCMFramesProcessed += framesToProcess;
5959  }
5960 
5961  /* 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. */
5962  if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) {
5963  pDevice->null_device.currentPeriodFramesRemainingCapture = 0;
5964  }
5965 
5966  /* If we've consumed the whole buffer we can return now. */
5967  ma_assert(totalPCMFramesProcessed <= frameCount);
5968  if (totalPCMFramesProcessed == frameCount) {
5969  break;
5970  }
5971 
5972  /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
5973  targetFrame = pDevice->null_device.lastProcessedFrameCapture + (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods);
5974  for (;;) {
5975  ma_uint64 currentFrame;
5976 
5977  /* Stop waiting if the device has been stopped. */
5978  if (!pDevice->null_device.isStarted) {
5979  break;
5980  }
5981 
5982  currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
5983  if (currentFrame >= targetFrame) {
5984  break;
5985  }
5986 
5987  /* Getting here means we haven't yet reached the target sample, so continue waiting. */
5988  ma_sleep(10);
5989  }
5990 
5991  pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
5992  pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
5993  }
5994 
5995  return result;
5996 }
5997 
5998 ma_result ma_context_uninit__null(ma_context* pContext)
5999 {
6000  ma_assert(pContext != NULL);
6001  ma_assert(pContext->backend == ma_backend_null);
6002 
6003  (void)pContext;
6004  return MA_SUCCESS;
6005 }
6006 
6007 ma_result ma_context_init__null(const ma_context_config* pConfig, ma_context* pContext)
6008 {
6009  ma_assert(pContext != NULL);
6010 
6011  (void)pConfig;
6012 
6013  pContext->onUninit = ma_context_uninit__null;
6014  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__null;
6015  pContext->onEnumDevices = ma_context_enumerate_devices__null;
6016  pContext->onGetDeviceInfo = ma_context_get_device_info__null;
6017  pContext->onDeviceInit = ma_device_init__null;
6018  pContext->onDeviceUninit = ma_device_uninit__null;
6019  pContext->onDeviceStart = ma_device_start__null;
6020  pContext->onDeviceStop = ma_device_stop__null;
6021  pContext->onDeviceWrite = ma_device_write__null;
6022  pContext->onDeviceRead = ma_device_read__null;
6023 
6024  /* The null backend always works. */
6025  return MA_SUCCESS;
6026 }
6027 #endif
6028 
6029 
6030 /*******************************************************************************
6031 
6032 WIN32 COMMON
6033 
6034 *******************************************************************************/
6035 #if defined(MA_WIN32)
6036 #if defined(MA_WIN32_DESKTOP)
6037  #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit)
6038  #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
6039  #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
6040  #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
6041  #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
6042 #else
6043  #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
6044  #define ma_CoUninitialize(pContext) CoUninitialize()
6045  #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
6046  #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
6047  #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
6048 #endif
6049 
6050 #if !defined(MAXULONG_PTR)
6051 typedef size_t DWORD_PTR;
6052 #endif
6053 
6054 #if !defined(WAVE_FORMAT_44M08)
6055 #define WAVE_FORMAT_44M08 0x00000100
6056 #define WAVE_FORMAT_44S08 0x00000200
6057 #define WAVE_FORMAT_44M16 0x00000400
6058 #define WAVE_FORMAT_44S16 0x00000800
6059 #define WAVE_FORMAT_48M08 0x00001000
6060 #define WAVE_FORMAT_48S08 0x00002000
6061 #define WAVE_FORMAT_48M16 0x00004000
6062 #define WAVE_FORMAT_48S16 0x00008000
6063 #define WAVE_FORMAT_96M08 0x00010000
6064 #define WAVE_FORMAT_96S08 0x00020000
6065 #define WAVE_FORMAT_96M16 0x00040000
6066 #define WAVE_FORMAT_96S16 0x00080000
6067 #endif
6068 
6069 #ifndef SPEAKER_FRONT_LEFT
6070 #define SPEAKER_FRONT_LEFT 0x1
6071 #define SPEAKER_FRONT_RIGHT 0x2
6072 #define SPEAKER_FRONT_CENTER 0x4
6073 #define SPEAKER_LOW_FREQUENCY 0x8
6074 #define SPEAKER_BACK_LEFT 0x10
6075 #define SPEAKER_BACK_RIGHT 0x20
6076 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
6077 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
6078 #define SPEAKER_BACK_CENTER 0x100
6079 #define SPEAKER_SIDE_LEFT 0x200
6080 #define SPEAKER_SIDE_RIGHT 0x400
6081 #define SPEAKER_TOP_CENTER 0x800
6082 #define SPEAKER_TOP_FRONT_LEFT 0x1000
6083 #define SPEAKER_TOP_FRONT_CENTER 0x2000
6084 #define SPEAKER_TOP_FRONT_RIGHT 0x4000
6085 #define SPEAKER_TOP_BACK_LEFT 0x8000
6086 #define SPEAKER_TOP_BACK_CENTER 0x10000
6087 #define SPEAKER_TOP_BACK_RIGHT 0x20000
6088 #endif
6089 
6090 /*
6091 The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We
6092 define our own implementation in this case.
6093 */
6094 #if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__)
6095 typedef struct
6096 {
6097  WAVEFORMATEX Format;
6098  union
6099  {
6100  WORD wValidBitsPerSample;
6101  WORD wSamplesPerBlock;
6102  WORD wReserved;
6103  } Samples;
6104  DWORD dwChannelMask;
6105  GUID SubFormat;
6106 } WAVEFORMATEXTENSIBLE;
6107 #endif
6108 
6109 #ifndef WAVE_FORMAT_EXTENSIBLE
6110 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
6111 #endif
6112 
6113 #ifndef WAVE_FORMAT_IEEE_FLOAT
6114 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
6115 #endif
6116 
6117 GUID MA_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
6118 
6119 /* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
6120 ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
6121 {
6122  switch (id)
6123  {
6124  case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
6125  case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
6126  case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
6127  case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
6128  case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
6129  case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
6130  case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
6131  case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
6132  case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
6133  case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
6134  case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
6135  case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
6136  case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
6137  case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
6138  case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
6139  case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
6140  case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
6141  case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
6142  default: return 0;
6143  }
6144 }
6145 
6146 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */
6147 DWORD ma_channel_id_to_win32(DWORD id)
6148 {
6149  switch (id)
6150  {
6151  case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER;
6152  case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT;
6153  case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT;
6154  case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER;
6155  case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY;
6156  case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT;
6157  case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT;
6158  case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER;
6159  case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;
6160  case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER;
6161  case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT;
6162  case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT;
6163  case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER;
6164  case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT;
6165  case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER;
6166  case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT;
6167  case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT;
6168  case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER;
6169  case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT;
6170  default: return 0;
6171  }
6172 }
6173 
6174 /* Converts a channel mapping to a Win32-style channel mask. */
6175 DWORD ma_channel_map_to_channel_mask__win32(const ma_channel channelMap[MA_MAX_CHANNELS], ma_uint32 channels)
6176 {
6177  DWORD dwChannelMask = 0;
6178  ma_uint32 iChannel;
6179 
6180  for (iChannel = 0; iChannel < channels; ++iChannel) {
6181  dwChannelMask |= ma_channel_id_to_win32(channelMap[iChannel]);
6182  }
6183 
6184  return dwChannelMask;
6185 }
6186 
6187 /* Converts a Win32-style channel mask to a miniaudio channel map. */
6188 void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
6189 {
6190  if (channels == 1 && dwChannelMask == 0) {
6191  channelMap[0] = MA_CHANNEL_MONO;
6192  } else if (channels == 2 && dwChannelMask == 0) {
6193  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
6194  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
6195  } else {
6196  if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
6197  channelMap[0] = MA_CHANNEL_MONO;
6198  } else {
6199  /* Just iterate over each bit. */
6200  ma_uint32 iChannel = 0;
6201  ma_uint32 iBit;
6202 
6203  for (iBit = 0; iBit < 32; ++iBit) {
6204  DWORD bitValue = (dwChannelMask & (1UL << iBit));
6205  if (bitValue != 0) {
6206  /* The bit is set. */
6207  channelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
6208  iChannel += 1;
6209  }
6210  }
6211  }
6212  }
6213 }
6214 
6215 #ifdef __cplusplus
6216 ma_bool32 ma_is_guid_equal(const void* a, const void* b)
6217 {
6218  return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);
6219 }
6220 #else
6221 #define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
6222 #endif
6223 
6224 ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF)
6225 {
6226  ma_assert(pWF != NULL);
6227 
6228  if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
6229  const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF;
6230  if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
6231  if (pWFEX->Samples.wValidBitsPerSample == 32) {
6232  return ma_format_s32;
6233  }
6234  if (pWFEX->Samples.wValidBitsPerSample == 24) {
6235  if (pWFEX->Format.wBitsPerSample == 32) {
6236  /*return ma_format_s24_32;*/
6237  }
6238  if (pWFEX->Format.wBitsPerSample == 24) {
6239  return ma_format_s24;
6240  }
6241  }
6242  if (pWFEX->Samples.wValidBitsPerSample == 16) {
6243  return ma_format_s16;
6244  }
6245  if (pWFEX->Samples.wValidBitsPerSample == 8) {
6246  return ma_format_u8;
6247  }
6248  }
6249  if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
6250  if (pWFEX->Samples.wValidBitsPerSample == 32) {
6251  return ma_format_f32;
6252  }
6253  /*
6254  if (pWFEX->Samples.wValidBitsPerSample == 64) {
6255  return ma_format_f64;
6256  }
6257  */
6258  }
6259  } else {
6260  if (pWF->wFormatTag == WAVE_FORMAT_PCM) {
6261  if (pWF->wBitsPerSample == 32) {
6262  return ma_format_s32;
6263  }
6264  if (pWF->wBitsPerSample == 24) {
6265  return ma_format_s24;
6266  }
6267  if (pWF->wBitsPerSample == 16) {
6268  return ma_format_s16;
6269  }
6270  if (pWF->wBitsPerSample == 8) {
6271  return ma_format_u8;
6272  }
6273  }
6274  if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
6275  if (pWF->wBitsPerSample == 32) {
6276  return ma_format_f32;
6277  }
6278  if (pWF->wBitsPerSample == 64) {
6279  /*return ma_format_f64;*/
6280  }
6281  }
6282  }
6283 
6284  return ma_format_unknown;
6285 }
6286 #endif
6287 
6288 
6289 /*******************************************************************************
6290 
6291 WASAPI Backend
6292 
6293 *******************************************************************************/
6294 #ifdef MA_HAS_WASAPI
6295 #if 0
6296 #if defined(_MSC_VER)
6297  #pragma warning(push)
6298  #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */
6299 #endif
6300 #include <audioclient.h>
6301 #include <mmdeviceapi.h>
6302 #if defined(_MSC_VER)
6303  #pragma warning(pop)
6304 #endif
6305 #endif /* 0 */
6306 
6307 
6308 /* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */
6309 #define MA_WIN32_WINNT_VISTA 0x0600
6310 #define MA_VER_MINORVERSION 0x01
6311 #define MA_VER_MAJORVERSION 0x02
6312 #define MA_VER_SERVICEPACKMAJOR 0x20
6313 #define MA_VER_GREATER_EQUAL 0x03
6314 
6315 typedef struct {
6316  DWORD dwOSVersionInfoSize;
6317  DWORD dwMajorVersion;
6318  DWORD dwMinorVersion;
6319  DWORD dwBuildNumber;
6320  DWORD dwPlatformId;
6321  WCHAR szCSDVersion[128];
6322  WORD wServicePackMajor;
6323  WORD wServicePackMinor;
6324  WORD wSuiteMask;
6325  BYTE wProductType;
6326  BYTE wReserved;
6327 } ma_OSVERSIONINFOEXW;
6328 
6329 typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
6330 typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
6331 
6332 
6333 #ifndef PROPERTYKEY_DEFINED
6334 #define PROPERTYKEY_DEFINED
6335 typedef struct
6336 {
6337  GUID fmtid;
6338  DWORD pid;
6339 } PROPERTYKEY;
6340 #endif
6341 
6342 /* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */
6343 static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp)
6344 {
6345  ma_zero_object(pProp);
6346 }
6347 
6348 
6349 const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};
6350 const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0};
6351 
6352 const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */
6353 const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */
6354 
6355 const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */
6356 const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */
6357 const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */
6358 const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */
6359 const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */
6360 const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */
6361 #ifndef MA_WIN32_DESKTOP
6362 const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */
6363 const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */
6364 const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */
6365 #endif
6366 
6367 const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */
6368 const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */
6369 #ifdef __cplusplus
6370 #define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance
6371 #define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance
6372 #else
6373 #define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance
6374 #define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance
6375 #endif
6376 
6377 typedef struct ma_IUnknown ma_IUnknown;
6378 #ifdef MA_WIN32_DESKTOP
6379 #define MA_MM_DEVICE_STATE_ACTIVE 1
6380 #define MA_MM_DEVICE_STATE_DISABLED 2
6381 #define MA_MM_DEVICE_STATE_NOTPRESENT 4
6382 #define MA_MM_DEVICE_STATE_UNPLUGGED 8
6383 
6384 typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator;
6385 typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection;
6386 typedef struct ma_IMMDevice ma_IMMDevice;
6387 #else
6388 typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;
6389 typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation;
6390 #endif
6391 typedef struct ma_IPropertyStore ma_IPropertyStore;
6392 typedef struct ma_IAudioClient ma_IAudioClient;
6393 typedef struct ma_IAudioClient2 ma_IAudioClient2;
6394 typedef struct ma_IAudioClient3 ma_IAudioClient3;
6395 typedef struct ma_IAudioRenderClient ma_IAudioRenderClient;
6396 typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient;
6397 
6398 typedef ma_int64 MA_REFERENCE_TIME;
6399 
6400 #define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000
6401 #define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000
6402 #define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
6403 #define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
6404 #define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
6405 #define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
6406 #define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
6407 #define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000
6408 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000
6409 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000
6410 
6411 /* We only care about a few error codes. */
6412 #define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD (-2004287456)
6413 #define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED (-2004287463)
6414 #define MA_AUDCLNT_S_BUFFER_EMPTY (143196161)
6415 #define MA_AUDCLNT_E_DEVICE_IN_USE (-2004287478)
6416 
6417 typedef enum
6418 {
6419  ma_eRender = 0,
6420  ma_eCapture = 1,
6421  ma_eAll = 2
6422 } ma_EDataFlow;
6423 
6424 typedef enum
6425 {
6426  ma_eConsole = 0,
6427  ma_eMultimedia = 1,
6428  ma_eCommunications = 2
6429 } ma_ERole;
6430 
6431 typedef enum
6432 {
6433  MA_AUDCLNT_SHAREMODE_SHARED,
6434  MA_AUDCLNT_SHAREMODE_EXCLUSIVE
6435 } MA_AUDCLNT_SHAREMODE;
6436 
6437 typedef enum
6438 {
6439  MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */
6440 } MA_AUDIO_STREAM_CATEGORY;
6441 
6442 typedef struct
6443 {
6444  UINT32 cbSize;
6445  BOOL bIsOffload;
6446  MA_AUDIO_STREAM_CATEGORY eCategory;
6447 } ma_AudioClientProperties;
6448 
6449 /* IUnknown */
6450 typedef struct
6451 {
6452  /* IUnknown */
6453  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);
6454  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis);
6455  ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis);
6456 } ma_IUnknownVtbl;
6457 struct ma_IUnknown
6458 {
6459  ma_IUnknownVtbl* lpVtbl;
6460 };
6461 HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6462 ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6463 ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); }
6464 
6465 #ifdef MA_WIN32_DESKTOP
6466  /* IMMNotificationClient */
6467  typedef struct
6468  {
6469  /* IUnknown */
6470  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);
6471  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis);
6472  ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis);
6473 
6474  /* IMMNotificationClient */
6475  HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState);
6476  HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
6477  HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
6478  HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID);
6479  HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key);
6480  } ma_IMMNotificationClientVtbl;
6481 
6482  /* IMMDeviceEnumerator */
6483  typedef struct
6484  {
6485  /* IUnknown */
6486  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);
6487  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis);
6488  ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis);
6489 
6490  /* IMMDeviceEnumerator */
6491  HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);
6492  HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);
6493  HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice);
6494  HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
6495  HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
6496  } ma_IMMDeviceEnumeratorVtbl;
6497  struct ma_IMMDeviceEnumerator
6498  {
6499  ma_IMMDeviceEnumeratorVtbl* lpVtbl;
6500  };
6501  HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6502  ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6503  ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); }
6504  HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); }
6505  HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); }
6506  HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }
6507  HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }
6508  HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }
6509 
6510 
6511  /* IMMDeviceCollection */
6512  typedef struct
6513  {
6514  /* IUnknown */
6515  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);
6516  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis);
6517  ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis);
6518 
6519  /* IMMDeviceCollection */
6520  HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);
6521  HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);
6522  } ma_IMMDeviceCollectionVtbl;
6523  struct ma_IMMDeviceCollection
6524  {
6525  ma_IMMDeviceCollectionVtbl* lpVtbl;
6526  };
6527  HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6528  ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6529  ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); }
6530  HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); }
6531  HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }
6532 
6533 
6534  /* IMMDevice */
6535  typedef struct
6536  {
6537  /* IUnknown */
6538  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);
6539  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis);
6540  ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis);
6541 
6542  /* IMMDevice */
6543  HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface);
6544  HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);
6545  HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID);
6546  HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState);
6547  } ma_IMMDeviceVtbl;
6548  struct ma_IMMDevice
6549  {
6550  ma_IMMDeviceVtbl* lpVtbl;
6551  };
6552  HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6553  ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6554  ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); }
6555  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); }
6556  HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }
6557  HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); }
6558  HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); }
6559 #else
6560  /* IActivateAudioInterfaceAsyncOperation */
6561  typedef struct
6562  {
6563  /* IUnknown */
6564  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);
6565  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
6566  ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
6567 
6568  /* IActivateAudioInterfaceAsyncOperation */
6569  HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);
6570  } ma_IActivateAudioInterfaceAsyncOperationVtbl;
6571  struct ma_IActivateAudioInterfaceAsyncOperation
6572  {
6573  ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;
6574  };
6575  HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6576  ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6577  ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); }
6578  HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }
6579 #endif
6580 
6581 /* IPropertyStore */
6582 typedef struct
6583 {
6584  /* IUnknown */
6585  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);
6586  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis);
6587  ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis);
6588 
6589  /* IPropertyStore */
6590  HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);
6591  HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);
6592  HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar);
6593  HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar);
6594  HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis);
6595 } ma_IPropertyStoreVtbl;
6596 struct ma_IPropertyStore
6597 {
6598  ma_IPropertyStoreVtbl* lpVtbl;
6599 };
6600 HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6601 ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6602 ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); }
6603 HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); }
6604 HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }
6605 HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }
6606 HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); }
6607 HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); }
6608 
6609 
6610 /* IAudioClient */
6611 typedef struct
6612 {
6613  /* IUnknown */
6614  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);
6615  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis);
6616  ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis);
6617 
6618  /* IAudioClient */
6619  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);
6620  HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);
6621  HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);
6622  HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);
6623  HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
6624  HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat);
6625  HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
6626  HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis);
6627  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis);
6628  HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis);
6629  HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle);
6630  HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp);
6631 } ma_IAudioClientVtbl;
6632 struct ma_IAudioClient
6633 {
6634  ma_IAudioClientVtbl* lpVtbl;
6635 };
6636 HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6637 ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6638 ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); }
6639 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); }
6640 HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
6641 HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
6642 HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
6643 HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
6644 HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
6645 HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
6646 HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); }
6647 HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); }
6648 HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); }
6649 HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
6650 HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
6651 
6652 /* IAudioClient2 */
6653 typedef struct
6654 {
6655  /* IUnknown */
6656  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);
6657  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis);
6658  ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis);
6659 
6660  /* IAudioClient */
6661  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);
6662  HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);
6663  HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);
6664  HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);
6665  HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
6666  HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat);
6667  HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
6668  HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis);
6669  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis);
6670  HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis);
6671  HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle);
6672  HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp);
6673 
6674  /* IAudioClient2 */
6675  HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
6676  HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);
6677  HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
6678 } ma_IAudioClient2Vtbl;
6679 struct ma_IAudioClient2
6680 {
6681  ma_IAudioClient2Vtbl* lpVtbl;
6682 };
6683 HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6684 ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6685 ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); }
6686 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); }
6687 HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
6688 HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
6689 HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
6690 HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
6691 HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
6692 HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
6693 HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); }
6694 HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); }
6695 HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); }
6696 HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
6697 HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
6698 HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
6699 HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
6700 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); }
6701 
6702 
6703 /* IAudioClient3 */
6704 typedef struct
6705 {
6706  /* IUnknown */
6707  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);
6708  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis);
6709  ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis);
6710 
6711  /* IAudioClient */
6712  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);
6713  HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);
6714  HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);
6715  HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);
6716  HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
6717  HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat);
6718  HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
6719  HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis);
6720  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis);
6721  HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis);
6722  HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle);
6723  HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp);
6724 
6725  /* IAudioClient2 */
6726  HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
6727  HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);
6728  HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
6729 
6730  /* IAudioClient3 */
6731  HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames);
6732  HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames);
6733  HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
6734 } ma_IAudioClient3Vtbl;
6735 struct ma_IAudioClient3
6736 {
6737  ma_IAudioClient3Vtbl* lpVtbl;
6738 };
6739 HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6740 ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6741 ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); }
6742 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); }
6743 HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
6744 HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
6745 HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
6746 HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
6747 HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
6748 HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
6749 HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); }
6750 HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); }
6751 HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); }
6752 HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
6753 HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
6754 HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
6755 HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
6756 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); }
6757 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); }
6758 HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }
6759 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); }
6760 
6761 
6762 /* IAudioRenderClient */
6763 typedef struct
6764 {
6765  /* IUnknown */
6766  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);
6767  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis);
6768  ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis);
6769 
6770  /* IAudioRenderClient */
6771  HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);
6772  HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);
6773 } ma_IAudioRenderClientVtbl;
6774 struct ma_IAudioRenderClient
6775 {
6776  ma_IAudioRenderClientVtbl* lpVtbl;
6777 };
6778 HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6779 ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6780 ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
6781 HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
6782 HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
6783 
6784 
6785 /* IAudioCaptureClient */
6786 typedef struct
6787 {
6788  /* IUnknown */
6789  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
6790  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
6791  ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
6792 
6793  /* IAudioRenderClient */
6794  HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
6795  HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
6796  HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
6797 } ma_IAudioCaptureClientVtbl;
6798 struct ma_IAudioCaptureClient
6799 {
6800  ma_IAudioCaptureClientVtbl* lpVtbl;
6801 };
6802 HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
6803 ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
6804 ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
6805 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); }
6806 HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
6807 HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
6808 
6809 #ifndef MA_WIN32_DESKTOP
6810 #include <mmdeviceapi.h>
6811 typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
6812 
6813 typedef struct
6814 {
6815  /* IUnknown */
6816  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
6817  ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
6818  ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
6819 
6820  /* IActivateAudioInterfaceCompletionHandler */
6821  HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
6822 } ma_completion_handler_uwp_vtbl;
6823 struct ma_completion_handler_uwp
6824 {
6825  ma_completion_handler_uwp_vtbl* lpVtbl;
6826  ma_uint32 counter;
6827  HANDLE hEvent;
6828 };
6829 
6830 HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
6831 {
6832  /*
6833  We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To
6834  "implement" this, we just make sure we return pThis when the IAgileObject is requested.
6835  */
6836  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)) {
6837  *ppObject = NULL;
6838  return E_NOINTERFACE;
6839  }
6840 
6841  /* Getting here means the IID is IUnknown or IMMNotificationClient. */
6842  *ppObject = (void*)pThis;
6843  ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
6844  return S_OK;
6845 }
6846 
6847 ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
6848 {
6849  return (ULONG)ma_atomic_increment_32(&pThis->counter);
6850 }
6851 
6852 ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
6853 {
6854  ma_uint32 newRefCount = ma_atomic_decrement_32(&pThis->counter);
6855  if (newRefCount == 0) {
6856  return 0; /* We don't free anything here because we never allocate the object on the heap. */
6857  }
6858 
6859  return (ULONG)newRefCount;
6860 }
6861 
6862 HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
6863 {
6864  (void)pActivateOperation;
6865  SetEvent(pThis->hEvent);
6866  return S_OK;
6867 }
6868 
6869 
6870 static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
6871  ma_completion_handler_uwp_QueryInterface,
6872  ma_completion_handler_uwp_AddRef,
6873  ma_completion_handler_uwp_Release,
6874  ma_completion_handler_uwp_ActivateCompleted
6875 };
6876 
6877 ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
6878 {
6879  ma_assert(pHandler != NULL);
6880  ma_zero_object(pHandler);
6881 
6882  pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
6883  pHandler->counter = 1;
6884  pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
6885  if (pHandler->hEvent == NULL) {
6886  return MA_ERROR;
6887  }
6888 
6889  return MA_SUCCESS;
6890 }
6891 
6892 void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
6893 {
6894  if (pHandler->hEvent != NULL) {
6895  CloseHandle(pHandler->hEvent);
6896  }
6897 }
6898 
6899 void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
6900 {
6901  WaitForSingleObject(pHandler->hEvent, INFINITE);
6902 }
6903 #endif /* !MA_WIN32_DESKTOP */
6904 
6905 /* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
6906 #ifdef MA_WIN32_DESKTOP
6907 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
6908 {
6909  /*
6910  We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
6911  we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
6912  */
6913  if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
6914  *ppObject = NULL;
6915  return E_NOINTERFACE;
6916  }
6917 
6918  /* Getting here means the IID is IUnknown or IMMNotificationClient. */
6919  *ppObject = (void*)pThis;
6920  ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
6921  return S_OK;
6922 }
6923 
6924 ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
6925 {
6926  return (ULONG)ma_atomic_increment_32(&pThis->counter);
6927 }
6928 
6929 ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
6930 {
6931  ma_uint32 newRefCount = ma_atomic_decrement_32(&pThis->counter);
6932  if (newRefCount == 0) {
6933  return 0; /* We don't free anything here because we never allocate the object on the heap. */
6934  }
6935 
6936  return (ULONG)newRefCount;
6937 }
6938 
6939 
6940 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState)
6941 {
6942 #ifdef MA_DEBUG_OUTPUT
6943  printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);
6944 #endif
6945 
6946  (void)pThis;
6947  (void)pDeviceID;
6948  (void)dwNewState;
6949  return S_OK;
6950 }
6951 
6952 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
6953 {
6954 #ifdef MA_DEBUG_OUTPUT
6955  printf("IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
6956 #endif
6957 
6958  /* We don't need to worry about this event for our purposes. */
6959  (void)pThis;
6960  (void)pDeviceID;
6961  return S_OK;
6962 }
6963 
6964 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
6965 {
6966 #ifdef MA_DEBUG_OUTPUT
6967  printf("IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
6968 #endif
6969 
6970  /* We don't need to worry about this event for our purposes. */
6971  (void)pThis;
6972  (void)pDeviceID;
6973  return S_OK;
6974 }
6975 
6976 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID)
6977 {
6978 #ifdef MA_DEBUG_OUTPUT
6979  printf("IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");
6980 #endif
6981 
6982  /* We only ever use the eConsole role in miniaudio. */
6983  if (role != ma_eConsole) {
6984  return S_OK;
6985  }
6986 
6987  /* We only care about devices with the same data flow and role as the current device. */
6988  if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
6989  (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) {
6990  return S_OK;
6991  }
6992 
6993  /*
6994  Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
6995  AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
6996  it's fixed.
6997  */
6998  if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
6999  (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
7000  return S_OK;
7001  }
7002 
7003  /*
7004  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
7005  indicate that the default device has changed.
7006  */
7007  if (dataFlow == ma_eRender) {
7008  ma_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_TRUE);
7009  }
7010  if (dataFlow == ma_eCapture) {
7011  ma_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_TRUE);
7012  }
7013 
7014  (void)pDefaultDeviceID;
7015  return S_OK;
7016 }
7017 
7018 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key)
7019 {
7020 #ifdef MA_DEBUG_OUTPUT
7021  printf("IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
7022 #endif
7023 
7024  (void)pThis;
7025  (void)pDeviceID;
7026  (void)key;
7027  return S_OK;
7028 }
7029 
7030 static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
7031  ma_IMMNotificationClient_QueryInterface,
7032  ma_IMMNotificationClient_AddRef,
7033  ma_IMMNotificationClient_Release,
7034  ma_IMMNotificationClient_OnDeviceStateChanged,
7035  ma_IMMNotificationClient_OnDeviceAdded,
7036  ma_IMMNotificationClient_OnDeviceRemoved,
7037  ma_IMMNotificationClient_OnDefaultDeviceChanged,
7038  ma_IMMNotificationClient_OnPropertyValueChanged
7039 };
7040 #endif /* MA_WIN32_DESKTOP */
7041 
7042 #ifdef MA_WIN32_DESKTOP
7043 typedef ma_IMMDevice ma_WASAPIDeviceInterface;
7044 #else
7045 typedef ma_IUnknown ma_WASAPIDeviceInterface;
7046 #endif
7047 
7048 
7049 
7050 ma_bool32 ma_context_is_device_id_equal__wasapi(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
7051 {
7052  ma_assert(pContext != NULL);
7053  ma_assert(pID0 != NULL);
7054  ma_assert(pID1 != NULL);
7055  (void)pContext;
7056 
7057  return memcmp(pID0->wasapi, pID1->wasapi, sizeof(pID0->wasapi)) == 0;
7058 }
7059 
7060 void ma_set_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_device_info* pInfo)
7061 {
7062  ma_assert(pWF != NULL);
7063  ma_assert(pInfo != NULL);
7064 
7065  pInfo->formatCount = 1;
7066  pInfo->formats[0] = ma_format_from_WAVEFORMATEX(pWF);
7067  pInfo->minChannels = pWF->nChannels;
7068  pInfo->maxChannels = pWF->nChannels;
7069  pInfo->minSampleRate = pWF->nSamplesPerSec;
7070  pInfo->maxSampleRate = pWF->nSamplesPerSec;
7071 }
7072 
7073 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)
7074 {
7075  ma_assert(pAudioClient != NULL);
7076  ma_assert(pInfo != NULL);
7077 
7078  /* We use a different technique to retrieve the device information depending on whether or not we are using shared or exclusive mode. */
7079  if (shareMode == ma_share_mode_shared) {
7080  /* Shared Mode. We use GetMixFormat() here. */
7081  WAVEFORMATEX* pWF = NULL;
7082  HRESULT hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF);
7083  if (SUCCEEDED(hr)) {
7084  ma_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
7085  return MA_SUCCESS;
7086  } else {
7087  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7088  }
7089  } else {
7090  /* Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently support on UWP. */
7091 #ifdef MA_WIN32_DESKTOP
7092  /*
7093  The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
7094  correct which will simplify our searching.
7095  */
7096  ma_IPropertyStore *pProperties;
7097  HRESULT hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
7098  if (SUCCEEDED(hr)) {
7099  PROPVARIANT var;
7100  ma_PropVariantInit(&var);
7101 
7102  hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);
7103  if (SUCCEEDED(hr)) {
7104  WAVEFORMATEX* pWF = (WAVEFORMATEX*)var.blob.pBlobData;
7105  ma_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
7106 
7107  /*
7108  In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
7109  first. If this fails, fall back to a search.
7110  */
7111  hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
7112  ma_PropVariantClear(pContext, &var);
7113 
7114  if (FAILED(hr)) {
7115  /*
7116  The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
7117  count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
7118  */
7119  ma_uint32 channels = pInfo->minChannels;
7120  ma_format formatsToSearch[] = {
7121  ma_format_s16,
7122  ma_format_s24,
7123  /*ma_format_s24_32,*/
7124  ma_format_f32,
7125  ma_format_s32,
7126  ma_format_u8
7127  };
7128  ma_channel defaultChannelMap[MA_MAX_CHANNELS];
7129  WAVEFORMATEXTENSIBLE wf;
7130  ma_bool32 found;
7131  ma_uint32 iFormat;
7132 
7134 
7135  ma_zero_object(&wf);
7136  wf.Format.cbSize = sizeof(wf);
7137  wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
7138  wf.Format.nChannels = (WORD)channels;
7139  wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
7140 
7141  found = MA_FALSE;
7142  for (iFormat = 0; iFormat < ma_countof(formatsToSearch); ++iFormat) {
7143  ma_format format = formatsToSearch[iFormat];
7144  ma_uint32 iSampleRate;
7145 
7146  wf.Format.wBitsPerSample = (WORD)ma_get_bytes_per_sample(format)*8;
7147  wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
7148  wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
7149  wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample;
7150  if (format == ma_format_f32) {
7151  wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
7152  } else {
7153  wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
7154  }
7155 
7156  for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
7157  wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
7158 
7159  hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL);
7160  if (SUCCEEDED(hr)) {
7161  ma_set_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, pInfo);
7162  found = MA_TRUE;
7163  break;
7164  }
7165  }
7166 
7167  if (found) {
7168  break;
7169  }
7170  }
7171 
7172  if (!found) {
7173  ma_IPropertyStore_Release(pProperties);
7174  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to find suitable device format for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7175  }
7176  }
7177  } else {
7178  ma_IPropertyStore_Release(pProperties);
7179  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve device format for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7180  }
7181  } else {
7182  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to open property store for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7183  }
7184 
7185  return MA_SUCCESS;
7186 #else
7187  /* Exclusive mode not fully supported in UWP right now. */
7188  return MA_ERROR;
7189 #endif
7190  }
7191 }
7192 
7193 #ifdef MA_WIN32_DESKTOP
7194 ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
7195 {
7196  ma_IMMDeviceEnumerator* pDeviceEnumerator;
7197  HRESULT hr;
7198 
7199  ma_assert(pContext != NULL);
7200  ma_assert(ppMMDevice != NULL);
7201 
7202  hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
7203  if (FAILED(hr)) {
7204  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.", MA_FAILED_TO_INIT_BACKEND);
7205  }
7206 
7207  if (pDeviceID == NULL) {
7208  hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_playback) ? ma_eRender : ma_eCapture, ma_eConsole, ppMMDevice);
7209  } else {
7210  hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
7211  }
7212 
7213  ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
7214  if (FAILED(hr)) {
7215  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7216  }
7217 
7218  return MA_SUCCESS;
7219 }
7220 
7221 ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_share_mode shareMode, ma_bool32 onlySimpleInfo, ma_device_info* pInfo)
7222 {
7223  LPWSTR id;
7224  HRESULT hr;
7225 
7226  ma_assert(pContext != NULL);
7227  ma_assert(pMMDevice != NULL);
7228  ma_assert(pInfo != NULL);
7229 
7230  /* ID. */
7231  hr = ma_IMMDevice_GetId(pMMDevice, &id);
7232  if (SUCCEEDED(hr)) {
7233  size_t idlen = wcslen(id);
7234  if (idlen+1 > ma_countof(pInfo->id.wasapi)) {
7235  ma_CoTaskMemFree(pContext, id);
7236  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. */
7237  return MA_ERROR;
7238  }
7239 
7240  ma_copy_memory(pInfo->id.wasapi, id, idlen * sizeof(wchar_t));
7241  pInfo->id.wasapi[idlen] = '\0';
7242 
7243  ma_CoTaskMemFree(pContext, id);
7244  }
7245 
7246  {
7247  ma_IPropertyStore *pProperties;
7248  hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
7249  if (SUCCEEDED(hr)) {
7250  PROPVARIANT var;
7251 
7252  /* Description / Friendly Name */
7253  ma_PropVariantInit(&var);
7254  hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
7255  if (SUCCEEDED(hr)) {
7256  WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
7257  ma_PropVariantClear(pContext, &var);
7258  }
7259 
7260  ma_IPropertyStore_Release(pProperties);
7261  }
7262  }
7263 
7264  /* Format */
7265  if (!onlySimpleInfo) {
7266  ma_IAudioClient* pAudioClient;
7267  hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
7268  if (SUCCEEDED(hr)) {
7269  ma_result result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, shareMode, pInfo);
7270 
7271  ma_IAudioClient_Release(pAudioClient);
7272  return result;
7273  } else {
7274  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7275  }
7276  }
7277 
7278  return MA_SUCCESS;
7279 }
7280 
7281 ma_result ma_context_enumerate_device_collection__wasapi(ma_context* pContext, ma_IMMDeviceCollection* pDeviceCollection, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData)
7282 {
7283  UINT deviceCount;
7284  HRESULT hr;
7285  ma_uint32 iDevice;
7286 
7287  ma_assert(pContext != NULL);
7288  ma_assert(callback != NULL);
7289 
7290  hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
7291  if (FAILED(hr)) {
7292  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get playback device count.", MA_NO_DEVICE);
7293  }
7294 
7295  for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
7296  ma_device_info deviceInfo;
7297  ma_IMMDevice* pMMDevice;
7298 
7299  ma_zero_object(&deviceInfo);
7300 
7301  hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
7302  if (SUCCEEDED(hr)) {
7303  ma_result result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, ma_share_mode_shared, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
7304 
7305  ma_IMMDevice_Release(pMMDevice);
7306  if (result == MA_SUCCESS) {
7307  ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
7308  if (cbResult == MA_FALSE) {
7309  break;
7310  }
7311  }
7312  }
7313  }
7314 
7315  return MA_SUCCESS;
7316 }
7317 #endif
7318 
7319 #ifdef MA_WIN32_DESKTOP
7320 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)
7321 {
7322  ma_result result;
7323  HRESULT hr;
7324 
7325  ma_assert(pContext != NULL);
7326  ma_assert(ppAudioClient != NULL);
7327  ma_assert(ppMMDevice != NULL);
7328 
7329  result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
7330  if (result != MA_SUCCESS) {
7331  return result;
7332  }
7333 
7334  hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient);
7335  if (FAILED(hr)) {
7337  }
7338 
7339  return MA_SUCCESS;
7340 }
7341 #else
7342 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)
7343 {
7344  ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
7345  ma_completion_handler_uwp completionHandler;
7346  IID iid;
7347  LPOLESTR iidStr;
7348  HRESULT hr;
7349  ma_result result;
7350  HRESULT activateResult;
7351  ma_IUnknown* pActivatedInterface;
7352 
7353  ma_assert(pContext != NULL);
7354  ma_assert(ppAudioClient != NULL);
7355 
7356  if (pDeviceID != NULL) {
7357  ma_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid));
7358  } else {
7359  if (deviceType == ma_device_type_playback) {
7360  iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
7361  } else {
7362  iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
7363  }
7364  }
7365 
7366 #if defined(__cplusplus)
7367  hr = StringFromIID(iid, &iidStr);
7368 #else
7369  hr = StringFromIID(&iid, &iidStr);
7370 #endif
7371  if (FAILED(hr)) {
7372  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", MA_OUT_OF_MEMORY);
7373  }
7374 
7375  result = ma_completion_handler_uwp_init(&completionHandler);
7376  if (result != MA_SUCCESS) {
7377  ma_CoTaskMemFree(pContext, iidStr);
7378  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7379  }
7380 
7381 #if defined(__cplusplus)
7382  hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
7383 #else
7384  hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
7385 #endif
7386  if (FAILED(hr)) {
7387  ma_completion_handler_uwp_uninit(&completionHandler);
7388  ma_CoTaskMemFree(pContext, iidStr);
7389  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7390  }
7391 
7392  ma_CoTaskMemFree(pContext, iidStr);
7393 
7394  /* Wait for the async operation for finish. */
7395  ma_completion_handler_uwp_wait(&completionHandler);
7396  ma_completion_handler_uwp_uninit(&completionHandler);
7397 
7398  hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
7399  ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
7400 
7401  if (FAILED(hr) || FAILED(activateResult)) {
7402  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7403  }
7404 
7405  /* Here is where we grab the IAudioClient interface. */
7406  hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
7407  if (FAILED(hr)) {
7408  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7409  }
7410 
7411  if (ppActivatedInterface) {
7412  *ppActivatedInterface = pActivatedInterface;
7413  } else {
7414  ma_IUnknown_Release(pActivatedInterface);
7415  }
7416 
7417  return MA_SUCCESS;
7418 }
7419 #endif
7420 
7421 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)
7422 {
7423 #ifdef MA_WIN32_DESKTOP
7424  return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
7425 #else
7426  return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
7427 #endif
7428 }
7429 
7430 
7431 ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
7432 {
7433  /* Different enumeration for desktop and UWP. */
7434 #ifdef MA_WIN32_DESKTOP
7435  /* Desktop */
7436  HRESULT hr;
7437  ma_IMMDeviceEnumerator* pDeviceEnumerator;
7438  ma_IMMDeviceCollection* pDeviceCollection;
7439 
7440  hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
7441  if (FAILED(hr)) {
7442  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7443  }
7444 
7445  /* Playback. */
7446  hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_eRender, MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
7447  if (SUCCEEDED(hr)) {
7448  ma_context_enumerate_device_collection__wasapi(pContext, pDeviceCollection, ma_device_type_playback, callback, pUserData);
7449  ma_IMMDeviceCollection_Release(pDeviceCollection);
7450  }
7451 
7452  /* Capture. */
7453  hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_eCapture, MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
7454  if (SUCCEEDED(hr)) {
7455  ma_context_enumerate_device_collection__wasapi(pContext, pDeviceCollection, ma_device_type_capture, callback, pUserData);
7456  ma_IMMDeviceCollection_Release(pDeviceCollection);
7457  }
7458 
7459  ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
7460 #else
7461  /*
7462  UWP
7463 
7464  The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
7465  over devices without using MMDevice, I'm restricting devices to defaults.
7466 
7467  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/
7468  */
7469  if (callback) {
7470  ma_bool32 cbResult = MA_TRUE;
7471 
7472  /* Playback. */
7473  if (cbResult) {
7474  ma_device_info deviceInfo;
7475  ma_zero_object(&deviceInfo);
7476  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
7477  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
7478  }
7479 
7480  /* Capture. */
7481  if (cbResult) {
7482  ma_device_info deviceInfo;
7483  ma_zero_object(&deviceInfo);
7484  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
7485  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
7486  }
7487  }
7488 #endif
7489 
7490  return MA_SUCCESS;
7491 }
7492 
7493 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)
7494 {
7495 #ifdef MA_WIN32_DESKTOP
7496  ma_IMMDevice* pMMDevice = NULL;
7497  ma_result result;
7498 
7499  result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
7500  if (result != MA_SUCCESS) {
7501  return result;
7502  }
7503 
7504  result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
7505 
7506  ma_IMMDevice_Release(pMMDevice);
7507  return result;
7508 #else
7509  ma_IAudioClient* pAudioClient;
7510  ma_result result;
7511 
7512  /* UWP currently only uses default devices. */
7513  if (deviceType == ma_device_type_playback) {
7514  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
7515  } else {
7516  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
7517  }
7518 
7519  /* Not currently supporting exclusive mode on UWP. */
7520  if (shareMode == ma_share_mode_exclusive) {
7521  return MA_ERROR;
7522  }
7523 
7524  result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL);
7525  if (result != MA_SUCCESS) {
7526  return result;
7527  }
7528 
7529  result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, shareMode, pDeviceInfo);
7530 
7531  ma_IAudioClient_Release(pAudioClient);
7532  return result;
7533 #endif
7534 }
7535 
7536 void ma_device_uninit__wasapi(ma_device* pDevice)
7537 {
7538  ma_assert(pDevice != NULL);
7539 
7540 #ifdef MA_WIN32_DESKTOP
7541  if (pDevice->wasapi.pDeviceEnumerator) {
7542  ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
7543  ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
7544  }
7545 #endif
7546 
7547  if (pDevice->wasapi.pRenderClient) {
7548  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
7549  }
7550  if (pDevice->wasapi.pCaptureClient) {
7551  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
7552  }
7553 
7554  if (pDevice->wasapi.pAudioClientPlayback) {
7555  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
7556  }
7557  if (pDevice->wasapi.pAudioClientCapture) {
7558  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
7559  }
7560 
7561  if (pDevice->wasapi.hEventPlayback) {
7562  CloseHandle(pDevice->wasapi.hEventPlayback);
7563  }
7564  if (pDevice->wasapi.hEventCapture) {
7565  CloseHandle(pDevice->wasapi.hEventCapture);
7566  }
7567 }
7568 
7569 
7570 typedef struct
7571 {
7572  /* Input. */
7573  ma_format formatIn;
7574  ma_uint32 channelsIn;
7575  ma_uint32 sampleRateIn;
7576  ma_channel channelMapIn[MA_MAX_CHANNELS];
7577  ma_uint32 bufferSizeInFramesIn;
7578  ma_uint32 bufferSizeInMillisecondsIn;
7579  ma_uint32 periodsIn;
7580  ma_bool32 usingDefaultFormat;
7581  ma_bool32 usingDefaultChannels;
7582  ma_bool32 usingDefaultSampleRate;
7583  ma_bool32 usingDefaultChannelMap;
7584  ma_share_mode shareMode;
7585 
7586  /* Output. */
7587  ma_IAudioClient* pAudioClient;
7588  ma_IAudioRenderClient* pRenderClient;
7589  ma_IAudioCaptureClient* pCaptureClient;
7590  ma_format formatOut;
7591  ma_uint32 channelsOut;
7592  ma_uint32 sampleRateOut;
7593  ma_channel channelMapOut[MA_MAX_CHANNELS];
7594  ma_uint32 bufferSizeInFramesOut;
7595  ma_uint32 periodSizeInFramesOut;
7596  ma_uint32 periodsOut;
7597  char deviceName[256];
7598 } ma_device_init_internal_data__wasapi;
7599 
7600 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)
7601 {
7602  HRESULT hr;
7603  ma_result result = MA_SUCCESS;
7604  const char* errorMsg = "";
7605  MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
7606  MA_REFERENCE_TIME bufferDurationInMicroseconds;
7607  ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
7608  WAVEFORMATEXTENSIBLE wf;
7609  ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
7610  ma_IAudioClient2* pAudioClient2;
7611 
7612  ma_assert(pContext != NULL);
7613  ma_assert(pData != NULL);
7614 
7615  /* This function is only used to initialize one device type: either playback or capture. Never full-duplex. */
7616  if (deviceType == ma_device_type_duplex) {
7617  return MA_INVALID_ARGS;
7618  }
7619 
7620  pData->pAudioClient = NULL;
7621  pData->pRenderClient = NULL;
7622  pData->pCaptureClient = NULL;
7623 
7624  result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface);
7625  if (result != MA_SUCCESS) {
7626  goto done;
7627  }
7628 
7629 
7630  /* Try enabling hardware offloading. */
7631  hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
7632  if (SUCCEEDED(hr)) {
7633  BOOL isHardwareOffloadingSupported = 0;
7634  hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
7635  if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
7636  ma_AudioClientProperties clientProperties;
7637  ma_zero_object(&clientProperties);
7638  clientProperties.cbSize = sizeof(clientProperties);
7639  clientProperties.bIsOffload = 1;
7640  clientProperties.eCategory = MA_AudioCategory_Other;
7641  ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
7642  }
7643  }
7644 
7645 
7646  /* 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. */
7647  result = MA_FORMAT_NOT_SUPPORTED;
7648  if (pData->shareMode == ma_share_mode_exclusive) {
7649  #ifdef MA_WIN32_DESKTOP
7650  /* In exclusive mode on desktop we always use the backend's native format. */
7651  ma_IPropertyStore* pStore = NULL;
7652  hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
7653  if (SUCCEEDED(hr)) {
7654  PROPVARIANT prop;
7655  ma_PropVariantInit(&prop);
7656  hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
7657  if (SUCCEEDED(hr)) {
7658  WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData;
7659  hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
7660  if (SUCCEEDED(hr)) {
7661  ma_copy_memory(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE));
7662  }
7663 
7664  ma_PropVariantClear(pContext, &prop);
7665  }
7666 
7667  ma_IPropertyStore_Release(pStore);
7668  }
7669  #else
7670  /*
7671  I do not know how to query the device's native format on UWP so for now I'm just disabling support for
7672  exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()
7673  until you find one that works.
7674 
7675  TODO: Add support for exclusive mode to UWP.
7676  */
7677  hr = S_FALSE;
7678  #endif
7679 
7680  if (hr == S_OK) {
7681  shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
7682  result = MA_SUCCESS;
7683  } else {
7684  result = MA_SHARE_MODE_NOT_SUPPORTED;
7685  }
7686  } else {
7687  /* In shared mode we are always using the format reported by the operating system. */
7688  WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
7689  hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat);
7690  if (hr != S_OK) {
7691  result = MA_FORMAT_NOT_SUPPORTED;
7692  } else {
7693  ma_copy_memory(&wf, pNativeFormat, sizeof(wf));
7694  result = MA_SUCCESS;
7695  }
7696 
7697  ma_CoTaskMemFree(pContext, pNativeFormat);
7698 
7699  shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
7700  }
7701 
7702  /* Return an error if we still haven't found a format. */
7703  if (result != MA_SUCCESS) {
7704  errorMsg = "[WASAPI] Failed to find best device mix format.";
7705  goto done;
7706  }
7707 
7708  pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf);
7709  pData->channelsOut = wf.Format.nChannels;
7710  pData->sampleRateOut = wf.Format.nSamplesPerSec;
7711 
7712  /* Get the internal channel map based on the channel mask. */
7713  ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
7714 
7715  /* If we're using a default buffer size we need to calculate it based on the efficiency of the system. */
7716  pData->periodsOut = pData->periodsIn;
7717  pData->bufferSizeInFramesOut = pData->bufferSizeInFramesIn;
7718  if (pData->bufferSizeInFramesOut == 0) {
7719  pData->bufferSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->bufferSizeInMillisecondsIn, pData->sampleRateOut);
7720  }
7721 
7722  bufferDurationInMicroseconds = ((ma_uint64)pData->bufferSizeInFramesOut * 1000 * 1000) / pData->sampleRateOut;
7723 
7724 
7725  /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
7726  if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
7727  MA_REFERENCE_TIME bufferDuration = (bufferDurationInMicroseconds / pData->periodsOut) * 10;
7728 
7729  /*
7730  If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
7731  it and trying it again.
7732  */
7733  hr = E_FAIL;
7734  for (;;) {
7735  hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
7736  if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
7737  if (bufferDuration > 500*10000) {
7738  break;
7739  } else {
7740  if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */
7741  break;
7742  }
7743 
7744  bufferDuration = bufferDuration * 2;
7745  continue;
7746  }
7747  } else {
7748  break;
7749  }
7750  }
7751 
7752  if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
7753  UINT bufferSizeInFrames;
7754  hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
7755  if (SUCCEEDED(hr)) {
7756  bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5);
7757 
7758  /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
7759  ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
7760 
7761  #ifdef MA_WIN32_DESKTOP
7762  hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
7763  #else
7764  hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
7765  #endif
7766 
7767  if (SUCCEEDED(hr)) {
7768  hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
7769  }
7770  }
7771  }
7772 
7773  if (FAILED(hr)) {
7774  /* 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. */
7775  if (hr == E_ACCESSDENIED) {
7776  errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
7777  } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
7778  errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_DEVICE_BUSY;
7779  } else {
7780  errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = MA_SHARE_MODE_NOT_SUPPORTED;
7781  }
7782  goto done;
7783  }
7784  }
7785 
7786  if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
7787  /* Low latency shared mode via IAudioClient3. */
7788 #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
7789  ma_IAudioClient3* pAudioClient3 = NULL;
7790  hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
7791  if (SUCCEEDED(hr)) {
7792  UINT32 defaultPeriodInFrames;
7793  UINT32 fundamentalPeriodInFrames;
7794  UINT32 minPeriodInFrames;
7795  UINT32 maxPeriodInFrames;
7796  hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
7797  if (SUCCEEDED(hr)) {
7798  UINT32 desiredPeriodInFrames = pData->bufferSizeInFramesOut / pData->periodsOut;
7799  UINT32 actualPeriodInFrames = desiredPeriodInFrames;
7800 
7801  /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
7802  actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
7803  actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
7804 
7805  /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
7806  actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
7807 
7808  /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
7809  if (actualPeriodInFrames >= desiredPeriodInFrames) {
7810  hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL);
7811  if (SUCCEEDED(hr)) {
7812  wasInitializedUsingIAudioClient3 = MA_TRUE;
7813  pData->periodSizeInFramesOut = actualPeriodInFrames;
7814  pData->bufferSizeInFramesOut = actualPeriodInFrames * pData->periodsOut;
7815  }
7816  }
7817  }
7818 
7819  ma_IAudioClient3_Release(pAudioClient3);
7820  pAudioClient3 = NULL;
7821  }
7822 #endif
7823 
7824  /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
7825  if (!wasInitializedUsingIAudioClient3) {
7826  MA_REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10;
7827  hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL);
7828  if (FAILED(hr)) {
7829  if (hr == E_ACCESSDENIED) {
7830  errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
7831  } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
7832  errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_DEVICE_BUSY;
7833  } else {
7834  errorMsg = "[WASAPI] Failed to initialize device.", result = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
7835  }
7836 
7837  goto done;
7838  }
7839  }
7840  }
7841 
7842  if (!wasInitializedUsingIAudioClient3) {
7843  hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &pData->bufferSizeInFramesOut);
7844  if (FAILED(hr)) {
7845  errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
7846  goto done;
7847  }
7848 
7849  pData->periodSizeInFramesOut = pData->bufferSizeInFramesOut / pData->periodsOut;
7850  }
7851 
7852  if (deviceType == ma_device_type_playback) {
7853  hr = ma_IAudioClient_GetService((ma_IAudioClient*)pData->pAudioClient, &MA_IID_IAudioRenderClient, (void**)&pData->pRenderClient);
7854  } else {
7855  hr = ma_IAudioClient_GetService((ma_IAudioClient*)pData->pAudioClient, &MA_IID_IAudioCaptureClient, (void**)&pData->pCaptureClient);
7856  }
7857 
7858  if (FAILED(hr)) {
7859  errorMsg = "[WASAPI] Failed to get audio client service.", result = MA_API_NOT_FOUND;
7860  goto done;
7861  }
7862 
7863 
7864  /* Grab the name of the device. */
7865 #ifdef MA_WIN32_DESKTOP
7866  {
7867  ma_IPropertyStore *pProperties;
7868  hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
7869  if (SUCCEEDED(hr)) {
7870  PROPVARIANT varName;
7871  ma_PropVariantInit(&varName);
7872  hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
7873  if (SUCCEEDED(hr)) {
7874  WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
7875  ma_PropVariantClear(pContext, &varName);
7876  }
7877 
7878  ma_IPropertyStore_Release(pProperties);
7879  }
7880  }
7881 #endif
7882 
7883 done:
7884  /* Clean up. */
7885 #ifdef MA_WIN32_DESKTOP
7886  if (pDeviceInterface != NULL) {
7887  ma_IMMDevice_Release(pDeviceInterface);
7888  }
7889 #else
7890  if (pDeviceInterface != NULL) {
7891  ma_IUnknown_Release(pDeviceInterface);
7892  }
7893 #endif
7894 
7895  if (result != MA_SUCCESS) {
7896  if (pData->pRenderClient) {
7897  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
7898  pData->pRenderClient = NULL;
7899  }
7900  if (pData->pCaptureClient) {
7901  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
7902  pData->pCaptureClient = NULL;
7903  }
7904  if (pData->pAudioClient) {
7905  ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
7906  pData->pAudioClient = NULL;
7907  }
7908 
7909  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, errorMsg, result);
7910  } else {
7911  return MA_SUCCESS;
7912  }
7913 }
7914 
7915 ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
7916 {
7917  ma_device_init_internal_data__wasapi data;
7918  ma_result result;
7919 
7920  ma_assert(pDevice != NULL);
7921 
7922  /* We only re-initialize the playback or capture device. Never a full-duplex device. */
7923  if (deviceType == ma_device_type_duplex) {
7924  return MA_INVALID_ARGS;
7925  }
7926 
7927  if (deviceType == ma_device_type_capture) {
7928  data.formatIn = pDevice->capture.format;
7929  data.channelsIn = pDevice->capture.channels;
7930  ma_copy_memory(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
7931  data.shareMode = pDevice->capture.shareMode;
7932  data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
7933  data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
7934  data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
7935  } else {
7936  data.formatIn = pDevice->playback.format;
7937  data.channelsIn = pDevice->playback.channels;
7938  ma_copy_memory(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
7939  data.shareMode = pDevice->playback.shareMode;
7940  data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
7941  data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
7942  data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
7943  }
7944 
7945  data.sampleRateIn = pDevice->sampleRate;
7946  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
7947  data.bufferSizeInFramesIn = pDevice->wasapi.originalBufferSizeInFrames;
7948  data.bufferSizeInMillisecondsIn = pDevice->wasapi.originalBufferSizeInMilliseconds;
7949  data.periodsIn = pDevice->wasapi.originalPeriods;
7950  result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
7951  if (result != MA_SUCCESS) {
7952  return result;
7953  }
7954 
7955  /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
7956  if (deviceType == ma_device_type_capture) {
7957  if (pDevice->wasapi.pCaptureClient) {
7958  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
7959  pDevice->wasapi.pCaptureClient = NULL;
7960  }
7961 
7962  if (pDevice->wasapi.pAudioClientCapture) {
7963  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
7964  pDevice->wasapi.pAudioClientCapture = NULL;
7965  }
7966 
7967  pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
7968  pDevice->wasapi.pCaptureClient = data.pCaptureClient;
7969 
7970  pDevice->capture.internalFormat = data.formatOut;
7971  pDevice->capture.internalChannels = data.channelsOut;
7972  pDevice->capture.internalSampleRate = data.sampleRateOut;
7973  ma_copy_memory(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
7974  pDevice->capture.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
7975  pDevice->capture.internalPeriods = data.periodsOut;
7976  ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
7977 
7978  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
7979 
7980  pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
7981  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
7982 
7983  /* The device may be in a started state. If so we need to immediately restart it. */
7984  if (pDevice->wasapi.isStartedCapture) {
7985  HRESULT hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
7986  if (FAILED(hr)) {
7987  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device after reinitialization.", MA_FAILED_TO_START_BACKEND_DEVICE);
7988  }
7989  }
7990  }
7991 
7992  if (deviceType == ma_device_type_playback) {
7993  if (pDevice->wasapi.pRenderClient) {
7994  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
7995  pDevice->wasapi.pRenderClient = NULL;
7996  }
7997 
7998  if (pDevice->wasapi.pAudioClientPlayback) {
7999  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8000  pDevice->wasapi.pAudioClientPlayback = NULL;
8001  }
8002 
8003  pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
8004  pDevice->wasapi.pRenderClient = data.pRenderClient;
8005 
8006  pDevice->playback.internalFormat = data.formatOut;
8007  pDevice->playback.internalChannels = data.channelsOut;
8008  pDevice->playback.internalSampleRate = data.sampleRateOut;
8009  ma_copy_memory(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
8010  pDevice->playback.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
8011  pDevice->playback.internalPeriods = data.periodsOut;
8012  ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
8013 
8014  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
8015 
8016  pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
8017  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
8018 
8019  /* The device may be in a started state. If so we need to immediately restart it. */
8020  if (pDevice->wasapi.isStartedPlayback) {
8021  HRESULT hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8022  if (FAILED(hr)) {
8023  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device after reinitialization.", MA_FAILED_TO_START_BACKEND_DEVICE);
8024  }
8025  }
8026  }
8027 
8028  return MA_SUCCESS;
8029 }
8030 
8031 ma_result ma_device_init__wasapi(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
8032 {
8033  ma_result result = MA_SUCCESS;
8034 
8035  (void)pContext;
8036 
8037  ma_assert(pContext != NULL);
8038  ma_assert(pDevice != NULL);
8039 
8040  ma_zero_object(&pDevice->wasapi);
8041  pDevice->wasapi.originalBufferSizeInFrames = pConfig->bufferSizeInFrames;
8042  pDevice->wasapi.originalBufferSizeInMilliseconds = pConfig->bufferSizeInMilliseconds;
8043  pDevice->wasapi.originalPeriods = pConfig->periods;
8044 
8045  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
8046  ma_device_init_internal_data__wasapi data;
8047  data.formatIn = pConfig->capture.format;
8048  data.channelsIn = pConfig->capture.channels;
8049  data.sampleRateIn = pConfig->sampleRate;
8050  ma_copy_memory(data.channelMapIn, pConfig->capture.channelMap, sizeof(pConfig->capture.channelMap));
8051  data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
8052  data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
8053  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
8054  data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
8055  data.shareMode = pConfig->capture.shareMode;
8056  data.bufferSizeInFramesIn = pConfig->bufferSizeInFrames;
8057  data.bufferSizeInMillisecondsIn = pConfig->bufferSizeInMilliseconds;
8058  data.periodsIn = pConfig->periods;
8059 
8060  result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_capture, pConfig->capture.pDeviceID, &data);
8061  if (result != MA_SUCCESS) {
8062  return result;
8063  }
8064 
8065  pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
8066  pDevice->wasapi.pCaptureClient = data.pCaptureClient;
8067 
8068  pDevice->capture.internalFormat = data.formatOut;
8069  pDevice->capture.internalChannels = data.channelsOut;
8070  pDevice->capture.internalSampleRate = data.sampleRateOut;
8071  ma_copy_memory(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
8072  pDevice->capture.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
8073  pDevice->capture.internalPeriods = data.periodsOut;
8074  ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
8075 
8076  /*
8077  The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
8078  however, because we want to block until we actually have something for the first call to ma_device_read().
8079  */
8080  pDevice->wasapi.hEventCapture = CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
8081  if (pDevice->wasapi.hEventCapture == NULL) {
8082  if (pDevice->wasapi.pCaptureClient != NULL) {
8083  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
8084  pDevice->wasapi.pCaptureClient = NULL;
8085  }
8086  if (pDevice->wasapi.pAudioClientCapture != NULL) {
8087  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8088  pDevice->wasapi.pAudioClientCapture = NULL;
8089  }
8090 
8091  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.", MA_FAILED_TO_CREATE_EVENT);
8092  }
8093  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
8094 
8095  pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
8096  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
8097  }
8098 
8099  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
8100  ma_device_init_internal_data__wasapi data;
8101  data.formatIn = pConfig->playback.format;
8102  data.channelsIn = pConfig->playback.channels;
8103  data.sampleRateIn = pConfig->sampleRate;
8104  ma_copy_memory(data.channelMapIn, pConfig->playback.channelMap, sizeof(pConfig->playback.channelMap));
8105  data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
8106  data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
8107  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
8108  data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
8109  data.shareMode = pConfig->playback.shareMode;
8110  data.bufferSizeInFramesIn = pConfig->bufferSizeInFrames;
8111  data.bufferSizeInMillisecondsIn = pConfig->bufferSizeInMilliseconds;
8112  data.periodsIn = pConfig->periods;
8113 
8114  result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pConfig->playback.pDeviceID, &data);
8115  if (result != MA_SUCCESS) {
8116  if (pConfig->deviceType == ma_device_type_duplex) {
8117  if (pDevice->wasapi.pCaptureClient != NULL) {
8118  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
8119  pDevice->wasapi.pCaptureClient = NULL;
8120  }
8121  if (pDevice->wasapi.pAudioClientCapture != NULL) {
8122  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8123  pDevice->wasapi.pAudioClientCapture = NULL;
8124  }
8125 
8126  CloseHandle(pDevice->wasapi.hEventCapture);
8127  pDevice->wasapi.hEventCapture = NULL;
8128  }
8129  return result;
8130  }
8131 
8132  pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
8133  pDevice->wasapi.pRenderClient = data.pRenderClient;
8134 
8135  pDevice->playback.internalFormat = data.formatOut;
8136  pDevice->playback.internalChannels = data.channelsOut;
8137  pDevice->playback.internalSampleRate = data.sampleRateOut;
8138  ma_copy_memory(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
8139  pDevice->playback.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
8140  pDevice->playback.internalPeriods = data.periodsOut;
8141  ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
8142 
8143  /*
8144  The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
8145  only after the whole available space has been filled, never before.
8146 
8147  The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
8148  to get passed WaitForMultipleObjects().
8149  */
8150  pDevice->wasapi.hEventPlayback = CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
8151  if (pDevice->wasapi.hEventPlayback == NULL) {
8152  if (pConfig->deviceType == ma_device_type_duplex) {
8153  if (pDevice->wasapi.pCaptureClient != NULL) {
8154  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
8155  pDevice->wasapi.pCaptureClient = NULL;
8156  }
8157  if (pDevice->wasapi.pAudioClientCapture != NULL) {
8158  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8159  pDevice->wasapi.pAudioClientCapture = NULL;
8160  }
8161 
8162  CloseHandle(pDevice->wasapi.hEventCapture);
8163  pDevice->wasapi.hEventCapture = NULL;
8164  }
8165 
8166  if (pDevice->wasapi.pRenderClient != NULL) {
8167  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
8168  pDevice->wasapi.pRenderClient = NULL;
8169  }
8170  if (pDevice->wasapi.pAudioClientPlayback != NULL) {
8171  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8172  pDevice->wasapi.pAudioClientPlayback = NULL;
8173  }
8174 
8175  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.", MA_FAILED_TO_CREATE_EVENT);
8176  }
8177  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
8178 
8179  pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
8180  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
8181  }
8182 
8183  /*
8184  We need to get notifications of when the default device changes. We do this through a device enumerator by
8185  registering a IMMNotificationClient with it. We only care about this if it's the default device.
8186  */
8187 #ifdef MA_WIN32_DESKTOP
8188  {
8189  ma_IMMDeviceEnumerator* pDeviceEnumerator;
8190  HRESULT hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
8191  if (FAILED(hr)) {
8192  ma_device_uninit__wasapi(pDevice);
8193  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
8194  }
8195 
8196  pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
8197  pDevice->wasapi.notificationClient.counter = 1;
8198  pDevice->wasapi.notificationClient.pDevice = pDevice;
8199 
8200  hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
8201  if (SUCCEEDED(hr)) {
8202  pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
8203  } else {
8204  /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
8205  ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
8206  }
8207  }
8208 #endif
8209 
8210  ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
8211  ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
8212 
8213  return MA_SUCCESS;
8214 }
8215 
8216 ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
8217 {
8218  ma_uint32 paddingFramesCount;
8219  HRESULT hr;
8220  ma_share_mode shareMode;
8221 
8222  ma_assert(pDevice != NULL);
8223  ma_assert(pFrameCount != NULL);
8224 
8225  *pFrameCount = 0;
8226 
8227  if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
8228  return MA_INVALID_OPERATION;
8229  }
8230 
8231  hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
8232  if (FAILED(hr)) {
8233  return MA_DEVICE_UNAVAILABLE;
8234  }
8235 
8236  /* Slightly different rules for exclusive and shared modes. */
8237  shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
8238  if (shareMode == ma_share_mode_exclusive) {
8239  *pFrameCount = paddingFramesCount;
8240  } else {
8241  if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
8242  *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount;
8243  } else {
8244  *pFrameCount = paddingFramesCount;
8245  }
8246  }
8247 
8248  return MA_SUCCESS;
8249 }
8250 
8251 ma_bool32 ma_device_is_reroute_required__wasapi(ma_device* pDevice, ma_device_type deviceType)
8252 {
8253  ma_assert(pDevice != NULL);
8254 
8255  if (deviceType == ma_device_type_playback) {
8256  return pDevice->wasapi.hasDefaultPlaybackDeviceChanged;
8257  }
8258 
8259  if (deviceType == ma_device_type_capture) {
8260  return pDevice->wasapi.hasDefaultCaptureDeviceChanged;
8261  }
8262 
8263  return MA_FALSE;
8264 }
8265 
8266 ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
8267 {
8268  ma_result result;
8269 
8270  if (deviceType == ma_device_type_duplex) {
8271  return MA_INVALID_ARGS;
8272  }
8273 
8274  if (deviceType == ma_device_type_playback) {
8275  ma_atomic_exchange_32(&pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_FALSE);
8276  }
8277  if (deviceType == ma_device_type_capture) {
8278  ma_atomic_exchange_32(&pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_FALSE);
8279  }
8280 
8281 
8282  #ifdef MA_DEBUG_OUTPUT
8283  printf("=== CHANGING DEVICE ===\n");
8284  #endif
8285 
8286  result = ma_device_reinit__wasapi(pDevice, deviceType);
8287  if (result != MA_SUCCESS) {
8288  return result;
8289  }
8290 
8291  ma_device__post_init_setup(pDevice, deviceType);
8292 
8293  return MA_SUCCESS;
8294 }
8295 
8296 
8297 ma_result ma_device_main_loop__wasapi(ma_device* pDevice)
8298 {
8299  ma_result result;
8300  HRESULT hr;
8301  ma_bool32 exitLoop = MA_FALSE;
8302  ma_uint32 framesWrittenToPlaybackDevice = 0;
8303  ma_uint32 mappedBufferSizeInFramesCapture = 0;
8304  ma_uint32 mappedBufferSizeInFramesPlayback = 0;
8305  ma_uint32 mappedBufferFramesRemainingCapture = 0;
8306  ma_uint32 mappedBufferFramesRemainingPlayback = 0;
8307  BYTE* pMappedBufferCapture = NULL;
8308  BYTE* pMappedBufferPlayback = NULL;
8309  ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
8310  ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
8311  ma_uint8 inputDataInExternalFormat[4096];
8312  ma_uint32 inputDataInExternalFormatCap = sizeof(inputDataInExternalFormat) / bpfCapture;
8313  ma_uint8 outputDataInExternalFormat[4096];
8314  ma_uint32 outputDataInExternalFormatCap = sizeof(outputDataInExternalFormat) / bpfPlayback;
8315 
8316  ma_assert(pDevice != NULL);
8317 
8318  /* The playback device needs to be started immediately. */
8319  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
8320  hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8321  if (FAILED(hr)) {
8322  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
8323  }
8324  ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE);
8325  }
8326 
8327  while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
8328  /* We may need to reroute the device. */
8329  if (ma_device_is_reroute_required__wasapi(pDevice, ma_device_type_playback)) {
8330  result = ma_device_reroute__wasapi(pDevice, ma_device_type_playback);
8331  if (result != MA_SUCCESS) {
8332  exitLoop = MA_TRUE;
8333  break;
8334  }
8335  }
8336  if (ma_device_is_reroute_required__wasapi(pDevice, ma_device_type_capture)) {
8337  result = ma_device_reroute__wasapi(pDevice, ma_device_type_capture);
8338  if (result != MA_SUCCESS) {
8339  exitLoop = MA_TRUE;
8340  break;
8341  }
8342  }
8343 
8344  switch (pDevice->type)
8345  {
8346  case ma_device_type_duplex:
8347  {
8348  ma_uint32 framesAvailableCapture;
8349  ma_uint32 framesAvailablePlayback;
8350  DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
8351 
8352  /* The process is to map the playback buffer and fill it as quickly as possible from input data. */
8353  if (pMappedBufferPlayback == NULL) {
8354  /* WASAPI is weird with exclusive mode. You need to wait on the event _before_ querying the available frames. */
8355  if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
8356  if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
8357  return MA_ERROR; /* Wait failed. */
8358  }
8359  }
8360 
8361  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
8362  if (result != MA_SUCCESS) {
8363  return result;
8364  }
8365 
8366  /*printf("TRACE 1: framesAvailablePlayback=%d\n", framesAvailablePlayback);*/
8367 
8368 
8369  /* In exclusive mode, the frame count needs to exactly match the value returned by GetCurrentPadding(). */
8370  if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
8371  if (framesAvailablePlayback >= pDevice->wasapi.periodSizeInFramesPlayback) {
8372  framesAvailablePlayback = pDevice->wasapi.periodSizeInFramesPlayback;
8373  }
8374  }
8375 
8376  /* If there's no frames available in the playback device we need to wait for more. */
8377  if (framesAvailablePlayback == 0) {
8378  /* In exclusive mode we waited at the top. */
8379  if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
8380  if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
8381  return MA_ERROR; /* Wait failed. */
8382  }
8383  }
8384 
8385  continue;
8386  }
8387 
8388  /* We're ready to map the playback device's buffer. We don't release this until it's been entirely filled. */
8389  hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedBufferPlayback);
8390  if (FAILED(hr)) {
8391  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
8392  exitLoop = MA_TRUE;
8393  break;
8394  }
8395 
8396  mappedBufferSizeInFramesPlayback = framesAvailablePlayback;
8397  mappedBufferFramesRemainingPlayback = framesAvailablePlayback;
8398  }
8399 
8400  /* At this point we should have a buffer available for output. We need to keep writing input samples to it. */
8401  for (;;) {
8402  /* Try grabbing some captured data if we haven't already got a mapped buffer. */
8403  if (pMappedBufferCapture == NULL) {
8404  if (pDevice->capture.shareMode == ma_share_mode_shared) {
8405  if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
8406  return MA_ERROR; /* Wait failed. */
8407  }
8408  }
8409 
8410  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
8411  if (result != MA_SUCCESS) {
8412  exitLoop = MA_TRUE;
8413  break;
8414  }
8415 
8416  /*printf("TRACE 2: framesAvailableCapture=%d\n", framesAvailableCapture);*/
8417 
8418  /* Wait for more if nothing is available. */
8419  if (framesAvailableCapture == 0) {
8420  /* In exclusive mode we waited at the top. */
8421  if (pDevice->capture.shareMode != ma_share_mode_shared) {
8422  if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
8423  return MA_ERROR; /* Wait failed. */
8424  }
8425  }
8426 
8427  continue;
8428  }
8429 
8430  /* Getting here means there's data available for writing to the output device. */
8431  mappedBufferSizeInFramesCapture = framesAvailableCapture;
8432  hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedBufferCapture, &mappedBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
8433  if (FAILED(hr)) {
8434  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
8435  exitLoop = MA_TRUE;
8436  break;
8437  }
8438 
8439  /* TODO: How do we handle the capture flags returned by GetBuffer()? In particular, AUDCLNT_BUFFERFLAGS_SILENT (1). */
8440  #ifdef MA_DEBUG_OUTPUT
8441  if (flagsCapture != 0) {
8442  printf("[WASAPI] Capture Flags: %d\n", flagsCapture);
8443  }
8444  #endif
8445 
8446  mappedBufferFramesRemainingCapture = mappedBufferSizeInFramesCapture;
8447 
8448  pDevice->capture._dspFrameCount = mappedBufferSizeInFramesCapture;
8449  pDevice->capture._dspFrames = (const ma_uint8*)pMappedBufferCapture;
8450  }
8451 
8452 
8453  /* At this point we should have both input and output data available. We now need to post it to the convert the data and post it to the client. */
8454  for (;;) {
8455  BYTE* pRunningBufferCapture;
8456  BYTE* pRunningBufferPlayback;
8457  ma_uint32 framesToProcess;
8458  ma_uint32 framesProcessed;
8459 
8460  pRunningBufferCapture = pMappedBufferCapture + ((mappedBufferSizeInFramesCapture - mappedBufferFramesRemainingCapture ) * bpfPlayback);
8461  pRunningBufferPlayback = pMappedBufferPlayback + ((mappedBufferSizeInFramesPlayback - mappedBufferFramesRemainingPlayback) * bpfPlayback);
8462 
8463  /* There may be some data sitting in the converter that needs to be processed first. Once this is exhaused, run the data callback again. */
8464  if (!pDevice->playback.converter.isPassthrough) {
8465  framesProcessed = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, pRunningBufferPlayback, mappedBufferFramesRemainingPlayback);
8466  if (framesProcessed > 0) {
8467  mappedBufferFramesRemainingPlayback -= framesProcessed;
8468  if (mappedBufferFramesRemainingPlayback == 0) {
8469  break;
8470  }
8471  }
8472  }
8473 
8474  /*
8475  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
8476  buffers directly to the callback.
8477  */
8478  if (pDevice->capture.converter.isPassthrough && pDevice->playback.converter.isPassthrough) {
8479  /* Optimal path. We can pass mapped pointers directly to the callback. */
8480  framesToProcess = ma_min(mappedBufferFramesRemainingCapture, mappedBufferFramesRemainingPlayback);
8481  framesProcessed = framesToProcess;
8482 
8483  pDevice->onData(pDevice, pRunningBufferPlayback, pRunningBufferCapture, framesToProcess);
8484 
8485  mappedBufferFramesRemainingCapture -= framesProcessed;
8486  mappedBufferFramesRemainingPlayback -= framesProcessed;
8487 
8488  if (mappedBufferFramesRemainingCapture == 0) {
8489  break; /* Exhausted input data. */
8490  }
8491  if (mappedBufferFramesRemainingPlayback == 0) {
8492  break; /* Exhausted output data. */
8493  }
8494  } else if (pDevice->capture.converter.isPassthrough) {
8495  /* The input buffer is a passthrough, but the playback buffer requires a conversion. */
8496  framesToProcess = ma_min(mappedBufferFramesRemainingCapture, outputDataInExternalFormatCap);
8497  framesProcessed = framesToProcess;
8498 
8499  pDevice->onData(pDevice, outputDataInExternalFormat, pRunningBufferCapture, framesToProcess);
8500  mappedBufferFramesRemainingCapture -= framesProcessed;
8501 
8502  pDevice->playback._dspFrameCount = framesProcessed;
8503  pDevice->playback._dspFrames = (const ma_uint8*)outputDataInExternalFormat;
8504 
8505  if (mappedBufferFramesRemainingCapture == 0) {
8506  break; /* Exhausted input data. */
8507  }
8508  } else if (pDevice->playback.converter.isPassthrough) {
8509  /* The input buffer requires conversion, the playback buffer is passthrough. */
8510  framesToProcess = ma_min(inputDataInExternalFormatCap, mappedBufferFramesRemainingPlayback);
8511  framesProcessed = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, inputDataInExternalFormat, framesToProcess);
8512  if (framesProcessed == 0) {
8513  /* Getting here means we've run out of input data. */
8514  mappedBufferFramesRemainingCapture = 0;
8515  break;
8516  }
8517 
8518  pDevice->onData(pDevice, pRunningBufferPlayback, inputDataInExternalFormat, framesProcessed);
8519  mappedBufferFramesRemainingPlayback -= framesProcessed;
8520 
8521  if (framesProcessed < framesToProcess) {
8522  mappedBufferFramesRemainingCapture = 0;
8523  break; /* Exhausted input data. */
8524  }
8525 
8526  if (mappedBufferFramesRemainingPlayback == 0) {
8527  break; /* Exhausted output data. */
8528  }
8529  } else {
8530  framesToProcess = ma_min(inputDataInExternalFormatCap, outputDataInExternalFormatCap);
8531  framesProcessed = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, inputDataInExternalFormat, framesToProcess);
8532  if (framesProcessed == 0) {
8533  /* Getting here means we've run out of input data. */
8534  mappedBufferFramesRemainingCapture = 0;
8535  break;
8536  }
8537 
8538  pDevice->onData(pDevice, outputDataInExternalFormat, inputDataInExternalFormat, framesProcessed);
8539 
8540  pDevice->playback._dspFrameCount = framesProcessed;
8541  pDevice->playback._dspFrames = (const ma_uint8*)outputDataInExternalFormat;
8542 
8543  if (framesProcessed < framesToProcess) {
8544  /* Getting here means we've run out of input data. */
8545  mappedBufferFramesRemainingCapture = 0;
8546  break;
8547  }
8548  }
8549  }
8550 
8551 
8552  /* If at this point we've run out of capture data we need to release the buffer. */
8553  if (mappedBufferFramesRemainingCapture == 0 && pMappedBufferCapture != NULL) {
8554  hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedBufferSizeInFramesCapture);
8555  if (FAILED(hr)) {
8556  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
8557  exitLoop = MA_TRUE;
8558  break;
8559  }
8560 
8561  /*printf("TRACE: Released capture buffer\n");*/
8562 
8563  pMappedBufferCapture = NULL;
8564  mappedBufferFramesRemainingCapture = 0;
8565  mappedBufferSizeInFramesCapture = 0;
8566  }
8567 
8568  /* Get out of this loop if we're run out of room in the playback buffer. */
8569  if (mappedBufferFramesRemainingPlayback == 0) {
8570  break;
8571  }
8572  }
8573 
8574 
8575  /* If at this point we've run out of data we need to release the buffer. */
8576  if (mappedBufferFramesRemainingPlayback == 0 && pMappedBufferPlayback != NULL) {
8577  hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedBufferSizeInFramesPlayback, 0);
8578  if (FAILED(hr)) {
8579  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
8580  exitLoop = MA_TRUE;
8581  break;
8582  }
8583 
8584  /*printf("TRACE: Released playback buffer\n");*/
8585  framesWrittenToPlaybackDevice += mappedBufferSizeInFramesPlayback;
8586 
8587  pMappedBufferPlayback = NULL;
8588  mappedBufferFramesRemainingPlayback = 0;
8589  mappedBufferSizeInFramesPlayback = 0;
8590  }
8591 
8592  if (!pDevice->wasapi.isStartedPlayback) {
8593  if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*2) {
8594  hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8595  if (FAILED(hr)) {
8596  ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8597  ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8598  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
8599  }
8600  ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
8601  }
8602  }
8603  } break;
8604 
8605 
8606 
8608  {
8609  ma_uint32 framesAvailableCapture;
8610  DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
8611 
8612  /* Wait for data to become available first. */
8613  if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
8614  exitLoop = MA_TRUE;
8615  break; /* Wait failed. */
8616  }
8617 
8618  /* 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. */
8619  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
8620  if (result != MA_SUCCESS) {
8621  exitLoop = MA_TRUE;
8622  break;
8623  }
8624 
8625  if (framesAvailableCapture < pDevice->wasapi.periodSizeInFramesCapture) {
8626  continue; /* Nothing available. Keep waiting. */
8627  }
8628 
8629  /* Map a the data buffer in preparation for sending to the client. */
8630  mappedBufferSizeInFramesCapture = framesAvailableCapture;
8631  hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedBufferCapture, &mappedBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
8632  if (FAILED(hr)) {
8633  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
8634  exitLoop = MA_TRUE;
8635  break;
8636  }
8637 
8638  /* We should have a buffer at this point. */
8639  ma_device__send_frames_to_client(pDevice, mappedBufferSizeInFramesCapture, pMappedBufferCapture);
8640 
8641  /* At this point we're done with the buffer. */
8642  hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedBufferSizeInFramesCapture);
8643  pMappedBufferCapture = 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. */
8644  mappedBufferSizeInFramesCapture = 0;
8645  if (FAILED(hr)) {
8646  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
8647  exitLoop = MA_TRUE;
8648  break;
8649  }
8650  } break;
8651 
8652 
8653 
8655  {
8656  ma_uint32 framesAvailablePlayback;
8657 
8658  /* Wait for space to become available first. */
8659  if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
8660  exitLoop = MA_TRUE;
8661  break; /* Wait failed. */
8662  }
8663 
8664  /* Check how much space is available. If this returns 0 we just keep waiting. */
8665  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
8666  if (result != MA_SUCCESS) {
8667  exitLoop = MA_TRUE;
8668  break;
8669  }
8670 
8671  if (framesAvailablePlayback < pDevice->wasapi.periodSizeInFramesPlayback) {
8672  continue; /* No space available. */
8673  }
8674 
8675  /* Map a the data buffer in preparation for the callback. */
8676  hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedBufferPlayback);
8677  if (FAILED(hr)) {
8678  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
8679  exitLoop = MA_TRUE;
8680  break;
8681  }
8682 
8683  /* We should have a buffer at this point. */
8684  ma_device__read_frames_from_client(pDevice, framesAvailablePlayback, pMappedBufferPlayback);
8685 
8686  /* At this point we're done writing to the device and we just need to release the buffer. */
8687  hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, 0);
8688  pMappedBufferPlayback = 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. */
8689  mappedBufferSizeInFramesPlayback = 0;
8690 
8691  if (FAILED(hr)) {
8692  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
8693  exitLoop = MA_TRUE;
8694  break;
8695  }
8696 
8697  framesWrittenToPlaybackDevice += framesAvailablePlayback;
8698  if (!pDevice->wasapi.isStartedPlayback) {
8699  if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*1) {
8700  hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8701  if (FAILED(hr)) {
8702  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
8703  exitLoop = MA_TRUE;
8704  break;
8705  }
8706  ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
8707  }
8708  }
8709  } break;
8710 
8711  default: return MA_INVALID_ARGS;
8712  }
8713  }
8714 
8715  /* Here is where the device needs to be stopped. */
8716  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
8717  /* Any mapped buffers need to be released. */
8718  if (pMappedBufferCapture != NULL) {
8719  hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedBufferSizeInFramesCapture);
8720  }
8721 
8722  hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8723  if (FAILED(hr)) {
8724  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
8725  }
8726 
8727  /* The audio client needs to be reset otherwise restarting will fail. */
8728  hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8729  if (FAILED(hr)) {
8730  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
8731  }
8732 
8733  ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
8734  }
8735 
8736  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
8737  /* Any mapped buffers need to be released. */
8738  if (pMappedBufferPlayback != NULL) {
8739  hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedBufferSizeInFramesPlayback, 0);
8740  }
8741 
8742  /*
8743  The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
8744  the speakers. This is a problem for very short sounds because it'll result in a significant potion of it not getting played.
8745  */
8746  if (pDevice->wasapi.isStartedPlayback) {
8747  if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
8748  WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
8749  } else {
8750  ma_uint32 prevFramesAvaialablePlayback = (size_t)-1;
8751  ma_uint32 framesAvailablePlayback;
8752  for (;;) {
8753  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
8754  if (result != MA_SUCCESS) {
8755  break;
8756  }
8757 
8758  if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) {
8759  break;
8760  }
8761 
8762  /*
8763  Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
8764  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.
8765  */
8766  if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
8767  break;
8768  }
8769  prevFramesAvaialablePlayback = framesAvailablePlayback;
8770 
8771  WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
8772  ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */
8773  }
8774  }
8775  }
8776 
8777  hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8778  if (FAILED(hr)) {
8779  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
8780  }
8781 
8782  /* The audio client needs to be reset otherwise restarting will fail. */
8783  hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8784  if (FAILED(hr)) {
8785  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
8786  }
8787 
8788  ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
8789  }
8790 
8791  return MA_SUCCESS;
8792 }
8793 
8794 ma_result ma_context_uninit__wasapi(ma_context* pContext)
8795 {
8796  ma_assert(pContext != NULL);
8797  ma_assert(pContext->backend == ma_backend_wasapi);
8798  (void)pContext;
8799 
8800  return MA_SUCCESS;
8801 }
8802 
8803 ma_result ma_context_init__wasapi(const ma_context_config* pConfig, ma_context* pContext)
8804 {
8805  ma_result result = MA_SUCCESS;
8806 
8807  ma_assert(pContext != NULL);
8808 
8809  (void)pConfig;
8810 
8811 #ifdef MA_WIN32_DESKTOP
8812  /*
8813  WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
8814  exclusive mode does not work until SP1.
8815 
8816  Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a lin error.
8817  */
8818  {
8819  ma_OSVERSIONINFOEXW osvi;
8820  ma_handle kernel32DLL;
8821  ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
8822  ma_PFNVerSetConditionMask _VerSetConditionMask;
8823 
8824  kernel32DLL = ma_dlopen(pContext, "kernel32.dll");
8825  if (kernel32DLL == NULL) {
8826  return MA_NO_BACKEND;
8827  }
8828 
8829  _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW)ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW");
8830  _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask");
8831  if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
8832  ma_dlclose(pContext, kernel32DLL);
8833  return MA_NO_BACKEND;
8834  }
8835 
8836  ma_zero_object(&osvi);
8837  osvi.dwOSVersionInfoSize = sizeof(osvi);
8838  osvi.dwMajorVersion = HIBYTE(MA_WIN32_WINNT_VISTA);
8839  osvi.dwMinorVersion = LOBYTE(MA_WIN32_WINNT_VISTA);
8840  osvi.wServicePackMajor = 1;
8841  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))) {
8842  result = MA_SUCCESS;
8843  } else {
8844  result = MA_NO_BACKEND;
8845  }
8846 
8847  ma_dlclose(pContext, kernel32DLL);
8848  }
8849 #endif
8850 
8851  if (result != MA_SUCCESS) {
8852  return result;
8853  }
8854 
8855  pContext->onUninit = ma_context_uninit__wasapi;
8856  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__wasapi;
8857  pContext->onEnumDevices = ma_context_enumerate_devices__wasapi;
8858  pContext->onGetDeviceInfo = ma_context_get_device_info__wasapi;
8859  pContext->onDeviceInit = ma_device_init__wasapi;
8860  pContext->onDeviceUninit = ma_device_uninit__wasapi;
8861  pContext->onDeviceStart = NULL; /* Not used. Started in onDeviceMainLoop. */
8862  pContext->onDeviceStop = NULL; /* Not used. Stopped in onDeviceMainLoop. */
8863  pContext->onDeviceWrite = NULL;
8864  pContext->onDeviceRead = NULL;
8865  pContext->onDeviceMainLoop = ma_device_main_loop__wasapi;
8866 
8867  return result;
8868 }
8869 #endif
8870 
8871 /******************************************************************************
8872 
8873 DirectSound Backend
8874 
8875 ******************************************************************************/
8876 #ifdef MA_HAS_DSOUND
8877 /*#include <dsound.h>*/
8878 
8879 GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};
8880 
8881 /* miniaudio only uses priority or exclusive modes. */
8882 #define MA_DSSCL_NORMAL 1
8883 #define MA_DSSCL_PRIORITY 2
8884 #define MA_DSSCL_EXCLUSIVE 3
8885 #define MA_DSSCL_WRITEPRIMARY 4
8886 
8887 #define MA_DSCAPS_PRIMARYMONO 0x00000001
8888 #define MA_DSCAPS_PRIMARYSTEREO 0x00000002
8889 #define MA_DSCAPS_PRIMARY8BIT 0x00000004
8890 #define MA_DSCAPS_PRIMARY16BIT 0x00000008
8891 #define MA_DSCAPS_CONTINUOUSRATE 0x00000010
8892 #define MA_DSCAPS_EMULDRIVER 0x00000020
8893 #define MA_DSCAPS_CERTIFIED 0x00000040
8894 #define MA_DSCAPS_SECONDARYMONO 0x00000100
8895 #define MA_DSCAPS_SECONDARYSTEREO 0x00000200
8896 #define MA_DSCAPS_SECONDARY8BIT 0x00000400
8897 #define MA_DSCAPS_SECONDARY16BIT 0x00000800
8898 
8899 #define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
8900 #define MA_DSBCAPS_STATIC 0x00000002
8901 #define MA_DSBCAPS_LOCHARDWARE 0x00000004
8902 #define MA_DSBCAPS_LOCSOFTWARE 0x00000008
8903 #define MA_DSBCAPS_CTRL3D 0x00000010
8904 #define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
8905 #define MA_DSBCAPS_CTRLPAN 0x00000040
8906 #define MA_DSBCAPS_CTRLVOLUME 0x00000080
8907 #define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
8908 #define MA_DSBCAPS_CTRLFX 0x00000200
8909 #define MA_DSBCAPS_STICKYFOCUS 0x00004000
8910 #define MA_DSBCAPS_GLOBALFOCUS 0x00008000
8911 #define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
8912 #define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
8913 #define MA_DSBCAPS_LOCDEFER 0x00040000
8914 #define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
8915 
8916 #define MA_DSBPLAY_LOOPING 0x00000001
8917 #define MA_DSBPLAY_LOCHARDWARE 0x00000002
8918 #define MA_DSBPLAY_LOCSOFTWARE 0x00000004
8919 #define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
8920 #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
8921 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
8922 
8923 #define MA_DSCBSTART_LOOPING 0x00000001
8924 
8925 typedef struct
8926 {
8927  DWORD dwSize;
8928  DWORD dwFlags;
8929  DWORD dwBufferBytes;
8930  DWORD dwReserved;
8931  WAVEFORMATEX* lpwfxFormat;
8932  GUID guid3DAlgorithm;
8933 } MA_DSBUFFERDESC;
8934 
8935 typedef struct
8936 {
8937  DWORD dwSize;
8938  DWORD dwFlags;
8939  DWORD dwBufferBytes;
8940  DWORD dwReserved;
8941  WAVEFORMATEX* lpwfxFormat;
8942  DWORD dwFXCount;
8943  void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */
8944 } MA_DSCBUFFERDESC;
8945 
8946 typedef struct
8947 {
8948  DWORD dwSize;
8949  DWORD dwFlags;
8950  DWORD dwMinSecondarySampleRate;
8951  DWORD dwMaxSecondarySampleRate;
8952  DWORD dwPrimaryBuffers;
8953  DWORD dwMaxHwMixingAllBuffers;
8954  DWORD dwMaxHwMixingStaticBuffers;
8955  DWORD dwMaxHwMixingStreamingBuffers;
8956  DWORD dwFreeHwMixingAllBuffers;
8957  DWORD dwFreeHwMixingStaticBuffers;
8958  DWORD dwFreeHwMixingStreamingBuffers;
8959  DWORD dwMaxHw3DAllBuffers;
8960  DWORD dwMaxHw3DStaticBuffers;
8961  DWORD dwMaxHw3DStreamingBuffers;
8962  DWORD dwFreeHw3DAllBuffers;
8963  DWORD dwFreeHw3DStaticBuffers;
8964  DWORD dwFreeHw3DStreamingBuffers;
8965  DWORD dwTotalHwMemBytes;
8966  DWORD dwFreeHwMemBytes;
8967  DWORD dwMaxContigFreeHwMemBytes;
8968  DWORD dwUnlockTransferRateHwBuffers;
8969  DWORD dwPlayCpuOverheadSwBuffers;
8970  DWORD dwReserved1;
8971  DWORD dwReserved2;
8972 } MA_DSCAPS;
8973 
8974 typedef struct
8975 {
8976  DWORD dwSize;
8977  DWORD dwFlags;
8978  DWORD dwBufferBytes;
8979  DWORD dwUnlockTransferRate;
8980  DWORD dwPlayCpuOverhead;
8981 } MA_DSBCAPS;
8982 
8983 typedef struct
8984 {
8985  DWORD dwSize;
8986  DWORD dwFlags;
8987  DWORD dwFormats;
8988  DWORD dwChannels;
8989 } MA_DSCCAPS;
8990 
8991 typedef struct
8992 {
8993  DWORD dwSize;
8994  DWORD dwFlags;
8995  DWORD dwBufferBytes;
8996  DWORD dwReserved;
8997 } MA_DSCBCAPS;
8998 
8999 typedef struct
9000 {
9001  DWORD dwOffset;
9002  HANDLE hEventNotify;
9003 } MA_DSBPOSITIONNOTIFY;
9004 
9005 typedef struct ma_IDirectSound ma_IDirectSound;
9006 typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
9007 typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
9008 typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
9009 typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
9010 
9011 
9012 /*
9013 COM objects. The way these work is that you have a vtable (a list of function pointers, kind of
9014 like how C++ works internally), and then you have a structure with a single member, which is a
9015 pointer to the vtable. The vtable is where the methods of the object are defined. Methods need
9016 to be in a specific order, and parent classes need to have their methods declared first.
9017 */
9018 
9019 /* IDirectSound */
9020 typedef struct
9021 {
9022  /* IUnknown */
9023  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
9024  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
9025  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
9026 
9027  /* IDirectSound */
9028  HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
9029  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
9030  HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
9031  HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
9032  HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
9033  HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
9034  HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
9035  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
9036 } ma_IDirectSoundVtbl;
9037 struct ma_IDirectSound
9038 {
9039  ma_IDirectSoundVtbl* lpVtbl;
9040 };
9041 HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9042 ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9043 ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
9044 HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); }
9045 HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
9046 HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
9047 HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
9048 HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
9049 HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
9050 HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
9051 HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
9052 
9053 
9054 /* IDirectSoundBuffer */
9055 typedef struct
9056 {
9057  /* IUnknown */
9058  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
9059  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
9060  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
9061 
9062  /* IDirectSoundBuffer */
9063  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
9064  HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
9065  HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
9066  HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
9067  HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
9068  HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
9069  HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
9070  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
9071  HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
9072  HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
9073  HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
9074  HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat);
9075  HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
9076  HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
9077  HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
9078  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
9079  HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
9080  HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
9081 } ma_IDirectSoundBufferVtbl;
9082 struct ma_IDirectSoundBuffer
9083 {
9084  ma_IDirectSoundBufferVtbl* lpVtbl;
9085 };
9086 HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9087 ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9088 ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
9089 HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
9090 HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
9091 HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
9092 HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
9093 HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
9094 HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
9095 HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
9096 HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
9097 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); }
9098 HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
9099 HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
9100 HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
9101 HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
9102 HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
9103 HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
9104 HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
9105 HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }
9106 HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
9107 
9108 
9109 /* IDirectSoundCapture */
9110 typedef struct
9111 {
9112  /* IUnknown */
9113  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
9114  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
9115  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
9116 
9117  /* IDirectSoundCapture */
9118  HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
9119  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
9120  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
9121 } ma_IDirectSoundCaptureVtbl;
9122 struct ma_IDirectSoundCapture
9123 {
9124  ma_IDirectSoundCaptureVtbl* lpVtbl;
9125 };
9126 HRESULT ma_IDirectSoundCapture_QueryInterface(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9127 ULONG ma_IDirectSoundCapture_AddRef(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9128 ULONG ma_IDirectSoundCapture_Release(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
9129 HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); }
9130 HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
9131 HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
9132 
9133 
9134 /* IDirectSoundCaptureBuffer */
9135 typedef struct
9136 {
9137  /* IUnknown */
9138  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
9139  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
9140  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
9141 
9142  /* IDirectSoundCaptureBuffer */
9143  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
9144  HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
9145  HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
9146  HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
9147  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
9148  HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
9149  HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
9150  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
9151  HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
9152 } ma_IDirectSoundCaptureBufferVtbl;
9153 struct ma_IDirectSoundCaptureBuffer
9154 {
9155  ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
9156 };
9157 HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9158 ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9159 ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
9160 HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
9161 HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
9162 HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
9163 HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
9164 HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
9165 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); }
9166 HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
9167 HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
9168 HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }
9169 
9170 
9171 /* IDirectSoundNotify */
9172 typedef struct
9173 {
9174  /* IUnknown */
9175  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
9176  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
9177  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
9178 
9179  /* IDirectSoundNotify */
9180  HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
9181 } ma_IDirectSoundNotifyVtbl;
9182 struct ma_IDirectSoundNotify
9183 {
9184  ma_IDirectSoundNotifyVtbl* lpVtbl;
9185 };
9186 HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9187 ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9188 ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
9189 HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
9190 
9191 
9192 typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext);
9193 typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter);
9194 typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
9195 typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter);
9196 typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
9197 
9198 
9199 /*
9200 Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,
9201 the channel count and channel map will be left unmodified.
9202 */
9203 void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
9204 {
9205  WORD channels;
9206  DWORD channelMap;
9207 
9208  channels = 0;
9209  if (pChannelsOut != NULL) {
9210  channels = *pChannelsOut;
9211  }
9212 
9213  channelMap = 0;
9214  if (pChannelMapOut != NULL) {
9215  channelMap = *pChannelMapOut;
9216  }
9217 
9218  /*
9219  The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper
9220  16 bits is for the geometry.
9221  */
9222  switch ((BYTE)(speakerConfig)) {
9223  case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
9224  case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
9225  case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
9226  case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
9227  case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
9228  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;
9229  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;
9230  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;
9231  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;
9232  default: break;
9233  }
9234 
9235  if (pChannelsOut != NULL) {
9236  *pChannelsOut = channels;
9237  }
9238 
9239  if (pChannelMapOut != NULL) {
9240  *pChannelMapOut = channelMap;
9241  }
9242 }
9243 
9244 
9245 ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
9246 {
9247  ma_IDirectSound* pDirectSound;
9248  HWND hWnd;
9249 
9250  ma_assert(pContext != NULL);
9251  ma_assert(ppDirectSound != NULL);
9252 
9253  *ppDirectSound = NULL;
9254  pDirectSound = NULL;
9255 
9256  if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
9257  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9258  }
9259 
9260  /* The cooperative level must be set before doing anything else. */
9261  hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
9262  if (hWnd == NULL) {
9263  hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
9264  }
9265  if (FAILED(ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY))) {
9266  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", MA_SHARE_MODE_NOT_SUPPORTED);
9267  }
9268 
9269  *ppDirectSound = pDirectSound;
9270  return MA_SUCCESS;
9271 }
9272 
9273 ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
9274 {
9275  ma_IDirectSoundCapture* pDirectSoundCapture;
9276 
9277  ma_assert(pContext != NULL);
9278  ma_assert(ppDirectSoundCapture != NULL);
9279 
9280  /* DirectSound does not support exclusive mode for capture. */
9281  if (shareMode == ma_share_mode_exclusive) {
9283  }
9284 
9285  *ppDirectSoundCapture = NULL;
9286  pDirectSoundCapture = NULL;
9287 
9288  if (FAILED(((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL))) {
9289  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9290  }
9291 
9292  *ppDirectSoundCapture = pDirectSoundCapture;
9293  return MA_SUCCESS;
9294 }
9295 
9296 ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
9297 {
9298  MA_DSCCAPS caps;
9299  WORD bitsPerSample;
9300  DWORD sampleRate;
9301 
9302  ma_assert(pContext != NULL);
9303  ma_assert(pDirectSoundCapture != NULL);
9304 
9305  if (pChannels) {
9306  *pChannels = 0;
9307  }
9308  if (pBitsPerSample) {
9309  *pBitsPerSample = 0;
9310  }
9311  if (pSampleRate) {
9312  *pSampleRate = 0;
9313  }
9314 
9315  ma_zero_object(&caps);
9316  caps.dwSize = sizeof(caps);
9317  if (FAILED(ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps))) {
9318  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9319  }
9320 
9321  if (pChannels) {
9322  *pChannels = (WORD)caps.dwChannels;
9323  }
9324 
9325  /* 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. */
9326  bitsPerSample = 16;
9327  sampleRate = 48000;
9328 
9329  if (caps.dwChannels == 1) {
9330  if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
9331  sampleRate = 48000;
9332  } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
9333  sampleRate = 44100;
9334  } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
9335  sampleRate = 22050;
9336  } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
9337  sampleRate = 11025;
9338  } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
9339  sampleRate = 96000;
9340  } else {
9341  bitsPerSample = 8;
9342  if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
9343  sampleRate = 48000;
9344  } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
9345  sampleRate = 44100;
9346  } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
9347  sampleRate = 22050;
9348  } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
9349  sampleRate = 11025;
9350  } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
9351  sampleRate = 96000;
9352  } else {
9353  bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
9354  }
9355  }
9356  } else if (caps.dwChannels == 2) {
9357  if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
9358  sampleRate = 48000;
9359  } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
9360  sampleRate = 44100;
9361  } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
9362  sampleRate = 22050;
9363  } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
9364  sampleRate = 11025;
9365  } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
9366  sampleRate = 96000;
9367  } else {
9368  bitsPerSample = 8;
9369  if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
9370  sampleRate = 48000;
9371  } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
9372  sampleRate = 44100;
9373  } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
9374  sampleRate = 22050;
9375  } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
9376  sampleRate = 11025;
9377  } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
9378  sampleRate = 96000;
9379  } else {
9380  bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
9381  }
9382  }
9383  }
9384 
9385  if (pBitsPerSample) {
9386  *pBitsPerSample = bitsPerSample;
9387  }
9388  if (pSampleRate) {
9389  *pSampleRate = sampleRate;
9390  }
9391 
9392  return MA_SUCCESS;
9393 }
9394 
9395 ma_bool32 ma_context_is_device_id_equal__dsound(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
9396 {
9397  ma_assert(pContext != NULL);
9398  ma_assert(pID0 != NULL);
9399  ma_assert(pID1 != NULL);
9400  (void)pContext;
9401 
9402  return memcmp(pID0->dsound, pID1->dsound, sizeof(pID0->dsound)) == 0;
9403 }
9404 
9405 
9406 typedef struct
9407 {
9408  ma_context* pContext;
9409  ma_device_type deviceType;
9411  void* pUserData;
9412  ma_bool32 terminated;
9413 } ma_context_enumerate_devices_callback_data__dsound;
9414 
9415 BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
9416 {
9417  ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
9418  ma_device_info deviceInfo;
9419 
9420  ma_zero_object(&deviceInfo);
9421 
9422  /* ID. */
9423  if (lpGuid != NULL) {
9424  ma_copy_memory(deviceInfo.id.dsound, lpGuid, 16);
9425  } else {
9426  ma_zero_memory(deviceInfo.id.dsound, 16);
9427  }
9428 
9429  /* Name / Description */
9430  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
9431 
9432 
9433  /* Call the callback function, but make sure we stop enumerating if the callee requested so. */
9434  ma_assert(pData != NULL);
9435  pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData);
9436  if (pData->terminated) {
9437  return FALSE; /* Stop enumeration. */
9438  } else {
9439  return TRUE; /* Continue enumeration. */
9440  }
9441 
9442  (void)lpcstrModule;
9443 }
9444 
9445 ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
9446 {
9447  ma_context_enumerate_devices_callback_data__dsound data;
9448 
9449  ma_assert(pContext != NULL);
9450  ma_assert(callback != NULL);
9451 
9452  data.pContext = pContext;
9453  data.callback = callback;
9454  data.pUserData = pUserData;
9455  data.terminated = MA_FALSE;
9456 
9457  /* Playback. */
9458  if (!data.terminated) {
9459  data.deviceType = ma_device_type_playback;
9460  ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
9461  }
9462 
9463  /* Capture. */
9464  if (!data.terminated) {
9465  data.deviceType = ma_device_type_capture;
9466  ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
9467  }
9468 
9469  return MA_SUCCESS;
9470 }
9471 
9472 
9473 typedef struct
9474 {
9475  const ma_device_id* pDeviceID;
9476  ma_device_info* pDeviceInfo;
9477  ma_bool32 found;
9478 } ma_context_get_device_info_callback_data__dsound;
9479 
9480 BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
9481 {
9482  ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
9483  ma_assert(pData != NULL);
9484 
9485  if ((pData->pDeviceID == NULL || ma_is_guid_equal(pData->pDeviceID->dsound, &MA_GUID_NULL)) && (lpGuid == NULL || ma_is_guid_equal(lpGuid, &MA_GUID_NULL))) {
9486  /* Default device. */
9487  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
9488  pData->found = MA_TRUE;
9489  return FALSE; /* Stop enumeration. */
9490  } else {
9491  /* Not the default device. */
9492  if (lpGuid != NULL) {
9493  if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
9494  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
9495  pData->found = MA_TRUE;
9496  return FALSE; /* Stop enumeration. */
9497  }
9498  }
9499  }
9500 
9501  (void)lpcstrModule;
9502  return TRUE;
9503 }
9504 
9505 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)
9506 {
9507  /* Exclusive mode and capture not supported with DirectSound. */
9508  if (deviceType == ma_device_type_capture && shareMode == ma_share_mode_exclusive) {
9510  }
9511 
9512  if (pDeviceID != NULL) {
9513  ma_context_get_device_info_callback_data__dsound data;
9514 
9515  /* ID. */
9516  ma_copy_memory(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
9517 
9518  /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */
9519  data.pDeviceID = pDeviceID;
9520  data.pDeviceInfo = pDeviceInfo;
9521  data.found = MA_FALSE;
9522  if (deviceType == ma_device_type_playback) {
9523  ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
9524  } else {
9525  ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
9526  }
9527 
9528  if (!data.found) {
9529  return MA_NO_DEVICE;
9530  }
9531  } else {
9532  /* 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. */
9533 
9534  /* ID */
9535  ma_zero_memory(pDeviceInfo->id.dsound, 16);
9536 
9537  /* Name / Description */
9538  if (deviceType == ma_device_type_playback) {
9539  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
9540  } else {
9541  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
9542  }
9543  }
9544 
9545  /* Retrieving detailed information is slightly different depending on the device type. */
9546  if (deviceType == ma_device_type_playback) {
9547  /* Playback. */
9548  ma_IDirectSound* pDirectSound;
9549  ma_result result;
9550  MA_DSCAPS caps;
9551  ma_uint32 iFormat;
9552 
9553  result = ma_context_create_IDirectSound__dsound(pContext, shareMode, pDeviceID, &pDirectSound);
9554  if (result != MA_SUCCESS) {
9555  return result;
9556  }
9557 
9558  ma_zero_object(&caps);
9559  caps.dwSize = sizeof(caps);
9560  if (FAILED(ma_IDirectSound_GetCaps(pDirectSound, &caps))) {
9561  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9562  }
9563 
9564  if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
9565  /* It supports at least stereo, but could support more. */
9566  WORD channels = 2;
9567 
9568  /* Look at the speaker configuration to get a better idea on the channel count. */
9569  DWORD speakerConfig;
9570  if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig))) {
9571  ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
9572  }
9573 
9574  pDeviceInfo->minChannels = channels;
9575  pDeviceInfo->maxChannels = channels;
9576  } else {
9577  /* It does not support stereo, which means we are stuck with mono. */
9578  pDeviceInfo->minChannels = 1;
9579  pDeviceInfo->maxChannels = 1;
9580  }
9581 
9582  /* Sample rate. */
9583  if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
9584  pDeviceInfo->minSampleRate = caps.dwMinSecondarySampleRate;
9585  pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate;
9586 
9587  /*
9588  On my machine the min and max sample rates can return 100 and 200000 respectively. I'd rather these be within
9589  the range of our standard sample rates so I'm clamping.
9590  */
9591  if (caps.dwMinSecondarySampleRate < MA_MIN_SAMPLE_RATE && caps.dwMaxSecondarySampleRate >= MA_MIN_SAMPLE_RATE) {
9592  pDeviceInfo->minSampleRate = MA_MIN_SAMPLE_RATE;
9593  }
9594  if (caps.dwMaxSecondarySampleRate > MA_MAX_SAMPLE_RATE && caps.dwMinSecondarySampleRate <= MA_MAX_SAMPLE_RATE) {
9595  pDeviceInfo->maxSampleRate = MA_MAX_SAMPLE_RATE;
9596  }
9597  } else {
9598  /* Only supports a single sample rate. Set both min an max to the same thing. Do not clamp within the standard rates. */
9599  pDeviceInfo->minSampleRate = caps.dwMaxSecondarySampleRate;
9600  pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate;
9601  }
9602 
9603  /* DirectSound can support all formats. */
9604  pDeviceInfo->formatCount = ma_format_count - 1; /* Minus one because we don't want to include ma_format_unknown. */
9605  for (iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
9606  pDeviceInfo->formats[iFormat] = (ma_format)(iFormat + 1); /* +1 to skip over ma_format_unknown. */
9607  }
9608 
9609  ma_IDirectSound_Release(pDirectSound);
9610  } else {
9611  /*
9612  Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
9613  devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just
9614  reporting the best format.
9615  */
9616  ma_IDirectSoundCapture* pDirectSoundCapture;
9617  ma_result result;
9618  WORD channels;
9619  WORD bitsPerSample;
9620  DWORD sampleRate;
9621 
9622  result = ma_context_create_IDirectSoundCapture__dsound(pContext, shareMode, pDeviceID, &pDirectSoundCapture);
9623  if (result != MA_SUCCESS) {
9624  return result;
9625  }
9626 
9627  result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
9628  if (result != MA_SUCCESS) {
9629  ma_IDirectSoundCapture_Release(pDirectSoundCapture);
9630  return result;
9631  }
9632 
9633  pDeviceInfo->minChannels = channels;
9634  pDeviceInfo->maxChannels = channels;
9635  pDeviceInfo->minSampleRate = sampleRate;
9636  pDeviceInfo->maxSampleRate = sampleRate;
9637  pDeviceInfo->formatCount = 1;
9638  if (bitsPerSample == 8) {
9639  pDeviceInfo->formats[0] = ma_format_u8;
9640  } else if (bitsPerSample == 16) {
9641  pDeviceInfo->formats[0] = ma_format_s16;
9642  } else if (bitsPerSample == 24) {
9643  pDeviceInfo->formats[0] = ma_format_s24;
9644  } else if (bitsPerSample == 32) {
9645  pDeviceInfo->formats[0] = ma_format_s32;
9646  } else {
9647  ma_IDirectSoundCapture_Release(pDirectSoundCapture);
9648  return MA_FORMAT_NOT_SUPPORTED;
9649  }
9650 
9651  ma_IDirectSoundCapture_Release(pDirectSoundCapture);
9652  }
9653 
9654  return MA_SUCCESS;
9655 }
9656 
9657 
9658 typedef struct
9659 {
9660  ma_uint32 deviceCount;
9661  ma_uint32 infoCount;
9662  ma_device_info* pInfo;
9663 } ma_device_enum_data__dsound;
9664 
9665 BOOL CALLBACK ma_enum_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
9666 {
9667  ma_device_enum_data__dsound* pData = (ma_device_enum_data__dsound*)lpContext;
9668  ma_assert(pData != NULL);
9669 
9670  if (pData->pInfo != NULL) {
9671  if (pData->infoCount > 0) {
9672  ma_zero_object(pData->pInfo);
9673  ma_strncpy_s(pData->pInfo->name, sizeof(pData->pInfo->name), lpcstrDescription, (size_t)-1);
9674 
9675  if (lpGuid != NULL) {
9676  ma_copy_memory(pData->pInfo->id.dsound, lpGuid, 16);
9677  } else {
9678  ma_zero_memory(pData->pInfo->id.dsound, 16);
9679  }
9680 
9681  pData->pInfo += 1;
9682  pData->infoCount -= 1;
9683  pData->deviceCount += 1;
9684  }
9685  } else {
9686  pData->deviceCount += 1;
9687  }
9688 
9689  (void)lpcstrModule;
9690  return TRUE;
9691 }
9692 
9693 void ma_device_uninit__dsound(ma_device* pDevice)
9694 {
9695  ma_assert(pDevice != NULL);
9696 
9697  if (pDevice->dsound.pCaptureBuffer != NULL) {
9698  ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
9699  }
9700  if (pDevice->dsound.pCapture != NULL) {
9701  ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
9702  }
9703 
9704  if (pDevice->dsound.pPlaybackBuffer != NULL) {
9705  ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
9706  }
9707  if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
9708  ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
9709  }
9710  if (pDevice->dsound.pPlayback != NULL) {
9711  ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
9712  }
9713 }
9714 
9715 ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF)
9716 {
9717  GUID subformat;
9718 
9719  switch (format)
9720  {
9721  case ma_format_u8:
9722  case ma_format_s16:
9723  case ma_format_s24:
9724  /*case ma_format_s24_32:*/
9725  case ma_format_s32:
9726  {
9727  subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
9728  } break;
9729 
9730  case ma_format_f32:
9731  {
9732  subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
9733  } break;
9734 
9735  default:
9736  return MA_FORMAT_NOT_SUPPORTED;
9737  }
9738 
9739  ma_zero_object(pWF);
9740  pWF->Format.cbSize = sizeof(*pWF);
9741  pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
9742  pWF->Format.nChannels = (WORD)channels;
9743  pWF->Format.nSamplesPerSec = (DWORD)sampleRate;
9744  pWF->Format.wBitsPerSample = (WORD)ma_get_bytes_per_sample(format)*8;
9745  pWF->Format.nBlockAlign = (pWF->Format.nChannels * pWF->Format.wBitsPerSample) / 8;
9746  pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec;
9747  pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample;
9748  pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
9749  pWF->SubFormat = subformat;
9750 
9751  return MA_SUCCESS;
9752 }
9753 
9754 ma_result ma_device_init__dsound(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
9755 {
9756  ma_result result;
9757  ma_uint32 bufferSizeInMilliseconds;
9758 
9759  ma_assert(pDevice != NULL);
9760  ma_zero_object(&pDevice->dsound);
9761 
9762  bufferSizeInMilliseconds = pConfig->bufferSizeInMilliseconds;
9763  if (bufferSizeInMilliseconds == 0) {
9764  bufferSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->bufferSizeInFrames, pConfig->sampleRate);
9765  }
9766 
9767  /* DirectSound should use a latency of about 20ms per period for low latency mode. */
9768  if (pDevice->usingDefaultBufferSize) {
9770  bufferSizeInMilliseconds = 20 * pConfig->periods;
9771  } else {
9772  bufferSizeInMilliseconds = 200 * pConfig->periods;
9773  }
9774  }
9775 
9776  /* 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. */
9777  if ((bufferSizeInMilliseconds/pConfig->periods) < 20) {
9778  bufferSizeInMilliseconds = pConfig->periods * 20;
9779  }
9780 
9781  /*
9782  Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize
9783  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
9784  full-duplex mode.
9785  */
9786  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
9787  WAVEFORMATEXTENSIBLE wf;
9788  MA_DSCBUFFERDESC descDS;
9789  ma_uint32 bufferSizeInFrames;
9790  char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
9791  WAVEFORMATEXTENSIBLE* pActualFormat;
9792 
9793  result = ma_config_to_WAVEFORMATEXTENSIBLE(pConfig->capture.format, pConfig->capture.channels, pConfig->sampleRate, pConfig->capture.channelMap, &wf);
9794  if (result != MA_SUCCESS) {
9795  return result;
9796  }
9797 
9798  result = ma_context_create_IDirectSoundCapture__dsound(pContext, pConfig->capture.shareMode, pConfig->capture.pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
9799  if (result != MA_SUCCESS) {
9800  ma_device_uninit__dsound(pDevice);
9801  return result;
9802  }
9803 
9804  result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec);
9805  if (result != MA_SUCCESS) {
9806  ma_device_uninit__dsound(pDevice);
9807  return result;
9808  }
9809 
9810  wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
9811  wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
9812  wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
9813  wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
9814 
9815  /* The size of the buffer must be a clean multiple of the period count. */
9816  bufferSizeInFrames = (ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, wf.Format.nSamplesPerSec) / pConfig->periods) * pConfig->periods;
9817 
9818  ma_zero_object(&descDS);
9819  descDS.dwSize = sizeof(descDS);
9820  descDS.dwFlags = 0;
9821  descDS.dwBufferBytes = bufferSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, wf.Format.nChannels);
9822  descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
9823  if (FAILED(ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL))) {
9824  ma_device_uninit__dsound(pDevice);
9825  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9826  }
9827 
9828  /* Get the _actual_ properties of the buffer. */
9829  pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
9830  if (FAILED(ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL))) {
9831  ma_device_uninit__dsound(pDevice);
9832  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer.", MA_FORMAT_NOT_SUPPORTED);
9833  }
9834 
9835  pDevice->capture.internalFormat = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
9836  pDevice->capture.internalChannels = pActualFormat->Format.nChannels;
9837  pDevice->capture.internalSampleRate = pActualFormat->Format.nSamplesPerSec;
9838 
9839  /* Get the internal channel map based on the channel mask. */
9840  if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
9841  ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
9842  } else {
9843  ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
9844  }
9845 
9846  /*
9847  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
9848  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.
9849  */
9850  if (bufferSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels))) {
9851  descDS.dwBufferBytes = bufferSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, wf.Format.nChannels);
9852  ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
9853 
9854  if (FAILED(ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL))) {
9855  ma_device_uninit__dsound(pDevice);
9856  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9857  }
9858  }
9859 
9860  /* DirectSound should give us a buffer exactly the size we asked for. */
9861  pDevice->capture.internalBufferSizeInFrames = bufferSizeInFrames;
9862  pDevice->capture.internalPeriods = pConfig->periods;
9863  }
9864 
9865  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
9866  WAVEFORMATEXTENSIBLE wf;
9867  MA_DSBUFFERDESC descDSPrimary;
9868  MA_DSCAPS caps;
9869  char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
9870  WAVEFORMATEXTENSIBLE* pActualFormat;
9871  ma_uint32 bufferSizeInFrames;
9872  MA_DSBUFFERDESC descDS;
9873 
9874  result = ma_config_to_WAVEFORMATEXTENSIBLE(pConfig->playback.format, pConfig->playback.channels, pConfig->sampleRate, pConfig->playback.channelMap, &wf);
9875  if (result != MA_SUCCESS) {
9876  return result;
9877  }
9878 
9879  result = ma_context_create_IDirectSound__dsound(pContext, pConfig->playback.shareMode, pConfig->playback.pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
9880  if (result != MA_SUCCESS) {
9881  ma_device_uninit__dsound(pDevice);
9882  return result;
9883  }
9884 
9885  ma_zero_object(&descDSPrimary);
9886  descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
9887  descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
9888  if (FAILED(ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL))) {
9889  ma_device_uninit__dsound(pDevice);
9890  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9891  }
9892 
9893 
9894  /* We may want to make some adjustments to the format if we are using defaults. */
9895  ma_zero_object(&caps);
9896  caps.dwSize = sizeof(caps);
9897  if (FAILED(ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps))) {
9898  ma_device_uninit__dsound(pDevice);
9899  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9900  }
9901 
9902  if (pDevice->playback.usingDefaultChannels) {
9903  if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
9904  DWORD speakerConfig;
9905 
9906  /* It supports at least stereo, but could support more. */
9907  wf.Format.nChannels = 2;
9908 
9909  /* Look at the speaker configuration to get a better idea on the channel count. */
9910  if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
9911  ma_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask);
9912  }
9913  } else {
9914  /* It does not support stereo, which means we are stuck with mono. */
9915  wf.Format.nChannels = 1;
9916  }
9917  }
9918 
9919  if (pDevice->usingDefaultSampleRate) {
9920  /* We base the sample rate on the values returned by GetCaps(). */
9921  if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
9922  wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
9923  } else {
9924  wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
9925  }
9926  }
9927 
9928  wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
9929  wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
9930 
9931  /*
9932  From MSDN:
9933 
9934  The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
9935  supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
9936  and compare the result with the format that was requested with the SetFormat method.
9937  */
9938  if (FAILED(ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf))) {
9939  ma_device_uninit__dsound(pDevice);
9940  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.", MA_FORMAT_NOT_SUPPORTED);
9941  }
9942 
9943  /* Get the _actual_ properties of the buffer. */
9944  pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
9945  if (FAILED(ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL))) {
9946  ma_device_uninit__dsound(pDevice);
9947  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", MA_FORMAT_NOT_SUPPORTED);
9948  }
9949 
9950  pDevice->playback.internalFormat = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
9951  pDevice->playback.internalChannels = pActualFormat->Format.nChannels;
9952  pDevice->playback.internalSampleRate = pActualFormat->Format.nSamplesPerSec;
9953 
9954  /* Get the internal channel map based on the channel mask. */
9955  if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
9956  ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
9957  } else {
9958  ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
9959  }
9960 
9961  /* The size of the buffer must be a clean multiple of the period count. */
9962  bufferSizeInFrames = (ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, pDevice->playback.internalSampleRate) / pConfig->periods) * pConfig->periods;
9963 
9964  /*
9965  Meaning of dwFlags (from MSDN):
9966 
9967  DSBCAPS_CTRLPOSITIONNOTIFY
9968  The buffer has position notification capability.
9969 
9970  DSBCAPS_GLOBALFOCUS
9971  With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
9972  another application, even if the new application uses DirectSound.
9973 
9974  DSBCAPS_GETCURRENTPOSITION2
9975  In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
9976  sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
9977  application can get a more accurate play cursor.
9978  */
9979  ma_zero_object(&descDS);
9980  descDS.dwSize = sizeof(descDS);
9981  descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
9982  descDS.dwBufferBytes = bufferSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
9983  descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
9984  if (FAILED(ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL))) {
9985  ma_device_uninit__dsound(pDevice);
9986  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
9987  }
9988 
9989  /* DirectSound should give us a buffer exactly the size we asked for. */
9990  pDevice->playback.internalBufferSizeInFrames = bufferSizeInFrames;
9991  pDevice->playback.internalPeriods = pConfig->periods;
9992  }
9993 
9994  (void)pContext;
9995  return MA_SUCCESS;
9996 }
9997 
9998 
9999 ma_result ma_device_main_loop__dsound(ma_device* pDevice)
10000 {
10001  ma_result result = MA_SUCCESS;
10002  ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
10003  ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
10004  HRESULT hr;
10005  DWORD lockOffsetInBytesCapture;
10006  DWORD lockSizeInBytesCapture;
10007  DWORD mappedSizeInBytesCapture;
10008  void* pMappedBufferCapture;
10009  DWORD lockOffsetInBytesPlayback;
10010  DWORD lockSizeInBytesPlayback;
10011  DWORD mappedSizeInBytesPlayback;
10012  void* pMappedBufferPlayback;
10013  DWORD prevReadCursorInBytesCapture = 0;
10014  DWORD prevPlayCursorInBytesPlayback = 0;
10015  ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
10016  DWORD virtualWriteCursorInBytesPlayback = 0;
10017  ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
10018  ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
10019  ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */
10020  ma_uint32 waitTimeInMilliseconds = 1;
10021 
10022  ma_assert(pDevice != NULL);
10023 
10024  /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */
10025  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
10026  if (FAILED(ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING))) {
10027  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
10028  }
10029  }
10030 
10031  while (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
10032  switch (pDevice->type)
10033  {
10034  case ma_device_type_duplex:
10035  {
10036  DWORD physicalCaptureCursorInBytes;
10037  DWORD physicalReadCursorInBytes;
10038  if (FAILED(ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes))) {
10039  return MA_ERROR;
10040  }
10041 
10042  /* If nothing is available we just sleep for a bit and return from this iteration. */
10043  if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
10044  ma_sleep(waitTimeInMilliseconds);
10045  continue; /* Nothing is available in the capture buffer. */
10046  }
10047 
10048  /*
10049  The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure
10050  we don't return until every frame has been copied over.
10051  */
10052  if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
10053  /* The capture position has not looped. This is the simple case. */
10054  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
10055  lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
10056  } else {
10057  /*
10058  The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
10059  do it again from the start.
10060  */
10061  if (prevReadCursorInBytesCapture < pDevice->capture.internalBufferSizeInFrames*bpfCapture) {
10062  /* Lock up to the end of the buffer. */
10063  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
10064  lockSizeInBytesCapture = (pDevice->capture.internalBufferSizeInFrames*bpfCapture) - prevReadCursorInBytesCapture;
10065  } else {
10066  /* Lock starting from the start of the buffer. */
10067  lockOffsetInBytesCapture = 0;
10068  lockSizeInBytesCapture = physicalReadCursorInBytes;
10069  }
10070  }
10071 
10072  if (lockSizeInBytesCapture == 0) {
10073  ma_sleep(waitTimeInMilliseconds);
10074  continue; /* Nothing is available in the capture buffer. */
10075  }
10076 
10077  hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
10078  if (FAILED(hr)) {
10079  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
10080  }
10081 
10082 
10083  /* 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. */
10084  pDevice->capture._dspFrameCount = mappedSizeInBytesCapture / bpfCapture;
10085  pDevice->capture._dspFrames = (const ma_uint8*)pMappedBufferCapture;
10086  for (;;) { /* Keep writing to the playback device. */
10087  ma_uint8 inputFramesInExternalFormat[4096];
10088  ma_uint32 inputFramesInExternalFormatCap = sizeof(inputFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
10089  ma_uint32 inputFramesInExternalFormatCount;
10090  ma_uint8 outputFramesInExternalFormat[4096];
10091  ma_uint32 outputFramesInExternalFormatCap = sizeof(outputFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
10092 
10093  inputFramesInExternalFormatCount = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, inputFramesInExternalFormat, ma_min(inputFramesInExternalFormatCap, outputFramesInExternalFormatCap));
10094  if (inputFramesInExternalFormatCount == 0) {
10095  break; /* No more input data. */
10096  }
10097 
10098  pDevice->onData(pDevice, outputFramesInExternalFormat, inputFramesInExternalFormat, inputFramesInExternalFormatCount);
10099 
10100  /* At this point we have input and output data in external format. All we need to do now is convert it to the output format. This may take a few passes. */
10101  pDevice->playback._dspFrameCount = inputFramesInExternalFormatCount;
10102  pDevice->playback._dspFrames = (const ma_uint8*)outputFramesInExternalFormat;
10103  for (;;) {
10104  ma_uint32 framesWrittenThisIteration;
10105  DWORD physicalPlayCursorInBytes;
10106  DWORD physicalWriteCursorInBytes;
10107  DWORD availableBytesPlayback;
10108  DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */
10109 
10110  /* We need the physical play and write cursors. */
10111  if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
10112  break;
10113  }
10114 
10115  if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
10116  physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
10117  }
10118  prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
10119 
10120  /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
10121  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
10122  /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
10123  if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
10124  availableBytesPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - virtualWriteCursorInBytesPlayback;
10125  availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
10126  } else {
10127  /* This is an error. */
10128  #ifdef MA_DEBUG_OUTPUT
10129  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);
10130  #endif
10131  availableBytesPlayback = 0;
10132  }
10133  } else {
10134  /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
10135  if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
10136  availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
10137  } else {
10138  /* This is an error. */
10139  #ifdef MA_DEBUG_OUTPUT
10140  printf("[DirectSound] (Duplex/Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
10141  #endif
10142  availableBytesPlayback = 0;
10143  }
10144  }
10145 
10146  #ifdef MA_DEBUG_OUTPUT
10147  /*printf("[DirectSound] (Duplex/Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
10148  #endif
10149 
10150  /* If there's no room available for writing we need to wait for more. */
10151  if (availableBytesPlayback == 0) {
10152  /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
10153  if (!isPlaybackDeviceStarted) {
10154  if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
10155  ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
10156  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
10157  }
10158  isPlaybackDeviceStarted = MA_TRUE;
10159  } else {
10160  ma_sleep(waitTimeInMilliseconds);
10161  continue;
10162  }
10163  }
10164 
10165 
10166  /* 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. */
10167  lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
10168  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
10169  /* Same loop iteration. Go up to the end of the buffer. */
10170  lockSizeInBytesPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - virtualWriteCursorInBytesPlayback;
10171  } else {
10172  /* Different loop iterations. Go up to the physical play cursor. */
10173  lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
10174  }
10175 
10176  hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
10177  if (FAILED(hr)) {
10178  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
10179  break;
10180  }
10181 
10182  /*
10183  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
10184  endless glitching due to it constantly running out of data.
10185  */
10186  if (isPlaybackDeviceStarted) {
10187  DWORD bytesQueuedForPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - availableBytesPlayback;
10188  if (bytesQueuedForPlayback < ((pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*bpfPlayback)) {
10189  silentPaddingInBytes = ((pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*2*bpfPlayback) - bytesQueuedForPlayback;
10190  if (silentPaddingInBytes > lockSizeInBytesPlayback) {
10191  silentPaddingInBytes = lockSizeInBytesPlayback;
10192  }
10193 
10194  #ifdef MA_DEBUG_OUTPUT
10195  printf("[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%d, silentPaddingInBytes=%d\n", availableBytesPlayback, silentPaddingInBytes);
10196  #endif
10197  }
10198  }
10199 
10200  /* At this point we have a buffer for output. */
10201  if (silentPaddingInBytes > 0) {
10202  ma_zero_memory(pMappedBufferPlayback, silentPaddingInBytes);
10203  framesWrittenThisIteration = silentPaddingInBytes/bpfPlayback;
10204  } else {
10205  framesWrittenThisIteration = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, pMappedBufferPlayback, mappedSizeInBytesPlayback/bpfPlayback);
10206  }
10207 
10208 
10209  hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedBufferPlayback, framesWrittenThisIteration*bpfPlayback, NULL, 0);
10210  if (FAILED(hr)) {
10211  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
10212  break;
10213  }
10214 
10215  virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfPlayback;
10216  if ((virtualWriteCursorInBytesPlayback/bpfPlayback) == pDevice->playback.internalBufferSizeInFrames) {
10217  virtualWriteCursorInBytesPlayback = 0;
10218  virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
10219  }
10220 
10221  /*
10222  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
10223  a bit of a buffer to prevent the playback buffer from getting starved.
10224  */
10225  framesWrittenToPlaybackDevice += framesWrittenThisIteration;
10226  if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= ((pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*2)) {
10227  if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
10228  ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
10229  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
10230  }
10231  isPlaybackDeviceStarted = MA_TRUE;
10232  }
10233 
10234  if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfPlayback) {
10235  break; /* We're finished with the output data.*/
10236  }
10237  }
10238 
10239  if (inputFramesInExternalFormatCount < inputFramesInExternalFormatCap) {
10240  break; /* We just consumed every input sample. */
10241  }
10242  }
10243 
10244 
10245  /* At this point we're done with the mapped portion of the capture buffer. */
10246  hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedBufferCapture, mappedSizeInBytesCapture, NULL, 0);
10247  if (FAILED(hr)) {
10248  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
10249  }
10250  prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
10251  } break;
10252 
10253 
10254 
10256  {
10257  DWORD physicalCaptureCursorInBytes;
10258  DWORD physicalReadCursorInBytes;
10259  if (FAILED(ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes))) {
10260  return MA_ERROR;
10261  }
10262 
10263  /* If the previous capture position is the same as the current position we need to wait a bit longer. */
10264  if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
10265  ma_sleep(waitTimeInMilliseconds);
10266  continue;
10267  }
10268 
10269  /* Getting here means we have capture data available. */
10270  if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
10271  /* The capture position has not looped. This is the simple case. */
10272  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
10273  lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
10274  } else {
10275  /*
10276  The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
10277  do it again from the start.
10278  */
10279  if (prevReadCursorInBytesCapture < pDevice->capture.internalBufferSizeInFrames*bpfCapture) {
10280  /* Lock up to the end of the buffer. */
10281  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
10282  lockSizeInBytesCapture = (pDevice->capture.internalBufferSizeInFrames*bpfCapture) - prevReadCursorInBytesCapture;
10283  } else {
10284  /* Lock starting from the start of the buffer. */
10285  lockOffsetInBytesCapture = 0;
10286  lockSizeInBytesCapture = physicalReadCursorInBytes;
10287  }
10288  }
10289 
10290  #ifdef MA_DEBUG_OUTPUT
10291  /*printf("[DirectSound] (Capture) physicalCaptureCursorInBytes=%d, physicalReadCursorInBytes=%d\n", physicalCaptureCursorInBytes, physicalReadCursorInBytes);*/
10292  /*printf("[DirectSound] (Capture) lockOffsetInBytesCapture=%d, lockSizeInBytesCapture=%d\n", lockOffsetInBytesCapture, lockSizeInBytesCapture);*/
10293  #endif
10294 
10295  if (lockSizeInBytesCapture < (pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods)) {
10296  ma_sleep(waitTimeInMilliseconds);
10297  continue; /* Nothing is available in the capture buffer. */
10298  }
10299 
10300  hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
10301  if (FAILED(hr)) {
10302  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
10303  }
10304 
10305  #ifdef MA_DEBUG_OUTPUT
10306  if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
10307  printf("[DirectSound] (Capture) lockSizeInBytesCapture=%d != mappedSizeInBytesCapture=%d\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
10308  }
10309  #endif
10310 
10311  ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfCapture, pMappedBufferCapture);
10312 
10313  hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedBufferCapture, mappedSizeInBytesCapture, NULL, 0);
10314  if (FAILED(hr)) {
10315  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
10316  }
10317  prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
10318 
10319  if (prevReadCursorInBytesCapture == (pDevice->capture.internalBufferSizeInFrames*bpfCapture)) {
10320  prevReadCursorInBytesCapture = 0;
10321  }
10322  } break;
10323 
10324 
10325 
10327  {
10328  DWORD availableBytesPlayback;
10329  DWORD physicalPlayCursorInBytes;
10330  DWORD physicalWriteCursorInBytes;
10331  if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
10332  break;
10333  }
10334 
10335  if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
10336  physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
10337  }
10338  prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
10339 
10340  /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
10341  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
10342  /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
10343  if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
10344  availableBytesPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - virtualWriteCursorInBytesPlayback;
10345  availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
10346  } else {
10347  /* This is an error. */
10348  #ifdef MA_DEBUG_OUTPUT
10349  printf("[DirectSound] (Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
10350  #endif
10351  availableBytesPlayback = 0;
10352  }
10353  } else {
10354  /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
10355  if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
10356  availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
10357  } else {
10358  /* This is an error. */
10359  #ifdef MA_DEBUG_OUTPUT
10360  printf("[DirectSound] (Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
10361  #endif
10362  availableBytesPlayback = 0;
10363  }
10364  }
10365 
10366  #ifdef MA_DEBUG_OUTPUT
10367  /*printf("[DirectSound] (Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
10368  #endif
10369 
10370  /* If there's no room available for writing we need to wait for more. */
10371  if (availableBytesPlayback < (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)) {
10372  /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
10373  if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
10374  if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
10375  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
10376  }
10377  isPlaybackDeviceStarted = MA_TRUE;
10378  } else {
10379  ma_sleep(waitTimeInMilliseconds);
10380  continue;
10381  }
10382  }
10383 
10384  /* 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. */
10385  lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
10386  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
10387  /* Same loop iteration. Go up to the end of the buffer. */
10388  lockSizeInBytesPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - virtualWriteCursorInBytesPlayback;
10389  } else {
10390  /* Different loop iterations. Go up to the physical play cursor. */
10391  lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
10392  }
10393 
10394  hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
10395  if (FAILED(hr)) {
10396  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
10397  break;
10398  }
10399 
10400  /* At this point we have a buffer for output. */
10401  ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfPlayback), pMappedBufferPlayback);
10402 
10403  hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
10404  if (FAILED(hr)) {
10405  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
10406  break;
10407  }
10408 
10409  virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
10410  if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalBufferSizeInFrames*bpfPlayback) {
10411  virtualWriteCursorInBytesPlayback = 0;
10412  virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
10413  }
10414 
10415  /*
10416  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
10417  a bit of a buffer to prevent the playback buffer from getting starved.
10418  */
10419  framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfPlayback;
10420  if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)) {
10421  if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
10422  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
10423  }
10424  isPlaybackDeviceStarted = MA_TRUE;
10425  }
10426  } break;
10427 
10428 
10429  default: return MA_INVALID_ARGS; /* Invalid device type. */
10430  }
10431 
10432  if (result != MA_SUCCESS) {
10433  return result;
10434  }
10435  }
10436 
10437  /* Getting here means the device is being stopped. */
10438  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
10439  if (FAILED(ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer))) {
10440  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
10441  }
10442  }
10443 
10444  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
10445  /* 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. */
10446  if (isPlaybackDeviceStarted) {
10447  for (;;) {
10448  DWORD availableBytesPlayback = 0;
10449  DWORD physicalPlayCursorInBytes;
10450  DWORD physicalWriteCursorInBytes;
10451  if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
10452  break;
10453  }
10454 
10455  if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
10456  physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
10457  }
10458  prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
10459 
10460  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
10461  /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
10462  if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
10463  availableBytesPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - virtualWriteCursorInBytesPlayback;
10464  availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
10465  } else {
10466  break;
10467  }
10468  } else {
10469  /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
10470  if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
10471  availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
10472  } else {
10473  break;
10474  }
10475  }
10476 
10477  if (availableBytesPlayback >= (pDevice->playback.internalBufferSizeInFrames*bpfPlayback)) {
10478  break;
10479  }
10480 
10481  ma_sleep(waitTimeInMilliseconds);
10482  }
10483  }
10484 
10485  if (FAILED(ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer))) {
10486  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
10487  }
10488 
10489  ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
10490  }
10491 
10492  return MA_SUCCESS;
10493 }
10494 
10495 ma_result ma_context_uninit__dsound(ma_context* pContext)
10496 {
10497  ma_assert(pContext != NULL);
10498  ma_assert(pContext->backend == ma_backend_dsound);
10499 
10500  ma_dlclose(pContext, pContext->dsound.hDSoundDLL);
10501 
10502  return MA_SUCCESS;
10503 }
10504 
10505 ma_result ma_context_init__dsound(const ma_context_config* pConfig, ma_context* pContext)
10506 {
10507  ma_assert(pContext != NULL);
10508 
10509  (void)pConfig;
10510 
10511  pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll");
10512  if (pContext->dsound.hDSoundDLL == NULL) {
10513  return MA_API_NOT_FOUND;
10514  }
10515 
10516  pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate");
10517  pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
10518  pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
10519  pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
10520 
10521  pContext->onUninit = ma_context_uninit__dsound;
10522  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__dsound;
10523  pContext->onEnumDevices = ma_context_enumerate_devices__dsound;
10524  pContext->onGetDeviceInfo = ma_context_get_device_info__dsound;
10525  pContext->onDeviceInit = ma_device_init__dsound;
10526  pContext->onDeviceUninit = ma_device_uninit__dsound;
10527  pContext->onDeviceStart = NULL; /* Not used. Started in onDeviceMainLoop. */
10528  pContext->onDeviceStop = NULL; /* Not used. Stopped in onDeviceMainLoop. */
10529  pContext->onDeviceWrite = NULL;
10530  pContext->onDeviceRead = NULL;
10531  pContext->onDeviceMainLoop = ma_device_main_loop__dsound;
10532 
10533  return MA_SUCCESS;
10534 }
10535 #endif
10536 
10537 
10538 
10539 /******************************************************************************
10540 
10541 WinMM Backend
10542 
10543 ******************************************************************************/
10544 #ifdef MA_HAS_WINMM
10545 
10546 /*
10547 Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures
10548 are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping
10549 the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version.
10550 */
10551 typedef struct
10552 {
10553  WORD wMid;
10554  WORD wPid;
10555  MMVERSION vDriverVersion;
10556  CHAR szPname[MAXPNAMELEN];
10557  DWORD dwFormats;
10558  WORD wChannels;
10559  WORD wReserved1;
10560  DWORD dwSupport;
10561  GUID ManufacturerGuid;
10562  GUID ProductGuid;
10563  GUID NameGuid;
10564 } MA_WAVEOUTCAPS2A;
10565 typedef struct
10566 {
10567  WORD wMid;
10568  WORD wPid;
10569  MMVERSION vDriverVersion;
10570  CHAR szPname[MAXPNAMELEN];
10571  DWORD dwFormats;
10572  WORD wChannels;
10573  WORD wReserved1;
10574  GUID ManufacturerGuid;
10575  GUID ProductGuid;
10576  GUID NameGuid;
10577 } MA_WAVEINCAPS2A;
10578 
10579 typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
10580 typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc);
10581 typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
10582 typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo);
10583 typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
10584 typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
10585 typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
10586 typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo);
10587 typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
10588 typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic);
10589 typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
10590 typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi);
10591 typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
10592 typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
10593 typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
10594 typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi);
10595 typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi);
10596 
10597 ma_result ma_result_from_MMRESULT(MMRESULT resultMM)
10598 {
10599  switch (resultMM) {
10600  case MMSYSERR_NOERROR: return MA_SUCCESS;
10601  case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
10602  case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
10603  case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
10604  case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
10605  case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
10606  case MMSYSERR_HANDLEBUSY: return MA_DEVICE_BUSY;
10607  case MMSYSERR_ERROR: return MA_ERROR;
10608  default: return MA_ERROR;
10609  }
10610 }
10611 
10612 char* ma_find_last_character(char* str, char ch)
10613 {
10614  char* last;
10615 
10616  if (str == NULL) {
10617  return NULL;
10618  }
10619 
10620  last = NULL;
10621  while (*str != '\0') {
10622  if (*str == ch) {
10623  last = str;
10624  }
10625 
10626  str += 1;
10627  }
10628 
10629  return last;
10630 }
10631 
10632 
10633 /*
10634 Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so
10635 we can do things generically and typesafely. Names are being kept the same for consistency.
10636 */
10637 typedef struct
10638 {
10639  CHAR szPname[MAXPNAMELEN];
10640  DWORD dwFormats;
10641  WORD wChannels;
10642  GUID NameGuid;
10643 } MA_WAVECAPSA;
10644 
10645 ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
10646 {
10647  WORD bitsPerSample = 0;
10648  DWORD sampleRate = 0;
10649 
10650  if (pBitsPerSample) {
10651  *pBitsPerSample = 0;
10652  }
10653  if (pSampleRate) {
10654  *pSampleRate = 0;
10655  }
10656 
10657  if (channels == 1) {
10658  bitsPerSample = 16;
10659  if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
10660  sampleRate = 48000;
10661  } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
10662  sampleRate = 44100;
10663  } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
10664  sampleRate = 22050;
10665  } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
10666  sampleRate = 11025;
10667  } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
10668  sampleRate = 96000;
10669  } else {
10670  bitsPerSample = 8;
10671  if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
10672  sampleRate = 48000;
10673  } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
10674  sampleRate = 44100;
10675  } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
10676  sampleRate = 22050;
10677  } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
10678  sampleRate = 11025;
10679  } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
10680  sampleRate = 96000;
10681  } else {
10682  return MA_FORMAT_NOT_SUPPORTED;
10683  }
10684  }
10685  } else {
10686  bitsPerSample = 16;
10687  if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
10688  sampleRate = 48000;
10689  } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
10690  sampleRate = 44100;
10691  } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
10692  sampleRate = 22050;
10693  } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
10694  sampleRate = 11025;
10695  } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
10696  sampleRate = 96000;
10697  } else {
10698  bitsPerSample = 8;
10699  if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
10700  sampleRate = 48000;
10701  } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
10702  sampleRate = 44100;
10703  } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
10704  sampleRate = 22050;
10705  } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
10706  sampleRate = 11025;
10707  } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
10708  sampleRate = 96000;
10709  } else {
10710  return MA_FORMAT_NOT_SUPPORTED;
10711  }
10712  }
10713  }
10714 
10715  if (pBitsPerSample) {
10716  *pBitsPerSample = bitsPerSample;
10717  }
10718  if (pSampleRate) {
10719  *pSampleRate = sampleRate;
10720  }
10721 
10722  return MA_SUCCESS;
10723 }
10724 
10725 ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF)
10726 {
10727  ma_assert(pWF != NULL);
10728 
10729  ma_zero_object(pWF);
10730  pWF->cbSize = sizeof(*pWF);
10731  pWF->wFormatTag = WAVE_FORMAT_PCM;
10732  pWF->nChannels = (WORD)channels;
10733  if (pWF->nChannels > 2) {
10734  pWF->nChannels = 2;
10735  }
10736 
10737  if (channels == 1) {
10738  pWF->wBitsPerSample = 16;
10739  if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
10740  pWF->nSamplesPerSec = 48000;
10741  } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
10742  pWF->nSamplesPerSec = 44100;
10743  } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
10744  pWF->nSamplesPerSec = 22050;
10745  } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
10746  pWF->nSamplesPerSec = 11025;
10747  } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
10748  pWF->nSamplesPerSec = 96000;
10749  } else {
10750  pWF->wBitsPerSample = 8;
10751  if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
10752  pWF->nSamplesPerSec = 48000;
10753  } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
10754  pWF->nSamplesPerSec = 44100;
10755  } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
10756  pWF->nSamplesPerSec = 22050;
10757  } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
10758  pWF->nSamplesPerSec = 11025;
10759  } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
10760  pWF->nSamplesPerSec = 96000;
10761  } else {
10762  return MA_FORMAT_NOT_SUPPORTED;
10763  }
10764  }
10765  } else {
10766  pWF->wBitsPerSample = 16;
10767  if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
10768  pWF->nSamplesPerSec = 48000;
10769  } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
10770  pWF->nSamplesPerSec = 44100;
10771  } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
10772  pWF->nSamplesPerSec = 22050;
10773  } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
10774  pWF->nSamplesPerSec = 11025;
10775  } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
10776  pWF->nSamplesPerSec = 96000;
10777  } else {
10778  pWF->wBitsPerSample = 8;
10779  if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
10780  pWF->nSamplesPerSec = 48000;
10781  } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
10782  pWF->nSamplesPerSec = 44100;
10783  } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
10784  pWF->nSamplesPerSec = 22050;
10785  } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
10786  pWF->nSamplesPerSec = 11025;
10787  } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
10788  pWF->nSamplesPerSec = 96000;
10789  } else {
10790  return MA_FORMAT_NOT_SUPPORTED;
10791  }
10792  }
10793  }
10794 
10795  pWF->nBlockAlign = (pWF->nChannels * pWF->wBitsPerSample) / 8;
10796  pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
10797 
10798  return MA_SUCCESS;
10799 }
10800 
10801 ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
10802 {
10803  WORD bitsPerSample;
10804  DWORD sampleRate;
10805  ma_result result;
10806 
10807  ma_assert(pContext != NULL);
10808  ma_assert(pCaps != NULL);
10809  ma_assert(pDeviceInfo != NULL);
10810 
10811  /*
10812  Name / Description
10813 
10814  Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking
10815  situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try
10816  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.
10817  */
10818 
10819  /* Set the default to begin with. */
10820  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
10821 
10822  /*
10823  Now try the registry. There's a few things to consider here:
10824  - The name GUID can be null, in which we case we just need to stick to the original 31 characters.
10825  - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
10826  - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
10827  problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"),
10828  but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to
10829  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
10830  name, and then concatenate the name from the registry.
10831  */
10832  if (!ma_is_guid_equal(&pCaps->NameGuid, &MA_GUID_NULL)) {
10833  wchar_t guidStrW[256];
10834  if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
10835  char guidStr[256];
10836  char keyStr[1024];
10837  HKEY hKey;
10838 
10839  WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
10840 
10841  ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
10842  ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
10843 
10844  if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
10845  BYTE nameFromReg[512];
10846  DWORD nameFromRegSize = sizeof(nameFromReg);
10847  result = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize);
10848  ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
10849 
10850  if (result == ERROR_SUCCESS) {
10851  /* We have the value from the registry, so now we need to construct the name string. */
10852  char name[1024];
10853  if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
10854  char* nameBeg = ma_find_last_character(name, '(');
10855  if (nameBeg != NULL) {
10856  size_t leadingLen = (nameBeg - name);
10857  ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
10858 
10859  /* The closing ")", if it can fit. */
10860  if (leadingLen + nameFromRegSize < sizeof(name)-1) {
10861  ma_strcat_s(name, sizeof(name), ")");
10862  }
10863 
10864  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
10865  }
10866  }
10867  }
10868  }
10869  }
10870  }
10871 
10872 
10873  result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
10874  if (result != MA_SUCCESS) {
10875  return result;
10876  }
10877 
10878  pDeviceInfo->minChannels = pCaps->wChannels;
10879  pDeviceInfo->maxChannels = pCaps->wChannels;
10880  pDeviceInfo->minSampleRate = sampleRate;
10881  pDeviceInfo->maxSampleRate = sampleRate;
10882  pDeviceInfo->formatCount = 1;
10883  if (bitsPerSample == 8) {
10884  pDeviceInfo->formats[0] = ma_format_u8;
10885  } else if (bitsPerSample == 16) {
10886  pDeviceInfo->formats[0] = ma_format_s16;
10887  } else if (bitsPerSample == 24) {
10888  pDeviceInfo->formats[0] = ma_format_s24;
10889  } else if (bitsPerSample == 32) {
10890  pDeviceInfo->formats[0] = ma_format_s32;
10891  } else {
10892  return MA_FORMAT_NOT_SUPPORTED;
10893  }
10894 
10895  return MA_SUCCESS;
10896 }
10897 
10898 ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
10899 {
10900  MA_WAVECAPSA caps;
10901 
10902  ma_assert(pContext != NULL);
10903  ma_assert(pCaps != NULL);
10904  ma_assert(pDeviceInfo != NULL);
10905 
10906  ma_copy_memory(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
10907  caps.dwFormats = pCaps->dwFormats;
10908  caps.wChannels = pCaps->wChannels;
10909  caps.NameGuid = pCaps->NameGuid;
10910  return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
10911 }
10912 
10913 ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
10914 {
10915  MA_WAVECAPSA caps;
10916 
10917  ma_assert(pContext != NULL);
10918  ma_assert(pCaps != NULL);
10919  ma_assert(pDeviceInfo != NULL);
10920 
10921  ma_copy_memory(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
10922  caps.dwFormats = pCaps->dwFormats;
10923  caps.wChannels = pCaps->wChannels;
10924  caps.NameGuid = pCaps->NameGuid;
10925  return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
10926 }
10927 
10928 
10929 ma_bool32 ma_context_is_device_id_equal__winmm(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
10930 {
10931  ma_assert(pContext != NULL);
10932  ma_assert(pID0 != NULL);
10933  ma_assert(pID1 != NULL);
10934  (void)pContext;
10935 
10936  return pID0->winmm == pID1->winmm;
10937 }
10938 
10939 ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
10940 {
10941  UINT playbackDeviceCount;
10942  UINT captureDeviceCount;
10943  UINT iPlaybackDevice;
10944  UINT iCaptureDevice;
10945 
10946  ma_assert(pContext != NULL);
10947  ma_assert(callback != NULL);
10948 
10949  /* Playback. */
10950  playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
10951  for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
10952  MMRESULT result;
10953  MA_WAVEOUTCAPS2A caps;
10954 
10955  ma_zero_object(&caps);
10956 
10957  result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps));
10958  if (result == MMSYSERR_NOERROR) {
10959  ma_device_info deviceInfo;
10960 
10961  ma_zero_object(&deviceInfo);
10962  deviceInfo.id.winmm = iPlaybackDevice;
10963 
10964  if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
10965  ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
10966  if (cbResult == MA_FALSE) {
10967  return MA_SUCCESS; /* Enumeration was stopped. */
10968  }
10969  }
10970  }
10971  }
10972 
10973  /* Capture. */
10974  captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
10975  for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
10976  MMRESULT result;
10977  MA_WAVEINCAPS2A caps;
10978 
10979  ma_zero_object(&caps);
10980 
10981  result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps));
10982  if (result == MMSYSERR_NOERROR) {
10983  ma_device_info deviceInfo;
10984 
10985  ma_zero_object(&deviceInfo);
10986  deviceInfo.id.winmm = iCaptureDevice;
10987 
10988  if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
10989  ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
10990  if (cbResult == MA_FALSE) {
10991  return MA_SUCCESS; /* Enumeration was stopped. */
10992  }
10993  }
10994  }
10995  }
10996 
10997  return MA_SUCCESS;
10998 }
10999 
11000 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)
11001 {
11002  UINT winMMDeviceID;
11003 
11004  ma_assert(pContext != NULL);
11005 
11006  if (shareMode == ma_share_mode_exclusive) {
11008  }
11009 
11010  winMMDeviceID = 0;
11011  if (pDeviceID != NULL) {
11012  winMMDeviceID = (UINT)pDeviceID->winmm;
11013  }
11014 
11015  pDeviceInfo->id.winmm = winMMDeviceID;
11016 
11017  if (deviceType == ma_device_type_playback) {
11018  MMRESULT result;
11019  MA_WAVEOUTCAPS2A caps;
11020 
11021  ma_zero_object(&caps);
11022 
11023  result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps));
11024  if (result == MMSYSERR_NOERROR) {
11025  return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
11026  }
11027  } else {
11028  MMRESULT result;
11029  MA_WAVEINCAPS2A caps;
11030 
11031  ma_zero_object(&caps);
11032 
11033  result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps));
11034  if (result == MMSYSERR_NOERROR) {
11035  return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
11036  }
11037  }
11038 
11039  return MA_NO_DEVICE;
11040 }
11041 
11042 
11043 void ma_device_uninit__winmm(ma_device* pDevice)
11044 {
11045  ma_assert(pDevice != NULL);
11046 
11047  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
11048  ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
11049  CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
11050  }
11051 
11052  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
11053  ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
11054  ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
11055  CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
11056  }
11057 
11058  ma_free(pDevice->winmm._pHeapData);
11059 
11060  ma_zero_object(&pDevice->winmm); /* Safety. */
11061 }
11062 
11063 ma_result ma_device_init__winmm(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
11064 {
11065  const char* errorMsg = "";
11066  ma_result errorCode = MA_ERROR;
11067  ma_result result = MA_SUCCESS;
11068  ma_uint32 heapSize;
11069  UINT winMMDeviceIDPlayback = 0;
11070  UINT winMMDeviceIDCapture = 0;
11071  ma_uint32 bufferSizeInMilliseconds;
11072 
11073  ma_assert(pDevice != NULL);
11074  ma_zero_object(&pDevice->winmm);
11075 
11076  /* No exlusive mode with WinMM. */
11080  }
11081 
11082  bufferSizeInMilliseconds = pConfig->bufferSizeInMilliseconds;
11083  if (bufferSizeInMilliseconds == 0) {
11084  bufferSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->bufferSizeInFrames, pConfig->sampleRate);
11085  }
11086 
11087  /* WinMM has horrible latency. */
11088  if (pDevice->usingDefaultBufferSize) {
11090  bufferSizeInMilliseconds = 40 * pConfig->periods;
11091  } else {
11092  bufferSizeInMilliseconds = 400 * pConfig->periods;
11093  }
11094  }
11095 
11096 
11097  if (pConfig->playback.pDeviceID != NULL) {
11098  winMMDeviceIDPlayback = (UINT)pConfig->playback.pDeviceID->winmm;
11099  }
11100  if (pConfig->capture.pDeviceID != NULL) {
11101  winMMDeviceIDCapture = (UINT)pConfig->capture.pDeviceID->winmm;
11102  }
11103 
11104  /* The capture device needs to be initialized first. */
11105  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
11106  WAVEINCAPSA caps;
11107  WAVEFORMATEX wf;
11108  MMRESULT resultMM;
11109 
11110  /* We use an event to know when a new fragment needs to be enqueued. */
11111  pDevice->winmm.hEventCapture = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL);
11112  if (pDevice->winmm.hEventCapture == NULL) {
11113  errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = MA_FAILED_TO_CREATE_EVENT;
11114  goto on_error;
11115  }
11116 
11117  /* The format should be based on the device's actual format. */
11118  if (((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
11119  errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
11120  goto on_error;
11121  }
11122 
11123  result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
11124  if (result != MA_SUCCESS) {
11125  errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
11126  goto on_error;
11127  }
11128 
11129  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);
11130  if (resultMM != MMSYSERR_NOERROR) {
11131  errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
11132  goto on_error;
11133  }
11134 
11135  pDevice->capture.internalFormat = ma_format_from_WAVEFORMATEX(&wf);
11136  pDevice->capture.internalChannels = wf.nChannels;
11137  pDevice->capture.internalSampleRate = wf.nSamplesPerSec;
11138  ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
11139  pDevice->capture.internalPeriods = pConfig->periods;
11140  pDevice->capture.internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, pDevice->capture.internalSampleRate);
11141  }
11142 
11143  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
11144  WAVEOUTCAPSA caps;
11145  WAVEFORMATEX wf;
11146  MMRESULT resultMM;
11147 
11148  /* We use an event to know when a new fragment needs to be enqueued. */
11149  pDevice->winmm.hEventPlayback = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL);
11150  if (pDevice->winmm.hEventPlayback == NULL) {
11151  errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = MA_FAILED_TO_CREATE_EVENT;
11152  goto on_error;
11153  }
11154 
11155  /* The format should be based on the device's actual format. */
11156  if (((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
11157  errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
11158  goto on_error;
11159  }
11160 
11161  result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
11162  if (result != MA_SUCCESS) {
11163  errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
11164  goto on_error;
11165  }
11166 
11167  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);
11168  if (resultMM != MMSYSERR_NOERROR) {
11169  errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
11170  goto on_error;
11171  }
11172 
11173  pDevice->playback.internalFormat = ma_format_from_WAVEFORMATEX(&wf);
11174  pDevice->playback.internalChannels = wf.nChannels;
11175  pDevice->playback.internalSampleRate = wf.nSamplesPerSec;
11176  ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
11177  pDevice->playback.internalPeriods = pConfig->periods;
11178  pDevice->playback.internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, pDevice->playback.internalSampleRate);
11179  }
11180 
11181  /*
11182  The heap allocated data is allocated like so:
11183 
11184  [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
11185  */
11186  heapSize = 0;
11187  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
11188  heapSize += sizeof(WAVEHDR)*pDevice->capture.internalPeriods + (pDevice->capture.internalBufferSizeInFrames*ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
11189  }
11190  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
11191  heapSize += sizeof(WAVEHDR)*pDevice->playback.internalPeriods + (pDevice->playback.internalBufferSizeInFrames*ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
11192  }
11193 
11194  pDevice->winmm._pHeapData = (ma_uint8*)ma_malloc(heapSize);
11195  if (pDevice->winmm._pHeapData == NULL) {
11196  errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
11197  goto on_error;
11198  }
11199 
11200  ma_zero_memory(pDevice->winmm._pHeapData, heapSize);
11201 
11202  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
11203  ma_uint32 iPeriod;
11204 
11205  if (pConfig->deviceType == ma_device_type_capture) {
11206  pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
11207  pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
11208  } else {
11209  pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
11210  pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods + pDevice->playback.internalPeriods));
11211  }
11212 
11213  /* Prepare headers. */
11214  for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
11215  ma_uint32 fragmentSizeInBytes = ma_get_fragment_size_in_bytes(pDevice->capture.internalBufferSizeInFrames, pDevice->capture.internalPeriods, pDevice->capture.internalFormat, pDevice->capture.internalChannels);
11216 
11217  ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (fragmentSizeInBytes*iPeriod));
11218  ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = fragmentSizeInBytes;
11219  ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
11220  ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
11221  ((MA_PFN_waveInPrepareHeader)pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
11222 
11223  /*
11224  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
11225  it's unlocked and available for writing. A value of 1 means it's locked.
11226  */
11227  ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
11228  }
11229  }
11230  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
11231  ma_uint32 iPeriod;
11232 
11233  if (pConfig->deviceType == ma_device_type_playback) {
11234  pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
11235  pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDevice->playback.internalPeriods);
11236  } else {
11237  pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
11238  pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods + pDevice->playback.internalPeriods)) + (pDevice->playback.internalBufferSizeInFrames*ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
11239  }
11240 
11241  /* Prepare headers. */
11242  for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
11243  ma_uint32 fragmentSizeInBytes = ma_get_fragment_size_in_bytes(pDevice->playback.internalBufferSizeInFrames, pDevice->playback.internalPeriods, pDevice->playback.internalFormat, pDevice->playback.internalChannels);
11244 
11245  ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (fragmentSizeInBytes*iPeriod));
11246  ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = fragmentSizeInBytes;
11247  ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
11248  ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
11249  ((MA_PFN_waveOutPrepareHeader)pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
11250 
11251  /*
11252  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
11253  it's unlocked and available for writing. A value of 1 means it's locked.
11254  */
11255  ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
11256  }
11257  }
11258 
11259  return MA_SUCCESS;
11260 
11261 on_error:
11262  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
11263  if (pDevice->winmm.pWAVEHDRCapture != NULL) {
11264  ma_uint32 iPeriod;
11265  for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
11266  ((MA_PFN_waveInUnprepareHeader)pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
11267  }
11268  }
11269 
11270  ((MA_PFN_waveInClose)pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
11271  }
11272 
11273  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
11274  if (pDevice->winmm.pWAVEHDRCapture != NULL) {
11275  ma_uint32 iPeriod;
11276  for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
11277  ((MA_PFN_waveOutUnprepareHeader)pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
11278  }
11279  }
11280 
11281  ((MA_PFN_waveOutClose)pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
11282  }
11283 
11284  ma_free(pDevice->winmm._pHeapData);
11285  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, errorMsg, errorCode);
11286 }
11287 
11288 ma_result ma_device_stop__winmm(ma_device* pDevice)
11289 {
11290  MMRESULT resultMM;
11291 
11292  ma_assert(pDevice != NULL);
11293 
11294  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
11295  if (pDevice->winmm.hDeviceCapture == NULL) {
11296  return MA_INVALID_ARGS;
11297  }
11298 
11299  resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture);
11300  if (resultMM != MMSYSERR_NOERROR) {
11301  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset capture device.", ma_result_from_MMRESULT(resultMM));
11302  }
11303  }
11304 
11305  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
11306  if (pDevice->winmm.hDevicePlayback == NULL) {
11307  return MA_INVALID_ARGS;
11308  }
11309 
11310  resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
11311  if (resultMM != MMSYSERR_NOERROR) {
11312  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset playback device.", ma_result_from_MMRESULT(resultMM));
11313  }
11314  }
11315 
11316  ma_atomic_exchange_32(&pDevice->winmm.isStarted, MA_FALSE);
11317  return MA_SUCCESS;
11318 }
11319 
11320 ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount)
11321 {
11322  ma_result result = MA_SUCCESS;
11323  MMRESULT resultMM;
11324  ma_uint32 totalFramesWritten;
11325  WAVEHDR* pWAVEHDR;
11326 
11327  ma_assert(pDevice != NULL);
11328  ma_assert(pPCMFrames != NULL);
11329 
11330  pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
11331 
11332  /* Keep processing as much data as possible. */
11333  totalFramesWritten = 0;
11334  while (totalFramesWritten < frameCount) {
11335  /* If the current header has some space available we need to write part of it. */
11336  if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
11337  /*
11338  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
11339  write it out and move on to the next iteration.
11340  */
11341  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
11342  ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
11343 
11344  ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
11345  const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
11346  void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
11347  ma_copy_memory(pDst, pSrc, framesToCopy*bpf);
11348 
11349  pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
11350  totalFramesWritten += framesToCopy;
11351 
11352  /* If we've consumed the buffer entirely we need to write it out to the device. */
11353  if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
11354  pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */
11355  pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
11356 
11357  /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
11358  ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
11359 
11360  /* The device will be started here. */
11361  resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR));
11362  if (resultMM != MMSYSERR_NOERROR) {
11363  result = ma_result_from_MMRESULT(resultMM);
11364  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.", result);
11365  break;
11366  }
11367  ma_atomic_exchange_32(&pDevice->winmm.isStarted, MA_TRUE);
11368 
11369  /* Make sure we move to the next header. */
11370  pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
11371  pDevice->winmm.headerFramesConsumedPlayback = 0;
11372  }
11373 
11374  /* If at this point we have consumed the entire input buffer we can return. */
11375  ma_assert(totalFramesWritten <= frameCount);
11376  if (totalFramesWritten == frameCount) {
11377  break;
11378  }
11379 
11380  /* Getting here means there's more to process. */
11381  continue;
11382  }
11383 
11384  /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
11385  if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
11386  result = MA_ERROR;
11387  break;
11388  }
11389 
11390  /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
11391  if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) {
11392  pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */
11393  pDevice->winmm.headerFramesConsumedPlayback = 0;
11394  }
11395 
11396  /* If the device has been stopped we need to break. */
11397  if (!pDevice->winmm.isStarted) {
11398  break;
11399  }
11400  }
11401 
11402  return result;
11403 }
11404 
11405 ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount)
11406 {
11407  ma_result result = MA_SUCCESS;
11408  MMRESULT resultMM;
11409  ma_uint32 totalFramesRead;
11410  WAVEHDR* pWAVEHDR;
11411 
11412  ma_assert(pDevice != NULL);
11413  ma_assert(pPCMFrames != NULL);
11414 
11415  pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
11416 
11417  /* We want to start the device immediately. */
11418  if (!pDevice->winmm.isStarted) {
11419  ma_uint32 iPeriod;
11420 
11421  /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
11422  ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
11423 
11424  /* 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. */
11425  for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
11426  resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
11427  if (resultMM != MMSYSERR_NOERROR) {
11428  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));
11429  }
11430 
11431  /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
11432  pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */
11433  }
11434 
11435  /* Capture devices need to be explicitly started, unlike playback devices. */
11436  resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture);
11437  if (resultMM != MMSYSERR_NOERROR) {
11438  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.", ma_result_from_MMRESULT(resultMM));
11439  }
11440 
11441  ma_atomic_exchange_32(&pDevice->winmm.isStarted, MA_TRUE);
11442  }
11443 
11444  /* Keep processing as much data as possible. */
11445  totalFramesRead = 0;
11446  while (totalFramesRead < frameCount) {
11447  /* If the current header has some space available we need to write part of it. */
11448  if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
11449  /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
11450  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
11451  ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
11452 
11453  ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
11454  const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
11455  void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
11456  ma_copy_memory(pDst, pSrc, framesToCopy*bpf);
11457 
11458  pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
11459  totalFramesRead += framesToCopy;
11460 
11461  /* If we've consumed the buffer entirely we need to add it back to the device. */
11462  if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
11463  pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */
11464  pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
11465 
11466  /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
11467  ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
11468 
11469  /* The device will be started here. */
11470  resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR));
11471  if (resultMM != MMSYSERR_NOERROR) {
11472  result = ma_result_from_MMRESULT(resultMM);
11473  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.", result);
11474  break;
11475  }
11476 
11477  /* Make sure we move to the next header. */
11478  pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
11479  pDevice->winmm.headerFramesConsumedCapture = 0;
11480  }
11481 
11482  /* If at this point we have filled the entire input buffer we can return. */
11483  ma_assert(totalFramesRead <= frameCount);
11484  if (totalFramesRead == frameCount) {
11485  break;
11486  }
11487 
11488  /* Getting here means there's more to process. */
11489  continue;
11490  }
11491 
11492  /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
11493  if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
11494  result = MA_ERROR;
11495  break;
11496  }
11497 
11498  /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
11499  if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) {
11500  pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */
11501  pDevice->winmm.headerFramesConsumedCapture = 0;
11502  }
11503 
11504  /* If the device has been stopped we need to break. */
11505  if (!pDevice->winmm.isStarted) {
11506  break;
11507  }
11508  }
11509 
11510  return result;
11511 }
11512 
11513 ma_result ma_context_uninit__winmm(ma_context* pContext)
11514 {
11515  ma_assert(pContext != NULL);
11516  ma_assert(pContext->backend == ma_backend_winmm);
11517 
11518  ma_dlclose(pContext, pContext->winmm.hWinMM);
11519  return MA_SUCCESS;
11520 }
11521 
11522 ma_result ma_context_init__winmm(const ma_context_config* pConfig, ma_context* pContext)
11523 {
11524  ma_assert(pContext != NULL);
11525 
11526  (void)pConfig;
11527 
11528  pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll");
11529  if (pContext->winmm.hWinMM == NULL) {
11530  return MA_NO_BACKEND;
11531  }
11532 
11533  pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs");
11534  pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA");
11535  pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen");
11536  pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose");
11537  pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader");
11538  pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader");
11539  pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite");
11540  pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset");
11541  pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs");
11542  pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA");
11543  pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen");
11544  pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose");
11545  pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader");
11546  pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader");
11547  pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer");
11548  pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart");
11549  pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset");
11550 
11551  pContext->onUninit = ma_context_uninit__winmm;
11552  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__winmm;
11553  pContext->onEnumDevices = ma_context_enumerate_devices__winmm;
11554  pContext->onGetDeviceInfo = ma_context_get_device_info__winmm;
11555  pContext->onDeviceInit = ma_device_init__winmm;
11556  pContext->onDeviceUninit = ma_device_uninit__winmm;
11557  pContext->onDeviceStart = NULL; /* Not used. Started in onDeviceWrite/onDeviceRead. */
11558  pContext->onDeviceStop = ma_device_stop__winmm;
11559  pContext->onDeviceWrite = ma_device_write__winmm;
11560  pContext->onDeviceRead = ma_device_read__winmm;
11561 
11562  return MA_SUCCESS;
11563 }
11564 #endif
11565 
11566 
11567 
11568 
11569 /******************************************************************************
11570 
11571 ALSA Backend
11572 
11573 ******************************************************************************/
11574 #ifdef MA_HAS_ALSA
11575 
11576 #ifdef MA_NO_RUNTIME_LINKING
11577 #include <alsa/asoundlib.h>
11578 typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
11579 typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
11580 typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
11581 typedef snd_pcm_format_t ma_snd_pcm_format_t;
11582 typedef snd_pcm_access_t ma_snd_pcm_access_t;
11583 typedef snd_pcm_t ma_snd_pcm_t;
11584 typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
11585 typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
11586 typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
11587 typedef snd_pcm_info_t ma_snd_pcm_info_t;
11588 typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
11589 typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
11590 
11591 /* snd_pcm_stream_t */
11592 #define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
11593 #define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
11594 
11595 /* snd_pcm_format_t */
11596 #define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
11597 #define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
11598 #define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
11599 #define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
11600 #define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
11601 #define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
11602 #define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
11603 #define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
11604 #define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
11605 #define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
11606 #define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
11607 #define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
11608 #define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
11609 #define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
11610 #define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
11611 #define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
11612 
11613 /* ma_snd_pcm_access_t */
11614 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
11615 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
11616 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
11617 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
11618 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
11619 
11620 /* Channel positions. */
11621 #define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
11622 #define MA_SND_CHMAP_NA SND_CHMAP_NA
11623 #define MA_SND_CHMAP_MONO SND_CHMAP_MONO
11624 #define MA_SND_CHMAP_FL SND_CHMAP_FL
11625 #define MA_SND_CHMAP_FR SND_CHMAP_FR
11626 #define MA_SND_CHMAP_RL SND_CHMAP_RL
11627 #define MA_SND_CHMAP_RR SND_CHMAP_RR
11628 #define MA_SND_CHMAP_FC SND_CHMAP_FC
11629 #define MA_SND_CHMAP_LFE SND_CHMAP_LFE
11630 #define MA_SND_CHMAP_SL SND_CHMAP_SL
11631 #define MA_SND_CHMAP_SR SND_CHMAP_SR
11632 #define MA_SND_CHMAP_RC SND_CHMAP_RC
11633 #define MA_SND_CHMAP_FLC SND_CHMAP_FLC
11634 #define MA_SND_CHMAP_FRC SND_CHMAP_FRC
11635 #define MA_SND_CHMAP_RLC SND_CHMAP_RLC
11636 #define MA_SND_CHMAP_RRC SND_CHMAP_RRC
11637 #define MA_SND_CHMAP_FLW SND_CHMAP_FLW
11638 #define MA_SND_CHMAP_FRW SND_CHMAP_FRW
11639 #define MA_SND_CHMAP_FLH SND_CHMAP_FLH
11640 #define MA_SND_CHMAP_FCH SND_CHMAP_FCH
11641 #define MA_SND_CHMAP_FRH SND_CHMAP_FRH
11642 #define MA_SND_CHMAP_TC SND_CHMAP_TC
11643 #define MA_SND_CHMAP_TFL SND_CHMAP_TFL
11644 #define MA_SND_CHMAP_TFR SND_CHMAP_TFR
11645 #define MA_SND_CHMAP_TFC SND_CHMAP_TFC
11646 #define MA_SND_CHMAP_TRL SND_CHMAP_TRL
11647 #define MA_SND_CHMAP_TRR SND_CHMAP_TRR
11648 #define MA_SND_CHMAP_TRC SND_CHMAP_TRC
11649 #define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
11650 #define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
11651 #define MA_SND_CHMAP_TSL SND_CHMAP_TSL
11652 #define MA_SND_CHMAP_TSR SND_CHMAP_TSR
11653 #define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
11654 #define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
11655 #define MA_SND_CHMAP_BC SND_CHMAP_BC
11656 #define MA_SND_CHMAP_BLC SND_CHMAP_BLC
11657 #define MA_SND_CHMAP_BRC SND_CHMAP_BRC
11658 
11659 /* Open mode flags. */
11660 #define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
11661 #define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
11662 #define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
11663 #else
11664 #include <errno.h> /* For EPIPE, etc. */
11665 typedef unsigned long ma_snd_pcm_uframes_t;
11666 typedef long ma_snd_pcm_sframes_t;
11667 typedef int ma_snd_pcm_stream_t;
11668 typedef int ma_snd_pcm_format_t;
11669 typedef int ma_snd_pcm_access_t;
11670 typedef struct ma_snd_pcm_t ma_snd_pcm_t;
11671 typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
11672 typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
11673 typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
11674 typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
11675 typedef struct
11676 {
11677  void* addr;
11678  unsigned int first;
11679  unsigned int step;
11680 } ma_snd_pcm_channel_area_t;
11681 typedef struct
11682 {
11683  unsigned int channels;
11684  unsigned int pos[1];
11685 } ma_snd_pcm_chmap_t;
11686 
11687 /* snd_pcm_state_t */
11688 #define MA_SND_PCM_STATE_OPEN 0
11689 #define MA_SND_PCM_STATE_SETUP 1
11690 #define MA_SND_PCM_STATE_PREPARED 2
11691 #define MA_SND_PCM_STATE_RUNNING 3
11692 #define MA_SND_PCM_STATE_XRUN 4
11693 #define MA_SND_PCM_STATE_DRAINING 5
11694 #define MA_SND_PCM_STATE_PAUSED 6
11695 #define MA_SND_PCM_STATE_SUSPENDED 7
11696 #define MA_SND_PCM_STATE_DISCONNECTED 8
11697 
11698 /* snd_pcm_stream_t */
11699 #define MA_SND_PCM_STREAM_PLAYBACK 0
11700 #define MA_SND_PCM_STREAM_CAPTURE 1
11701 
11702 /* snd_pcm_format_t */
11703 #define MA_SND_PCM_FORMAT_UNKNOWN -1
11704 #define MA_SND_PCM_FORMAT_U8 1
11705 #define MA_SND_PCM_FORMAT_S16_LE 2
11706 #define MA_SND_PCM_FORMAT_S16_BE 3
11707 #define MA_SND_PCM_FORMAT_S24_LE 6
11708 #define MA_SND_PCM_FORMAT_S24_BE 7
11709 #define MA_SND_PCM_FORMAT_S32_LE 10
11710 #define MA_SND_PCM_FORMAT_S32_BE 11
11711 #define MA_SND_PCM_FORMAT_FLOAT_LE 14
11712 #define MA_SND_PCM_FORMAT_FLOAT_BE 15
11713 #define MA_SND_PCM_FORMAT_FLOAT64_LE 16
11714 #define MA_SND_PCM_FORMAT_FLOAT64_BE 17
11715 #define MA_SND_PCM_FORMAT_MU_LAW 20
11716 #define MA_SND_PCM_FORMAT_A_LAW 21
11717 #define MA_SND_PCM_FORMAT_S24_3LE 32
11718 #define MA_SND_PCM_FORMAT_S24_3BE 33
11719 
11720 /* snd_pcm_access_t */
11721 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
11722 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
11723 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
11724 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
11725 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
11726 
11727 /* Channel positions. */
11728 #define MA_SND_CHMAP_UNKNOWN 0
11729 #define MA_SND_CHMAP_NA 1
11730 #define MA_SND_CHMAP_MONO 2
11731 #define MA_SND_CHMAP_FL 3
11732 #define MA_SND_CHMAP_FR 4
11733 #define MA_SND_CHMAP_RL 5
11734 #define MA_SND_CHMAP_RR 6
11735 #define MA_SND_CHMAP_FC 7
11736 #define MA_SND_CHMAP_LFE 8
11737 #define MA_SND_CHMAP_SL 9
11738 #define MA_SND_CHMAP_SR 10
11739 #define MA_SND_CHMAP_RC 11
11740 #define MA_SND_CHMAP_FLC 12
11741 #define MA_SND_CHMAP_FRC 13
11742 #define MA_SND_CHMAP_RLC 14
11743 #define MA_SND_CHMAP_RRC 15
11744 #define MA_SND_CHMAP_FLW 16
11745 #define MA_SND_CHMAP_FRW 17
11746 #define MA_SND_CHMAP_FLH 18
11747 #define MA_SND_CHMAP_FCH 19
11748 #define MA_SND_CHMAP_FRH 20
11749 #define MA_SND_CHMAP_TC 21
11750 #define MA_SND_CHMAP_TFL 22
11751 #define MA_SND_CHMAP_TFR 23
11752 #define MA_SND_CHMAP_TFC 24
11753 #define MA_SND_CHMAP_TRL 25
11754 #define MA_SND_CHMAP_TRR 26
11755 #define MA_SND_CHMAP_TRC 27
11756 #define MA_SND_CHMAP_TFLC 28
11757 #define MA_SND_CHMAP_TFRC 29
11758 #define MA_SND_CHMAP_TSL 30
11759 #define MA_SND_CHMAP_TSR 31
11760 #define MA_SND_CHMAP_LLFE 32
11761 #define MA_SND_CHMAP_RLFE 33
11762 #define MA_SND_CHMAP_BC 34
11763 #define MA_SND_CHMAP_BLC 35
11764 #define MA_SND_CHMAP_BRC 36
11765 
11766 /* Open mode flags. */
11767 #define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
11768 #define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
11769 #define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
11770 #endif
11771 
11772 typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
11773 typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
11774 typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
11775 typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
11776 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);
11777 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);
11778 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);
11779 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);
11780 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);
11781 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);
11782 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);
11783 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);
11784 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);
11785 typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
11786 typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
11787 typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
11788 typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
11789 typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
11790 typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
11791 typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
11792 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);
11793 typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
11794 typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
11795 typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
11796 typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
11797 typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
11798 typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
11799 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);
11800 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);
11801 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);
11802 typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
11803 typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
11804 typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
11805 typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
11806 typedef int (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
11807 typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
11808 typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
11809 typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
11810 typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
11811 typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
11812 typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
11813 typedef int (* ma_snd_card_get_index_proc) (const char *name);
11814 typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
11815 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);
11816 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);
11817 typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
11818 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
11819 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);
11820 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
11821 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
11822 typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
11823 typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
11824 typedef size_t (* ma_snd_pcm_info_sizeof_proc) ();
11825 typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
11826 typedef int (* ma_snd_config_update_free_global_proc) ();
11827 
11828 /* This array specifies each of the common devices that can be used for both playback and capture. */
11829 const char* g_maCommonDeviceNamesALSA[] = {
11830  "default",
11831  "null",
11832  "pulse",
11833  "jack"
11834 };
11835 
11836 /* This array allows us to blacklist specific playback devices. */
11837 const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
11838  ""
11839 };
11840 
11841 /* This array allows us to blacklist specific capture devices. */
11842 const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
11843  ""
11844 };
11845 
11846 
11847 /*
11848 This array allows miniaudio to control device-specific default buffer sizes. This uses a scaling factor. Order is important. If
11849 any part of the string is present in the device's name, the associated scale will be used.
11850 */
11851 static struct
11852 {
11853  const char* name;
11854  float scale;
11855 } g_maDefaultBufferSizeScalesALSA[] = {
11856  {"bcm2835 IEC958/HDMI", 2.0f},
11857  {"bcm2835 ALSA", 2.0f}
11858 };
11859 
11860 float ma_find_default_buffer_size_scale__alsa(const char* deviceName)
11861 {
11862  size_t i;
11863 
11864  if (deviceName == NULL) {
11865  return 1;
11866  }
11867 
11868  for (i = 0; i < ma_countof(g_maDefaultBufferSizeScalesALSA); ++i) {
11869  if (strstr(g_maDefaultBufferSizeScalesALSA[i].name, deviceName) != NULL) {
11870  return g_maDefaultBufferSizeScalesALSA[i].scale;
11871  }
11872  }
11873 
11874  return 1;
11875 }
11876 
11877 ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
11878 {
11879  ma_snd_pcm_format_t ALSAFormats[] = {
11880  MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */
11881  MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */
11882  MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
11883  MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
11884  MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
11885  MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */
11886  };
11887 
11888  if (ma_is_big_endian()) {
11889  ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
11890  ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
11891  ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
11892  ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
11893  ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
11894  ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
11895  }
11896 
11897  return ALSAFormats[format];
11898 }
11899 
11900 ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
11901 {
11902  if (ma_is_little_endian()) {
11903  switch (formatALSA) {
11904  case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
11905  case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
11906  case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
11907  case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
11908  default: break;
11909  }
11910  } else {
11911  switch (formatALSA) {
11912  case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
11913  case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
11914  case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
11915  case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
11916  default: break;
11917  }
11918  }
11919 
11920  /* Endian agnostic. */
11921  switch (formatALSA) {
11922  case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
11923  default: return ma_format_unknown;
11924  }
11925 }
11926 
11927 ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
11928 {
11929  switch (alsaChannelPos)
11930  {
11931  case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
11932  case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
11933  case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
11934  case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
11935  case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
11936  case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
11937  case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
11938  case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
11939  case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
11940  case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
11941  case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
11942  case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
11943  case MA_SND_CHMAP_RLC: return 0;
11944  case MA_SND_CHMAP_RRC: return 0;
11945  case MA_SND_CHMAP_FLW: return 0;
11946  case MA_SND_CHMAP_FRW: return 0;
11947  case MA_SND_CHMAP_FLH: return 0;
11948  case MA_SND_CHMAP_FCH: return 0;
11949  case MA_SND_CHMAP_FRH: return 0;
11950  case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
11951  case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
11952  case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
11953  case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
11954  case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
11955  case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
11956  case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
11957  default: break;
11958  }
11959 
11960  return 0;
11961 }
11962 
11963 ma_bool32 ma_is_common_device_name__alsa(const char* name)
11964 {
11965  size_t iName;
11966  for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
11967  if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
11968  return MA_TRUE;
11969  }
11970  }
11971 
11972  return MA_FALSE;
11973 }
11974 
11975 
11976 ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
11977 {
11978  size_t iName;
11979  for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
11980  if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
11981  return MA_TRUE;
11982  }
11983  }
11984 
11985  return MA_FALSE;
11986 }
11987 
11988 ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
11989 {
11990  size_t iName;
11991  for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
11992  if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
11993  return MA_TRUE;
11994  }
11995  }
11996 
11997  return MA_FALSE;
11998 }
11999 
12000 ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
12001 {
12002  if (deviceType == ma_device_type_playback) {
12003  return ma_is_playback_device_blacklisted__alsa(name);
12004  } else {
12005  return ma_is_capture_device_blacklisted__alsa(name);
12006  }
12007 }
12008 
12009 
12010 const char* ma_find_char(const char* str, char c, int* index)
12011 {
12012  int i = 0;
12013  for (;;) {
12014  if (str[i] == '\0') {
12015  if (index) *index = -1;
12016  return NULL;
12017  }
12018 
12019  if (str[i] == c) {
12020  if (index) *index = i;
12021  return str + i;
12022  }
12023 
12024  i += 1;
12025  }
12026 
12027  /* Should never get here, but treat it as though the character was not found to make me feel better inside. */
12028  if (index) *index = -1;
12029  return NULL;
12030 }
12031 
12032 ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
12033 {
12034  /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */
12035 
12036  int commaPos;
12037  const char* dev;
12038  int i;
12039 
12040  if (hwid == NULL) {
12041  return MA_FALSE;
12042  }
12043 
12044  if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
12045  return MA_FALSE;
12046  }
12047 
12048  hwid += 3;
12049 
12050  dev = ma_find_char(hwid, ',', &commaPos);
12051  if (dev == NULL) {
12052  return MA_FALSE;
12053  } else {
12054  dev += 1; /* Skip past the ",". */
12055  }
12056 
12057  /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */
12058  for (i = 0; i < commaPos; ++i) {
12059  if (hwid[i] < '0' || hwid[i] > '9') {
12060  return MA_FALSE;
12061  }
12062  }
12063 
12064  /* Check if everything after the "," is numeric. If not, return false. */
12065  i = 0;
12066  while (dev[i] != '\0') {
12067  if (dev[i] < '0' || dev[i] > '9') {
12068  return MA_FALSE;
12069  }
12070  i += 1;
12071  }
12072 
12073  return MA_TRUE;
12074 }
12075 
12076 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. */
12077 {
12078  /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */
12079 
12080  int colonPos;
12081  int commaPos;
12082  char card[256];
12083  const char* dev;
12084  int cardIndex;
12085 
12086  if (dst == NULL) {
12087  return -1;
12088  }
12089  if (dstSize < 7) {
12090  return -1; /* Absolute minimum size of the output buffer is 7 bytes. */
12091  }
12092 
12093  *dst = '\0'; /* Safety. */
12094  if (src == NULL) {
12095  return -1;
12096  }
12097 
12098  /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */
12099  if (ma_is_device_name_in_hw_format__alsa(src)) {
12100  return ma_strcpy_s(dst, dstSize, src);
12101  }
12102 
12103  src = ma_find_char(src, ':', &colonPos);
12104  if (src == NULL) {
12105  return -1; /* Couldn't find a colon */
12106  }
12107 
12108  dev = ma_find_char(src, ',', &commaPos);
12109  if (dev == NULL) {
12110  dev = "0";
12111  ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */
12112  } else {
12113  dev = dev + 5; /* +5 = ",DEV=" */
12114  ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */
12115  }
12116 
12117  cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
12118  if (cardIndex < 0) {
12119  return -2; /* Failed to retrieve the card index. */
12120  }
12121 
12122  /*printf("TESTING: CARD=%s,DEV=%s\n", card, dev); */
12123 
12124 
12125  /* Construction. */
12126  dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
12127  if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
12128  return -3;
12129  }
12130  if (ma_strcat_s(dst, dstSize, ",") != 0) {
12131  return -3;
12132  }
12133  if (ma_strcat_s(dst, dstSize, dev) != 0) {
12134  return -3;
12135  }
12136 
12137  return 0;
12138 }
12139 
12140 ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
12141 {
12142  ma_uint32 i;
12143 
12144  ma_assert(pHWID != NULL);
12145 
12146  for (i = 0; i < count; ++i) {
12147  if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
12148  return MA_TRUE;
12149  }
12150  }
12151 
12152  return MA_FALSE;
12153 }
12154 
12155 
12156 ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_snd_pcm_t** ppPCM)
12157 {
12158  ma_snd_pcm_t* pPCM;
12159  ma_snd_pcm_stream_t stream;
12160  int openMode;
12161 
12162  ma_assert(pContext != NULL);
12163  ma_assert(ppPCM != NULL);
12164 
12165  *ppPCM = NULL;
12166  pPCM = NULL;
12167 
12168  stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
12169  openMode = MA_SND_PCM_NO_AUTO_RESAMPLE | MA_SND_PCM_NO_AUTO_CHANNELS | MA_SND_PCM_NO_AUTO_FORMAT;
12170 
12171  if (pDeviceID == NULL) {
12172  ma_bool32 isDeviceOpen;
12173  size_t i;
12174 
12175  /*
12176  We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
12177  me feel better to try as hard as we can get to get _something_ working.
12178  */
12179  const char* defaultDeviceNames[] = {
12180  "default",
12181  NULL,
12182  NULL,
12183  NULL,
12184  NULL,
12185  NULL,
12186  NULL
12187  };
12188 
12189  if (shareMode == ma_share_mode_exclusive) {
12190  defaultDeviceNames[1] = "hw";
12191  defaultDeviceNames[2] = "hw:0";
12192  defaultDeviceNames[3] = "hw:0,0";
12193  } else {
12194  if (deviceType == ma_device_type_playback) {
12195  defaultDeviceNames[1] = "dmix";
12196  defaultDeviceNames[2] = "dmix:0";
12197  defaultDeviceNames[3] = "dmix:0,0";
12198  } else {
12199  defaultDeviceNames[1] = "dsnoop";
12200  defaultDeviceNames[2] = "dsnoop:0";
12201  defaultDeviceNames[3] = "dsnoop:0,0";
12202  }
12203  defaultDeviceNames[4] = "hw";
12204  defaultDeviceNames[5] = "hw:0";
12205  defaultDeviceNames[6] = "hw:0,0";
12206  }
12207 
12208  isDeviceOpen = MA_FALSE;
12209  for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
12210  if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
12211  if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
12212  isDeviceOpen = MA_TRUE;
12213  break;
12214  }
12215  }
12216  }
12217 
12218  if (!isDeviceOpen) {
12219  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);
12220  }
12221  } else {
12222  /*
12223  We're trying to open a specific device. There's a few things to consider here:
12224 
12225  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
12226  an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
12227  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").
12228  */
12229 
12230  /* May end up needing to make small adjustments to the ID, so make a copy. */
12231  ma_device_id deviceID = *pDeviceID;
12232  ma_bool32 isDeviceOpen = MA_FALSE;
12233 
12234  if (deviceID.alsa[0] != ':') {
12235  /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
12236  if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode) == 0) {
12237  isDeviceOpen = MA_TRUE;
12238  }
12239  } else {
12240  char hwid[256];
12241 
12242  /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
12243  if (deviceID.alsa[1] == '\0') {
12244  deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */
12245  }
12246 
12247  if (shareMode == ma_share_mode_shared) {
12248  if (deviceType == ma_device_type_playback) {
12249  ma_strcpy_s(hwid, sizeof(hwid), "dmix");
12250  } else {
12251  ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
12252  }
12253 
12254  if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
12255  if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode) == 0) {
12256  isDeviceOpen = MA_TRUE;
12257  }
12258  }
12259  }
12260 
12261  /* 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. */
12262  if (!isDeviceOpen) {
12263  ma_strcpy_s(hwid, sizeof(hwid), "hw");
12264  if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
12265  if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode) == 0) {
12266  isDeviceOpen = MA_TRUE;
12267  }
12268  }
12269  }
12270  }
12271 
12272  if (!isDeviceOpen) {
12273  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
12274  }
12275  }
12276 
12277  *ppPCM = pPCM;
12278  return MA_SUCCESS;
12279 }
12280 
12281 
12282 ma_bool32 ma_context_is_device_id_equal__alsa(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
12283 {
12284  ma_assert(pContext != NULL);
12285  ma_assert(pID0 != NULL);
12286  ma_assert(pID1 != NULL);
12287  (void)pContext;
12288 
12289  return ma_strcmp(pID0->alsa, pID1->alsa) == 0;
12290 }
12291 
12292 ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
12293 {
12294  ma_bool32 cbResult = MA_TRUE;
12295  char** ppDeviceHints;
12296  ma_device_id* pUniqueIDs = NULL;
12297  ma_uint32 uniqueIDCount = 0;
12298  char** ppNextDeviceHint;
12299 
12300  ma_assert(pContext != NULL);
12301  ma_assert(callback != NULL);
12302 
12303  ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
12304 
12305  if (((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) {
12306  ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
12307  return MA_NO_BACKEND;
12308  }
12309 
12310  ppNextDeviceHint = ppDeviceHints;
12311  while (*ppNextDeviceHint != NULL) {
12312  char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
12313  char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
12314  char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
12316  ma_bool32 stopEnumeration = MA_FALSE;
12317  char hwid[sizeof(pUniqueIDs->alsa)];
12318  ma_device_info deviceInfo;
12319 
12320  if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
12321  deviceType = ma_device_type_playback;
12322  }
12323  if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
12324  deviceType = ma_device_type_capture;
12325  }
12326 
12327  if (NAME != NULL) {
12328  if (pContext->alsa.useVerboseDeviceEnumeration) {
12329  /* Verbose mode. Use the name exactly as-is. */
12330  ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
12331  } else {
12332  /* Simplified mode. Use ":%d,%d" format. */
12333  if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
12334  /*
12335  At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
12336  plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
12337  initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
12338  device type and sharing mode.
12339  */
12340  char* dst = hwid;
12341  char* src = hwid+2;
12342  while ((*dst++ = *src++));
12343  } else {
12344  /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
12345  ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
12346  }
12347 
12348  if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
12349  goto next_device; /* The device has already been enumerated. Move on to the next one. */
12350  } else {
12351  /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
12352  ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, sizeof(*pUniqueIDs) * (uniqueIDCount + 1));
12353  if (pNewUniqueIDs == NULL) {
12354  goto next_device; /* Failed to allocate memory. */
12355  }
12356 
12357  pUniqueIDs = pNewUniqueIDs;
12358  ma_copy_memory(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
12359  uniqueIDCount += 1;
12360  }
12361  }
12362  } else {
12363  ma_zero_memory(hwid, sizeof(hwid));
12364  }
12365 
12366  ma_zero_object(&deviceInfo);
12367  ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
12368 
12369  /*
12370  DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
12371  device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
12372  between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
12373  description.
12374 
12375  The value in DESC seems to be split into two lines, with the first line being the name of the device and the
12376  second line being a description of the device. I don't like having the description be across two lines because
12377  it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
12378  being put into parentheses. In simplified mode I'm just stripping the second line entirely.
12379  */
12380  if (DESC != NULL) {
12381  int lfPos;
12382  const char* line2 = ma_find_char(DESC, '\n', &lfPos);
12383  if (line2 != NULL) {
12384  line2 += 1; /* Skip past the new-line character. */
12385 
12386  if (pContext->alsa.useVerboseDeviceEnumeration) {
12387  /* Verbose mode. Put the second line in brackets. */
12388  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
12389  ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
12390  ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
12391  ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
12392  } else {
12393  /* Simplified mode. Strip the second line entirely. */
12394  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
12395  }
12396  } else {
12397  /* There's no second line. Just copy the whole description. */
12398  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
12399  }
12400  }
12401 
12402  if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
12403  cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
12404  }
12405 
12406  /*
12407  Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback
12408  again for the other device type in this case. We do this for known devices.
12409  */
12410  if (cbResult) {
12411  if (ma_is_common_device_name__alsa(NAME)) {
12412  if (deviceType == ma_device_type_playback) {
12413  if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
12414  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
12415  }
12416  } else {
12417  if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
12418  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
12419  }
12420  }
12421  }
12422  }
12423 
12424  if (cbResult == MA_FALSE) {
12425  stopEnumeration = MA_TRUE;
12426  }
12427 
12428  next_device:
12429  free(NAME);
12430  free(DESC);
12431  free(IOID);
12432  ppNextDeviceHint += 1;
12433 
12434  /* We need to stop enumeration if the callback returned false. */
12435  if (stopEnumeration) {
12436  break;
12437  }
12438  }
12439 
12440  ma_free(pUniqueIDs);
12441  ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
12442 
12443  ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
12444 
12445  return MA_SUCCESS;
12446 }
12447 
12448 
12449 typedef struct
12450 {
12451  ma_device_type deviceType;
12452  const ma_device_id* pDeviceID;
12453  ma_share_mode shareMode;
12454  ma_device_info* pDeviceInfo;
12455  ma_bool32 foundDevice;
12456 } ma_context_get_device_info_enum_callback_data__alsa;
12457 
12458 ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
12459 {
12460  ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
12461  ma_assert(pData != NULL);
12462 
12463  if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
12464  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
12465  pData->foundDevice = MA_TRUE;
12466  } else {
12467  if (pData->deviceType == deviceType && ma_context_is_device_id_equal__alsa(pContext, pData->pDeviceID, &pDeviceInfo->id)) {
12468  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
12469  pData->foundDevice = MA_TRUE;
12470  }
12471  }
12472 
12473  /* Keep enumerating until we have found the device. */
12474  return !pData->foundDevice;
12475 }
12476 
12477 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)
12478 {
12479  ma_context_get_device_info_enum_callback_data__alsa data;
12480  ma_result result;
12481  ma_snd_pcm_t* pPCM;
12482  ma_snd_pcm_hw_params_t* pHWParams;
12483  ma_snd_pcm_format_mask_t* pFormatMask;
12484  int sampleRateDir = 0;
12485 
12486  ma_assert(pContext != NULL);
12487 
12488  /* We just enumerate to find basic information about the device. */
12489  data.deviceType = deviceType;
12490  data.pDeviceID = pDeviceID;
12491  data.shareMode = shareMode;
12492  data.pDeviceInfo = pDeviceInfo;
12493  data.foundDevice = MA_FALSE;
12494  result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
12495  if (result != MA_SUCCESS) {
12496  return result;
12497  }
12498 
12499  if (!data.foundDevice) {
12500  return MA_NO_DEVICE;
12501  }
12502 
12503  /* For detailed info we need to open the device. */
12504  result = ma_context_open_pcm__alsa(pContext, shareMode, deviceType, pDeviceID, &pPCM);
12505  if (result != MA_SUCCESS) {
12506  return result;
12507  }
12508 
12509  /* We need to initialize a HW parameters object in order to know what formats are supported. */
12510  pHWParams = (ma_snd_pcm_hw_params_t*)calloc(1, ((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)());
12511  if (pHWParams == NULL) {
12512  return MA_OUT_OF_MEMORY;
12513  }
12514 
12515  if (((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams) < 0) {
12516  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
12517  }
12518 
12519  ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &pDeviceInfo->minChannels);
12520  ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &pDeviceInfo->maxChannels);
12521  ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &pDeviceInfo->minSampleRate, &sampleRateDir);
12522  ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &pDeviceInfo->maxSampleRate, &sampleRateDir);
12523 
12524  /* Formats. */
12525  pFormatMask = (ma_snd_pcm_format_mask_t*)calloc(1, ((ma_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)());
12526  if (pFormatMask == NULL) {
12527  return MA_OUT_OF_MEMORY;
12528  }
12529 
12530  ((ma_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask);
12531 
12532  pDeviceInfo->formatCount = 0;
12533  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_U8)) {
12534  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_u8;
12535  }
12536  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S16_LE)) {
12537  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s16;
12538  }
12539  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S24_3LE)) {
12540  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s24;
12541  }
12542  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S32_LE)) {
12543  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s32;
12544  }
12545  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_FLOAT_LE)) {
12546  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_f32;
12547  }
12548 
12549  ma_free(pFormatMask);
12550  ma_free(pHWParams);
12551 
12552  ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
12553  return MA_SUCCESS;
12554 }
12555 
12556 
12557 #if 0
12558 /*
12559 Waits for a number of frames to become available for either capture or playback. The return
12560 value is the number of frames available.
12561 
12562 This will return early if the main loop is broken with ma_device__break_main_loop().
12563 */
12564 ma_uint32 ma_device__wait_for_frames__alsa(ma_device* pDevice, ma_bool32* pRequiresRestart)
12565 {
12566  ma_assert(pDevice != NULL);
12567 
12568  if (pRequiresRestart) *pRequiresRestart = MA_FALSE;
12569 
12570  /* 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. */
12571  ma_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods;
12572  while (!pDevice->alsa.breakFromMainLoop) {
12573  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);
12574  if (framesAvailable < 0) {
12575  if (framesAvailable == -EPIPE) {
12576  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesAvailable, MA_TRUE) < 0) {
12577  return 0;
12578  }
12579 
12580  /* A device recovery means a restart for mmap mode. */
12581  if (pRequiresRestart) {
12582  *pRequiresRestart = MA_TRUE;
12583  }
12584 
12585  /* Try again, but if it fails this time just return an error. */
12586  framesAvailable = ((ma_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((ma_snd_pcm_t*)pDevice->alsa.pPCM);
12587  if (framesAvailable < 0) {
12588  return 0;
12589  }
12590  }
12591  }
12592 
12593  if (framesAvailable >= periodSizeInFrames) {
12594  return periodSizeInFrames;
12595  }
12596 
12597  if (framesAvailable < periodSizeInFrames) {
12598  /* Less than a whole period is available so keep waiting. */
12599  int waitResult = ((ma_snd_pcm_wait_proc)pDevice->pContext->alsa.snd_pcm_wait)((ma_snd_pcm_t*)pDevice->alsa.pPCM, -1);
12600  if (waitResult < 0) {
12601  if (waitResult == -EPIPE) {
12602  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, waitResult, MA_TRUE) < 0) {
12603  return 0;
12604  }
12605 
12606  /* A device recovery means a restart for mmap mode. */
12607  if (pRequiresRestart) {
12608  *pRequiresRestart = MA_TRUE;
12609  }
12610  }
12611  }
12612  }
12613  }
12614 
12615  /* We'll get here if the loop was terminated. Just return whatever's available. */
12616  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);
12617  if (framesAvailable < 0) {
12618  return 0;
12619  }
12620 
12621  return framesAvailable;
12622 }
12623 
12624 ma_bool32 ma_device_read_from_client_and_write__alsa(ma_device* pDevice)
12625 {
12626  ma_assert(pDevice != NULL);
12627  if (!ma_device_is_started(pDevice) && ma_device__get_state(pDevice) != MA_STATE_STARTING) {
12628  return MA_FALSE;
12629  }
12630  if (pDevice->alsa.breakFromMainLoop) {
12631  return MA_FALSE;
12632  }
12633 
12634  if (pDevice->alsa.isUsingMMap) {
12635  /* mmap. */
12636  ma_bool32 requiresRestart;
12637  ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, &requiresRestart);
12638  if (framesAvailable == 0) {
12639  return MA_FALSE;
12640  }
12641 
12642  /* Don't bother asking the client for more audio data if we're just stopping the device anyway. */
12643  if (pDevice->alsa.breakFromMainLoop) {
12644  return MA_FALSE;
12645  }
12646 
12647  const ma_snd_pcm_channel_area_t* pAreas;
12648  ma_snd_pcm_uframes_t mappedOffset;
12649  ma_snd_pcm_uframes_t mappedFrames = framesAvailable;
12650  while (framesAvailable > 0) {
12651  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);
12652  if (result < 0) {
12653  return MA_FALSE;
12654  }
12655 
12656  if (mappedFrames > 0) {
12657  void* pBuffer = (ma_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8);
12658  ma_device__read_frames_from_client(pDevice, mappedFrames, pBuffer);
12659  }
12660 
12661  result = ((ma_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((ma_snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames);
12662  if (result < 0 || (ma_snd_pcm_uframes_t)result != mappedFrames) {
12663  ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, result, MA_TRUE);
12664  return MA_FALSE;
12665  }
12666 
12667  if (requiresRestart) {
12668  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
12669  return MA_FALSE;
12670  }
12671  }
12672 
12673  if (framesAvailable >= mappedFrames) {
12674  framesAvailable -= mappedFrames;
12675  } else {
12676  framesAvailable = 0;
12677  }
12678  }
12679  } else {
12680  /* readi/writei. */
12681  while (!pDevice->alsa.breakFromMainLoop) {
12682  ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, NULL);
12683  if (framesAvailable == 0) {
12684  continue;
12685  }
12686 
12687  /* Don't bother asking the client for more audio data if we're just stopping the device anyway. */
12688  if (pDevice->alsa.breakFromMainLoop) {
12689  return MA_FALSE;
12690  }
12691 
12692  ma_device__read_frames_from_client(pDevice, framesAvailable, pDevice->alsa.pIntermediaryBuffer);
12693 
12694  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);
12695  if (framesWritten < 0) {
12696  if (framesWritten == -EAGAIN) {
12697  continue; /* Just keep trying... */
12698  } else if (framesWritten == -EPIPE) {
12699  /* Underrun. Just recover and try writing again. */
12700  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesWritten, MA_TRUE) < 0) {
12701  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
12702  return MA_FALSE;
12703  }
12704 
12705  framesWritten = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
12706  if (framesWritten < 0) {
12707  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to the internal device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
12708  return MA_FALSE;
12709  }
12710 
12711  break; /* Success. */
12712  } else {
12713  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_writei() failed when writing initial data.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
12714  return MA_FALSE;
12715  }
12716  } else {
12717  break; /* Success. */
12718  }
12719  }
12720  }
12721 
12722  return MA_TRUE;
12723 }
12724 
12725 ma_bool32 ma_device_read_and_send_to_client__alsa(ma_device* pDevice)
12726 {
12727  ma_assert(pDevice != NULL);
12728  if (!ma_device_is_started(pDevice)) {
12729  return MA_FALSE;
12730  }
12731  if (pDevice->alsa.breakFromMainLoop) {
12732  return MA_FALSE;
12733  }
12734 
12735  ma_uint32 framesToSend = 0;
12736  void* pBuffer = NULL;
12737  if (pDevice->alsa.pIntermediaryBuffer == NULL) {
12738  /* mmap. */
12739  ma_bool32 requiresRestart;
12740  ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, &requiresRestart);
12741  if (framesAvailable == 0) {
12742  return MA_FALSE;
12743  }
12744 
12745  const ma_snd_pcm_channel_area_t* pAreas;
12746  ma_snd_pcm_uframes_t mappedOffset;
12747  ma_snd_pcm_uframes_t mappedFrames = framesAvailable;
12748  while (framesAvailable > 0) {
12749  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);
12750  if (result < 0) {
12751  return MA_FALSE;
12752  }
12753 
12754  if (mappedFrames > 0) {
12755  void* pBuffer = (ma_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8);
12756  ma_device__send_frames_to_client(pDevice, mappedFrames, pBuffer);
12757  }
12758 
12759  result = ((ma_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((ma_snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames);
12760  if (result < 0 || (ma_snd_pcm_uframes_t)result != mappedFrames) {
12761  ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, result, MA_TRUE);
12762  return MA_FALSE;
12763  }
12764 
12765  if (requiresRestart) {
12766  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
12767  return MA_FALSE;
12768  }
12769  }
12770 
12771  if (framesAvailable >= mappedFrames) {
12772  framesAvailable -= mappedFrames;
12773  } else {
12774  framesAvailable = 0;
12775  }
12776  }
12777  } else {
12778  /* readi/writei. */
12779  ma_snd_pcm_sframes_t framesRead = 0;
12780  while (!pDevice->alsa.breakFromMainLoop) {
12781  ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, NULL);
12782  if (framesAvailable == 0) {
12783  continue;
12784  }
12785 
12786  framesRead = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
12787  if (framesRead < 0) {
12788  if (framesRead == -EAGAIN) {
12789  continue; /* Just keep trying... */
12790  } else if (framesRead == -EPIPE) {
12791  /* Overrun. Just recover and try reading again. */
12792  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesRead, MA_TRUE) < 0) {
12793  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
12794  return MA_FALSE;
12795  }
12796 
12797  framesRead = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
12798  if (framesRead < 0) {
12799  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
12800  return MA_FALSE;
12801  }
12802 
12803  break; /* Success. */
12804  } else {
12805  return MA_FALSE;
12806  }
12807  } else {
12808  break; /* Success. */
12809  }
12810  }
12811 
12812  framesToSend = framesRead;
12813  pBuffer = pDevice->alsa.pIntermediaryBuffer;
12814  }
12815 
12816  if (framesToSend > 0) {
12817  ma_device__send_frames_to_client(pDevice, framesToSend, pBuffer);
12818  }
12819 
12820  return MA_TRUE;
12821 }
12822 #endif /* 0 */
12823 
12824 void ma_device_uninit__alsa(ma_device* pDevice)
12825 {
12826  ma_assert(pDevice != NULL);
12827 
12828  if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
12829  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
12830  }
12831 
12832  if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
12833  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
12834  }
12835 }
12836 
12837 ma_result ma_device_init_by_type__alsa(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
12838 {
12839  ma_result result;
12840  ma_snd_pcm_t* pPCM;
12841  ma_bool32 isUsingMMap;
12842  ma_snd_pcm_format_t formatALSA;
12843  ma_share_mode shareMode;
12844  ma_device_id* pDeviceID;
12846  ma_uint32 internalChannels;
12847  ma_uint32 internalSampleRate;
12848  ma_channel internalChannelMap[MA_MAX_CHANNELS];
12849  ma_uint32 internalBufferSizeInFrames;
12850  ma_uint32 internalPeriods;
12851  ma_snd_pcm_hw_params_t* pHWParams;
12852  ma_snd_pcm_sw_params_t* pSWParams;
12853  ma_snd_pcm_uframes_t bufferBoundary;
12854  float bufferSizeScaleFactor;
12855 
12856  ma_assert(pContext != NULL);
12857  ma_assert(pConfig != NULL);
12858  ma_assert(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
12859  ma_assert(pDevice != NULL);
12860 
12861  formatALSA = ma_convert_ma_format_to_alsa_format((deviceType == ma_device_type_capture) ? pConfig->capture.format : pConfig->playback.format);
12862  shareMode = (deviceType == ma_device_type_capture) ? pConfig->capture.shareMode : pConfig->playback.shareMode;
12863  pDeviceID = (deviceType == ma_device_type_capture) ? pConfig->capture.pDeviceID : pConfig->playback.pDeviceID;
12864 
12865  result = ma_context_open_pcm__alsa(pContext, shareMode, deviceType, pDeviceID, &pPCM);
12866  if (result != MA_SUCCESS) {
12867  return result;
12868  }
12869 
12870  /* If using the default buffer size we may want to apply some device-specific scaling for known devices that have peculiar latency characteristics */
12871  bufferSizeScaleFactor = 1;
12872  if (pDevice->usingDefaultBufferSize) {
12873  ma_snd_pcm_info_t* pInfo = (ma_snd_pcm_info_t*)calloc(1, ((ma_snd_pcm_info_sizeof_proc)pContext->alsa.snd_pcm_info_sizeof)());
12874  if (pInfo == NULL) {
12875  return MA_OUT_OF_MEMORY;
12876  }
12877 
12878  /* We may need to scale the size of the buffer depending on the device. */
12879  if (((ma_snd_pcm_info_proc)pContext->alsa.snd_pcm_info)(pPCM, pInfo) == 0) {
12880  const char* deviceName = ((ma_snd_pcm_info_get_name_proc)pContext->alsa.snd_pcm_info_get_name)(pInfo);
12881  if (deviceName != NULL) {
12882  if (ma_strcmp(deviceName, "default") == 0) {
12883  char** ppDeviceHints;
12884  char** ppNextDeviceHint;
12885 
12886  /* It's the default device. We need to use DESC from snd_device_name_hint(). */
12887  if (((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) {
12888  ma_free(pInfo);
12889  return MA_NO_BACKEND;
12890  }
12891 
12892  ppNextDeviceHint = ppDeviceHints;
12893  while (*ppNextDeviceHint != NULL) {
12894  char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
12895  char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
12896  char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
12897 
12898  ma_bool32 foundDevice = MA_FALSE;
12899  if ((deviceType == ma_device_type_playback && (IOID == NULL || ma_strcmp(IOID, "Output") == 0)) ||
12900  (deviceType == ma_device_type_capture && (IOID != NULL && ma_strcmp(IOID, "Input" ) == 0))) {
12901  if (ma_strcmp(NAME, deviceName) == 0) {
12902  bufferSizeScaleFactor = ma_find_default_buffer_size_scale__alsa(DESC);
12903  foundDevice = MA_TRUE;
12904  }
12905  }
12906 
12907  free(NAME);
12908  free(DESC);
12909  free(IOID);
12910  ppNextDeviceHint += 1;
12911 
12912  if (foundDevice) {
12913  break;
12914  }
12915  }
12916 
12917  ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
12918  } else {
12919  bufferSizeScaleFactor = ma_find_default_buffer_size_scale__alsa(deviceName);
12920  }
12921  }
12922  }
12923 
12924  ma_free(pInfo);
12925  }
12926 
12927 
12928  /* Hardware parameters. */
12929  pHWParams = (ma_snd_pcm_hw_params_t*)calloc(1, ((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)());
12930  if (pHWParams == NULL) {
12931  return MA_OUT_OF_MEMORY;
12932  }
12933 
12934  if (((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams) < 0) {
12935  ma_free(pHWParams);
12936  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
12937  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
12938  }
12939 
12940  /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */
12941  isUsingMMap = MA_FALSE;
12942 #if 0 /* NOTE: MMAP mode temporarily disabled. */
12943  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. */
12944  if (!pConfig->alsa.noMMap && ma_device__is_async(pDevice)) {
12945  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) {
12946  pDevice->alsa.isUsingMMap = MA_TRUE;
12947  }
12948  }
12949  }
12950 #endif
12951 
12952  if (!isUsingMMap) {
12953  if (((ma_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
12954  ma_free(pHWParams);
12955  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
12956  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.", MA_FORMAT_NOT_SUPPORTED);
12957  }
12958  }
12959 
12960  /*
12961  Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
12962  find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
12963  */
12964 
12965  /* Format. */
12966  {
12967  ma_snd_pcm_format_mask_t* pFormatMask;
12968 
12969  /* Try getting every supported format first. */
12970  pFormatMask = (ma_snd_pcm_format_mask_t*)calloc(1, ((ma_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)());
12971  if (pFormatMask == NULL) {
12972  ma_free(pHWParams);
12973  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
12974  return MA_OUT_OF_MEMORY;
12975  }
12976 
12977  ((ma_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask);
12978 
12979  /*
12980  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
12981  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.
12982  */
12983  if (!((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, formatALSA)) {
12984  size_t i;
12985 
12986  /* The requested format is not supported so now try running through the list of formats and return the best one. */
12987  ma_snd_pcm_format_t preferredFormatsALSA[] = {
12988  MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
12989  MA_SND_PCM_FORMAT_FLOAT_LE, /* ma_format_f32 */
12990  MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
12991  MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
12992  MA_SND_PCM_FORMAT_U8 /* ma_format_u8 */
12993  };
12994 
12995  if (ma_is_big_endian()) {
12996  preferredFormatsALSA[0] = MA_SND_PCM_FORMAT_S16_BE;
12997  preferredFormatsALSA[1] = MA_SND_PCM_FORMAT_FLOAT_BE;
12998  preferredFormatsALSA[2] = MA_SND_PCM_FORMAT_S32_BE;
12999  preferredFormatsALSA[3] = MA_SND_PCM_FORMAT_S24_3BE;
13000  preferredFormatsALSA[4] = MA_SND_PCM_FORMAT_U8;
13001  }
13002 
13003  formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
13004  for (i = 0; i < (sizeof(preferredFormatsALSA) / sizeof(preferredFormatsALSA[0])); ++i) {
13005  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, preferredFormatsALSA[i])) {
13006  formatALSA = preferredFormatsALSA[i];
13007  break;
13008  }
13009  }
13010 
13011  if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
13012  ma_free(pHWParams);
13013  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13014  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);
13015  }
13016  }
13017 
13018  ma_free(pFormatMask);
13019  pFormatMask = NULL;
13020 
13021  if (((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA) < 0) {
13022  ma_free(pHWParams);
13023  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13024  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", MA_FORMAT_NOT_SUPPORTED);
13025  }
13026 
13027  internalFormat = ma_format_from_alsa(formatALSA);
13029  ma_free(pHWParams);
13030  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13031  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
13032  }
13033  }
13034 
13035  /* Channels. */
13036  {
13037  unsigned int channels = (deviceType == ma_device_type_capture) ? pConfig->capture.channels : pConfig->playback.channels;
13038  if (((ma_snd_pcm_hw_params_set_channels_near_proc)pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels) < 0) {
13039  ma_free(pHWParams);
13040  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13041  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.", MA_FORMAT_NOT_SUPPORTED);
13042  }
13043  internalChannels = (ma_uint32)channels;
13044  }
13045 
13046  /* Sample Rate */
13047  {
13048  unsigned int sampleRate;
13049 
13050  /*
13051  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
13052  problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
13053  resampling.
13054 
13055  To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
13056  sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
13057  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
13058  faster rate.
13059 
13060  miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
13061  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
13062  good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
13063 
13064  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
13065  this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
13066  */
13067  ((ma_snd_pcm_hw_params_set_rate_resample_proc)pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
13068 
13069  sampleRate = pConfig->sampleRate;
13070  if (((ma_snd_pcm_hw_params_set_rate_near_proc)pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0) < 0) {
13071  ma_free(pHWParams);
13072  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13073  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", MA_FORMAT_NOT_SUPPORTED);
13074  }
13075  internalSampleRate = (ma_uint32)sampleRate;
13076  }
13077 
13078  /* Buffer Size */
13079  {
13080  ma_snd_pcm_uframes_t actualBufferSizeInFrames = pConfig->bufferSizeInFrames;
13081  if (actualBufferSizeInFrames == 0) {
13082  actualBufferSizeInFrames = ma_scale_buffer_size(ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, internalSampleRate), bufferSizeScaleFactor);
13083  }
13084 
13085  if (((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames) < 0) {
13086  ma_free(pHWParams);
13087  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13088  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", MA_FORMAT_NOT_SUPPORTED);
13089  }
13090  internalBufferSizeInFrames = actualBufferSizeInFrames;
13091  }
13092 
13093  /* Periods. */
13094  {
13095  ma_uint32 periods = pConfig->periods;
13096  if (((ma_snd_pcm_hw_params_set_periods_near_proc)pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL) < 0) {
13097  ma_free(pHWParams);
13098  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13099  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", MA_FORMAT_NOT_SUPPORTED);
13100  }
13101  internalPeriods = periods;
13102  }
13103 
13104  /* Apply hardware parameters. */
13105  if (((ma_snd_pcm_hw_params_proc)pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams) < 0) {
13106  ma_free(pHWParams);
13107  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13108  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
13109  }
13110 
13111  ma_free(pHWParams);
13112  pHWParams = NULL;
13113 
13114 
13115  /* Software parameters. */
13116  pSWParams = (ma_snd_pcm_sw_params_t*)calloc(1, ((ma_snd_pcm_sw_params_sizeof_proc)pContext->alsa.snd_pcm_sw_params_sizeof)());
13117  if (pSWParams == NULL) {
13118  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13119  return MA_OUT_OF_MEMORY;
13120  }
13121 
13122  if (((ma_snd_pcm_sw_params_current_proc)pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams) != 0) {
13123  ma_free(pSWParams);
13124  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13125  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
13126  }
13127 
13128  if (deviceType == ma_device_type_capture) {
13129  if (((ma_snd_pcm_sw_params_set_avail_min_proc)pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, 1) != 0) {
13130  ma_free(pSWParams);
13131  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13132  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", MA_FORMAT_NOT_SUPPORTED);
13133  }
13134  } else {
13135  if (((ma_snd_pcm_sw_params_set_avail_min_proc)pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, 1) != 0) {
13136  ma_free(pSWParams);
13137  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13138  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", MA_FORMAT_NOT_SUPPORTED);
13139  }
13140  }
13141 
13142 
13143  if (((ma_snd_pcm_sw_params_get_boundary_proc)pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary) < 0) {
13144  bufferBoundary = internalBufferSizeInFrames;
13145  }
13146 
13147  /*printf("TRACE: bufferBoundary=%ld\n", bufferBoundary);*/
13148 
13149  if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
13150  /*
13151  Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
13152  the size of a period. But for full-duplex we need to set it such that it is at least two periods.
13153  */
13154  if (((ma_snd_pcm_sw_params_set_start_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalBufferSizeInFrames) != 0) {
13155  ma_free(pSWParams);
13156  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13157  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
13158  }
13159  if (((ma_snd_pcm_sw_params_set_stop_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary) != 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
13160  ma_free(pSWParams);
13161  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13162  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
13163  }
13164  }
13165 
13166  if (((ma_snd_pcm_sw_params_proc)pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams) != 0) {
13167  ma_free(pSWParams);
13168  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13169  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
13170  }
13171 
13172  ma_free(pSWParams);
13173  pSWParams = NULL;
13174 
13175 
13176  /* 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. */
13177  {
13178  ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pContext->alsa.snd_pcm_get_chmap)(pPCM);
13179  if (pChmap != NULL) {
13180  ma_uint32 iChannel;
13181 
13182  /* 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(). */
13183  if (pChmap->channels >= internalChannels) {
13184  /* Drop excess channels. */
13185  for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
13186  internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
13187  }
13188  } else {
13189  ma_uint32 i;
13190 
13191  /*
13192  Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
13193  channels. If validation fails, fall back to defaults.
13194  */
13195  ma_bool32 isValid = MA_TRUE;
13196 
13197  /* Fill with defaults. */
13198  ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
13199 
13200  /* Overwrite first pChmap->channels channels. */
13201  for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
13202  internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
13203  }
13204 
13205  /* Validate. */
13206  for (i = 0; i < internalChannels && isValid; ++i) {
13207  ma_uint32 j;
13208  for (j = i+1; j < internalChannels; ++j) {
13209  if (internalChannelMap[i] == internalChannelMap[j]) {
13210  isValid = MA_FALSE;
13211  break;
13212  }
13213  }
13214  }
13215 
13216  /* If our channel map is invalid, fall back to defaults. */
13217  if (!isValid) {
13218  ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
13219  }
13220  }
13221 
13222  free(pChmap);
13223  pChmap = NULL;
13224  } else {
13225  /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
13226  ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
13227  }
13228  }
13229 
13230 
13231  /* We're done. Prepare the device. */
13232  if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM) < 0) {
13233  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13234  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", MA_FAILED_TO_START_BACKEND_DEVICE);
13235  }
13236 
13237 
13238  if (deviceType == ma_device_type_capture) {
13239  pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
13240  pDevice->alsa.isUsingMMapCapture = isUsingMMap;
13241  pDevice->capture.internalFormat = internalFormat;
13242  pDevice->capture.internalChannels = internalChannels;
13243  pDevice->capture.internalSampleRate = internalSampleRate;
13244  ma_channel_map_copy(pDevice->capture.internalChannelMap, internalChannelMap, internalChannels);
13245  pDevice->capture.internalBufferSizeInFrames = internalBufferSizeInFrames;
13246  pDevice->capture.internalPeriods = internalPeriods;
13247  } else {
13248  pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
13249  pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
13250  pDevice->playback.internalFormat = internalFormat;
13251  pDevice->playback.internalChannels = internalChannels;
13252  pDevice->playback.internalSampleRate = internalSampleRate;
13253  ma_channel_map_copy(pDevice->playback.internalChannelMap, internalChannelMap, internalChannels);
13254  pDevice->playback.internalBufferSizeInFrames = internalBufferSizeInFrames;
13255  pDevice->playback.internalPeriods = internalPeriods;
13256  }
13257 
13258  return MA_SUCCESS;
13259 }
13260 
13261 ma_result ma_device_init__alsa(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
13262 {
13263  ma_assert(pDevice != NULL);
13264 
13265  ma_zero_object(&pDevice->alsa);
13266 
13267  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
13268  ma_result result = ma_device_init_by_type__alsa(pContext, pConfig, ma_device_type_capture, pDevice);
13269  if (result != MA_SUCCESS) {
13270  return result;
13271  }
13272  }
13273 
13274  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
13275  ma_result result = ma_device_init_by_type__alsa(pContext, pConfig, ma_device_type_playback, pDevice);
13276  if (result != MA_SUCCESS) {
13277  return result;
13278  }
13279  }
13280 
13281  return MA_SUCCESS;
13282 }
13283 
13284 #if 0
13285 ma_result ma_device_start__alsa(ma_device* pDevice)
13286 {
13287  ma_assert(pDevice != NULL);
13288 
13289  /* Prepare the device first... */
13290  if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
13291  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", MA_FAILED_TO_START_BACKEND_DEVICE);
13292  }
13293 
13294  /*
13295  ... and then grab an initial chunk from the client. After this is done, the device should
13296  automatically start playing, since that's how we configured the software parameters.
13297  */
13298  if (pDevice->type == ma_device_type_playback) {
13299  if (!ma_device_read_from_client_and_write__alsa(pDevice)) {
13300  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write initial chunk of data to the playback device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
13301  }
13302 
13303  /* mmap mode requires an explicit start. */
13304  if (pDevice->alsa.isUsingMMap) {
13305  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
13306  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
13307  }
13308  }
13309  } else {
13310  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
13311  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
13312  }
13313  }
13314 
13315  return MA_SUCCESS;
13316 }
13317 #endif /* 0 */
13318 
13319 ma_result ma_device_stop__alsa(ma_device* pDevice)
13320 {
13321  ma_assert(pDevice != NULL);
13322 
13323  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
13324  ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
13325 
13326  /* We need to prepare the device again, otherwise we won't be able to restart the device. */
13327  if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
13328  #ifdef MA_DEBUG_OUTPUT
13329  printf("[ALSA] Failed to prepare capture device after stopping.\n");
13330  #endif
13331  }
13332  }
13333 
13334  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
13335  /* Using drain instead of drop because ma_device_stop() is defined such that pending frames are processed before returning. */
13336  ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
13337 
13338  /* We need to prepare the device again, otherwise we won't be able to restart the device. */
13339  if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
13340  #ifdef MA_DEBUG_OUTPUT
13341  printf("[ALSA] Failed to prepare playback device after stopping.\n");
13342  #endif
13343  }
13344  }
13345 
13346 
13347 
13348  return MA_SUCCESS;
13349 }
13350 
13351 ma_result ma_device_write__alsa(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount)
13352 {
13353  ma_snd_pcm_sframes_t resultALSA;
13354  ma_uint32 totalPCMFramesProcessed;
13355 
13356  ma_assert(pDevice != NULL);
13357  ma_assert(pPCMFrames != NULL);
13358 
13359  /*printf("TRACE: Enter write()\n");*/
13360 
13361  totalPCMFramesProcessed = 0;
13362  while (totalPCMFramesProcessed < frameCount) {
13363  const void* pSrc = ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
13364  ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
13365 
13366  /*printf("TRACE: Writing %d frames (frameCount=%d)\n", framesRemaining, frameCount);*/
13367 
13368  resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pSrc, framesRemaining);
13369  if (resultALSA < 0) {
13370  if (resultALSA == -EAGAIN) {
13371  /*printf("TRACE: EGAIN (write)\n");*/
13372  continue; /* Try again. */
13373  } else if (resultALSA == -EPIPE) {
13374  /*printf("TRACE: EPIPE (write)\n");*/
13375 
13376  /* Underrun. Recover and try again. If this fails we need to return an error. */
13377  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE) < 0) { /* MA_TRUE=silent (don't print anything on error). */
13378  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
13379  }
13380 
13381  /*
13382  In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
13383  up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
13384  frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
13385  if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
13386  quite right here.
13387  */
13388  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
13389  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
13390  }
13391 
13392  resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pSrc, framesRemaining);
13393  if (resultALSA < 0) {
13394  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
13395  }
13396  }
13397  }
13398 
13399  totalPCMFramesProcessed += resultALSA;
13400  }
13401 
13402  return MA_SUCCESS;
13403 }
13404 
13405 ma_result ma_device_read__alsa(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount)
13406 {
13407  ma_snd_pcm_sframes_t resultALSA;
13408  ma_uint32 totalPCMFramesProcessed;
13409 
13410  ma_assert(pDevice != NULL);
13411  ma_assert(pPCMFrames != NULL);
13412 
13413  /* We need to explicitly start the device if it isn't already. */
13414  if (((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) != MA_SND_PCM_STATE_RUNNING) {
13415  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
13416  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device in preparation for reading.", MA_FAILED_TO_START_BACKEND_DEVICE);
13417  }
13418  }
13419 
13420  totalPCMFramesProcessed = 0;
13421  while (totalPCMFramesProcessed < frameCount) {
13422  void* pDst = ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
13423  ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
13424 
13425  /*printf("TRACE: snd_pcm_readi(framesRemaining=%d)\n", framesRemaining);*/
13426 
13427  resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pDst, framesRemaining);
13428  if (resultALSA < 0) {
13429  if (resultALSA == -EAGAIN) {
13430  /*printf("TRACE: EGAIN (read)\n");*/
13431  continue;
13432  } else if (resultALSA == -EPIPE) {
13433  /*printf("TRACE: EPIPE (read)\n");*/
13434 
13435  /* Overrun. Recover and try again. If this fails we need to return an error. */
13436  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE) < 0) {
13437  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
13438  }
13439 
13440  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
13441  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
13442  }
13443 
13444  resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pDst, framesRemaining);
13445  if (resultALSA < 0) {
13446  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
13447  }
13448  }
13449  }
13450 
13451  totalPCMFramesProcessed += resultALSA;
13452  }
13453 
13454  return MA_SUCCESS;
13455 }
13456 
13457 #if 0
13458 ma_result ma_device_break_main_loop__alsa(ma_device* pDevice)
13459 {
13460  ma_assert(pDevice != NULL);
13461 
13462  pDevice->alsa.breakFromMainLoop = MA_TRUE;
13463  return MA_SUCCESS;
13464 }
13465 
13466 ma_result ma_device_main_loop__alsa(ma_device* pDevice)
13467 {
13468  ma_assert(pDevice != NULL);
13469 
13470  pDevice->alsa.breakFromMainLoop = MA_FALSE;
13471  if (pDevice->type == ma_device_type_playback) {
13472  /* Playback. Read from client, write to device. */
13473  while (!pDevice->alsa.breakFromMainLoop && ma_device_read_from_client_and_write__alsa(pDevice)) {
13474  }
13475  } else {
13476  /* Capture. Read from device, write to client. */
13477  while (!pDevice->alsa.breakFromMainLoop && ma_device_read_and_send_to_client__alsa(pDevice)) {
13478  }
13479  }
13480 
13481  return MA_SUCCESS;
13482 }
13483 #endif /* 0 */
13484 
13485 ma_result ma_context_uninit__alsa(ma_context* pContext)
13486 {
13487  ma_assert(pContext != NULL);
13488  ma_assert(pContext->backend == ma_backend_alsa);
13489 
13490  /* Clean up memory for memory leak checkers. */
13491  ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
13492 
13493 #ifndef MA_NO_RUNTIME_LINKING
13494  ma_dlclose(pContext, pContext->alsa.asoundSO);
13495 #endif
13496 
13497  ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
13498 
13499  return MA_SUCCESS;
13500 }
13501 
13502 ma_result ma_context_init__alsa(const ma_context_config* pConfig, ma_context* pContext)
13503 {
13504 #ifndef MA_NO_RUNTIME_LINKING
13505  const char* libasoundNames[] = {
13506  "libasound.so.2",
13507  "libasound.so"
13508  };
13509  size_t i;
13510 
13511  for (i = 0; i < ma_countof(libasoundNames); ++i) {
13512  pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]);
13513  if (pContext->alsa.asoundSO != NULL) {
13514  break;
13515  }
13516  }
13517 
13518  if (pContext->alsa.asoundSO == NULL) {
13519 #ifdef MA_DEBUG_OUTPUT
13520  printf("[ALSA] Failed to open shared object.\n");
13521 #endif
13522  return MA_NO_BACKEND;
13523  }
13524 
13525  pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open");
13526  pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close");
13527  pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
13528  pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
13529  pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
13530  pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
13531  pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
13532  pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
13533  pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
13534  pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
13535  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");
13536  pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
13537  pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
13538  pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
13539  pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
13540  pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
13541  pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
13542  pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
13543  pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
13544  pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
13545  pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
13546  pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
13547  pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
13548  pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params");
13549  pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
13550  pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
13551  pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
13552  pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
13553  pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
13554  pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
13555  pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params");
13556  pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
13557  pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
13558  pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap");
13559  pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state");
13560  pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare");
13561  pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start");
13562  pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop");
13563  pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain");
13564  pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint");
13565  pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint");
13566  pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index");
13567  pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint");
13568  pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
13569  pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
13570  pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover");
13571  pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi");
13572  pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei");
13573  pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail");
13574  pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update");
13575  pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait");
13576  pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info");
13577  pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
13578  pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name");
13579  pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global");
13580 #else
13581  /* The system below is just for type safety. */
13582  ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
13583  ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
13584  ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
13585  ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
13586  ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
13587  ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
13588  ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
13589  ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
13590  ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
13591  ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
13592  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;
13593  ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
13594  ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
13595  ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
13596  ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
13597  ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
13598  ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
13599  ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
13600  ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
13601  ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
13602  ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
13603  ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
13604  ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
13605  ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
13606  ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
13607  ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
13608  ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
13609  ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
13610  ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
13611  ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
13612  ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
13613  ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
13614  ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
13615  ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
13616  ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
13617  ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
13618  ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
13619  ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
13620  ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
13621  ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
13622  ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
13623  ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
13624  ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
13625  ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
13626  ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
13627  ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
13628  ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
13629  ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
13630  ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
13631  ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
13632  ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
13633  ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
13634  ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
13635  ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
13636  ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
13637 
13638  pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
13639  pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
13640  pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
13641  pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
13642  pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
13643  pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
13644  pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
13645  pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
13646  pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
13647  pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
13648  pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
13649  pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
13650  pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
13651  pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
13652  pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
13653  pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
13654  pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
13655  pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
13656  pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
13657  pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
13658  pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
13659  pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
13660  pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
13661  pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
13662  pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
13663  pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
13664  pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
13665  pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
13666  pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
13667  pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
13668  pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
13669  pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
13670  pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
13671  pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
13672  pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
13673  pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
13674  pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
13675  pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
13676  pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
13677  pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
13678  pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
13679  pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
13680  pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
13681  pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
13682  pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
13683  pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
13684  pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
13685  pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
13686  pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
13687  pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
13688  pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
13689  pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
13690  pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
13691 #endif
13692 
13693  pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
13694 
13695  if (ma_mutex_init(pContext, &pContext->alsa.internalDeviceEnumLock) != MA_SUCCESS) {
13696  ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.", MA_ERROR);
13697  }
13698 
13699  pContext->onUninit = ma_context_uninit__alsa;
13700  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__alsa;
13701  pContext->onEnumDevices = ma_context_enumerate_devices__alsa;
13702  pContext->onGetDeviceInfo = ma_context_get_device_info__alsa;
13703  pContext->onDeviceInit = ma_device_init__alsa;
13704  pContext->onDeviceUninit = ma_device_uninit__alsa;
13705  pContext->onDeviceStart = NULL; /*ma_device_start__alsa;*/
13706  pContext->onDeviceStop = ma_device_stop__alsa;
13707  pContext->onDeviceWrite = ma_device_write__alsa;
13708  pContext->onDeviceRead = ma_device_read__alsa;
13709 
13710  return MA_SUCCESS;
13711 }
13712 #endif /* ALSA */
13713 
13714 
13715 
13716 /******************************************************************************
13717 
13718 PulseAudio Backend
13719 
13720 ******************************************************************************/
13721 #ifdef MA_HAS_PULSEAUDIO
13722 /*
13723 It is assumed pulseaudio.h is available when compile-time linking is being used. We use this for type safety when using
13724 compile time linking (we don't have this luxury when using runtime linking without headers).
13725 
13726 When using compile time linking, each of our ma_* equivalents should use the sames types as defined by the header. The
13727 reason for this is that it allow us to take advantage of proper type safety.
13728 */
13729 #ifdef MA_NO_RUNTIME_LINKING
13730 #include <pulse/pulseaudio.h>
13731 
13732 #define MA_PA_OK PA_OK
13733 #define MA_PA_ERR_ACCESS PA_ERR_ACCESS
13734 #define MA_PA_ERR_INVALID PA_ERR_INVALID
13735 #define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
13736 
13737 #define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
13738 #define MA_PA_RATE_MAX PA_RATE_MAX
13739 
13740 typedef pa_context_flags_t ma_pa_context_flags_t;
13741 #define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
13742 #define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
13743 #define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
13744 
13745 typedef pa_stream_flags_t ma_pa_stream_flags_t;
13746 #define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
13747 #define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
13748 #define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
13749 #define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
13750 #define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
13751 #define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
13752 #define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
13753 #define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
13754 #define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
13755 #define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
13756 #define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
13757 #define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
13758 #define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
13759 #define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
13760 #define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
13761 #define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
13762 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
13763 #define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
13764 #define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
13765 #define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
13766 #define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
13767 
13768 typedef pa_sink_flags_t ma_pa_sink_flags_t;
13769 #define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
13770 #define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
13771 #define MA_PA_SINK_LATENCY PA_SINK_LATENCY
13772 #define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
13773 #define MA_PA_SINK_NETWORK PA_SINK_NETWORK
13774 #define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
13775 #define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
13776 #define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
13777 #define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
13778 #define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
13779 
13780 typedef pa_source_flags_t ma_pa_source_flags_t;
13781 #define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
13782 #define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
13783 #define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
13784 #define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
13785 #define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
13786 #define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
13787 #define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
13788 #define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
13789 #define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
13790 
13791 typedef pa_context_state_t ma_pa_context_state_t;
13792 #define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
13793 #define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
13794 #define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
13795 #define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
13796 #define MA_PA_CONTEXT_READY PA_CONTEXT_READY
13797 #define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
13798 #define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
13799 
13800 typedef pa_stream_state_t ma_pa_stream_state_t;
13801 #define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
13802 #define MA_PA_STREAM_CREATING PA_STREAM_CREATING
13803 #define MA_PA_STREAM_READY PA_STREAM_READY
13804 #define MA_PA_STREAM_FAILED PA_STREAM_FAILED
13805 #define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
13806 
13807 typedef pa_operation_state_t ma_pa_operation_state_t;
13808 #define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
13809 #define MA_PA_OPERATION_DONE PA_OPERATION_DONE
13810 #define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
13811 
13812 typedef pa_sink_state_t ma_pa_sink_state_t;
13813 #define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
13814 #define MA_PA_SINK_RUNNING PA_SINK_RUNNING
13815 #define MA_PA_SINK_IDLE PA_SINK_IDLE
13816 #define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
13817 
13818 typedef pa_source_state_t ma_pa_source_state_t;
13819 #define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
13820 #define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
13821 #define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
13822 #define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
13823 
13824 typedef pa_seek_mode_t ma_pa_seek_mode_t;
13825 #define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
13826 #define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
13827 #define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
13828 #define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
13829 
13830 typedef pa_channel_position_t ma_pa_channel_position_t;
13831 #define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
13832 #define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
13833 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
13834 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
13835 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
13836 #define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
13837 #define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
13838 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
13839 #define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
13840 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
13841 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
13842 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
13843 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
13844 #define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
13845 #define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
13846 #define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
13847 #define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
13848 #define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
13849 #define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
13850 #define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
13851 #define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
13852 #define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
13853 #define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
13854 #define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
13855 #define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
13856 #define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
13857 #define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
13858 #define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
13859 #define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
13860 #define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
13861 #define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
13862 #define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
13863 #define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
13864 #define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
13865 #define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
13866 #define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
13867 #define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
13868 #define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
13869 #define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
13870 #define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
13871 #define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
13872 #define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
13873 #define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
13874 #define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
13875 #define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
13876 #define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
13877 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
13878 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
13879 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
13880 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
13881 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
13882 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
13883 #define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
13884 #define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
13885 #define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
13886 #define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
13887 
13888 typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
13889 #define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
13890 #define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
13891 #define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
13892 #define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
13893 #define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
13894 #define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
13895 
13896 typedef pa_sample_format_t ma_pa_sample_format_t;
13897 #define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
13898 #define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
13899 #define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
13900 #define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
13901 #define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
13902 #define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
13903 #define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
13904 #define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
13905 #define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
13906 #define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
13907 #define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
13908 #define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
13909 #define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
13910 #define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
13911 
13912 typedef pa_mainloop ma_pa_mainloop;
13913 typedef pa_mainloop_api ma_pa_mainloop_api;
13914 typedef pa_context ma_pa_context;
13915 typedef pa_operation ma_pa_operation;
13916 typedef pa_stream ma_pa_stream;
13917 typedef pa_spawn_api ma_pa_spawn_api;
13918 typedef pa_buffer_attr ma_pa_buffer_attr;
13919 typedef pa_channel_map ma_pa_channel_map;
13920 typedef pa_cvolume ma_pa_cvolume;
13921 typedef pa_sample_spec ma_pa_sample_spec;
13922 typedef pa_sink_info ma_pa_sink_info;
13923 typedef pa_source_info ma_pa_source_info;
13924 
13925 typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
13926 typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
13927 typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
13928 typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
13929 typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
13930 typedef pa_free_cb_t ma_pa_free_cb_t;
13931 #else
13932 #define MA_PA_OK 0
13933 #define MA_PA_ERR_ACCESS 1
13934 #define MA_PA_ERR_INVALID 2
13935 #define MA_PA_ERR_NOENTITY 5
13936 
13937 #define MA_PA_CHANNELS_MAX 32
13938 #define MA_PA_RATE_MAX 384000
13939 
13940 typedef int ma_pa_context_flags_t;
13941 #define MA_PA_CONTEXT_NOFLAGS 0x00000000
13942 #define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
13943 #define MA_PA_CONTEXT_NOFAIL 0x00000002
13944 
13945 typedef int ma_pa_stream_flags_t;
13946 #define MA_PA_STREAM_NOFLAGS 0x00000000
13947 #define MA_PA_STREAM_START_CORKED 0x00000001
13948 #define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
13949 #define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
13950 #define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
13951 #define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
13952 #define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
13953 #define MA_PA_STREAM_FIX_FORMAT 0x00000040
13954 #define MA_PA_STREAM_FIX_RATE 0x00000080
13955 #define MA_PA_STREAM_FIX_CHANNELS 0x00000100
13956 #define MA_PA_STREAM_DONT_MOVE 0x00000200
13957 #define MA_PA_STREAM_VARIABLE_RATE 0x00000400
13958 #define MA_PA_STREAM_PEAK_DETECT 0x00000800
13959 #define MA_PA_STREAM_START_MUTED 0x00001000
13960 #define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
13961 #define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
13962 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
13963 #define MA_PA_STREAM_START_UNMUTED 0x00010000
13964 #define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
13965 #define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
13966 #define MA_PA_STREAM_PASSTHROUGH 0x00080000
13967 
13968 typedef int ma_pa_sink_flags_t;
13969 #define MA_PA_SINK_NOFLAGS 0x00000000
13970 #define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
13971 #define MA_PA_SINK_LATENCY 0x00000002
13972 #define MA_PA_SINK_HARDWARE 0x00000004
13973 #define MA_PA_SINK_NETWORK 0x00000008
13974 #define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
13975 #define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
13976 #define MA_PA_SINK_FLAT_VOLUME 0x00000040
13977 #define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
13978 #define MA_PA_SINK_SET_FORMATS 0x00000100
13979 
13980 typedef int ma_pa_source_flags_t;
13981 #define MA_PA_SOURCE_NOFLAGS 0x00000000
13982 #define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
13983 #define MA_PA_SOURCE_LATENCY 0x00000002
13984 #define MA_PA_SOURCE_HARDWARE 0x00000004
13985 #define MA_PA_SOURCE_NETWORK 0x00000008
13986 #define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
13987 #define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
13988 #define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
13989 #define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
13990 
13991 typedef int ma_pa_context_state_t;
13992 #define MA_PA_CONTEXT_UNCONNECTED 0
13993 #define MA_PA_CONTEXT_CONNECTING 1
13994 #define MA_PA_CONTEXT_AUTHORIZING 2
13995 #define MA_PA_CONTEXT_SETTING_NAME 3
13996 #define MA_PA_CONTEXT_READY 4
13997 #define MA_PA_CONTEXT_FAILED 5
13998 #define MA_PA_CONTEXT_TERMINATED 6
13999 
14000 typedef int ma_pa_stream_state_t;
14001 #define MA_PA_STREAM_UNCONNECTED 0
14002 #define MA_PA_STREAM_CREATING 1
14003 #define MA_PA_STREAM_READY 2
14004 #define MA_PA_STREAM_FAILED 3
14005 #define MA_PA_STREAM_TERMINATED 4
14006 
14007 typedef int ma_pa_operation_state_t;
14008 #define MA_PA_OPERATION_RUNNING 0
14009 #define MA_PA_OPERATION_DONE 1
14010 #define MA_PA_OPERATION_CANCELLED 2
14011 
14012 typedef int ma_pa_sink_state_t;
14013 #define MA_PA_SINK_INVALID_STATE -1
14014 #define MA_PA_SINK_RUNNING 0
14015 #define MA_PA_SINK_IDLE 1
14016 #define MA_PA_SINK_SUSPENDED 2
14017 
14018 typedef int ma_pa_source_state_t;
14019 #define MA_PA_SOURCE_INVALID_STATE -1
14020 #define MA_PA_SOURCE_RUNNING 0
14021 #define MA_PA_SOURCE_IDLE 1
14022 #define MA_PA_SOURCE_SUSPENDED 2
14023 
14024 typedef int ma_pa_seek_mode_t;
14025 #define MA_PA_SEEK_RELATIVE 0
14026 #define MA_PA_SEEK_ABSOLUTE 1
14027 #define MA_PA_SEEK_RELATIVE_ON_READ 2
14028 #define MA_PA_SEEK_RELATIVE_END 3
14029 
14030 typedef int ma_pa_channel_position_t;
14031 #define MA_PA_CHANNEL_POSITION_INVALID -1
14032 #define MA_PA_CHANNEL_POSITION_MONO 0
14033 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
14034 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
14035 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
14036 #define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
14037 #define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
14038 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
14039 #define MA_PA_CHANNEL_POSITION_LFE 7
14040 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
14041 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
14042 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
14043 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
14044 #define MA_PA_CHANNEL_POSITION_AUX0 12
14045 #define MA_PA_CHANNEL_POSITION_AUX1 13
14046 #define MA_PA_CHANNEL_POSITION_AUX2 14
14047 #define MA_PA_CHANNEL_POSITION_AUX3 15
14048 #define MA_PA_CHANNEL_POSITION_AUX4 16
14049 #define MA_PA_CHANNEL_POSITION_AUX5 17
14050 #define MA_PA_CHANNEL_POSITION_AUX6 18
14051 #define MA_PA_CHANNEL_POSITION_AUX7 19
14052 #define MA_PA_CHANNEL_POSITION_AUX8 20
14053 #define MA_PA_CHANNEL_POSITION_AUX9 21
14054 #define MA_PA_CHANNEL_POSITION_AUX10 22
14055 #define MA_PA_CHANNEL_POSITION_AUX11 23
14056 #define MA_PA_CHANNEL_POSITION_AUX12 24
14057 #define MA_PA_CHANNEL_POSITION_AUX13 25
14058 #define MA_PA_CHANNEL_POSITION_AUX14 26
14059 #define MA_PA_CHANNEL_POSITION_AUX15 27
14060 #define MA_PA_CHANNEL_POSITION_AUX16 28
14061 #define MA_PA_CHANNEL_POSITION_AUX17 29
14062 #define MA_PA_CHANNEL_POSITION_AUX18 30
14063 #define MA_PA_CHANNEL_POSITION_AUX19 31
14064 #define MA_PA_CHANNEL_POSITION_AUX20 32
14065 #define MA_PA_CHANNEL_POSITION_AUX21 33
14066 #define MA_PA_CHANNEL_POSITION_AUX22 34
14067 #define MA_PA_CHANNEL_POSITION_AUX23 35
14068 #define MA_PA_CHANNEL_POSITION_AUX24 36
14069 #define MA_PA_CHANNEL_POSITION_AUX25 37
14070 #define MA_PA_CHANNEL_POSITION_AUX26 38
14071 #define MA_PA_CHANNEL_POSITION_AUX27 39
14072 #define MA_PA_CHANNEL_POSITION_AUX28 40
14073 #define MA_PA_CHANNEL_POSITION_AUX29 41
14074 #define MA_PA_CHANNEL_POSITION_AUX30 42
14075 #define MA_PA_CHANNEL_POSITION_AUX31 43
14076 #define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
14077 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
14078 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
14079 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
14080 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
14081 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
14082 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
14083 #define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
14084 #define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
14085 #define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
14086 #define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
14087 
14088 typedef int ma_pa_channel_map_def_t;
14089 #define MA_PA_CHANNEL_MAP_AIFF 0
14090 #define MA_PA_CHANNEL_MAP_ALSA 1
14091 #define MA_PA_CHANNEL_MAP_AUX 2
14092 #define MA_PA_CHANNEL_MAP_WAVEEX 3
14093 #define MA_PA_CHANNEL_MAP_OSS 4
14094 #define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
14095 
14096 typedef int ma_pa_sample_format_t;
14097 #define MA_PA_SAMPLE_INVALID -1
14098 #define MA_PA_SAMPLE_U8 0
14099 #define MA_PA_SAMPLE_ALAW 1
14100 #define MA_PA_SAMPLE_ULAW 2
14101 #define MA_PA_SAMPLE_S16LE 3
14102 #define MA_PA_SAMPLE_S16BE 4
14103 #define MA_PA_SAMPLE_FLOAT32LE 5
14104 #define MA_PA_SAMPLE_FLOAT32BE 6
14105 #define MA_PA_SAMPLE_S32LE 7
14106 #define MA_PA_SAMPLE_S32BE 8
14107 #define MA_PA_SAMPLE_S24LE 9
14108 #define MA_PA_SAMPLE_S24BE 10
14109 #define MA_PA_SAMPLE_S24_32LE 11
14110 #define MA_PA_SAMPLE_S24_32BE 12
14111 
14112 typedef struct ma_pa_mainloop ma_pa_mainloop;
14113 typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
14114 typedef struct ma_pa_context ma_pa_context;
14115 typedef struct ma_pa_operation ma_pa_operation;
14116 typedef struct ma_pa_stream ma_pa_stream;
14117 typedef struct ma_pa_spawn_api ma_pa_spawn_api;
14118 
14119 typedef struct
14120 {
14121  ma_uint32 maxlength;
14122  ma_uint32 tlength;
14123  ma_uint32 prebuf;
14124  ma_uint32 minreq;
14125  ma_uint32 fragsize;
14126 } ma_pa_buffer_attr;
14127 
14128 typedef struct
14129 {
14131  ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
14132 } ma_pa_channel_map;
14133 
14134 typedef struct
14135 {
14137  ma_uint32 values[MA_PA_CHANNELS_MAX];
14138 } ma_pa_cvolume;
14139 
14140 typedef struct
14141 {
14142  ma_pa_sample_format_t format;
14143  ma_uint32 rate;
14145 } ma_pa_sample_spec;
14146 
14147 typedef struct
14148 {
14149  const char* name;
14150  ma_uint32 index;
14151  const char* description;
14152  ma_pa_sample_spec sample_spec;
14153  ma_pa_channel_map channel_map;
14154  ma_uint32 owner_module;
14155  ma_pa_cvolume volume;
14156  int mute;
14157  ma_uint32 monitor_source;
14158  const char* monitor_source_name;
14159  ma_uint64 latency;
14160  const char* driver;
14161  ma_pa_sink_flags_t flags;
14162  void* proplist;
14163  ma_uint64 configured_latency;
14164  ma_uint32 base_volume;
14165  ma_pa_sink_state_t state;
14166  ma_uint32 n_volume_steps;
14167  ma_uint32 card;
14168  ma_uint32 n_ports;
14169  void** ports;
14170  void* active_port;
14171  ma_uint8 n_formats;
14172  void** formats;
14173 } ma_pa_sink_info;
14174 
14175 typedef struct
14176 {
14177  const char *name;
14178  ma_uint32 index;
14179  const char *description;
14180  ma_pa_sample_spec sample_spec;
14181  ma_pa_channel_map channel_map;
14182  ma_uint32 owner_module;
14183  ma_pa_cvolume volume;
14184  int mute;
14185  ma_uint32 monitor_of_sink;
14186  const char *monitor_of_sink_name;
14187  ma_uint64 latency;
14188  const char *driver;
14189  ma_pa_source_flags_t flags;
14190  void* proplist;
14191  ma_uint64 configured_latency;
14192  ma_uint32 base_volume;
14193  ma_pa_source_state_t state;
14194  ma_uint32 n_volume_steps;
14195  ma_uint32 card;
14196  ma_uint32 n_ports;
14197  void** ports;
14198  void* active_port;
14199  ma_uint8 n_formats;
14200  void** formats;
14201 } ma_pa_source_info;
14202 
14203 typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
14204 typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
14205 typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
14206 typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
14207 typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
14208 typedef void (* ma_pa_free_cb_t) (void* p);
14209 #endif
14210 
14211 
14212 typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) ();
14213 typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
14214 typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
14215 typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
14216 typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
14217 typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
14218 typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
14219 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);
14220 typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
14221 typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
14222 typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c);
14223 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);
14224 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);
14225 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);
14226 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);
14227 typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
14228 typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o);
14229 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);
14230 typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
14231 typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
14232 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);
14233 typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
14234 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);
14235 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);
14236 typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
14237 typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s);
14238 typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
14239 typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
14240 typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
14241 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);
14242 typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s);
14243 typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
14244 typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
14245 typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
14246 typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
14247 typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s);
14248 typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
14249 typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
14250 typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
14251 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);
14252 typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
14253 typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
14254 typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s);
14255 typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s);
14256 
14257 typedef struct
14258 {
14259  ma_uint32 count;
14260  ma_uint32 capacity;
14261  ma_device_info* pInfo;
14262 } ma_pulse_device_enum_data;
14263 
14264 ma_result ma_result_from_pulse(int result)
14265 {
14266  switch (result) {
14267  case MA_PA_OK: return MA_SUCCESS;
14268  case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
14269  case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
14270  case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
14271  default: return MA_ERROR;
14272  }
14273 }
14274 
14275 #if 0
14276 ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
14277 {
14278  if (ma_is_little_endian()) {
14279  switch (format) {
14280  case ma_format_s16: return MA_PA_SAMPLE_S16LE;
14281  case ma_format_s24: return MA_PA_SAMPLE_S24LE;
14282  case ma_format_s32: return MA_PA_SAMPLE_S32LE;
14283  case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
14284  default: break;
14285  }
14286  } else {
14287  switch (format) {
14288  case ma_format_s16: return MA_PA_SAMPLE_S16BE;
14289  case ma_format_s24: return MA_PA_SAMPLE_S24BE;
14290  case ma_format_s32: return MA_PA_SAMPLE_S32BE;
14291  case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
14292  default: break;
14293  }
14294  }
14295 
14296  /* Endian agnostic. */
14297  switch (format) {
14298  case ma_format_u8: return MA_PA_SAMPLE_U8;
14299  default: return MA_PA_SAMPLE_INVALID;
14300  }
14301 }
14302 #endif
14303 
14304 ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
14305 {
14306  if (ma_is_little_endian()) {
14307  switch (format) {
14308  case MA_PA_SAMPLE_S16LE: return ma_format_s16;
14309  case MA_PA_SAMPLE_S24LE: return ma_format_s24;
14310  case MA_PA_SAMPLE_S32LE: return ma_format_s32;
14311  case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
14312  default: break;
14313  }
14314  } else {
14315  switch (format) {
14316  case MA_PA_SAMPLE_S16BE: return ma_format_s16;
14317  case MA_PA_SAMPLE_S24BE: return ma_format_s24;
14318  case MA_PA_SAMPLE_S32BE: return ma_format_s32;
14319  case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
14320  default: break;
14321  }
14322  }
14323 
14324  /* Endian agnostic. */
14325  switch (format) {
14326  case MA_PA_SAMPLE_U8: return ma_format_u8;
14327  default: return ma_format_unknown;
14328  }
14329 }
14330 
14331 ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
14332 {
14333  switch (position)
14334  {
14335  case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
14336  case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
14337  case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
14338  case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
14339  case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
14340  case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
14341  case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
14342  case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
14343  case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
14344  case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
14345  case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
14346  case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
14347  case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
14348  case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
14349  case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
14350  case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
14351  case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
14352  case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
14353  case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
14354  case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
14355  case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
14356  case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
14357  case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
14358  case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
14359  case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
14360  case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
14361  case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
14362  case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
14363  case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
14364  case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
14365  case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
14366  case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
14367  case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
14368  case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
14369  case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
14370  case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
14371  case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
14372  case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
14373  case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
14374  case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
14375  case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
14376  case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
14377  case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
14378  case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
14379  case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
14380  case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
14381  case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
14382  case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
14383  case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
14384  case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
14385  case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
14386  case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
14387  default: return MA_CHANNEL_NONE;
14388  }
14389 }
14390 
14391 #if 0
14392 ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
14393 {
14394  switch (position)
14395  {
14396  case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
14397  case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
14398  case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
14399  case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
14400  case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
14401  case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
14402  case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
14403  case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
14404  case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
14405  case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
14406  case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
14407  case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
14408  case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
14409  case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
14410  case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
14411  case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
14412  case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
14413  case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
14414  case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
14415  case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
14416  case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
14417  case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
14418  case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
14419  case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
14420  case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
14421  case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
14422  case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
14423  case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
14424  case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
14425  case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
14426  case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
14427  case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
14428  case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
14429  default: return (ma_pa_channel_position_t)position;
14430  }
14431 }
14432 #endif
14433 
14434 ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_mainloop* pMainLoop, ma_pa_operation* pOP)
14435 {
14436  ma_assert(pContext != NULL);
14437  ma_assert(pMainLoop != NULL);
14438  ma_assert(pOP != NULL);
14439 
14440  while (((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP) == MA_PA_OPERATION_RUNNING) {
14441  int error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
14442  if (error < 0) {
14443  return ma_result_from_pulse(error);
14444  }
14445  }
14446 
14447  return MA_SUCCESS;
14448 }
14449 
14450 ma_result ma_device__wait_for_operation__pulse(ma_device* pDevice, ma_pa_operation* pOP)
14451 {
14452  ma_assert(pDevice != NULL);
14453  ma_assert(pOP != NULL);
14454 
14455  return ma_wait_for_operation__pulse(pDevice->pContext, (ma_pa_mainloop*)pDevice->pulse.pMainLoop, pOP);
14456 }
14457 
14458 
14459 ma_bool32 ma_context_is_device_id_equal__pulse(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
14460 {
14461  ma_assert(pContext != NULL);
14462  ma_assert(pID0 != NULL);
14463  ma_assert(pID1 != NULL);
14464  (void)pContext;
14465 
14466  return ma_strcmp(pID0->pulse, pID1->pulse) == 0;
14467 }
14468 
14469 
14470 typedef struct
14471 {
14472  ma_context* pContext;
14474  void* pUserData;
14475  ma_bool32 isTerminated;
14476 } ma_context_enumerate_devices_callback_data__pulse;
14477 
14478 void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
14479 {
14480  ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
14481  ma_device_info deviceInfo;
14482 
14483  ma_assert(pData != NULL);
14484 
14485  if (endOfList || pData->isTerminated) {
14486  return;
14487  }
14488 
14489  ma_zero_object(&deviceInfo);
14490 
14491  /* The name from PulseAudio is the ID for miniaudio. */
14492  if (pSinkInfo->name != NULL) {
14493  ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
14494  }
14495 
14496  /* The description from PulseAudio is the name for miniaudio. */
14497  if (pSinkInfo->description != NULL) {
14498  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
14499  }
14500 
14501  pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
14502 
14503  (void)pPulseContext; /* Unused. */
14504 }
14505 
14506 void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSinkInfo, int endOfList, void* pUserData)
14507 {
14508  ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
14509  ma_device_info deviceInfo;
14510 
14511  ma_assert(pData != NULL);
14512 
14513  if (endOfList || pData->isTerminated) {
14514  return;
14515  }
14516 
14517  ma_zero_object(&deviceInfo);
14518 
14519  /* The name from PulseAudio is the ID for miniaudio. */
14520  if (pSinkInfo->name != NULL) {
14521  ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
14522  }
14523 
14524  /* The description from PulseAudio is the name for miniaudio. */
14525  if (pSinkInfo->description != NULL) {
14526  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
14527  }
14528 
14529  pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
14530 
14531  (void)pPulseContext; /* Unused. */
14532 }
14533 
14534 ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
14535 {
14536  ma_result result = MA_SUCCESS;
14537  ma_context_enumerate_devices_callback_data__pulse callbackData;
14538  ma_pa_operation* pOP = NULL;
14539  ma_pa_mainloop* pMainLoop;
14540  ma_pa_mainloop_api* pAPI;
14541  ma_pa_context* pPulseContext;
14542  int error;
14543 
14544  ma_assert(pContext != NULL);
14545  ma_assert(callback != NULL);
14546 
14547  callbackData.pContext = pContext;
14548  callbackData.callback = callback;
14549  callbackData.pUserData = pUserData;
14550  callbackData.isTerminated = MA_FALSE;
14551 
14552  pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
14553  if (pMainLoop == NULL) {
14555  }
14556 
14557  pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
14558  if (pAPI == NULL) {
14559  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
14561  }
14562 
14563  pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
14564  if (pPulseContext == NULL) {
14565  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
14567  }
14568 
14569  error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, (pContext->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL);
14570  if (error != MA_PA_OK) {
14571  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
14572  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
14573  return ma_result_from_pulse(error);
14574  }
14575 
14576  for (;;) {
14577  ma_pa_context_state_t state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
14578  if (state == MA_PA_CONTEXT_READY) {
14579  break; /* Success. */
14580  }
14581  if (state == MA_PA_CONTEXT_CONNECTING || state == MA_PA_CONTEXT_AUTHORIZING || state == MA_PA_CONTEXT_SETTING_NAME) {
14582  error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
14583  if (error < 0) {
14584  result = ma_result_from_pulse(error);
14585  goto done;
14586  }
14587 
14588 #ifdef MA_DEBUG_OUTPUT
14589  printf("[PulseAudio] pa_context_get_state() returned %d. Waiting.\n", state);
14590 #endif
14591  continue; /* Keep trying. */
14592  }
14593  if (state == MA_PA_CONTEXT_UNCONNECTED || state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
14594 #ifdef MA_DEBUG_OUTPUT
14595  printf("[PulseAudio] pa_context_get_state() returned %d. Failed.\n", state);
14596 #endif
14597  goto done; /* Failed. */
14598  }
14599  }
14600 
14601 
14602  /* Playback. */
14603  if (!callbackData.isTerminated) {
14604  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);
14605  if (pOP == NULL) {
14606  result = MA_ERROR;
14607  goto done;
14608  }
14609 
14610  result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
14611  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
14612  if (result != MA_SUCCESS) {
14613  goto done;
14614  }
14615  }
14616 
14617 
14618  /* Capture. */
14619  if (!callbackData.isTerminated) {
14620  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);
14621  if (pOP == NULL) {
14622  result = MA_ERROR;
14623  goto done;
14624  }
14625 
14626  result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
14627  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
14628  if (result != MA_SUCCESS) {
14629  goto done;
14630  }
14631  }
14632 
14633 done:
14634  ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
14635  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
14636  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
14637  return result;
14638 }
14639 
14640 
14641 typedef struct
14642 {
14643  ma_device_info* pDeviceInfo;
14644  ma_bool32 foundDevice;
14645 } ma_context_get_device_info_callback_data__pulse;
14646 
14647 void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
14648 {
14649  ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
14650 
14651  if (endOfList > 0) {
14652  return;
14653  }
14654 
14655  ma_assert(pData != NULL);
14656  pData->foundDevice = MA_TRUE;
14657 
14658  if (pInfo->name != NULL) {
14659  ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
14660  }
14661 
14662  if (pInfo->description != NULL) {
14663  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
14664  }
14665 
14666  pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels;
14667  pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels;
14668  pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate;
14669  pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate;
14670  pData->pDeviceInfo->formatCount = 1;
14671  pData->pDeviceInfo->formats[0] = ma_format_from_pulse(pInfo->sample_spec.format);
14672 
14673  (void)pPulseContext; /* Unused. */
14674 }
14675 
14676 void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
14677 {
14678  ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
14679 
14680  if (endOfList > 0) {
14681  return;
14682  }
14683 
14684  ma_assert(pData != NULL);
14685  pData->foundDevice = MA_TRUE;
14686 
14687  if (pInfo->name != NULL) {
14688  ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
14689  }
14690 
14691  if (pInfo->description != NULL) {
14692  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
14693  }
14694 
14695  pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels;
14696  pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels;
14697  pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate;
14698  pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate;
14699  pData->pDeviceInfo->formatCount = 1;
14700  pData->pDeviceInfo->formats[0] = ma_format_from_pulse(pInfo->sample_spec.format);
14701 
14702  (void)pPulseContext; /* Unused. */
14703 }
14704 
14705 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)
14706 {
14707  ma_result result = MA_SUCCESS;
14708  ma_context_get_device_info_callback_data__pulse callbackData;
14709  ma_pa_operation* pOP = NULL;
14710  ma_pa_mainloop* pMainLoop;
14711  ma_pa_mainloop_api* pAPI;
14712  ma_pa_context* pPulseContext;
14713  int error;
14714 
14715  ma_assert(pContext != NULL);
14716 
14717  /* No exclusive mode with the PulseAudio backend. */
14718  if (shareMode == ma_share_mode_exclusive) {
14720  }
14721 
14722  callbackData.pDeviceInfo = pDeviceInfo;
14723  callbackData.foundDevice = MA_FALSE;
14724 
14725  pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
14726  if (pMainLoop == NULL) {
14728  }
14729 
14730  pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
14731  if (pAPI == NULL) {
14732  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
14734  }
14735 
14736  pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
14737  if (pPulseContext == NULL) {
14738  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
14740  }
14741 
14742  error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, 0, NULL);
14743  if (error != MA_PA_OK) {
14744  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
14745  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
14746  return ma_result_from_pulse(error);
14747  }
14748 
14749  for (;;) {
14750  ma_pa_context_state_t state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
14751  if (state == MA_PA_CONTEXT_READY) {
14752  break; /* Success. */
14753  }
14754  if (state == MA_PA_CONTEXT_CONNECTING || state == MA_PA_CONTEXT_AUTHORIZING || state == MA_PA_CONTEXT_SETTING_NAME) {
14755  error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
14756  if (error < 0) {
14757  result = ma_result_from_pulse(error);
14758  goto done;
14759  }
14760 
14761 #ifdef MA_DEBUG_OUTPUT
14762  printf("[PulseAudio] pa_context_get_state() returned %d. Waiting.\n", state);
14763 #endif
14764  continue; /* Keep trying. */
14765  }
14766  if (state == MA_PA_CONTEXT_UNCONNECTED || state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
14767 #ifdef MA_DEBUG_OUTPUT
14768  printf("[PulseAudio] pa_context_get_state() returned %d. Failed.\n", state);
14769 #endif
14770  goto done; /* Failed. */
14771  }
14772  }
14773 
14774  if (deviceType == ma_device_type_playback) {
14775  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);
14776  } else {
14777  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);
14778  }
14779 
14780  if (pOP != NULL) {
14781  ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
14782  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
14783  } else {
14784  result = MA_ERROR;
14785  goto done;
14786  }
14787 
14788  if (!callbackData.foundDevice) {
14789  result = MA_NO_DEVICE;
14790  goto done;
14791  }
14792 
14793 
14794 done:
14795  ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
14796  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
14797  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
14798  return result;
14799 }
14800 
14801 
14802 void ma_pulse_device_state_callback(ma_pa_context* pPulseContext, void* pUserData)
14803 {
14804  ma_device* pDevice;
14805  ma_context* pContext;
14806 
14807  pDevice = (ma_device*)pUserData;
14808  ma_assert(pDevice != NULL);
14809 
14810  pContext = pDevice->pContext;
14811  ma_assert(pContext != NULL);
14812 
14813  pDevice->pulse.pulseContextState = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
14814 }
14815 
14816 void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
14817 {
14818  ma_pa_sink_info* pInfoOut;
14819 
14820  if (endOfList > 0) {
14821  return;
14822  }
14823 
14824  pInfoOut = (ma_pa_sink_info*)pUserData;
14825  ma_assert(pInfoOut != NULL);
14826 
14827  *pInfoOut = *pInfo;
14828 
14829  (void)pPulseContext; /* Unused. */
14830 }
14831 
14832 void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
14833 {
14834  ma_pa_source_info* pInfoOut;
14835 
14836  if (endOfList > 0) {
14837  return;
14838  }
14839 
14840  pInfoOut = (ma_pa_source_info*)pUserData;
14841  ma_assert(pInfoOut != NULL);
14842 
14843  *pInfoOut = *pInfo;
14844 
14845  (void)pPulseContext; /* Unused. */
14846 }
14847 
14848 void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
14849 {
14850  ma_device* pDevice;
14851 
14852  if (endOfList > 0) {
14853  return;
14854  }
14855 
14856  pDevice = (ma_device*)pUserData;
14857  ma_assert(pDevice != NULL);
14858 
14859  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
14860 
14861  (void)pPulseContext; /* Unused. */
14862 }
14863 
14864 void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
14865 {
14866  ma_device* pDevice;
14867 
14868  if (endOfList > 0) {
14869  return;
14870  }
14871 
14872  pDevice = (ma_device*)pUserData;
14873  ma_assert(pDevice != NULL);
14874 
14875  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
14876 
14877  (void)pPulseContext; /* Unused. */
14878 }
14879 
14880 void ma_device_uninit__pulse(ma_device* pDevice)
14881 {
14882  ma_context* pContext;
14883 
14884  ma_assert(pDevice != NULL);
14885 
14886  pContext = pDevice->pContext;
14887  ma_assert(pContext != NULL);
14888 
14889  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
14890  ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
14891  ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
14892  }
14893  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
14894  ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
14895  ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
14896  }
14897 
14898  ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
14899  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
14900  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
14901 }
14902 
14903 ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 bufferSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
14904 {
14905  ma_pa_buffer_attr attr;
14906  attr.maxlength = bufferSizeInFrames * ma_get_bytes_per_sample(ma_format_from_pulse(ss->format)) * ss->channels;
14907  attr.tlength = attr.maxlength / periods;
14908  attr.prebuf = (ma_uint32)-1;
14909  attr.minreq = attr.maxlength / periods;
14910  attr.fragsize = attr.maxlength / periods;
14911 
14912  return attr;
14913 }
14914 
14915 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)
14916 {
14917  static int g_StreamCounter = 0;
14918  char actualStreamName[256];
14919 
14920  if (pStreamName != NULL) {
14921  ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
14922  } else {
14923  ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
14924  ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
14925  }
14926  g_StreamCounter += 1;
14927 
14928  return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
14929 }
14930 
14931 ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
14932 {
14933  ma_result result = MA_SUCCESS;
14934  int error = 0;
14935  const char* devPlayback = NULL;
14936  const char* devCapture = NULL;
14937  ma_uint32 bufferSizeInMilliseconds;
14938  ma_pa_sink_info sinkInfo;
14939  ma_pa_source_info sourceInfo;
14940  ma_pa_operation* pOP = NULL;
14941  ma_pa_sample_spec ss;
14942  ma_pa_channel_map cmap;
14943  ma_pa_buffer_attr attr;
14944  const ma_pa_sample_spec* pActualSS = NULL;
14945  const ma_pa_channel_map* pActualCMap = NULL;
14946  const ma_pa_buffer_attr* pActualAttr = NULL;
14947  ma_uint32 iChannel;
14948  ma_pa_stream_flags_t streamFlags;
14949 
14950  ma_assert(pDevice != NULL);
14951  ma_zero_object(&pDevice->pulse);
14952 
14953  /* No exclusive mode with the PulseAudio backend. */
14957  }
14958 
14959  if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID != NULL) {
14960  devPlayback = pConfig->playback.pDeviceID->pulse;
14961  }
14962  if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID != NULL) {
14963  devCapture = pConfig->capture.pDeviceID->pulse;
14964  }
14965 
14966  bufferSizeInMilliseconds = pConfig->bufferSizeInMilliseconds;
14967  if (bufferSizeInMilliseconds == 0) {
14968  bufferSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->bufferSizeInFrames, pConfig->sampleRate);
14969  }
14970 
14971  pDevice->pulse.pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
14972  if (pDevice->pulse.pMainLoop == NULL) {
14973  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create main loop for device.", MA_FAILED_TO_INIT_BACKEND);
14974  goto on_error0;
14975  }
14976 
14977  pDevice->pulse.pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
14978  if (pDevice->pulse.pAPI == NULL) {
14979  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve PulseAudio main loop.", MA_FAILED_TO_INIT_BACKEND);
14980  goto on_error1;
14981  }
14982 
14983  pDevice->pulse.pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)((ma_pa_mainloop_api*)pDevice->pulse.pAPI, pContext->pulse.pApplicationName);
14984  if (pDevice->pulse.pPulseContext == NULL) {
14985  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context for device.", MA_FAILED_TO_INIT_BACKEND);
14986  goto on_error1;
14987  }
14988 
14989  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);
14990  if (error != MA_PA_OK) {
14991  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", ma_result_from_pulse(error));
14992  goto on_error2;
14993  }
14994 
14995 
14996  pDevice->pulse.pulseContextState = MA_PA_CONTEXT_UNCONNECTED;
14997  ((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);
14998 
14999  /* Wait for PulseAudio to get itself ready before returning. */
15000  for (;;) {
15001  if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_READY) {
15002  break;
15003  }
15004 
15005  /* An error may have occurred. */
15006  if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_FAILED || pDevice->pulse.pulseContextState == MA_PA_CONTEXT_TERMINATED) {
15007  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.", MA_ERROR);
15008  goto on_error3;
15009  }
15010 
15011  error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
15012  if (error < 0) {
15013  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));
15014  goto on_error3;
15015  }
15016  }
15017 
15018  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
15019  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);
15020  if (pOP != NULL) {
15021  ma_device__wait_for_operation__pulse(pDevice, pOP);
15022  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
15023  } else {
15024  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.", ma_result_from_pulse(error));
15025  goto on_error3;
15026  }
15027 
15028  ss = sourceInfo.sample_spec;
15029  cmap = sourceInfo.channel_map;
15030 
15031  pDevice->capture.internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, ss.rate);
15032  pDevice->capture.internalPeriods = pConfig->periods;
15033 
15034  attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalBufferSizeInFrames, pConfig->periods, &ss);
15035  #ifdef MA_DEBUG_OUTPUT
15036  printf("[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalBufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->capture.internalBufferSizeInFrames);
15037  #endif
15038 
15039  pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
15040  if (pDevice->pulse.pStreamCapture == NULL) {
15041  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
15042  goto on_error3;
15043  }
15044 
15045  streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
15046  if (devCapture != NULL) {
15047  streamFlags |= MA_PA_STREAM_DONT_MOVE;
15048  }
15049 
15050  error = ((ma_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
15051  if (error != MA_PA_OK) {
15052  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.", ma_result_from_pulse(error));
15053  goto on_error4;
15054  }
15055 
15056  while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamCapture) != MA_PA_STREAM_READY) {
15057  error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
15058  if (error < 0) {
15059  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));
15060  goto on_error5;
15061  }
15062  }
15063 
15064  /* Internal format. */
15065  pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
15066  if (pActualSS != NULL) {
15067  /* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
15068  if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
15069  attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalBufferSizeInFrames, pConfig->periods, pActualSS);
15070 
15071  pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &attr, NULL, NULL);
15072  if (pOP != NULL) {
15073  ma_device__wait_for_operation__pulse(pDevice, pOP);
15074  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
15075  }
15076  }
15077 
15078  ss = *pActualSS;
15079  }
15080 
15081  pDevice->capture.internalFormat = ma_format_from_pulse(ss.format);
15082  pDevice->capture.internalChannels = ss.channels;
15083  pDevice->capture.internalSampleRate = ss.rate;
15084 
15085  /* Internal channel map. */
15086  pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
15087  if (pActualCMap != NULL) {
15088  cmap = *pActualCMap;
15089  }
15090  for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
15091  pDevice->capture.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
15092  }
15093 
15094  /* Buffer. */
15095  pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
15096  if (pActualAttr != NULL) {
15097  attr = *pActualAttr;
15098  }
15099  pDevice->capture.internalBufferSizeInFrames = attr.maxlength / (ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pDevice->capture.internalChannels);
15100  pDevice->capture.internalPeriods = attr.maxlength / attr.fragsize;
15101  #ifdef MA_DEBUG_OUTPUT
15102  printf("[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalBufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->capture.internalBufferSizeInFrames);
15103  #endif
15104 
15105  /* Name. */
15106  devCapture = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
15107  if (devCapture != NULL) {
15108  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);
15109  if (pOP != NULL) {
15110  ma_device__wait_for_operation__pulse(pDevice, pOP);
15111  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
15112  }
15113  }
15114  }
15115 
15116  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
15117  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);
15118  if (pOP != NULL) {
15119  ma_device__wait_for_operation__pulse(pDevice, pOP);
15120  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
15121  } else {
15122  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.", ma_result_from_pulse(error));
15123  goto on_error3;
15124  }
15125 
15126  ss = sinkInfo.sample_spec;
15127  cmap = sinkInfo.channel_map;
15128 
15129  pDevice->playback.internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, ss.rate);
15130  pDevice->playback.internalPeriods = pConfig->periods;
15131 
15132  attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalBufferSizeInFrames, pConfig->periods, &ss);
15133  #ifdef MA_DEBUG_OUTPUT
15134  printf("[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalBufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->playback.internalBufferSizeInFrames);
15135  #endif
15136 
15137  pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
15138  if (pDevice->pulse.pStreamPlayback == NULL) {
15139  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
15140  goto on_error3;
15141  }
15142 
15143  streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
15144  if (devPlayback != NULL) {
15145  streamFlags |= MA_PA_STREAM_DONT_MOVE;
15146  }
15147 
15148  error = ((ma_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL);
15149  if (error != MA_PA_OK) {
15150  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.", ma_result_from_pulse(error));
15151  goto on_error6;
15152  }
15153 
15154  while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamPlayback) != MA_PA_STREAM_READY) {
15155  error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
15156  if (error < 0) {
15157  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));
15158  goto on_error7;
15159  }
15160  }
15161 
15162  /* Internal format. */
15163  pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
15164  if (pActualSS != NULL) {
15165  /* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
15166  if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
15167  attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalBufferSizeInFrames, pConfig->periods, pActualSS);
15168 
15169  pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, &attr, NULL, NULL);
15170  if (pOP != NULL) {
15171  ma_device__wait_for_operation__pulse(pDevice, pOP);
15172  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
15173  }
15174  }
15175 
15176  ss = *pActualSS;
15177  }
15178 
15179  pDevice->playback.internalFormat = ma_format_from_pulse(ss.format);
15180  pDevice->playback.internalChannels = ss.channels;
15181  pDevice->playback.internalSampleRate = ss.rate;
15182 
15183  /* Internal channel map. */
15184  pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
15185  if (pActualCMap != NULL) {
15186  cmap = *pActualCMap;
15187  }
15188  for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
15189  pDevice->playback.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
15190  }
15191 
15192  /* Buffer. */
15193  pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
15194  if (pActualAttr != NULL) {
15195  attr = *pActualAttr;
15196  }
15197  pDevice->playback.internalBufferSizeInFrames = attr.maxlength / (ma_get_bytes_per_sample(pDevice->playback.internalFormat) * pDevice->playback.internalChannels);
15198  pDevice->playback.internalPeriods = /*pConfig->periods;*/attr.maxlength / attr.tlength;
15199  #ifdef MA_DEBUG_OUTPUT
15200  printf("[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalBufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->playback.internalBufferSizeInFrames);
15201  #endif
15202 
15203  /* Name. */
15204  devPlayback = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
15205  if (devPlayback != NULL) {
15206  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);
15207  if (pOP != NULL) {
15208  ma_device__wait_for_operation__pulse(pDevice, pOP);
15209  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
15210  }
15211  }
15212  }
15213 
15214  return MA_SUCCESS;
15215 
15216 
15217 on_error7:
15218  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
15219  ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
15220  }
15221 on_error6:
15222  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
15223  ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
15224  }
15225 on_error5:
15226  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
15227  ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
15228  }
15229 on_error4:
15230  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
15231  ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
15232  }
15233 on_error3: ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
15234 on_error2: ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
15235 on_error1: ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
15236 on_error0:
15237  return result;
15238 }
15239 
15240 
15241 void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
15242 {
15243  ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
15244  ma_assert(pIsSuccessful != NULL);
15245 
15246  *pIsSuccessful = (ma_bool32)success;
15247 
15248  (void)pStream; /* Unused. */
15249 }
15250 
15251 ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
15252 {
15253  ma_context* pContext = pDevice->pContext;
15254  ma_bool32 wasSuccessful;
15255  ma_pa_stream* pStream;
15256  ma_pa_operation* pOP;
15257  ma_result result;
15258 
15259  /* This should not be called with a duplex device type. */
15260  if (deviceType == ma_device_type_duplex) {
15261  return MA_INVALID_ARGS;
15262  }
15263 
15264  wasSuccessful = MA_FALSE;
15265 
15266  pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
15267  ma_assert(pStream != NULL);
15268 
15269  pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
15270  if (pOP == NULL) {
15271  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);
15272  }
15273 
15274  result = ma_device__wait_for_operation__pulse(pDevice, pOP);
15275  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
15276 
15277  if (result != MA_SUCCESS) {
15278  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.", result);
15279  }
15280 
15281  if (!wasSuccessful) {
15282  if (cork) {
15283  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to stop PulseAudio stream.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
15284  } else {
15285  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to start PulseAudio stream.", MA_FAILED_TO_START_BACKEND_DEVICE);
15286  }
15287  }
15288 
15289  return MA_SUCCESS;
15290 }
15291 
15292 ma_result ma_device_stop__pulse(ma_device* pDevice)
15293 {
15294  ma_result result;
15295  ma_bool32 wasSuccessful;
15296  ma_pa_operation* pOP;
15297 
15298  ma_assert(pDevice != NULL);
15299 
15300  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
15301  result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
15302  if (result != MA_SUCCESS) {
15303  return result;
15304  }
15305  }
15306 
15307  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
15308  /* The stream needs to be drained if it's a playback device. */
15309  pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful);
15310  if (pOP != NULL) {
15311  ma_device__wait_for_operation__pulse(pDevice, pOP);
15312  ((ma_pa_operation_unref_proc)pDevice->pContext->pulse.pa_operation_unref)(pOP);
15313  }
15314 
15315  result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
15316  if (result != MA_SUCCESS) {
15317  return result;
15318  }
15319  }
15320 
15321  return MA_SUCCESS;
15322 }
15323 
15324 ma_result ma_device_write__pulse(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount)
15325 {
15326  ma_uint32 totalFramesWritten;
15327 
15328  ma_assert(pDevice != NULL);
15329  ma_assert(pPCMFrames != NULL);
15330  ma_assert(frameCount > 0);
15331 
15332  /* The stream needs to be uncorked first. */
15333  if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamPlayback)) {
15334  ma_result result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
15335  if (result != MA_SUCCESS) {
15336  return result;
15337  }
15338  }
15339 
15340  totalFramesWritten = 0;
15341  while (totalFramesWritten < frameCount) {
15342  /* Place the data into the mapped buffer if we have one. */
15343  if (pDevice->pulse.pMappedBufferPlayback != NULL && pDevice->pulse.mappedBufferFramesRemainingPlayback > 0) {
15344  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
15345  ma_uint32 mappedBufferFramesConsumed = pDevice->pulse.mappedBufferFramesCapacityPlayback - pDevice->pulse.mappedBufferFramesRemainingPlayback;
15346 
15347  void* pDst = (ma_uint8*)pDevice->pulse.pMappedBufferPlayback + (mappedBufferFramesConsumed * bpf);
15348  const void* pSrc = (const ma_uint8*)pPCMFrames + (totalFramesWritten * bpf);
15349  ma_uint32 framesToCopy = ma_min(pDevice->pulse.mappedBufferFramesRemainingPlayback, (frameCount - totalFramesWritten));
15350  ma_copy_memory(pDst, pSrc, framesToCopy * bpf);
15351 
15352  pDevice->pulse.mappedBufferFramesRemainingPlayback -= framesToCopy;
15353  totalFramesWritten += framesToCopy;
15354  }
15355 
15356  /*
15357  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
15358  mapping another chunk. If this fails we need to wait for space to become available.
15359  */
15360  if (pDevice->pulse.mappedBufferFramesCapacityPlayback > 0 && pDevice->pulse.mappedBufferFramesRemainingPlayback == 0) {
15361  size_t nbytes = pDevice->pulse.mappedBufferFramesCapacityPlayback * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
15362 
15363  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);
15364  if (error < 0) {
15365  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to write data to the PulseAudio stream.", ma_result_from_pulse(error));
15366  }
15367 
15368  pDevice->pulse.pMappedBufferPlayback = NULL;
15369  pDevice->pulse.mappedBufferFramesRemainingPlayback = 0;
15370  pDevice->pulse.mappedBufferFramesCapacityPlayback = 0;
15371  }
15372 
15373  ma_assert(totalFramesWritten <= frameCount);
15374  if (totalFramesWritten == frameCount) {
15375  break;
15376  }
15377 
15378  /* Getting here means we need to map a new buffer. If we don't have enough space we need to wait for more. */
15379  for (;;) {
15380  size_t writableSizeInBytes;
15381 
15382  /* If the device has been corked, don't try to continue. */
15383  if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamPlayback)) {
15384  break;
15385  }
15386 
15387  writableSizeInBytes = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
15388  if (writableSizeInBytes != (size_t)-1) {
15389  /*size_t periodSizeInBytes = (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods) * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);*/
15390  if (writableSizeInBytes > 0) {
15391  /* Data is avaialable. */
15392  size_t bytesToMap = writableSizeInBytes;
15393  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);
15394  if (error < 0) {
15395  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to map write buffer.", ma_result_from_pulse(error));
15396  }
15397 
15398  pDevice->pulse.mappedBufferFramesCapacityPlayback = bytesToMap / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
15399  pDevice->pulse.mappedBufferFramesRemainingPlayback = pDevice->pulse.mappedBufferFramesCapacityPlayback;
15400 
15401  break;
15402  } else {
15403  /* No data available. Need to wait for more. */
15404  int error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
15405  if (error < 0) {
15406  return ma_result_from_pulse(error);
15407  }
15408 
15409  continue;
15410  }
15411  } else {
15412  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to query the stream's writable size.", MA_ERROR);
15413  }
15414  }
15415  }
15416 
15417  return MA_SUCCESS;
15418 }
15419 
15420 ma_result ma_device_read__pulse(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount)
15421 {
15422  ma_uint32 totalFramesRead;
15423 
15424  ma_assert(pDevice != NULL);
15425  ma_assert(pPCMFrames != NULL);
15426  ma_assert(frameCount > 0);
15427 
15428  /* The stream needs to be uncorked first. */
15429  if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamCapture)) {
15430  ma_result result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
15431  if (result != MA_SUCCESS) {
15432  return result;
15433  }
15434  }
15435 
15436  totalFramesRead = 0;
15437  while (totalFramesRead < frameCount) {
15438  if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
15439  break;
15440  }
15441 
15442  /* If a buffer is mapped we need to write to that first. Once it's consumed we reset the event and unmap it. */
15443  if (pDevice->pulse.pMappedBufferCapture != NULL && pDevice->pulse.mappedBufferFramesRemainingCapture > 0) {
15444  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
15445  ma_uint32 mappedBufferFramesConsumed = pDevice->pulse.mappedBufferFramesCapacityCapture - pDevice->pulse.mappedBufferFramesRemainingCapture;
15446 
15447  ma_uint32 framesToCopy = ma_min(pDevice->pulse.mappedBufferFramesRemainingCapture, (frameCount - totalFramesRead));
15448  void* pDst = (ma_uint8*)pPCMFrames + (totalFramesRead * bpf);
15449 
15450  /*
15451  This little bit of logic here is specifically for PulseAudio and it's hole management. The buffer pointer will be set to NULL
15452  when the current fragment is a hole. For a hole we just output silence.
15453  */
15454  if (pDevice->pulse.pMappedBufferCapture != NULL) {
15455  const void* pSrc = (const ma_uint8*)pDevice->pulse.pMappedBufferCapture + (mappedBufferFramesConsumed * bpf);
15456  ma_copy_memory(pDst, pSrc, framesToCopy * bpf);
15457  } else {
15458  ma_zero_memory(pDst, framesToCopy * bpf);
15459  }
15460 
15461  pDevice->pulse.mappedBufferFramesRemainingCapture -= framesToCopy;
15462  totalFramesRead += framesToCopy;
15463  }
15464 
15465  /*
15466  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
15467  mapping another chunk. If this fails we need to wait for data to become available.
15468  */
15469  if (pDevice->pulse.mappedBufferFramesCapacityCapture > 0 && pDevice->pulse.mappedBufferFramesRemainingCapture == 0) {
15470  int error = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
15471  if (error != 0) {
15472  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to drop fragment.", ma_result_from_pulse(error));
15473  }
15474 
15475  pDevice->pulse.pMappedBufferCapture = NULL;
15476  pDevice->pulse.mappedBufferFramesRemainingCapture = 0;
15477  pDevice->pulse.mappedBufferFramesCapacityCapture = 0;
15478  }
15479 
15480  ma_assert(totalFramesRead <= frameCount);
15481  if (totalFramesRead == frameCount) {
15482  break;
15483  }
15484 
15485  /* Getting here means we need to map a new buffer. If we don't have enough data we wait for more. */
15486  for (;;) {
15487  size_t readableSizeInBytes;
15488 
15489  if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
15490  break;
15491  }
15492 
15493  /* If the device has been corked, don't try to continue. */
15494  if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamCapture)) {
15495  break;
15496  }
15497 
15498  readableSizeInBytes = ((ma_pa_stream_readable_size_proc)pDevice->pContext->pulse.pa_stream_readable_size)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
15499  if (readableSizeInBytes != (size_t)-1) {
15500  /*size_t periodSizeInBytes = (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods) * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);*/
15501  if (readableSizeInBytes > 0) {
15502  /* Data is avaialable. */
15503  size_t bytesMapped = (size_t)-1;
15504  int error = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &pDevice->pulse.pMappedBufferCapture, &bytesMapped);
15505  if (error < 0) {
15506  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to peek capture buffer.", ma_result_from_pulse(error));
15507  }
15508 
15509  if (pDevice->pulse.pMappedBufferCapture == NULL && bytesMapped == 0) {
15510  /* Nothing available. This shouldn't happen because we checked earlier with pa_stream_readable_size(). I'm going to throw an error in this case. */
15511  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Nothing available after peeking capture buffer.", MA_ERROR);
15512  }
15513 
15514  pDevice->pulse.mappedBufferFramesCapacityCapture = bytesMapped / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
15515  pDevice->pulse.mappedBufferFramesRemainingCapture = pDevice->pulse.mappedBufferFramesCapacityCapture;
15516 
15517  break;
15518  } else {
15519  /* No data available. Need to wait for more. */
15520 
15521  /*
15522  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
15523  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
15524  dispatches.
15525  */
15526  int error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 0, NULL);
15527  if (error < 0) {
15528  return ma_result_from_pulse(error);
15529  }
15530 
15531  /* Sleep for a bit if nothing was dispatched. */
15532  if (error == 0) {
15533  ma_sleep(1);
15534  }
15535 
15536  continue;
15537  }
15538  } else {
15539  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to query the stream's readable size.", MA_ERROR);
15540  }
15541  }
15542  }
15543 
15544  return MA_SUCCESS;
15545 }
15546 
15547 
15548 ma_result ma_context_uninit__pulse(ma_context* pContext)
15549 {
15550  ma_assert(pContext != NULL);
15551  ma_assert(pContext->backend == ma_backend_pulseaudio);
15552 
15553  ma_free(pContext->pulse.pServerName);
15554  pContext->pulse.pServerName = NULL;
15555 
15556  ma_free(pContext->pulse.pApplicationName);
15557  pContext->pulse.pApplicationName = NULL;
15558 
15559 #ifndef MA_NO_RUNTIME_LINKING
15560  ma_dlclose(pContext, pContext->pulse.pulseSO);
15561 #endif
15562 
15563  return MA_SUCCESS;
15564 }
15565 
15566 ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_context* pContext)
15567 {
15568 #ifndef MA_NO_RUNTIME_LINKING
15569  const char* libpulseNames[] = {
15570  "libpulse.so",
15571  "libpulse.so.0"
15572  };
15573  size_t i;
15574 
15575  for (i = 0; i < ma_countof(libpulseNames); ++i) {
15576  pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]);
15577  if (pContext->pulse.pulseSO != NULL) {
15578  break;
15579  }
15580  }
15581 
15582  if (pContext->pulse.pulseSO == NULL) {
15583  return MA_NO_BACKEND;
15584  }
15585 
15586  pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new");
15587  pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free");
15588  pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api");
15589  pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate");
15590  pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup");
15591  pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new");
15592  pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref");
15593  pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect");
15594  pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect");
15595  pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback");
15596  pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state");
15597  pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
15598  pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list");
15599  pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
15600  pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
15601  pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref");
15602  pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state");
15603  pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend");
15604  pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid");
15605  pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible");
15606  pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new");
15607  pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref");
15608  pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback");
15609  pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record");
15610  pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect");
15611  pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state");
15612  pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
15613  pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map");
15614  pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
15615  pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
15616  pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name");
15617  pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback");
15618  pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback");
15619  pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush");
15620  pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain");
15621  pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked");
15622  pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork");
15623  pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger");
15624  pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write");
15625  pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write");
15626  pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek");
15627  pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop");
15628  pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size");
15629  pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size");
15630 #else
15631  /* This strange assignment system is just for type safety. */
15632  ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
15633  ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
15634  ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
15635  ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
15636  ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
15637  ma_pa_context_new_proc _pa_context_new = pa_context_new;
15638  ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
15639  ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
15640  ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
15641  ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
15642  ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
15643  ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
15644  ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
15645  ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
15646  ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
15647  ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
15648  ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
15649  ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
15650  ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
15651  ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
15652  ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
15653  ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
15654  ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
15655  ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
15656  ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
15657  ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
15658  ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
15659  ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
15660  ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
15661  ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
15662  ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
15663  ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
15664  ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
15665  ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
15666  ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
15667  ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
15668  ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
15669  ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
15670  ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
15671  ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
15672  ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
15673  ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
15674  ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
15675  ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
15676 
15677  pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
15678  pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
15679  pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
15680  pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
15681  pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
15682  pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
15683  pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
15684  pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
15685  pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
15686  pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
15687  pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
15688  pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
15689  pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
15690  pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
15691  pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
15692  pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
15693  pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
15694  pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
15695  pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
15696  pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
15697  pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
15698  pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
15699  pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
15700  pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
15701  pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
15702  pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
15703  pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
15704  pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
15705  pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
15706  pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
15707  pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
15708  pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
15709  pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
15710  pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
15711  pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
15712  pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
15713  pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
15714  pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
15715  pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
15716  pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
15717  pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
15718  pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
15719  pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
15720  pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
15721 #endif
15722 
15723  pContext->onUninit = ma_context_uninit__pulse;
15724  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__pulse;
15725  pContext->onEnumDevices = ma_context_enumerate_devices__pulse;
15726  pContext->onGetDeviceInfo = ma_context_get_device_info__pulse;
15727  pContext->onDeviceInit = ma_device_init__pulse;
15728  pContext->onDeviceUninit = ma_device_uninit__pulse;
15729  pContext->onDeviceStart = NULL;
15730  pContext->onDeviceStop = ma_device_stop__pulse;
15731  pContext->onDeviceWrite = ma_device_write__pulse;
15732  pContext->onDeviceRead = ma_device_read__pulse;
15733 
15734  if (pConfig->pulse.pApplicationName) {
15735  pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName);
15736  }
15737  if (pConfig->pulse.pServerName) {
15738  pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName);
15739  }
15740  pContext->pulse.tryAutoSpawn = pConfig->pulse.tryAutoSpawn;
15741 
15742  /*
15743  Although we have found the libpulse library, it doesn't necessarily mean PulseAudio is useable. We need to initialize
15744  and connect a dummy PulseAudio context to test PulseAudio's usability.
15745  */
15746  {
15747  ma_pa_mainloop* pMainLoop;
15748  ma_pa_mainloop_api* pAPI;
15749  ma_pa_context* pPulseContext;
15750  int error;
15751 
15752  pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
15753  if (pMainLoop == NULL) {
15754  ma_free(pContext->pulse.pServerName);
15755  ma_free(pContext->pulse.pApplicationName);
15756  #ifndef MA_NO_RUNTIME_LINKING
15757  ma_dlclose(pContext, pContext->pulse.pulseSO);
15758  #endif
15759  return MA_NO_BACKEND;
15760  }
15761 
15762  pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
15763  if (pAPI == NULL) {
15764  ma_free(pContext->pulse.pServerName);
15765  ma_free(pContext->pulse.pApplicationName);
15766  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15767  #ifndef MA_NO_RUNTIME_LINKING
15768  ma_dlclose(pContext, pContext->pulse.pulseSO);
15769  #endif
15770  return MA_NO_BACKEND;
15771  }
15772 
15773  pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
15774  if (pPulseContext == NULL) {
15775  ma_free(pContext->pulse.pServerName);
15776  ma_free(pContext->pulse.pApplicationName);
15777  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15778  #ifndef MA_NO_RUNTIME_LINKING
15779  ma_dlclose(pContext, pContext->pulse.pulseSO);
15780  #endif
15781  return MA_NO_BACKEND;
15782  }
15783 
15784  error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, 0, NULL);
15785  if (error != MA_PA_OK) {
15786  ma_free(pContext->pulse.pServerName);
15787  ma_free(pContext->pulse.pApplicationName);
15788  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
15789  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15790  #ifndef MA_NO_RUNTIME_LINKING
15791  ma_dlclose(pContext, pContext->pulse.pulseSO);
15792  #endif
15793  return MA_NO_BACKEND;
15794  }
15795 
15796  ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
15797  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
15798  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15799  }
15800 
15801  return MA_SUCCESS;
15802 }
15803 #endif
15804 
15805 
15806 /******************************************************************************
15807 
15808 JACK Backend
15809 
15810 ******************************************************************************/
15811 #ifdef MA_HAS_JACK
15812 
15813 /* It is assumed jack.h is available when compile-time linking is being used. */
15814 #ifdef MA_NO_RUNTIME_LINKING
15815 #include <jack/jack.h>
15816 
15817 typedef jack_nframes_t ma_jack_nframes_t;
15818 typedef jack_options_t ma_jack_options_t;
15819 typedef jack_status_t ma_jack_status_t;
15820 typedef jack_client_t ma_jack_client_t;
15821 typedef jack_port_t ma_jack_port_t;
15822 typedef JackProcessCallback ma_JackProcessCallback;
15823 typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
15824 typedef JackShutdownCallback ma_JackShutdownCallback;
15825 #define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
15826 #define ma_JackNoStartServer JackNoStartServer
15827 #define ma_JackPortIsInput JackPortIsInput
15828 #define ma_JackPortIsOutput JackPortIsOutput
15829 #define ma_JackPortIsPhysical JackPortIsPhysical
15830 #else
15831 typedef ma_uint32 ma_jack_nframes_t;
15832 typedef int ma_jack_options_t;
15833 typedef int ma_jack_status_t;
15834 typedef struct ma_jack_client_t ma_jack_client_t;
15835 typedef struct ma_jack_port_t ma_jack_port_t;
15836 typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
15837 typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
15838 typedef void (* ma_JackShutdownCallback) (void* arg);
15839 #define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
15840 #define ma_JackNoStartServer 1
15841 #define ma_JackPortIsInput 1
15842 #define ma_JackPortIsOutput 2
15843 #define ma_JackPortIsPhysical 4
15844 #endif
15845 
15846 typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
15847 typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
15848 typedef int (* ma_jack_client_name_size_proc) ();
15849 typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
15850 typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
15851 typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
15852 typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
15853 typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
15854 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);
15855 typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
15856 typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
15857 typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
15858 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);
15859 typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
15860 typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
15861 typedef void (* ma_jack_free_proc) (void* ptr);
15862 
15863 ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
15864 {
15865  size_t maxClientNameSize;
15866  char clientName[256];
15867  ma_jack_status_t status;
15868  ma_jack_client_t* pClient;
15869 
15870  ma_assert(pContext != NULL);
15871  ma_assert(ppClient != NULL);
15872 
15873  if (ppClient) {
15874  *ppClient = NULL;
15875  }
15876 
15877  maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */
15878  ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
15879 
15880  pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL);
15881  if (pClient == NULL) {
15883  }
15884 
15885  if (ppClient) {
15886  *ppClient = pClient;
15887  }
15888 
15889  return MA_SUCCESS;
15890 }
15891 
15892 ma_bool32 ma_context_is_device_id_equal__jack(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
15893 {
15894  ma_assert(pContext != NULL);
15895  ma_assert(pID0 != NULL);
15896  ma_assert(pID1 != NULL);
15897  (void)pContext;
15898 
15899  return pID0->jack == pID1->jack;
15900 }
15901 
15902 ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
15903 {
15904  ma_bool32 cbResult = MA_TRUE;
15905 
15906  ma_assert(pContext != NULL);
15907  ma_assert(callback != NULL);
15908 
15909  /* Playback. */
15910  if (cbResult) {
15911  ma_device_info deviceInfo;
15912  ma_zero_object(&deviceInfo);
15913  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
15914  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
15915  }
15916 
15917  /* Capture. */
15918  if (cbResult) {
15919  ma_device_info deviceInfo;
15920  ma_zero_object(&deviceInfo);
15921  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
15922  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
15923  }
15924 
15925  return MA_SUCCESS;
15926 }
15927 
15928 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)
15929 {
15930  ma_jack_client_t* pClient;
15931  ma_result result;
15932  const char** ppPorts;
15933 
15934  ma_assert(pContext != NULL);
15935 
15936  /* No exclusive mode with the JACK backend. */
15937  if (shareMode == ma_share_mode_exclusive) {
15939  }
15940 
15941  if (pDeviceID != NULL && pDeviceID->jack != 0) {
15942  return MA_NO_DEVICE; /* Don't know the device. */
15943  }
15944 
15945  /* Name / Description */
15946  if (deviceType == ma_device_type_playback) {
15947  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
15948  } else {
15949  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
15950  }
15951 
15952  /* Jack only supports f32 and has a specific channel count and sample rate. */
15953  pDeviceInfo->formatCount = 1;
15954  pDeviceInfo->formats[0] = ma_format_f32;
15955 
15956  /* The channel count and sample rate can only be determined by opening the device. */
15957  result = ma_context_open_client__jack(pContext, &pClient);
15958  if (result != MA_SUCCESS) {
15959  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
15960  }
15961 
15962  pDeviceInfo->minSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
15963  pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
15964 
15965  pDeviceInfo->minChannels = 0;
15966  pDeviceInfo->maxChannels = 0;
15967 
15968  ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, NULL, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput));
15969  if (ppPorts == NULL) {
15970  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
15971  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
15972  }
15973 
15974  while (ppPorts[pDeviceInfo->minChannels] != NULL) {
15975  pDeviceInfo->minChannels += 1;
15976  pDeviceInfo->maxChannels += 1;
15977  }
15978 
15979  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
15980  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
15981 
15982  (void)pContext;
15983  return MA_SUCCESS;
15984 }
15985 
15986 
15987 void ma_device_uninit__jack(ma_device* pDevice)
15988 {
15989  ma_context* pContext;
15990 
15991  ma_assert(pDevice != NULL);
15992 
15993  pContext = pDevice->pContext;
15994  ma_assert(pContext != NULL);
15995 
15996  if (pDevice->jack.pClient != NULL) {
15997  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
15998  }
15999 
16000  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
16001  ma_free(pDevice->jack.pIntermediaryBufferCapture);
16002  }
16003 
16004  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16005  ma_free(pDevice->jack.pIntermediaryBufferPlayback);
16006  }
16007 
16008  if (pDevice->type == ma_device_type_duplex) {
16009  ma_pcm_rb_uninit(&pDevice->jack.duplexRB);
16010  }
16011 }
16012 
16013 void ma_device__jack_shutdown_callback(void* pUserData)
16014 {
16015  /* JACK died. Stop the device. */
16016  ma_device* pDevice = (ma_device*)pUserData;
16017  ma_assert(pDevice != NULL);
16018 
16019  ma_device_stop(pDevice);
16020 }
16021 
16022 int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
16023 {
16024  ma_device* pDevice = (ma_device*)pUserData;
16025  ma_assert(pDevice != NULL);
16026 
16027  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
16028  float* pNewBuffer = (float*)ma_realloc(pDevice->jack.pIntermediaryBufferCapture, frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)));
16029  if (pNewBuffer == NULL) {
16030  return MA_OUT_OF_MEMORY;
16031  }
16032 
16033  pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
16034  pDevice->playback.internalBufferSizeInFrames = frameCount * pDevice->capture.internalPeriods;
16035  }
16036 
16037  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16038  float* pNewBuffer = (float*)ma_realloc(pDevice->jack.pIntermediaryBufferPlayback, frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)));
16039  if (pNewBuffer == NULL) {
16040  return MA_OUT_OF_MEMORY;
16041  }
16042 
16043  pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
16044  pDevice->playback.internalBufferSizeInFrames = frameCount * pDevice->playback.internalPeriods;
16045  }
16046 
16047  return 0;
16048 }
16049 
16050 int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
16051 {
16052  ma_device* pDevice;
16053  ma_context* pContext;
16054  ma_uint32 iChannel;
16055 
16056  pDevice = (ma_device*)pUserData;
16057  ma_assert(pDevice != NULL);
16058 
16059  pContext = pDevice->pContext;
16060  ma_assert(pContext != NULL);
16061 
16062  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
16063  /* Channels need to be interleaved. */
16064  for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
16065  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);
16066  if (pSrc != NULL) {
16067  float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
16068  ma_jack_nframes_t iFrame;
16069  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
16070  *pDst = *pSrc;
16071 
16072  pDst += pDevice->capture.internalChannels;
16073  pSrc += 1;
16074  }
16075  }
16076  }
16077 
16078  if (pDevice->type == ma_device_type_duplex) {
16079  ma_device__handle_duplex_callback_capture(pDevice, frameCount, pDevice->jack.pIntermediaryBufferCapture, &pDevice->jack.duplexRB);
16080  } else {
16081  ma_device__send_frames_to_client(pDevice, frameCount, pDevice->jack.pIntermediaryBufferCapture);
16082  }
16083  }
16084 
16085  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16086  if (pDevice->type == ma_device_type_duplex) {
16087  ma_device__handle_duplex_callback_playback(pDevice, frameCount, pDevice->jack.pIntermediaryBufferPlayback, &pDevice->jack.duplexRB);
16088  } else {
16089  ma_device__read_frames_from_client(pDevice, frameCount, pDevice->jack.pIntermediaryBufferPlayback);
16090  }
16091 
16092  /* Channels need to be deinterleaved. */
16093  for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
16094  float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[iChannel], frameCount);
16095  if (pDst != NULL) {
16096  const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
16097  ma_jack_nframes_t iFrame;
16098  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
16099  *pDst = *pSrc;
16100 
16101  pDst += 1;
16102  pSrc += pDevice->playback.internalChannels;
16103  }
16104  }
16105  }
16106  }
16107 
16108  return 0;
16109 }
16110 
16111 ma_result ma_device_init__jack(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
16112 {
16113  ma_result result;
16114  ma_uint32 periods;
16115  ma_uint32 bufferSizeInFrames;
16116 
16117  ma_assert(pContext != NULL);
16118  ma_assert(pConfig != NULL);
16119  ma_assert(pDevice != NULL);
16120 
16121  /* Only supporting default devices with JACK. */
16122  if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID != NULL && pConfig->playback.pDeviceID->jack != 0) ||
16123  ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID != NULL && pConfig->capture.pDeviceID->jack != 0)) {
16124  return MA_NO_DEVICE;
16125  }
16126 
16127  /* No exclusive mode with the JACK backend. */
16131  }
16132 
16133  /* Open the client. */
16134  result = ma_context_open_client__jack(pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
16135  if (result != MA_SUCCESS) {
16136  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
16137  }
16138 
16139  /* Callbacks. */
16140  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) {
16141  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
16142  }
16143  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) {
16144  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
16145  }
16146 
16147  ((ma_jack_on_shutdown_proc)pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
16148 
16149 
16150  /* The buffer size in frames can change. */
16151  periods = 2;
16152  bufferSizeInFrames = ((ma_jack_get_buffer_size_proc)pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient) * periods;
16153 
16154  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
16155  const char** ppPorts;
16156 
16157  pDevice->capture.internalFormat = ma_format_f32;
16158  pDevice->capture.internalChannels = 0;
16159  pDevice->capture.internalSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
16160  ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
16161 
16162  ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, NULL, ma_JackPortIsPhysical | ma_JackPortIsOutput);
16163  if (ppPorts == NULL) {
16164  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
16165  }
16166 
16167  while (ppPorts[pDevice->capture.internalChannels] != NULL) {
16168  char name[64];
16169  ma_strcpy_s(name, sizeof(name), "capture");
16170  ma_itoa_s((int)pDevice->capture.internalChannels, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */
16171 
16172  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);
16173  if (pDevice->jack.pPortsCapture[pDevice->capture.internalChannels] == NULL) {
16174  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
16175  ma_device_uninit__jack(pDevice);
16176  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
16177  }
16178 
16179  pDevice->capture.internalChannels += 1;
16180  }
16181 
16182  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
16183 
16184  pDevice->capture.internalBufferSizeInFrames = bufferSizeInFrames;
16185  pDevice->capture.internalPeriods = periods;
16186 
16187  pDevice->jack.pIntermediaryBufferCapture = (float*)ma_malloc((pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods) * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)));
16188  if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
16189  ma_device_uninit__jack(pDevice);
16190  return MA_OUT_OF_MEMORY;
16191  }
16192  }
16193 
16194  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
16195  const char** ppPorts;
16196 
16197  pDevice->playback.internalFormat = ma_format_f32;
16198  pDevice->playback.internalChannels = 0;
16199  pDevice->playback.internalSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
16200  ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
16201 
16202  ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, NULL, ma_JackPortIsPhysical | ma_JackPortIsInput);
16203  if (ppPorts == NULL) {
16204  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
16205  }
16206 
16207  while (ppPorts[pDevice->playback.internalChannels] != NULL) {
16208  char name[64];
16209  ma_strcpy_s(name, sizeof(name), "playback");
16210  ma_itoa_s((int)pDevice->playback.internalChannels, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */
16211 
16212  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);
16213  if (pDevice->jack.pPortsPlayback[pDevice->playback.internalChannels] == NULL) {
16214  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
16215  ma_device_uninit__jack(pDevice);
16216  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
16217  }
16218 
16219  pDevice->playback.internalChannels += 1;
16220  }
16221 
16222  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
16223 
16224  pDevice->playback.internalBufferSizeInFrames = bufferSizeInFrames;
16225  pDevice->playback.internalPeriods = periods;
16226 
16227  pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_malloc((pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods) * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)));
16228  if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
16229  ma_device_uninit__jack(pDevice);
16230  return MA_OUT_OF_MEMORY;
16231  }
16232  }
16233 
16234  if (pDevice->type == ma_device_type_duplex) {
16235  ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalBufferSizeInFrames);
16236  result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->jack.duplexRB);
16237  if (result != MA_SUCCESS) {
16238  ma_device_uninit__jack(pDevice);
16239  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to initialize ring buffer.", result);
16240  }
16241  }
16242 
16243  return MA_SUCCESS;
16244 }
16245 
16246 
16247 ma_result ma_device_start__jack(ma_device* pDevice)
16248 {
16249  ma_context* pContext = pDevice->pContext;
16250  int resultJACK;
16251  size_t i;
16252 
16253  resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
16254  if (resultJACK != 0) {
16255  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.", MA_FAILED_TO_START_BACKEND_DEVICE);
16256  }
16257 
16258  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
16259  const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, NULL, ma_JackPortIsPhysical | ma_JackPortIsOutput);
16260  if (ppServerPorts == NULL) {
16261  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
16262  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
16263  }
16264 
16265  for (i = 0; ppServerPorts[i] != NULL; ++i) {
16266  const char* pServerPort = ppServerPorts[i];
16267  const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsCapture[i]);
16268 
16269  resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
16270  if (resultJACK != 0) {
16271  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
16272  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
16273  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
16274  }
16275  }
16276 
16277  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
16278  }
16279 
16280  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16281  const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, NULL, ma_JackPortIsPhysical | ma_JackPortIsInput);
16282  if (ppServerPorts == NULL) {
16283  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
16284  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
16285  }
16286 
16287  for (i = 0; ppServerPorts[i] != NULL; ++i) {
16288  const char* pServerPort = ppServerPorts[i];
16289  const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[i]);
16290 
16291  resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
16292  if (resultJACK != 0) {
16293  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
16294  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
16295  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
16296  }
16297  }
16298 
16299  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
16300  }
16301 
16302  return MA_SUCCESS;
16303 }
16304 
16305 ma_result ma_device_stop__jack(ma_device* pDevice)
16306 {
16307  ma_context* pContext = pDevice->pContext;
16308  ma_stop_proc onStop;
16309 
16310  if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
16311  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MA_ERROR);
16312  }
16313 
16314  onStop = pDevice->onStop;
16315  if (onStop) {
16316  onStop(pDevice);
16317  }
16318 
16319  return MA_SUCCESS;
16320 }
16321 
16322 
16323 ma_result ma_context_uninit__jack(ma_context* pContext)
16324 {
16325  ma_assert(pContext != NULL);
16326  ma_assert(pContext->backend == ma_backend_jack);
16327 
16328  ma_free(pContext->jack.pClientName);
16329  pContext->jack.pClientName = NULL;
16330 
16331 #ifndef MA_NO_RUNTIME_LINKING
16332  ma_dlclose(pContext, pContext->jack.jackSO);
16333 #endif
16334 
16335  return MA_SUCCESS;
16336 }
16337 
16338 ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_context* pContext)
16339 {
16340 #ifndef MA_NO_RUNTIME_LINKING
16341  const char* libjackNames[] = {
16342 #ifdef MA_WIN32
16343  "libjack.dll"
16344 #else
16345  "libjack.so",
16346  "libjack.so.0"
16347 #endif
16348  };
16349  size_t i;
16350 
16351  for (i = 0; i < ma_countof(libjackNames); ++i) {
16352  pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]);
16353  if (pContext->jack.jackSO != NULL) {
16354  break;
16355  }
16356  }
16357 
16358  if (pContext->jack.jackSO == NULL) {
16359  return MA_NO_BACKEND;
16360  }
16361 
16362  pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open");
16363  pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close");
16364  pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size");
16365  pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback");
16366  pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback");
16367  pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown");
16368  pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate");
16369  pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size");
16370  pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports");
16371  pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate");
16372  pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate");
16373  pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect");
16374  pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register");
16375  pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name");
16376  pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer");
16377  pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free");
16378 #else
16379  /*
16380  This strange assignment system is here just to ensure type safety of miniaudio's function pointer
16381  types. If anything differs slightly the compiler should throw a warning.
16382  */
16383  ma_jack_client_open_proc _jack_client_open = jack_client_open;
16384  ma_jack_client_close_proc _jack_client_close = jack_client_close;
16385  ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
16386  ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
16387  ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
16388  ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
16389  ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
16390  ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
16391  ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
16392  ma_jack_activate_proc _jack_activate = jack_activate;
16393  ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
16394  ma_jack_connect_proc _jack_connect = jack_connect;
16395  ma_jack_port_register_proc _jack_port_register = jack_port_register;
16396  ma_jack_port_name_proc _jack_port_name = jack_port_name;
16397  ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
16398  ma_jack_free_proc _jack_free = jack_free;
16399 
16400  pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
16401  pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
16402  pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
16403  pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
16404  pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
16405  pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
16406  pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
16407  pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
16408  pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
16409  pContext->jack.jack_activate = (ma_proc)_jack_activate;
16410  pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
16411  pContext->jack.jack_connect = (ma_proc)_jack_connect;
16412  pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
16413  pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
16414  pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
16415  pContext->jack.jack_free = (ma_proc)_jack_free;
16416 #endif
16417 
16418  pContext->isBackendAsynchronous = MA_TRUE;
16419 
16420  pContext->onUninit = ma_context_uninit__jack;
16421  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__jack;
16422  pContext->onEnumDevices = ma_context_enumerate_devices__jack;
16423  pContext->onGetDeviceInfo = ma_context_get_device_info__jack;
16424  pContext->onDeviceInit = ma_device_init__jack;
16425  pContext->onDeviceUninit = ma_device_uninit__jack;
16426  pContext->onDeviceStart = ma_device_start__jack;
16427  pContext->onDeviceStop = ma_device_stop__jack;
16428 
16429  if (pConfig->jack.pClientName != NULL) {
16430  pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName);
16431  }
16432  pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
16433 
16434  /*
16435  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
16436  a temporary client.
16437  */
16438  {
16439  ma_jack_client_t* pDummyClient;
16440  ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
16441  if (result != MA_SUCCESS) {
16442  ma_free(pContext->jack.pClientName);
16443  #ifndef MA_NO_RUNTIME_LINKING
16444  ma_dlclose(pContext, pContext->jack.jackSO);
16445  #endif
16446  return MA_NO_BACKEND;
16447  }
16448 
16449  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
16450  }
16451 
16452  return MA_SUCCESS;
16453 }
16454 #endif /* JACK */
16455 
16456 
16457 
16458 /******************************************************************************
16459 
16460 Core Audio Backend
16461 
16462 ******************************************************************************/
16463 #ifdef MA_HAS_COREAUDIO
16464 #include <TargetConditionals.h>
16465 
16466 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
16467  #define MA_APPLE_MOBILE
16468 #else
16469  #define MA_APPLE_DESKTOP
16470 #endif
16471 
16472 #if defined(MA_APPLE_DESKTOP)
16473 #include <CoreAudio/CoreAudio.h>
16474 #else
16475 #include <AVFoundation/AVFoundation.h>
16476 #endif
16477 
16478 #include <AudioToolbox/AudioToolbox.h>
16479 
16480 /* CoreFoundation */
16481 typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
16482 
16483 /* CoreAudio */
16484 #if defined(MA_APPLE_DESKTOP)
16485 typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
16486 typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
16487 typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
16488 typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
16489 #endif
16490 
16491 /* AudioToolbox */
16492 typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
16493 typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
16494 typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
16495 typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
16496 typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
16497 typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
16498 typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
16499 typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
16500 typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
16501 typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
16502 typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
16503 
16504 
16505 #define MA_COREAUDIO_OUTPUT_BUS 0
16506 #define MA_COREAUDIO_INPUT_BUS 1
16507 
16508 ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
16509 
16510 /*
16511 Core Audio
16512 
16513 So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
16514 apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
16515 needing to figure out how this darn thing works, I'm going to outline a few things here.
16516 
16517 Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
16518 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
16519 that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
16520 and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
16521 distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
16522 
16523 Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
16524 retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
16525 data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
16526 devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
16527 the central APIs for retrieving information about the system and specific devices.
16528 
16529 To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
16530 structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
16531 which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
16532 typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
16533 kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
16534 kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different.
16535 
16536 Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
16537 of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
16538 address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
16539 size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
16540 AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
16541 */
16542 
16543 ma_result ma_result_from_OSStatus(OSStatus status)
16544 {
16545  switch (status)
16546  {
16547  case noErr: return MA_SUCCESS;
16548  #if defined(MA_APPLE_DESKTOP)
16549  case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
16550  case kAudioHardwareUnspecifiedError: return MA_ERROR;
16551  case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
16552  case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
16553  case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
16554  case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
16555  case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
16556  case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
16557  case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
16558  case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
16559  case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
16560  #endif
16561  default: return MA_ERROR;
16562  }
16563 }
16564 
16565 #if 0
16566 ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
16567 {
16568  switch (bit)
16569  {
16570  case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
16571  case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
16572  case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
16573  case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
16574  case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
16575  case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
16576  case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
16577  case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
16578  case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
16579  case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
16580  case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
16581  case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
16582  case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
16583  case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
16584  case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
16585  case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
16586  case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
16587  case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
16588  default: return MA_CHANNEL_NONE;
16589  }
16590 }
16591 #endif
16592 
16593 ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
16594 {
16595  switch (label)
16596  {
16597  case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
16598  case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
16599  case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
16600  case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
16601  case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
16602  case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
16603  case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
16604  case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
16605  case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
16606  case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
16607  case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
16608  case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
16609  case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
16610  case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
16611  case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
16612  case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
16613  case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
16614  case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
16615  case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
16616  case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
16617  case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
16618  case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
16619  case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
16620  case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
16621  case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
16622  case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
16623  case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
16624  case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
16625  case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
16626  case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
16627  case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
16628  case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
16629  case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
16630  case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
16631  case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
16632  case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
16633  case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
16634  case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
16635  case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
16636  case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
16637  case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
16638  case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
16639  case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
16640  case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
16641  case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
16642  case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
16643  case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
16644  case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
16645  case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
16646  case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
16647  case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
16648  case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
16649  case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
16650  case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
16651  case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
16652  case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
16653  case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
16654  case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
16655  case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
16656  case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
16657  case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
16658  case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
16659  case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
16660  case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
16661 
16662  #if 0 /* Introduced in a later version of macOS. */
16663  case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
16664  case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
16665  case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
16666  case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
16667  case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
16668  case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
16669  case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
16670  case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
16671  case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
16672  case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
16673  case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
16674  case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
16675  case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
16676  case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
16677  case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
16678  case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
16679  case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
16680  case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
16681  #endif
16682 
16683  default: return MA_CHANNEL_NONE;
16684  }
16685 }
16686 
16687 ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
16688 {
16689  ma_assert(pDescription != NULL);
16690  ma_assert(pFormatOut != NULL);
16691 
16692  *pFormatOut = ma_format_unknown; /* Safety. */
16693 
16694  /* There's a few things miniaudio doesn't support. */
16695  if (pDescription->mFormatID != kAudioFormatLinearPCM) {
16696  return MA_FORMAT_NOT_SUPPORTED;
16697  }
16698 
16699  /* We don't support any non-packed formats that are aligned high. */
16700  if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
16701  return MA_FORMAT_NOT_SUPPORTED;
16702  }
16703 
16704  /* Only supporting native-endian. */
16705  if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
16706  return MA_FORMAT_NOT_SUPPORTED;
16707  }
16708 
16709  /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */
16710  /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {
16711  return MA_FORMAT_NOT_SUPPORTED;
16712  }*/
16713 
16714  if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
16715  if (pDescription->mBitsPerChannel == 32) {
16716  *pFormatOut = ma_format_f32;
16717  return MA_SUCCESS;
16718  }
16719  } else {
16720  if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
16721  if (pDescription->mBitsPerChannel == 16) {
16722  *pFormatOut = ma_format_s16;
16723  return MA_SUCCESS;
16724  } else if (pDescription->mBitsPerChannel == 24) {
16725  if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
16726  *pFormatOut = ma_format_s24;
16727  return MA_SUCCESS;
16728  } else {
16729  if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
16730  /* TODO: Implement ma_format_s24_32. */
16732  /*return MA_SUCCESS;*/
16733  return MA_FORMAT_NOT_SUPPORTED;
16734  }
16735  }
16736  } else if (pDescription->mBitsPerChannel == 32) {
16737  *pFormatOut = ma_format_s32;
16738  return MA_SUCCESS;
16739  }
16740  } else {
16741  if (pDescription->mBitsPerChannel == 8) {
16742  *pFormatOut = ma_format_u8;
16743  return MA_SUCCESS;
16744  }
16745  }
16746  }
16747 
16748  /* Getting here means the format is not supported. */
16749  return MA_FORMAT_NOT_SUPPORTED;
16750 }
16751 
16752 ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel channelMap[MA_MAX_CHANNELS])
16753 {
16754  ma_assert(pChannelLayout != NULL);
16755 
16756  if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
16757  UInt32 iChannel;
16758  for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) {
16759  channelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
16760  }
16761  } else
16762 #if 0
16763  if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
16764  /* This is the same kind of system that's used by Windows audio APIs. */
16765  UInt32 iChannel = 0;
16766  UInt32 iBit;
16767  AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
16768  for (iBit = 0; iBit < 32; ++iBit) {
16769  AudioChannelBitmap bit = bitmap & (1 << iBit);
16770  if (bit != 0) {
16771  channelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
16772  }
16773  }
16774  } else
16775 #endif
16776  {
16777  /*
16778  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
16779  be updated to determine the mapping based on the tag.
16780  */
16781  UInt32 channelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
16782  switch (pChannelLayout->mChannelLayoutTag)
16783  {
16784  case kAudioChannelLayoutTag_Mono:
16785  case kAudioChannelLayoutTag_Stereo:
16786  case kAudioChannelLayoutTag_StereoHeadphones:
16787  case kAudioChannelLayoutTag_MatrixStereo:
16788  case kAudioChannelLayoutTag_MidSide:
16789  case kAudioChannelLayoutTag_XY:
16790  case kAudioChannelLayoutTag_Binaural:
16791  case kAudioChannelLayoutTag_Ambisonic_B_Format:
16792  {
16794  } break;
16795 
16796  case kAudioChannelLayoutTag_Octagonal:
16797  {
16798  channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
16799  channelMap[6] = MA_CHANNEL_SIDE_LEFT;
16800  } /* Intentional fallthrough. */
16801  case kAudioChannelLayoutTag_Hexagonal:
16802  {
16803  channelMap[5] = MA_CHANNEL_BACK_CENTER;
16804  } /* Intentional fallthrough. */
16805  case kAudioChannelLayoutTag_Pentagonal:
16806  {
16807  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
16808  } /* Intentional fallghrough. */
16809  case kAudioChannelLayoutTag_Quadraphonic:
16810  {
16811  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
16812  channelMap[2] = MA_CHANNEL_BACK_LEFT;
16813  channelMap[1] = MA_CHANNEL_RIGHT;
16814  channelMap[0] = MA_CHANNEL_LEFT;
16815  } break;
16816 
16817  /* TODO: Add support for more tags here. */
16818 
16819  default:
16820  {
16822  } break;
16823  }
16824  }
16825 
16826  return MA_SUCCESS;
16827 }
16828 
16829 
16830 #if defined(MA_APPLE_DESKTOP)
16831 ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
16832 {
16833  AudioObjectPropertyAddress propAddressDevices;
16834  UInt32 deviceObjectsDataSize;
16835  OSStatus status;
16836  AudioObjectID* pDeviceObjectIDs;
16837 
16838  ma_assert(pContext != NULL);
16839  ma_assert(pDeviceCount != NULL);
16840  ma_assert(ppDeviceObjectIDs != NULL);
16841 
16842  /* Safety. */
16843  *pDeviceCount = 0;
16844  *ppDeviceObjectIDs = NULL;
16845 
16846  propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
16847  propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
16848  propAddressDevices.mElement = kAudioObjectPropertyElementMaster;
16849 
16850  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
16851  if (status != noErr) {
16852  return ma_result_from_OSStatus(status);
16853  }
16854 
16855  pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize);
16856  if (pDeviceObjectIDs == NULL) {
16857  return MA_OUT_OF_MEMORY;
16858  }
16859 
16860  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
16861  if (status != noErr) {
16862  ma_free(pDeviceObjectIDs);
16863  return ma_result_from_OSStatus(status);
16864  }
16865 
16866  *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
16867  *ppDeviceObjectIDs = pDeviceObjectIDs;
16868 
16869  (void)pContext; /* Unused. */
16870  return MA_SUCCESS;
16871 }
16872 
16873 ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
16874 {
16875  AudioObjectPropertyAddress propAddress;
16876  UInt32 dataSize;
16877  OSStatus status;
16878 
16879  ma_assert(pContext != NULL);
16880 
16881  propAddress.mSelector = kAudioDevicePropertyDeviceUID;
16882  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
16883  propAddress.mElement = kAudioObjectPropertyElementMaster;
16884 
16885  dataSize = sizeof(*pUID);
16886  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
16887  if (status != noErr) {
16888  return ma_result_from_OSStatus(status);
16889  }
16890 
16891  return MA_SUCCESS;
16892 }
16893 
16894 ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
16895 {
16896  CFStringRef uid;
16897  ma_result result;
16898 
16899  ma_assert(pContext != NULL);
16900 
16901  result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
16902  if (result != MA_SUCCESS) {
16903  return result;
16904  }
16905 
16906  if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
16907  return MA_ERROR;
16908  }
16909 
16910  return MA_SUCCESS;
16911 }
16912 
16913 ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
16914 {
16915  AudioObjectPropertyAddress propAddress;
16916  CFStringRef deviceName = NULL;
16917  UInt32 dataSize;
16918  OSStatus status;
16919 
16920  ma_assert(pContext != NULL);
16921 
16922  propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
16923  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
16924  propAddress.mElement = kAudioObjectPropertyElementMaster;
16925 
16926  dataSize = sizeof(deviceName);
16927  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
16928  if (status != noErr) {
16929  return ma_result_from_OSStatus(status);
16930  }
16931 
16932  if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
16933  return MA_ERROR;
16934  }
16935 
16936  return MA_SUCCESS;
16937 }
16938 
16939 ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
16940 {
16941  AudioObjectPropertyAddress propAddress;
16942  UInt32 dataSize;
16943  OSStatus status;
16944  AudioBufferList* pBufferList;
16945  ma_bool32 isSupported;
16946 
16947  ma_assert(pContext != NULL);
16948 
16949  /* 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. */
16950  propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
16951  propAddress.mScope = scope;
16952  propAddress.mElement = kAudioObjectPropertyElementMaster;
16953 
16954  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
16955  if (status != noErr) {
16956  return MA_FALSE;
16957  }
16958 
16959  pBufferList = (AudioBufferList*)ma_malloc(dataSize);
16960  if (pBufferList == NULL) {
16961  return MA_FALSE; /* Out of memory. */
16962  }
16963 
16964  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
16965  if (status != noErr) {
16966  ma_free(pBufferList);
16967  return MA_FALSE;
16968  }
16969 
16970  isSupported = MA_FALSE;
16971  if (pBufferList->mNumberBuffers > 0) {
16972  isSupported = MA_TRUE;
16973  }
16974 
16975  ma_free(pBufferList);
16976  return isSupported;
16977 }
16978 
16979 ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
16980 {
16981  return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
16982 }
16983 
16984 ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
16985 {
16986  return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
16987 }
16988 
16989 
16990 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(). */
16991 {
16992  AudioObjectPropertyAddress propAddress;
16993  UInt32 dataSize;
16994  OSStatus status;
16995  AudioStreamRangedDescription* pDescriptions;
16996 
16997  ma_assert(pContext != NULL);
16998  ma_assert(pDescriptionCount != NULL);
16999  ma_assert(ppDescriptions != NULL);
17000 
17001  /*
17002  TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My
17003  MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.
17004  */
17005  propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/
17006  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
17007  propAddress.mElement = kAudioObjectPropertyElementMaster;
17008 
17009  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
17010  if (status != noErr) {
17011  return ma_result_from_OSStatus(status);
17012  }
17013 
17014  pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize);
17015  if (pDescriptions == NULL) {
17016  return MA_OUT_OF_MEMORY;
17017  }
17018 
17019  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
17020  if (status != noErr) {
17021  ma_free(pDescriptions);
17022  return ma_result_from_OSStatus(status);
17023  }
17024 
17025  *pDescriptionCount = dataSize / sizeof(*pDescriptions);
17026  *ppDescriptions = pDescriptions;
17027  return MA_SUCCESS;
17028 }
17029 
17030 
17031 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(). */
17032 {
17033  AudioObjectPropertyAddress propAddress;
17034  UInt32 dataSize;
17035  OSStatus status;
17036  AudioChannelLayout* pChannelLayout;
17037 
17038  ma_assert(pContext != NULL);
17039  ma_assert(ppChannelLayout != NULL);
17040 
17041  *ppChannelLayout = NULL; /* Safety. */
17042 
17043  propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
17044  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
17045  propAddress.mElement = kAudioObjectPropertyElementMaster;
17046 
17047  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
17048  if (status != noErr) {
17049  return ma_result_from_OSStatus(status);
17050  }
17051 
17052  pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize);
17053  if (pChannelLayout == NULL) {
17054  return MA_OUT_OF_MEMORY;
17055  }
17056 
17057  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
17058  if (status != noErr) {
17059  ma_free(pChannelLayout);
17060  return ma_result_from_OSStatus(status);
17061  }
17062 
17063  *ppChannelLayout = pChannelLayout;
17064  return MA_SUCCESS;
17065 }
17066 
17067 ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
17068 {
17069  AudioChannelLayout* pChannelLayout;
17070  ma_result result;
17071 
17072  ma_assert(pContext != NULL);
17073  ma_assert(pChannelCount != NULL);
17074 
17075  *pChannelCount = 0; /* Safety. */
17076 
17077  result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
17078  if (result != MA_SUCCESS) {
17079  return result;
17080  }
17081 
17082  if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
17083  *pChannelCount = pChannelLayout->mNumberChannelDescriptions;
17084  } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
17085  *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
17086  } else {
17087  *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
17088  }
17089 
17090  ma_free(pChannelLayout);
17091  return MA_SUCCESS;
17092 }
17093 
17094 ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel channelMap[MA_MAX_CHANNELS])
17095 {
17096  AudioChannelLayout* pChannelLayout;
17097  ma_result result;
17098 
17099  ma_assert(pContext != NULL);
17100 
17101  result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
17102  if (result != MA_SUCCESS) {
17103  return result; /* Rather than always failing here, would it be more robust to simply assume a default? */
17104  }
17105 
17106  result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
17107  if (result != MA_SUCCESS) {
17108  ma_free(pChannelLayout);
17109  return result;
17110  }
17111 
17112  ma_free(pChannelLayout);
17113  return result;
17114 }
17115 
17116 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(). */
17117 {
17118  AudioObjectPropertyAddress propAddress;
17119  UInt32 dataSize;
17120  OSStatus status;
17121  AudioValueRange* pSampleRateRanges;
17122 
17123  ma_assert(pContext != NULL);
17124  ma_assert(pSampleRateRangesCount != NULL);
17125  ma_assert(ppSampleRateRanges != NULL);
17126 
17127  /* Safety. */
17128  *pSampleRateRangesCount = 0;
17129  *ppSampleRateRanges = NULL;
17130 
17131  propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
17132  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
17133  propAddress.mElement = kAudioObjectPropertyElementMaster;
17134 
17135  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
17136  if (status != noErr) {
17137  return ma_result_from_OSStatus(status);
17138  }
17139 
17140  pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize);
17141  if (pSampleRateRanges == NULL) {
17142  return MA_OUT_OF_MEMORY;
17143  }
17144 
17145  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
17146  if (status != noErr) {
17147  ma_free(pSampleRateRanges);
17148  return ma_result_from_OSStatus(status);
17149  }
17150 
17151  *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
17152  *ppSampleRateRanges = pSampleRateRanges;
17153  return MA_SUCCESS;
17154 }
17155 
17156 ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut)
17157 {
17158  UInt32 sampleRateRangeCount;
17159  AudioValueRange* pSampleRateRanges;
17160  ma_result result;
17161 
17162  ma_assert(pContext != NULL);
17163  ma_assert(pSampleRateOut != NULL);
17164 
17165  *pSampleRateOut = 0; /* Safety. */
17166 
17167  result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
17168  if (result != MA_SUCCESS) {
17169  return result;
17170  }
17171 
17172  if (sampleRateRangeCount == 0) {
17173  ma_free(pSampleRateRanges);
17174  return MA_ERROR; /* Should never hit this case should we? */
17175  }
17176 
17177  if (sampleRateIn == 0) {
17178  /* Search in order of miniaudio's preferred priority. */
17179  UInt32 iMALSampleRate;
17180  for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
17181  ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
17182  UInt32 iCASampleRate;
17183  for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
17184  AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
17185  if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
17186  *pSampleRateOut = malSampleRate;
17187  ma_free(pSampleRateRanges);
17188  return MA_SUCCESS;
17189  }
17190  }
17191  }
17192 
17193  /*
17194  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
17195  case we just fall back to the first one reported by Core Audio.
17196  */
17197  ma_assert(sampleRateRangeCount > 0);
17198 
17199  *pSampleRateOut = pSampleRateRanges[0].mMinimum;
17200  ma_free(pSampleRateRanges);
17201  return MA_SUCCESS;
17202  } else {
17203  /* Find the closest match to this sample rate. */
17204  UInt32 currentAbsoluteDifference = INT32_MAX;
17205  UInt32 iCurrentClosestRange = (UInt32)-1;
17206  UInt32 iRange;
17207  for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
17208  if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
17209  *pSampleRateOut = sampleRateIn;
17210  ma_free(pSampleRateRanges);
17211  return MA_SUCCESS;
17212  } else {
17213  UInt32 absoluteDifference;
17214  if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
17215  absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
17216  } else {
17217  absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
17218  }
17219 
17220  if (currentAbsoluteDifference > absoluteDifference) {
17221  currentAbsoluteDifference = absoluteDifference;
17222  iCurrentClosestRange = iRange;
17223  }
17224  }
17225  }
17226 
17227  ma_assert(iCurrentClosestRange != (UInt32)-1);
17228 
17229  *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
17230  ma_free(pSampleRateRanges);
17231  return MA_SUCCESS;
17232  }
17233 
17234  /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */
17235  /*ma_free(pSampleRateRanges);*/
17236  /*return MA_ERROR;*/
17237 }
17238 
17239 
17240 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)
17241 {
17242  AudioObjectPropertyAddress propAddress;
17243  AudioValueRange bufferSizeRange;
17244  UInt32 dataSize;
17245  OSStatus status;
17246 
17247  ma_assert(pContext != NULL);
17248  ma_assert(pBufferSizeInFramesOut != NULL);
17249 
17250  *pBufferSizeInFramesOut = 0; /* Safety. */
17251 
17252  propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
17253  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
17254  propAddress.mElement = kAudioObjectPropertyElementMaster;
17255 
17256  dataSize = sizeof(bufferSizeRange);
17257  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
17258  if (status != noErr) {
17259  return ma_result_from_OSStatus(status);
17260  }
17261 
17262  /* This is just a clamp. */
17263  if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
17264  *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
17265  } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
17266  *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
17267  } else {
17268  *pBufferSizeInFramesOut = bufferSizeInFramesIn;
17269  }
17270 
17271  return MA_SUCCESS;
17272 }
17273 
17274 ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pBufferSizeInOut)
17275 {
17276  ma_result result;
17277  ma_uint32 chosenBufferSizeInFrames;
17278  AudioObjectPropertyAddress propAddress;
17279  UInt32 dataSize;
17280  OSStatus status;
17281 
17282  ma_assert(pContext != NULL);
17283 
17284  result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pBufferSizeInOut, &chosenBufferSizeInFrames);
17285  if (result != MA_SUCCESS) {
17286  return result;
17287  }
17288 
17289  /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */
17290  propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
17291  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
17292  propAddress.mElement = kAudioObjectPropertyElementMaster;
17293 
17294  ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
17295 
17296  /* Get the actual size of the buffer. */
17297  dataSize = sizeof(*pBufferSizeInOut);
17298  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
17299  if (status != noErr) {
17300  return ma_result_from_OSStatus(status);
17301  }
17302 
17303  *pBufferSizeInOut = chosenBufferSizeInFrames;
17304  return MA_SUCCESS;
17305 }
17306 
17307 
17308 ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
17309 {
17310  ma_assert(pContext != NULL);
17311  ma_assert(pDeviceObjectID != NULL);
17312 
17313  /* Safety. */
17314  *pDeviceObjectID = 0;
17315 
17316  if (pDeviceID == NULL) {
17317  /* Default device. */
17318  AudioObjectPropertyAddress propAddressDefaultDevice;
17319  UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
17320  AudioObjectID defaultDeviceObjectID;
17321  OSStatus status;
17322 
17323  propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
17324  propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster;
17325  if (deviceType == ma_device_type_playback) {
17326  propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
17327  } else {
17328  propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
17329  }
17330 
17331  defaultDeviceObjectIDSize = sizeof(AudioObjectID);
17332  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
17333  if (status == noErr) {
17334  *pDeviceObjectID = defaultDeviceObjectID;
17335  return MA_SUCCESS;
17336  }
17337  } else {
17338  /* Explicit device. */
17339  UInt32 deviceCount;
17340  AudioObjectID* pDeviceObjectIDs;
17341  ma_result result;
17342  UInt32 iDevice;
17343 
17344  result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
17345  if (result != MA_SUCCESS) {
17346  return result;
17347  }
17348 
17349  for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
17350  AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
17351 
17352  char uid[256];
17353  if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
17354  continue;
17355  }
17356 
17357  if (deviceType == ma_device_type_playback) {
17358  if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
17359  if (strcmp(uid, pDeviceID->coreaudio) == 0) {
17360  *pDeviceObjectID = deviceObjectID;
17361  return MA_SUCCESS;
17362  }
17363  }
17364  } else {
17365  if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
17366  if (strcmp(uid, pDeviceID->coreaudio) == 0) {
17367  *pDeviceObjectID = deviceObjectID;
17368  return MA_SUCCESS;
17369  }
17370  }
17371  }
17372  }
17373  }
17374 
17375  /* If we get here it means we couldn't find the device. */
17376  return MA_NO_DEVICE;
17377 }
17378 
17379 
17380 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)
17381 {
17382  UInt32 deviceFormatDescriptionCount;
17383  AudioStreamRangedDescription* pDeviceFormatDescriptions;
17384  ma_result result;
17385  ma_uint32 desiredSampleRate;
17386  ma_uint32 desiredChannelCount;
17387  ma_format desiredFormat;
17388  AudioStreamBasicDescription bestDeviceFormatSoFar;
17389  ma_bool32 hasSupportedFormat;
17390  UInt32 iFormat;
17391 
17392  result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
17393  if (result != MA_SUCCESS) {
17394  return result;
17395  }
17396 
17397  desiredSampleRate = sampleRate;
17398  if (usingDefaultSampleRate) {
17399  /*
17400  When using the device's default sample rate, we get the highest priority standard rate supported by the device. Otherwise
17401  we just use the pre-set rate.
17402  */
17403  ma_uint32 iStandardRate;
17404  for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
17405  ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
17406  ma_bool32 foundRate = MA_FALSE;
17407  UInt32 iDeviceRate;
17408 
17409  for (iDeviceRate = 0; iDeviceRate < deviceFormatDescriptionCount; ++iDeviceRate) {
17410  ma_uint32 deviceRate = (ma_uint32)pDeviceFormatDescriptions[iDeviceRate].mFormat.mSampleRate;
17411 
17412  if (deviceRate == standardRate) {
17413  desiredSampleRate = standardRate;
17414  foundRate = MA_TRUE;
17415  break;
17416  }
17417  }
17418 
17419  if (foundRate) {
17420  break;
17421  }
17422  }
17423  }
17424 
17425  desiredChannelCount = channels;
17426  if (usingDefaultChannels) {
17427  ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &desiredChannelCount); /* <-- Not critical if this fails. */
17428  }
17429 
17430  desiredFormat = format;
17431  if (usingDefaultFormat) {
17432  desiredFormat = g_maFormatPriorities[0];
17433  }
17434 
17435  /*
17436  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
17437  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.
17438  */
17439  ma_zero_object(&bestDeviceFormatSoFar);
17440 
17441  hasSupportedFormat = MA_FALSE;
17442  for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
17443  ma_format format;
17444  ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format);
17445  if (formatResult == MA_SUCCESS && format != ma_format_unknown) {
17446  hasSupportedFormat = MA_TRUE;
17447  bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
17448  break;
17449  }
17450  }
17451 
17452  if (!hasSupportedFormat) {
17453  return MA_FORMAT_NOT_SUPPORTED;
17454  }
17455 
17456 
17457  for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
17458  AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
17459  ma_format thisSampleFormat;
17460  ma_result formatResult;
17461  ma_format bestSampleFormatSoFar;
17462 
17463  /* If the format is not supported by miniaudio we need to skip this one entirely. */
17464  formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
17465  if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
17466  continue; /* The format is not supported by miniaudio. Skip. */
17467  }
17468 
17469  ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
17470 
17471  /* Getting here means the format is supported by miniaudio which makes this format a candidate. */
17472  if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
17473  /*
17474  The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format
17475  so far has an equal sample rate we can just ignore this one.
17476  */
17477  if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
17478  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. */
17479  } else {
17480  /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */
17481  if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
17482  /* This format has a different sample rate _and_ a different channel count. */
17483  if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
17484  continue; /* No change to the best format. */
17485  } else {
17486  /*
17487  Both this format and the best so far have different sample rates and different channel counts. Whichever has the
17488  best format is the new best.
17489  */
17490  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
17491  bestDeviceFormatSoFar = thisDeviceFormat;
17492  continue;
17493  } else {
17494  continue; /* No change to the best format. */
17495  }
17496  }
17497  } else {
17498  /* This format has a different sample rate but the desired channel count. */
17499  if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
17500  /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */
17501  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
17502  bestDeviceFormatSoFar = thisDeviceFormat;
17503  continue;
17504  } else {
17505  continue; /* No change to the best format for now. */
17506  }
17507  } else {
17508  /* This format has the desired channel count, but the best so far does not. We have a new best. */
17509  bestDeviceFormatSoFar = thisDeviceFormat;
17510  continue;
17511  }
17512  }
17513  }
17514  } else {
17515  /*
17516  The sample rates match which makes this format a very high priority contender. If the best format so far has a different
17517  sample rate it needs to be replaced with this one.
17518  */
17519  if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
17520  bestDeviceFormatSoFar = thisDeviceFormat;
17521  continue;
17522  } else {
17523  /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */
17524  if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
17525  /*
17526  In this case this format has the same channel count as what the client is requesting. If the best format so far has
17527  a different count, this one becomes the new best.
17528  */
17529  if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
17530  bestDeviceFormatSoFar = thisDeviceFormat;
17531  continue;
17532  } else {
17533  /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
17534  if (thisSampleFormat == desiredFormat) {
17535  bestDeviceFormatSoFar = thisDeviceFormat;
17536  break; /* Found the exact match. */
17537  } else {
17538  /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
17539  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
17540  bestDeviceFormatSoFar = thisDeviceFormat;
17541  continue;
17542  } else {
17543  continue; /* No change to the best format for now. */
17544  }
17545  }
17546  }
17547  } else {
17548  /*
17549  In this case the channel count is different to what the client has requested. If the best so far has the same channel
17550  count as the requested count then it remains the best.
17551  */
17552  if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
17553  continue;
17554  } else {
17555  /*
17556  This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
17557  the same priority, but we need to compare the format now.
17558  */
17559  if (thisSampleFormat == bestSampleFormatSoFar) {
17560  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
17561  bestDeviceFormatSoFar = thisDeviceFormat;
17562  continue;
17563  } else {
17564  continue; /* No change to the best format for now. */
17565  }
17566  }
17567  }
17568  }
17569  }
17570  }
17571  }
17572 
17573  *pFormat = bestDeviceFormatSoFar;
17574  return MA_SUCCESS;
17575 }
17576 #endif
17577 
17578 ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel channelMap[MA_MAX_CHANNELS])
17579 {
17580  AudioUnitScope deviceScope;
17581  AudioUnitElement deviceBus;
17582  UInt32 channelLayoutSize;
17583  OSStatus status;
17584  AudioChannelLayout* pChannelLayout;
17585  ma_result result;
17586 
17587  ma_assert(pContext != NULL);
17588 
17589  if (deviceType == ma_device_type_playback) {
17590  deviceScope = kAudioUnitScope_Output;
17591  deviceBus = MA_COREAUDIO_OUTPUT_BUS;
17592  } else {
17593  deviceScope = kAudioUnitScope_Input;
17594  deviceBus = MA_COREAUDIO_INPUT_BUS;
17595  }
17596 
17597  status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
17598  if (status != noErr) {
17599  return ma_result_from_OSStatus(status);
17600  }
17601 
17602  pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize);
17603  if (pChannelLayout == NULL) {
17604  return MA_OUT_OF_MEMORY;
17605  }
17606 
17607  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
17608  if (status != noErr) {
17609  ma_free(pChannelLayout);
17610  return ma_result_from_OSStatus(status);
17611  }
17612 
17613  result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
17614  if (result != MA_SUCCESS) {
17615  ma_free(pChannelLayout);
17616  return result;
17617  }
17618 
17619  ma_free(pChannelLayout);
17620  return MA_SUCCESS;
17621 }
17622 
17623 ma_bool32 ma_context_is_device_id_equal__coreaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
17624 {
17625  ma_assert(pContext != NULL);
17626  ma_assert(pID0 != NULL);
17627  ma_assert(pID1 != NULL);
17628  (void)pContext;
17629 
17630  return strcmp(pID0->coreaudio, pID1->coreaudio) == 0;
17631 }
17632 
17633 ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
17634 {
17635 #if defined(MA_APPLE_DESKTOP)
17636  UInt32 deviceCount;
17637  AudioObjectID* pDeviceObjectIDs;
17638  ma_result result;
17639  UInt32 iDevice;
17640 
17641  result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
17642  if (result != MA_SUCCESS) {
17643  return result;
17644  }
17645 
17646  for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
17647  AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
17648  ma_device_info info;
17649 
17650  ma_zero_object(&info);
17651  if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
17652  continue;
17653  }
17654  if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
17655  continue;
17656  }
17657 
17658  if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
17659  if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
17660  break;
17661  }
17662  }
17663  if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
17664  if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
17665  break;
17666  }
17667  }
17668  }
17669 
17670  ma_free(pDeviceObjectIDs);
17671 #else
17672  /* Only supporting default devices on non-Desktop platforms. */
17673  ma_device_info info;
17674 
17675  ma_zero_object(&info);
17676  ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
17677  if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
17678  return MA_SUCCESS;
17679  }
17680 
17681  ma_zero_object(&info);
17682  ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
17683  if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
17684  return MA_SUCCESS;
17685  }
17686 #endif
17687 
17688  return MA_SUCCESS;
17689 }
17690 
17691 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)
17692 {
17693  ma_result result;
17694 
17695  ma_assert(pContext != NULL);
17696 
17697  /* No exclusive mode with the Core Audio backend for now. */
17698  if (shareMode == ma_share_mode_exclusive) {
17700  }
17701 
17702 #if defined(MA_APPLE_DESKTOP)
17703  /* Desktop */
17704  {
17705  AudioObjectID deviceObjectID;
17706  UInt32 streamDescriptionCount;
17707  AudioStreamRangedDescription* pStreamDescriptions;
17708  UInt32 iStreamDescription;
17709  UInt32 sampleRateRangeCount;
17710  AudioValueRange* pSampleRateRanges;
17711 
17712  result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
17713  if (result != MA_SUCCESS) {
17714  return result;
17715  }
17716 
17717  result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
17718  if (result != MA_SUCCESS) {
17719  return result;
17720  }
17721 
17722  result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
17723  if (result != MA_SUCCESS) {
17724  return result;
17725  }
17726 
17727  /* Formats. */
17728  result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
17729  if (result != MA_SUCCESS) {
17730  return result;
17731  }
17732 
17733  for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
17734  ma_format format;
17735  ma_bool32 formatExists = MA_FALSE;
17736  ma_uint32 iOutputFormat;
17737 
17738  result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
17739  if (result != MA_SUCCESS) {
17740  continue;
17741  }
17742 
17743  ma_assert(format != ma_format_unknown);
17744 
17745  /* Make sure the format isn't already in the output list. */
17746  for (iOutputFormat = 0; iOutputFormat < pDeviceInfo->formatCount; ++iOutputFormat) {
17747  if (pDeviceInfo->formats[iOutputFormat] == format) {
17748  formatExists = MA_TRUE;
17749  break;
17750  }
17751  }
17752 
17753  if (!formatExists) {
17754  pDeviceInfo->formats[pDeviceInfo->formatCount++] = format;
17755  }
17756  }
17757 
17758  ma_free(pStreamDescriptions);
17759 
17760 
17761  /* Channels. */
17762  result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &pDeviceInfo->minChannels);
17763  if (result != MA_SUCCESS) {
17764  return result;
17765  }
17766  pDeviceInfo->maxChannels = pDeviceInfo->minChannels;
17767 
17768 
17769  /* Sample rates. */
17770  result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
17771  if (result != MA_SUCCESS) {
17772  return result;
17773  }
17774 
17775  if (sampleRateRangeCount > 0) {
17776  UInt32 iSampleRate;
17777  pDeviceInfo->minSampleRate = UINT32_MAX;
17778  pDeviceInfo->maxSampleRate = 0;
17779  for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
17780  if (pDeviceInfo->minSampleRate > pSampleRateRanges[iSampleRate].mMinimum) {
17781  pDeviceInfo->minSampleRate = pSampleRateRanges[iSampleRate].mMinimum;
17782  }
17783  if (pDeviceInfo->maxSampleRate < pSampleRateRanges[iSampleRate].mMaximum) {
17784  pDeviceInfo->maxSampleRate = pSampleRateRanges[iSampleRate].mMaximum;
17785  }
17786  }
17787  }
17788  }
17789 #else
17790  /* Mobile */
17791  {
17792  AudioComponentDescription desc;
17793  AudioComponent component;
17794  AudioUnit audioUnit;
17795  OSStatus status;
17796  AudioUnitScope formatScope;
17797  AudioUnitElement formatElement;
17798  AudioStreamBasicDescription bestFormat;
17799  UInt32 propSize;
17800 
17801  if (deviceType == ma_device_type_playback) {
17802  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
17803  } else {
17804  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
17805  }
17806 
17807  /*
17808  Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
17809  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
17810  retrieve from the AVAudioSession shared instance.
17811  */
17812  desc.componentType = kAudioUnitType_Output;
17813  desc.componentSubType = kAudioUnitSubType_RemoteIO;
17814  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
17815  desc.componentFlags = 0;
17816  desc.componentFlagsMask = 0;
17817 
17818  component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
17819  if (component == NULL) {
17821  }
17822 
17823  status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
17824  if (status != noErr) {
17825  return ma_result_from_OSStatus(status);
17826  }
17827 
17828  formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
17829  formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
17830 
17831  propSize = sizeof(bestFormat);
17832  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
17833  if (status != noErr) {
17834  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
17835  return ma_result_from_OSStatus(status);
17836  }
17837 
17838  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
17839  audioUnit = NULL;
17840 
17841 
17842  pDeviceInfo->minChannels = bestFormat.mChannelsPerFrame;
17843  pDeviceInfo->maxChannels = bestFormat.mChannelsPerFrame;
17844 
17845  pDeviceInfo->formatCount = 1;
17846  result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->formats[0]);
17847  if (result != MA_SUCCESS) {
17848  return result;
17849  }
17850 
17851  /*
17852  It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
17853  this we just get the shared instance and inspect.
17854  */
17855  @autoreleasepool {
17856  AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
17857  ma_assert(pAudioSession != NULL);
17858 
17859  pDeviceInfo->minSampleRate = (ma_uint32)pAudioSession.sampleRate;
17860  pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
17861  }
17862  }
17863 #endif
17864 
17865  (void)pDeviceInfo; /* Unused. */
17866  return MA_SUCCESS;
17867 }
17868 
17869 
17870 void ma_device_uninit__coreaudio(ma_device* pDevice)
17871 {
17872  ma_assert(pDevice != NULL);
17873  ma_assert(ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED);
17874 
17875  if (pDevice->coreaudio.audioUnitCapture != NULL) {
17876  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
17877  }
17878  if (pDevice->coreaudio.audioUnitPlayback != NULL) {
17879  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
17880  }
17881 
17882  if (pDevice->coreaudio.pAudioBufferList) {
17883  ma_free(pDevice->coreaudio.pAudioBufferList);
17884  }
17885 
17886  if (pDevice->type == ma_device_type_duplex) {
17887  ma_pcm_rb_uninit(&pDevice->coreaudio.duplexRB);
17888  }
17889 }
17890 
17891 
17892 OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
17893 {
17894  ma_device* pDevice = (ma_device*)pUserData;
17895  ma_stream_layout layout;
17896 
17897  ma_assert(pDevice != NULL);
17898 
17899 #if defined(MA_DEBUG_OUTPUT)
17900  printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
17901 #endif
17902 
17903  /* 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. */
17905  if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
17907  }
17908 
17909  if (layout == ma_stream_layout_interleaved) {
17910  /* For now we can assume everything is interleaved. */
17911  UInt32 iBuffer;
17912  for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
17913  if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
17914  ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
17915  if (frameCountForThisBuffer > 0) {
17916  if (pDevice->type == ma_device_type_duplex) {
17917  ma_device__handle_duplex_callback_playback(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
17918  } else {
17919  ma_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
17920  }
17921  }
17922 
17923  #if defined(MA_DEBUG_OUTPUT)
17924  printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
17925  #endif
17926  } else {
17927  /*
17928  This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
17929  not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
17930  output silence here.
17931  */
17932  ma_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
17933 
17934  #if defined(MA_DEBUG_OUTPUT)
17935  printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
17936  #endif
17937  }
17938  }
17939  } else {
17940  /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
17941  ma_uint8 tempBuffer[4096];
17942  UInt32 iBuffer;
17943  for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
17944  ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
17945  ma_uint32 framesRemaining = frameCountPerBuffer;
17946 
17947  while (framesRemaining > 0) {
17948  void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
17949  ma_uint32 iChannel;
17950  ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
17951  if (framesToRead > framesRemaining) {
17952  framesToRead = framesRemaining;
17953  }
17954 
17955  if (pDevice->type == ma_device_type_duplex) {
17956  ma_device__handle_duplex_callback_playback(pDevice, framesToRead, tempBuffer, &pDevice->coreaudio.duplexRB);
17957  } else {
17958  ma_device__read_frames_from_client(pDevice, framesToRead, tempBuffer);
17959  }
17960 
17961  for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
17962  ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
17963  }
17964 
17965  ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
17966 
17967  framesRemaining -= framesToRead;
17968  }
17969  }
17970  }
17971 
17972  (void)pActionFlags;
17973  (void)pTimeStamp;
17974  (void)busNumber;
17975 
17976  return noErr;
17977 }
17978 
17979 OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
17980 {
17981  ma_device* pDevice = (ma_device*)pUserData;
17982  AudioBufferList* pRenderedBufferList;
17983  ma_stream_layout layout;
17984  OSStatus status;
17985 
17986  ma_assert(pDevice != NULL);
17987 
17988  pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
17989  ma_assert(pRenderedBufferList);
17990 
17991  /* 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. */
17993  if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
17995  }
17996 
17997 #if defined(MA_DEBUG_OUTPUT)
17998  printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
17999 #endif
18000 
18001  status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
18002  if (status != noErr) {
18003  #if defined(MA_DEBUG_OUTPUT)
18004  printf(" ERROR: AudioUnitRender() failed with %d\n", status);
18005  #endif
18006  return status;
18007  }
18008 
18009  if (layout == ma_stream_layout_interleaved) {
18010  UInt32 iBuffer;
18011  for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
18012  if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
18013  if (pDevice->type == ma_device_type_duplex) {
18014  ma_device__handle_duplex_callback_capture(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
18015  } else {
18016  ma_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
18017  }
18018  #if defined(MA_DEBUG_OUTPUT)
18019  printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
18020  #endif
18021  } else {
18022  /*
18023  This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
18024  not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
18025  */
18026  ma_uint8 silentBuffer[4096];
18027  ma_uint32 framesRemaining;
18028 
18029  ma_zero_memory(silentBuffer, sizeof(silentBuffer));
18030 
18031  framesRemaining = frameCount;
18032  while (framesRemaining > 0) {
18033  ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
18034  if (framesToSend > framesRemaining) {
18035  framesToSend = framesRemaining;
18036  }
18037 
18038  if (pDevice->type == ma_device_type_duplex) {
18039  ma_device__handle_duplex_callback_capture(pDevice, framesToSend, silentBuffer, &pDevice->coreaudio.duplexRB);
18040  } else {
18041  ma_device__send_frames_to_client(pDevice, framesToSend, silentBuffer);
18042  }
18043 
18044  framesRemaining -= framesToSend;
18045  }
18046 
18047  #if defined(MA_DEBUG_OUTPUT)
18048  printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
18049  #endif
18050  }
18051  }
18052  } else {
18053  /* 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. */
18054  ma_uint8 tempBuffer[4096];
18055  UInt32 iBuffer;
18056  for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
18057  ma_uint32 framesRemaining = frameCount;
18058  while (framesRemaining > 0) {
18059  void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
18060  ma_uint32 iChannel;
18061  ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_sample(pDevice->capture.internalFormat);
18062  if (framesToSend > framesRemaining) {
18063  framesToSend = framesRemaining;
18064  }
18065 
18066  for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
18067  ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
18068  }
18069 
18070  ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
18071 
18072  if (pDevice->type == ma_device_type_duplex) {
18073  ma_device__handle_duplex_callback_capture(pDevice, framesToSend, tempBuffer, &pDevice->coreaudio.duplexRB);
18074  } else {
18075  ma_device__send_frames_to_client(pDevice, framesToSend, tempBuffer);
18076  }
18077 
18078  framesRemaining -= framesToSend;
18079  }
18080  }
18081  }
18082 
18083  (void)pActionFlags;
18084  (void)pTimeStamp;
18085  (void)busNumber;
18086  (void)frameCount;
18087  (void)pUnusedBufferList;
18088 
18089  return noErr;
18090 }
18091 
18092 void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
18093 {
18094  ma_device* pDevice = (ma_device*)pUserData;
18095  ma_assert(pDevice != NULL);
18096 
18097  /*
18098  There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
18099  AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
18100  can try waiting on the same lock. I'm going to try working around this by not calling any Core
18101  Audio APIs in the callback when the device has been stopped or uninitialized.
18102  */
18103  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) {
18104  ma_stop_proc onStop = pDevice->onStop;
18105  if (onStop) {
18106  onStop(pDevice);
18107  }
18108 
18109  ma_event_signal(&pDevice->coreaudio.stopEvent);
18110  } else {
18111  UInt32 isRunning;
18112  UInt32 isRunningSize = sizeof(isRunning);
18113  OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
18114  if (status != noErr) {
18115  return; /* Don't really know what to do in this case... just ignore it, I suppose... */
18116  }
18117 
18118  if (!isRunning) {
18119  ma_stop_proc onStop;
18120 
18121  /*
18122  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:
18123 
18124  1) When the device is unplugged, this will be called _before_ the default device change notification.
18125  2) When the device is changed via the default device change notification, this will be called _after_ the switch.
18126 
18127  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.
18128  */
18129  if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
18130  ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
18131  /*
18132  It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
18133  via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
18134  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
18135  hasn't!).
18136  */
18137  if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
18138  ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
18139  return;
18140  }
18141 
18142  /*
18143  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
18144  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
18145  likely be successful in switching to the new device.
18146 
18147  TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
18148  */
18149  return;
18150  }
18151 
18152  /* Getting here means we need to stop the device. */
18153  onStop = pDevice->onStop;
18154  if (onStop) {
18155  onStop(pDevice);
18156  }
18157  }
18158  }
18159 
18160  (void)propertyID; /* Unused. */
18161 }
18162 
18163 #if defined(MA_APPLE_DESKTOP)
18164 OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
18165 {
18166  ma_device* pDevice = (ma_device*)pUserData;
18167  ma_assert(pDevice != NULL);
18168 
18169  /* Not sure if I really need to check this, but it makes me feel better. */
18170  if (addressCount == 0) {
18171  return noErr;
18172  }
18173 
18174  if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
18175  ma_result reinitResult;
18176 
18177  pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
18178  reinitResult = ma_device_reinit_internal__coreaudio(pDevice, ma_device_type_playback, MA_TRUE);
18179  pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
18180 
18181  if (reinitResult == MA_SUCCESS) {
18182  ma_device__post_init_setup(pDevice, ma_device_type_playback);
18183 
18184  /* Restart the device if required. If this fails we need to stop the device entirely. */
18185  if (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
18186  OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
18187  if (status != noErr) {
18188  if (pDevice->type == ma_device_type_duplex) {
18189  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
18190  }
18191  ma_device__set_state(pDevice, MA_STATE_STOPPED);
18192  }
18193  }
18194  }
18195  }
18196 
18197  if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
18198  ma_result reinitResult;
18199 
18200  pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
18201  reinitResult = ma_device_reinit_internal__coreaudio(pDevice, ma_device_type_capture, MA_TRUE);
18202  pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
18203 
18204  if (reinitResult == MA_SUCCESS) {
18205  ma_device__post_init_setup(pDevice, ma_device_type_capture);
18206 
18207  /* Restart the device if required. If this fails we need to stop the device entirely. */
18208  if (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
18209  OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
18210  if (status != noErr) {
18211  if (pDevice->type == ma_device_type_duplex) {
18212  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
18213  }
18214  ma_device__set_state(pDevice, MA_STATE_STOPPED);
18215  }
18216  }
18217  }
18218  }
18219 
18220  (void)objectID; /* Unused. */
18221  return noErr;
18222 }
18223 #endif
18224 
18225 typedef struct
18226 {
18227  /* Input. */
18228  ma_format formatIn;
18229  ma_uint32 channelsIn;
18230  ma_uint32 sampleRateIn;
18231  ma_channel channelMapIn[MA_MAX_CHANNELS];
18232  ma_uint32 bufferSizeInFramesIn;
18233  ma_uint32 bufferSizeInMillisecondsIn;
18234  ma_uint32 periodsIn;
18235  ma_bool32 usingDefaultFormat;
18236  ma_bool32 usingDefaultChannels;
18237  ma_bool32 usingDefaultSampleRate;
18238  ma_bool32 usingDefaultChannelMap;
18239  ma_share_mode shareMode;
18240  ma_bool32 registerStopEvent;
18241 
18242  /* Output. */
18243 #if defined(MA_APPLE_DESKTOP)
18244  AudioObjectID deviceObjectID;
18245 #endif
18246  AudioComponent component;
18247  AudioUnit audioUnit;
18248  AudioBufferList* pAudioBufferList; /* Only used for input devices. */
18249  ma_format formatOut;
18250  ma_uint32 channelsOut;
18251  ma_uint32 sampleRateOut;
18252  ma_channel channelMapOut[MA_MAX_CHANNELS];
18253  ma_uint32 bufferSizeInFramesOut;
18254  ma_uint32 periodsOut;
18255  char deviceName[256];
18256 } ma_device_init_internal_data__coreaudio;
18257 
18258 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. */
18259 {
18260  ma_result result;
18261  OSStatus status;
18262  UInt32 enableIOFlag;
18263  AudioStreamBasicDescription bestFormat;
18264  ma_uint32 actualBufferSizeInFrames;
18265  AURenderCallbackStruct callbackInfo;
18266 #if defined(MA_APPLE_DESKTOP)
18267  AudioObjectID deviceObjectID;
18268 #endif
18269 
18270  /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
18271  if (deviceType == ma_device_type_duplex) {
18272  return MA_INVALID_ARGS;
18273  }
18274 
18275  ma_assert(pContext != NULL);
18276  ma_assert(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
18277 
18278 #if defined(MA_APPLE_DESKTOP)
18279  pData->deviceObjectID = 0;
18280 #endif
18281  pData->component = NULL;
18282  pData->audioUnit = NULL;
18283  pData->pAudioBufferList = NULL;
18284 
18285 #if defined(MA_APPLE_DESKTOP)
18286  result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
18287  if (result != MA_SUCCESS) {
18288  return result;
18289  }
18290 
18291  pData->deviceObjectID = deviceObjectID;
18292 #endif
18293 
18294  /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
18295  pData->periodsOut = pData->periodsIn;
18296  if (pData->periodsOut < 1) {
18297  pData->periodsOut = 1;
18298  }
18299  if (pData->periodsOut > 16) {
18300  pData->periodsOut = 16;
18301  }
18302 
18303 
18304  /* Audio unit. */
18305  status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
18306  if (status != noErr) {
18307  return ma_result_from_OSStatus(status);
18308  }
18309 
18310 
18311  /* 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. */
18312  enableIOFlag = 1;
18313  if (deviceType == ma_device_type_capture) {
18314  enableIOFlag = 0;
18315  }
18316 
18317  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
18318  if (status != noErr) {
18319  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18320  return ma_result_from_OSStatus(status);
18321  }
18322 
18323  enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
18324  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
18325  if (status != noErr) {
18326  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18327  return ma_result_from_OSStatus(status);
18328  }
18329 
18330 
18331  /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
18332 #if defined(MA_APPLE_DESKTOP)
18333  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));
18334  if (status != noErr) {
18335  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18336  return ma_result_from_OSStatus(result);
18337  }
18338 #endif
18339 
18340  /*
18341  Format. This is the hardest part of initialization because there's a few variables to take into account.
18342  1) The format must be supported by the device.
18343  2) The format must be supported miniaudio.
18344  3) There's a priority that miniaudio prefers.
18345 
18346  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
18347  most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
18348  for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
18349 
18350  On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
18351  */
18352  {
18353  AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
18354  AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
18355 
18356  #if defined(MA_APPLE_DESKTOP)
18357  AudioStreamBasicDescription origFormat;
18358  UInt32 origFormatSize;
18359 
18360  result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, pData->usingDefaultFormat, pData->usingDefaultChannels, pData->usingDefaultSampleRate, &bestFormat);
18361  if (result != MA_SUCCESS) {
18362  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18363  return result;
18364  }
18365 
18366  /* From what I can see, Apple's documentation implies that we should keep the sample rate consistent. */
18367  origFormatSize = sizeof(origFormat);
18368  if (deviceType == ma_device_type_playback) {
18369  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
18370  } else {
18371  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
18372  }
18373 
18374  if (status != noErr) {
18375  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18376  return result;
18377  }
18378 
18379  bestFormat.mSampleRate = origFormat.mSampleRate;
18380 
18381  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
18382  if (status != noErr) {
18383  /* We failed to set the format, so fall back to the current format of the audio unit. */
18384  bestFormat = origFormat;
18385  }
18386  #else
18387  UInt32 propSize = sizeof(bestFormat);
18388  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
18389  if (status != noErr) {
18390  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18391  return ma_result_from_OSStatus(status);
18392  }
18393 
18394  /*
18395  Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
18396  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
18397  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
18398  can tell, it looks like the sample rate is shared between playback and capture for everything.
18399  */
18400  @autoreleasepool {
18401  AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
18402  ma_assert(pAudioSession != NULL);
18403 
18404  [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
18405  bestFormat.mSampleRate = pAudioSession.sampleRate;
18406  }
18407 
18408  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
18409  if (status != noErr) {
18410  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18411  return ma_result_from_OSStatus(status);
18412  }
18413  #endif
18414 
18415  result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
18416  if (result != MA_SUCCESS) {
18417  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18418  return result;
18419  }
18420 
18421  if (pData->formatOut == ma_format_unknown) {
18422  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18423  return MA_FORMAT_NOT_SUPPORTED;
18424  }
18425 
18426  pData->channelsOut = bestFormat.mChannelsPerFrame;
18427  pData->sampleRateOut = bestFormat.mSampleRate;
18428  }
18429 
18430  /*
18431  Internal channel map. This is weird in my testing. If I use the AudioObject to get the
18432  channel map, the channel descriptions are set to "Unknown" for some reason. To work around
18433  this it looks like retrieving it from the AudioUnit will work. However, and this is where
18434  it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
18435  I'm going to fall back to a default assumption in these cases.
18436  */
18437 #if defined(MA_APPLE_DESKTOP)
18438  result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut);
18439  if (result != MA_SUCCESS) {
18440  #if 0
18441  /* Try falling back to the channel map from the AudioObject. */
18442  result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut);
18443  if (result != MA_SUCCESS) {
18444  return result;
18445  }
18446  #else
18447  /* Fall back to default assumptions. */
18448  ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
18449  #endif
18450  }
18451 #else
18452  /* TODO: Figure out how to get the channel map using AVAudioSession. */
18453  ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
18454 #endif
18455 
18456 
18457  /* Buffer size. Not allowing this to be configurable on iOS. */
18458  actualBufferSizeInFrames = pData->bufferSizeInFramesIn;
18459 
18460 #if defined(MA_APPLE_DESKTOP)
18461  if (actualBufferSizeInFrames == 0) {
18462  actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->bufferSizeInMillisecondsIn, pData->sampleRateOut);
18463  }
18464 
18465  actualBufferSizeInFrames = actualBufferSizeInFrames / pData->periodsOut;
18466  result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualBufferSizeInFrames);
18467  if (result != MA_SUCCESS) {
18468  return result;
18469  }
18470 
18471  pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut;
18472 #else
18473  actualBufferSizeInFrames = 4096;
18474  pData->bufferSizeInFramesOut = actualBufferSizeInFrames;
18475 #endif
18476 
18477 
18478  /*
18479  During testing I discovered that the buffer size can be too big. You'll get an error like this:
18480 
18481  kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
18482 
18483  Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
18484  of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
18485  */
18486  {
18487  /*AudioUnitScope propScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
18488  AudioUnitElement propBus = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
18489 
18490  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, propScope, propBus, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames));
18491  if (status != noErr) {
18492  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18493  return ma_result_from_OSStatus(status);
18494  }*/
18495 
18496  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames));
18497  if (status != noErr) {
18498  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18499  return ma_result_from_OSStatus(status);
18500  }
18501  }
18502 
18503  /* We need a buffer list if this is an input device. We render into this in the input callback. */
18504  if (deviceType == ma_device_type_capture) {
18505  ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
18506  size_t allocationSize;
18507  AudioBufferList* pBufferList;
18508 
18509  allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
18510  if (isInterleaved) {
18511  /* Interleaved case. This is the simple case because we just have one buffer. */
18512  allocationSize += sizeof(AudioBuffer) * 1;
18513  allocationSize += actualBufferSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut);
18514  } else {
18515  /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
18516  allocationSize += sizeof(AudioBuffer) * pData->channelsOut;
18517  allocationSize += actualBufferSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * pData->channelsOut;
18518  }
18519 
18520  pBufferList = (AudioBufferList*)ma_malloc(allocationSize);
18521  if (pBufferList == NULL) {
18522  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18523  return MA_OUT_OF_MEMORY;
18524  }
18525 
18526  if (isInterleaved) {
18527  pBufferList->mNumberBuffers = 1;
18528  pBufferList->mBuffers[0].mNumberChannels = pData->channelsOut;
18529  pBufferList->mBuffers[0].mDataByteSize = actualBufferSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut);
18530  pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
18531  } else {
18532  ma_uint32 iBuffer;
18533  pBufferList->mNumberBuffers = pData->channelsOut;
18534  for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
18535  pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
18536  pBufferList->mBuffers[iBuffer].mDataByteSize = actualBufferSizeInFrames * ma_get_bytes_per_sample(pData->formatOut);
18537  pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * pData->channelsOut)) + (actualBufferSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * iBuffer);
18538  }
18539  }
18540 
18541  pData->pAudioBufferList = pBufferList;
18542  }
18543 
18544  /* Callbacks. */
18545  callbackInfo.inputProcRefCon = pDevice_DoNotReference;
18546  if (deviceType == ma_device_type_playback) {
18547  callbackInfo.inputProc = ma_on_output__coreaudio;
18548  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, MA_COREAUDIO_OUTPUT_BUS, &callbackInfo, sizeof(callbackInfo));
18549  if (status != noErr) {
18550  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18551  return ma_result_from_OSStatus(status);
18552  }
18553  } else {
18554  callbackInfo.inputProc = ma_on_input__coreaudio;
18555  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, MA_COREAUDIO_INPUT_BUS, &callbackInfo, sizeof(callbackInfo));
18556  if (status != noErr) {
18557  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18558  return ma_result_from_OSStatus(status);
18559  }
18560  }
18561 
18562  /* We need to listen for stop events. */
18563  if (pData->registerStopEvent) {
18564  status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
18565  if (status != noErr) {
18566  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18567  return ma_result_from_OSStatus(status);
18568  }
18569  }
18570 
18571  /* Initialize the audio unit. */
18572  status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
18573  if (status != noErr) {
18574  ma_free(pData->pAudioBufferList);
18575  pData->pAudioBufferList = NULL;
18576  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
18577  return ma_result_from_OSStatus(status);
18578  }
18579 
18580  /* Grab the name. */
18581 #if defined(MA_APPLE_DESKTOP)
18582  ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
18583 #else
18584  if (deviceType == ma_device_type_playback) {
18585  ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
18586  } else {
18587  ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
18588  }
18589 #endif
18590 
18591  return result;
18592 }
18593 
18594 ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
18595 {
18596  ma_device_init_internal_data__coreaudio data;
18597  ma_result result;
18598 
18599  /* This should only be called for playback or capture, not duplex. */
18600  if (deviceType == ma_device_type_duplex) {
18601  return MA_INVALID_ARGS;
18602  }
18603 
18604  if (deviceType == ma_device_type_capture) {
18605  data.formatIn = pDevice->capture.format;
18606  data.channelsIn = pDevice->capture.channels;
18607  data.sampleRateIn = pDevice->sampleRate;
18608  ma_copy_memory(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
18609  data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
18610  data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
18611  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
18612  data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
18613  data.shareMode = pDevice->capture.shareMode;
18614  data.registerStopEvent = MA_TRUE;
18615 
18616  if (disposePreviousAudioUnit) {
18617  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
18618  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
18619  }
18620  if (pDevice->coreaudio.pAudioBufferList) {
18621  ma_free(pDevice->coreaudio.pAudioBufferList);
18622  }
18623 
18624  #if defined(MA_APPLE_DESKTOP)
18625  pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
18626  #endif
18627  pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
18628  pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
18629  }
18630  if (deviceType == ma_device_type_playback) {
18631  data.formatIn = pDevice->playback.format;
18632  data.channelsIn = pDevice->playback.channels;
18633  data.sampleRateIn = pDevice->sampleRate;
18634  ma_copy_memory(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
18635  data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
18636  data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
18637  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
18638  data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
18639  data.shareMode = pDevice->playback.shareMode;
18640  data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
18641 
18642  if (disposePreviousAudioUnit) {
18643  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
18644  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
18645  }
18646 
18647  #if defined(MA_APPLE_DESKTOP)
18648  pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
18649  #endif
18650  pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
18651  }
18652  data.bufferSizeInFramesIn = pDevice->coreaudio.originalBufferSizeInFrames;
18653  data.bufferSizeInMillisecondsIn = pDevice->coreaudio.originalBufferSizeInMilliseconds;
18654  data.periodsIn = pDevice->coreaudio.originalPeriods;
18655 
18656  result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
18657  if (result != MA_SUCCESS) {
18658  return result;
18659  }
18660 
18661  return MA_SUCCESS;
18662 }
18663 
18664 
18665 ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
18666 {
18667  ma_result result;
18668 
18669  ma_assert(pContext != NULL);
18670  ma_assert(pConfig != NULL);
18671  ma_assert(pDevice != NULL);
18672 
18673  /* No exclusive mode with the Core Audio backend for now. */
18677  }
18678 
18679  /* Capture needs to be initialized first. */
18680  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
18681  ma_device_init_internal_data__coreaudio data;
18682  data.formatIn = pConfig->capture.format;
18683  data.channelsIn = pConfig->capture.channels;
18684  data.sampleRateIn = pConfig->sampleRate;
18685  ma_copy_memory(data.channelMapIn, pConfig->capture.channelMap, sizeof(pConfig->capture.channelMap));
18686  data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
18687  data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
18688  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
18689  data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
18690  data.shareMode = pConfig->capture.shareMode;
18691  data.bufferSizeInFramesIn = pConfig->bufferSizeInFrames;
18692  data.bufferSizeInMillisecondsIn = pConfig->bufferSizeInMilliseconds;
18693  data.registerStopEvent = MA_TRUE;
18694 
18695  result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pConfig->capture.pDeviceID, &data, (void*)pDevice);
18696  if (result != MA_SUCCESS) {
18697  return result;
18698  }
18699 
18700  pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
18701  #if defined(MA_APPLE_DESKTOP)
18702  pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
18703  #endif
18704  pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
18705  pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
18706 
18707  pDevice->capture.internalFormat = data.formatOut;
18708  pDevice->capture.internalChannels = data.channelsOut;
18709  pDevice->capture.internalSampleRate = data.sampleRateOut;
18710  ma_copy_memory(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
18711  pDevice->capture.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
18712  pDevice->capture.internalPeriods = data.periodsOut;
18713 
18714  /* TODO: This needs to be made global. */
18715  #if defined(MA_APPLE_DESKTOP)
18716  /*
18717  If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
18718  switch the device in the background.
18719  */
18720  if (pConfig->capture.pDeviceID == NULL) {
18721  AudioObjectPropertyAddress propAddress;
18722  propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
18723  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
18724  propAddress.mElement = kAudioObjectPropertyElementMaster;
18725  ((ma_AudioObjectAddPropertyListener_proc)pDevice->pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, pDevice);
18726  }
18727  #endif
18728  }
18729 
18730  /* Playback. */
18731  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
18732  ma_device_init_internal_data__coreaudio data;
18733  data.formatIn = pConfig->playback.format;
18734  data.channelsIn = pConfig->playback.channels;
18735  data.sampleRateIn = pConfig->sampleRate;
18736  ma_copy_memory(data.channelMapIn, pConfig->playback.channelMap, sizeof(pConfig->playback.channelMap));
18737  data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
18738  data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
18739  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
18740  data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
18741  data.shareMode = pConfig->playback.shareMode;
18742 
18743  /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
18744  if (pConfig->deviceType == ma_device_type_duplex) {
18745  data.bufferSizeInFramesIn = pDevice->capture.internalBufferSizeInFrames;
18746  data.periodsIn = pDevice->capture.internalPeriods;
18747  data.registerStopEvent = MA_FALSE;
18748  } else {
18749  data.bufferSizeInFramesIn = pConfig->bufferSizeInFrames;
18750  data.bufferSizeInMillisecondsIn = pConfig->bufferSizeInMilliseconds;
18751  data.periodsIn = pConfig->periods;
18752  data.registerStopEvent = MA_TRUE;
18753  }
18754 
18755  result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pConfig->playback.pDeviceID, &data, (void*)pDevice);
18756  if (result != MA_SUCCESS) {
18757  if (pConfig->deviceType == ma_device_type_duplex) {
18758  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
18759  if (pDevice->coreaudio.pAudioBufferList) {
18760  ma_free(pDevice->coreaudio.pAudioBufferList);
18761  }
18762  }
18763  return result;
18764  }
18765 
18766  pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
18767  #if defined(MA_APPLE_DESKTOP)
18768  pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
18769  #endif
18770  pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
18771 
18772  pDevice->playback.internalFormat = data.formatOut;
18773  pDevice->playback.internalChannels = data.channelsOut;
18774  pDevice->playback.internalSampleRate = data.sampleRateOut;
18775  ma_copy_memory(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
18776  pDevice->playback.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
18777  pDevice->playback.internalPeriods = data.periodsOut;
18778 
18779  /* TODO: This needs to be made global. */
18780  #if defined(MA_APPLE_DESKTOP)
18781  /*
18782  If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
18783  switch the device in the background.
18784  */
18785  if (pConfig->playback.pDeviceID == NULL) {
18786  AudioObjectPropertyAddress propAddress;
18787  propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
18788  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
18789  propAddress.mElement = kAudioObjectPropertyElementMaster;
18790  ((ma_AudioObjectAddPropertyListener_proc)pDevice->pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, pDevice);
18791  }
18792  #endif
18793  }
18794 
18795  pDevice->coreaudio.originalBufferSizeInFrames = pConfig->bufferSizeInFrames;
18796  pDevice->coreaudio.originalBufferSizeInMilliseconds = pConfig->bufferSizeInMilliseconds;
18797  pDevice->coreaudio.originalPeriods = pConfig->periods;
18798 
18799  /*
18800  When stopping the device, a callback is called on another thread. We need to wait for this callback
18801  before returning from ma_device_stop(). This event is used for this.
18802  */
18803  ma_event_init(pContext, &pDevice->coreaudio.stopEvent);
18804 
18805  /* Need a ring buffer for duplex mode. */
18806  if (pConfig->deviceType == ma_device_type_duplex) {
18807  ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalBufferSizeInFrames);
18808  ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->coreaudio.duplexRB);
18809  if (result != MA_SUCCESS) {
18810  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[Core Audio] Failed to initialize ring buffer.", result);
18811  }
18812  }
18813 
18814  return MA_SUCCESS;
18815 }
18816 
18817 
18818 ma_result ma_device_start__coreaudio(ma_device* pDevice)
18819 {
18820  ma_assert(pDevice != NULL);
18821 
18822  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18823  OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
18824  if (status != noErr) {
18825  return ma_result_from_OSStatus(status);
18826  }
18827  }
18828 
18829  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18830  OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
18831  if (status != noErr) {
18832  if (pDevice->type == ma_device_type_duplex) {
18833  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
18834  }
18835  return ma_result_from_OSStatus(status);
18836  }
18837  }
18838 
18839  return MA_SUCCESS;
18840 }
18841 
18842 ma_result ma_device_stop__coreaudio(ma_device* pDevice)
18843 {
18844  ma_assert(pDevice != NULL);
18845 
18846  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18847  OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
18848  if (status != noErr) {
18849  return ma_result_from_OSStatus(status);
18850  }
18851  }
18852 
18853  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18854  OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
18855  if (status != noErr) {
18856  return ma_result_from_OSStatus(status);
18857  }
18858  }
18859 
18860  /* We need to wait for the callback to finish before returning. */
18861  ma_event_wait(&pDevice->coreaudio.stopEvent);
18862  return MA_SUCCESS;
18863 }
18864 
18865 
18866 ma_result ma_context_uninit__coreaudio(ma_context* pContext)
18867 {
18868  ma_assert(pContext != NULL);
18869  ma_assert(pContext->backend == ma_backend_coreaudio);
18870 
18871 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
18872  ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
18873  ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
18874  ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
18875 #endif
18876 
18877  (void)pContext;
18878  return MA_SUCCESS;
18879 }
18880 
18881 ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma_context* pContext)
18882 {
18883  ma_assert(pContext != NULL);
18884 
18885  (void)pConfig;
18886 
18887 #if defined(MA_APPLE_MOBILE)
18888  @autoreleasepool {
18889  AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
18890  ma_assert(pAudioSession != NULL);
18891 
18892  [pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord error:nil];
18893 
18894  /* By default we want miniaudio to use the speakers instead of the receiver. In the future this may be customizable. */
18895  ma_bool32 useSpeakers = MA_TRUE;
18896  if (useSpeakers) {
18897  [pAudioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
18898  }
18899  }
18900 #endif
18901 
18902 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
18903  pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation");
18904  if (pContext->coreaudio.hCoreFoundation == NULL) {
18905  return MA_API_NOT_FOUND;
18906  }
18907 
18908  pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
18909 
18910 
18911  pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio");
18912  if (pContext->coreaudio.hCoreAudio == NULL) {
18913  ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
18914  return MA_API_NOT_FOUND;
18915  }
18916 
18917  pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
18918  pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
18919  pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
18920  pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
18921 
18922  /*
18923  It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
18924  defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
18925  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
18926  AudioToolbox.
18927  */
18928  pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit");
18929  if (pContext->coreaudio.hAudioUnit == NULL) {
18930  ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
18931  ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
18932  return MA_API_NOT_FOUND;
18933  }
18934 
18935  if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
18936  /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
18937  ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
18938  pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox");
18939  if (pContext->coreaudio.hAudioUnit == NULL) {
18940  ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
18941  ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
18942  return MA_API_NOT_FOUND;
18943  }
18944  }
18945 
18946  pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
18947  pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
18948  pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
18949  pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
18950  pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
18951  pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
18952  pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
18953  pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
18954  pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
18955  pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
18956  pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender");
18957 #else
18958  pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
18959 
18960  #if defined(MA_APPLE_DESKTOP)
18961  pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
18962  pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
18963  pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
18964  pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
18965  #endif
18966 
18967  pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
18968  pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
18969  pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
18970  pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
18971  pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
18972  pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
18973  pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
18974  pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
18975  pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
18976  pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
18977  pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
18978 #endif
18979 
18980  pContext->isBackendAsynchronous = MA_TRUE;
18981 
18982  pContext->onUninit = ma_context_uninit__coreaudio;
18983  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__coreaudio;
18984  pContext->onEnumDevices = ma_context_enumerate_devices__coreaudio;
18985  pContext->onGetDeviceInfo = ma_context_get_device_info__coreaudio;
18986  pContext->onDeviceInit = ma_device_init__coreaudio;
18987  pContext->onDeviceUninit = ma_device_uninit__coreaudio;
18988  pContext->onDeviceStart = ma_device_start__coreaudio;
18989  pContext->onDeviceStop = ma_device_stop__coreaudio;
18990 
18991  /* Audio component. */
18992  {
18993  AudioComponentDescription desc;
18994  desc.componentType = kAudioUnitType_Output;
18995  #if defined(MA_APPLE_DESKTOP)
18996  desc.componentSubType = kAudioUnitSubType_HALOutput;
18997  #else
18998  desc.componentSubType = kAudioUnitSubType_RemoteIO;
18999  #endif
19000  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
19001  desc.componentFlags = 0;
19002  desc.componentFlagsMask = 0;
19003 
19004  pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
19005  if (pContext->coreaudio.component == NULL) {
19006  #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
19007  ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
19008  ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
19009  ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
19010  #endif
19012  }
19013  }
19014 
19015  return MA_SUCCESS;
19016 }
19017 #endif /* Core Audio */
19018 
19019 
19020 
19021 /******************************************************************************
19022 
19023 sndio Backend
19024 
19025 ******************************************************************************/
19026 #ifdef MA_HAS_SNDIO
19027 #include <fcntl.h>
19028 #include <sys/stat.h>
19029 
19030 /*
19031 Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
19032 to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device
19033 just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
19034 demand for it or if I can get it tested and debugged more thoroughly.
19035 */
19036 #if 0
19037 #if defined(__NetBSD__) || defined(__OpenBSD__)
19038 #include <sys/audioio.h>
19039 #endif
19040 #if defined(__FreeBSD__) || defined(__DragonFly__)
19041 #include <sys/soundcard.h>
19042 #endif
19043 #endif
19044 
19045 #define MA_SIO_DEVANY "default"
19046 #define MA_SIO_PLAY 1
19047 #define MA_SIO_REC 2
19048 #define MA_SIO_NENC 8
19049 #define MA_SIO_NCHAN 8
19050 #define MA_SIO_NRATE 16
19051 #define MA_SIO_NCONF 4
19052 
19053 struct ma_sio_hdl; /* <-- Opaque */
19054 
19055 struct ma_sio_par
19056 {
19057  unsigned int bits;
19058  unsigned int bps;
19059  unsigned int sig;
19060  unsigned int le;
19061  unsigned int msb;
19062  unsigned int rchan;
19063  unsigned int pchan;
19064  unsigned int rate;
19065  unsigned int bufsz;
19066  unsigned int xrun;
19067  unsigned int round;
19068  unsigned int appbufsz;
19069  int __pad[3];
19070  unsigned int __magic;
19071 };
19072 
19073 struct ma_sio_enc
19074 {
19075  unsigned int bits;
19076  unsigned int bps;
19077  unsigned int sig;
19078  unsigned int le;
19079  unsigned int msb;
19080 };
19081 
19082 struct ma_sio_conf
19083 {
19084  unsigned int enc;
19085  unsigned int rchan;
19086  unsigned int pchan;
19087  unsigned int rate;
19088 };
19089 
19090 struct ma_sio_cap
19091 {
19092  struct ma_sio_enc enc[MA_SIO_NENC];
19093  unsigned int rchan[MA_SIO_NCHAN];
19094  unsigned int pchan[MA_SIO_NCHAN];
19095  unsigned int rate[MA_SIO_NRATE];
19096  int __pad[7];
19097  unsigned int nconf;
19098  struct ma_sio_conf confs[MA_SIO_NCONF];
19099 };
19100 
19101 typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
19102 typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
19103 typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
19104 typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
19105 typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
19106 typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
19107 typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
19108 typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
19109 typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
19110 typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
19111 
19112 ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
19113 {
19114  /* We only support native-endian right now. */
19115  if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
19116  return ma_format_unknown;
19117  }
19118 
19119  if (bits == 8 && bps == 1 && sig == 0) {
19120  return ma_format_u8;
19121  }
19122  if (bits == 16 && bps == 2 && sig == 1) {
19123  return ma_format_s16;
19124  }
19125  if (bits == 24 && bps == 3 && sig == 1) {
19126  return ma_format_s24;
19127  }
19128  if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
19129  /*return ma_format_s24_32;*/
19130  }
19131  if (bits == 32 && bps == 4 && sig == 1) {
19132  return ma_format_s32;
19133  }
19134 
19135  return ma_format_unknown;
19136 }
19137 
19138 ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
19139 {
19140  ma_format bestFormat;
19141  unsigned int iConfig;
19142 
19143  ma_assert(caps != NULL);
19144 
19145  bestFormat = ma_format_unknown;
19146  for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
19147  unsigned int iEncoding;
19148  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
19149  unsigned int bits;
19150  unsigned int bps;
19151  unsigned int sig;
19152  unsigned int le;
19153  unsigned int msb;
19154  ma_format format;
19155 
19156  if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
19157  continue;
19158  }
19159 
19160  bits = caps->enc[iEncoding].bits;
19161  bps = caps->enc[iEncoding].bps;
19162  sig = caps->enc[iEncoding].sig;
19163  le = caps->enc[iEncoding].le;
19164  msb = caps->enc[iEncoding].msb;
19165  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
19166  if (format == ma_format_unknown) {
19167  continue; /* Format not supported. */
19168  }
19169 
19170  if (bestFormat == ma_format_unknown) {
19171  bestFormat = format;
19172  } else {
19173  if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */
19174  bestFormat = format;
19175  }
19176  }
19177  }
19178  }
19179 
19180  return ma_format_unknown;
19181 }
19182 
19183 ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
19184 {
19185  ma_uint32 maxChannels;
19186  unsigned int iConfig;
19187 
19188  ma_assert(caps != NULL);
19189  ma_assert(requiredFormat != ma_format_unknown);
19190 
19191  /* Just pick whatever configuration has the most channels. */
19192  maxChannels = 0;
19193  for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
19194  /* The encoding should be of requiredFormat. */
19195  unsigned int iEncoding;
19196  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
19197  unsigned int iChannel;
19198  unsigned int bits;
19199  unsigned int bps;
19200  unsigned int sig;
19201  unsigned int le;
19202  unsigned int msb;
19203  ma_format format;
19204 
19205  if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
19206  continue;
19207  }
19208 
19209  bits = caps->enc[iEncoding].bits;
19210  bps = caps->enc[iEncoding].bps;
19211  sig = caps->enc[iEncoding].sig;
19212  le = caps->enc[iEncoding].le;
19213  msb = caps->enc[iEncoding].msb;
19214  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
19215  if (format != requiredFormat) {
19216  continue;
19217  }
19218 
19219  /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
19220  for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
19221  unsigned int chan = 0;
19222  unsigned int channels;
19223 
19224  if (deviceType == ma_device_type_playback) {
19225  chan = caps->confs[iConfig].pchan;
19226  } else {
19227  chan = caps->confs[iConfig].rchan;
19228  }
19229 
19230  if ((chan & (1UL << iChannel)) == 0) {
19231  continue;
19232  }
19233 
19234  if (deviceType == ma_device_type_playback) {
19235  channels = caps->pchan[iChannel];
19236  } else {
19237  channels = caps->rchan[iChannel];
19238  }
19239 
19240  if (maxChannels < channels) {
19241  maxChannels = channels;
19242  }
19243  }
19244  }
19245  }
19246 
19247  return maxChannels;
19248 }
19249 
19250 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)
19251 {
19252  ma_uint32 firstSampleRate;
19253  ma_uint32 bestSampleRate;
19254  unsigned int iConfig;
19255 
19256  ma_assert(caps != NULL);
19257  ma_assert(requiredFormat != ma_format_unknown);
19258  ma_assert(requiredChannels > 0);
19259  ma_assert(requiredChannels <= MA_MAX_CHANNELS);
19260 
19261  firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */
19262  bestSampleRate = 0;
19263 
19264  for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
19265  /* The encoding should be of requiredFormat. */
19266  unsigned int iEncoding;
19267  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
19268  unsigned int iChannel;
19269  unsigned int bits;
19270  unsigned int bps;
19271  unsigned int sig;
19272  unsigned int le;
19273  unsigned int msb;
19274  ma_format format;
19275 
19276  if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
19277  continue;
19278  }
19279 
19280  bits = caps->enc[iEncoding].bits;
19281  bps = caps->enc[iEncoding].bps;
19282  sig = caps->enc[iEncoding].sig;
19283  le = caps->enc[iEncoding].le;
19284  msb = caps->enc[iEncoding].msb;
19285  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
19286  if (format != requiredFormat) {
19287  continue;
19288  }
19289 
19290  /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
19291  for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
19292  unsigned int chan = 0;
19293  unsigned int channels;
19294  unsigned int iRate;
19295 
19296  if (deviceType == ma_device_type_playback) {
19297  chan = caps->confs[iConfig].pchan;
19298  } else {
19299  chan = caps->confs[iConfig].rchan;
19300  }
19301 
19302  if ((chan & (1UL << iChannel)) == 0) {
19303  continue;
19304  }
19305 
19306  if (deviceType == ma_device_type_playback) {
19307  channels = caps->pchan[iChannel];
19308  } else {
19309  channels = caps->rchan[iChannel];
19310  }
19311 
19312  if (channels != requiredChannels) {
19313  continue;
19314  }
19315 
19316  /* Getting here means we have found a compatible encoding/channel pair. */
19317  for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
19318  ma_uint32 rate = (ma_uint32)caps->rate[iRate];
19319  ma_uint32 ratePriority;
19320 
19321  if (firstSampleRate == 0) {
19322  firstSampleRate = rate;
19323  }
19324 
19325  /* Disregard this rate if it's not a standard one. */
19326  ratePriority = ma_get_standard_sample_rate_priority_index(rate);
19327  if (ratePriority == (ma_uint32)-1) {
19328  continue;
19329  }
19330 
19331  if (ma_get_standard_sample_rate_priority_index(bestSampleRate) > ratePriority) { /* Lower = better. */
19332  bestSampleRate = rate;
19333  }
19334  }
19335  }
19336  }
19337  }
19338 
19339  /* If a standard sample rate was not found just fall back to the first one that was iterated. */
19340  if (bestSampleRate == 0) {
19341  bestSampleRate = firstSampleRate;
19342  }
19343 
19344  return bestSampleRate;
19345 }
19346 
19347 
19348 ma_bool32 ma_context_is_device_id_equal__sndio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
19349 {
19350  ma_assert(pContext != NULL);
19351  ma_assert(pID0 != NULL);
19352  ma_assert(pID1 != NULL);
19353  (void)pContext;
19354 
19355  return ma_strcmp(pID0->sndio, pID1->sndio) == 0;
19356 }
19357 
19358 ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
19359 {
19360  ma_bool32 isTerminating = MA_FALSE;
19361  struct ma_sio_hdl* handle;
19362 
19363  ma_assert(pContext != NULL);
19364  ma_assert(callback != NULL);
19365 
19366  /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */
19367 
19368  /* Playback. */
19369  if (!isTerminating) {
19370  handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
19371  if (handle != NULL) {
19372  /* Supports playback. */
19373  ma_device_info deviceInfo;
19374  ma_zero_object(&deviceInfo);
19375  ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
19376  ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
19377 
19378  isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
19379 
19380  ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
19381  }
19382  }
19383 
19384  /* Capture. */
19385  if (!isTerminating) {
19386  handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
19387  if (handle != NULL) {
19388  /* Supports capture. */
19389  ma_device_info deviceInfo;
19390  ma_zero_object(&deviceInfo);
19391  ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
19392  ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
19393 
19394  isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
19395 
19396  ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
19397  }
19398  }
19399 
19400  return MA_SUCCESS;
19401 }
19402 
19403 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)
19404 {
19405  char devid[256];
19406  struct ma_sio_hdl* handle;
19407  struct ma_sio_cap caps;
19408  unsigned int iConfig;
19409 
19410  ma_assert(pContext != NULL);
19411  (void)shareMode;
19412 
19413  /* We need to open the device before we can get information about it. */
19414  if (pDeviceID == NULL) {
19415  ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
19416  ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
19417  } else {
19418  ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
19419  ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
19420  }
19421 
19422  handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
19423  if (handle == NULL) {
19424  return MA_NO_DEVICE;
19425  }
19426 
19427  if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
19428  return MA_ERROR;
19429  }
19430 
19431  for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
19432  /*
19433  The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give
19434  preference to some formats over others.
19435  */
19436  unsigned int iEncoding;
19437  unsigned int iChannel;
19438  unsigned int iRate;
19439 
19440  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
19441  unsigned int bits;
19442  unsigned int bps;
19443  unsigned int sig;
19444  unsigned int le;
19445  unsigned int msb;
19446  ma_format format;
19447  ma_bool32 formatExists = MA_FALSE;
19448  ma_uint32 iExistingFormat;
19449 
19450  if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
19451  continue;
19452  }
19453 
19454  bits = caps.enc[iEncoding].bits;
19455  bps = caps.enc[iEncoding].bps;
19456  sig = caps.enc[iEncoding].sig;
19457  le = caps.enc[iEncoding].le;
19458  msb = caps.enc[iEncoding].msb;
19459  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
19460  if (format == ma_format_unknown) {
19461  continue; /* Format not supported. */
19462  }
19463 
19464  /* Add this format if it doesn't already exist. */
19465  for (iExistingFormat = 0; iExistingFormat < pDeviceInfo->formatCount; iExistingFormat += 1) {
19466  if (pDeviceInfo->formats[iExistingFormat] == format) {
19467  formatExists = MA_TRUE;
19468  break;
19469  }
19470  }
19471 
19472  if (!formatExists) {
19473  pDeviceInfo->formats[pDeviceInfo->formatCount++] = format;
19474  }
19475  }
19476 
19477  /* Channels. */
19478  for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
19479  unsigned int chan = 0;
19480  unsigned int channels;
19481 
19482  if (deviceType == ma_device_type_playback) {
19483  chan = caps.confs[iConfig].pchan;
19484  } else {
19485  chan = caps.confs[iConfig].rchan;
19486  }
19487 
19488  if ((chan & (1UL << iChannel)) == 0) {
19489  continue;
19490  }
19491 
19492  if (deviceType == ma_device_type_playback) {
19493  channels = caps.pchan[iChannel];
19494  } else {
19495  channels = caps.rchan[iChannel];
19496  }
19497 
19498  if (pDeviceInfo->minChannels > channels) {
19499  pDeviceInfo->minChannels = channels;
19500  }
19501  if (pDeviceInfo->maxChannels < channels) {
19502  pDeviceInfo->maxChannels = channels;
19503  }
19504  }
19505 
19506  /* Sample rates. */
19507  for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
19508  if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
19509  unsigned int rate = caps.rate[iRate];
19510  if (pDeviceInfo->minSampleRate > rate) {
19511  pDeviceInfo->minSampleRate = rate;
19512  }
19513  if (pDeviceInfo->maxSampleRate < rate) {
19514  pDeviceInfo->maxSampleRate = rate;
19515  }
19516  }
19517  }
19518  }
19519 
19520  ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
19521  return MA_SUCCESS;
19522 }
19523 
19524 void ma_device_uninit__sndio(ma_device* pDevice)
19525 {
19526  ma_assert(pDevice != NULL);
19527 
19528  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19529  ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
19530  }
19531 
19532  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19533  ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
19534  }
19535 }
19536 
19537 ma_result ma_device_init_handle__sndio(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
19538 {
19539  const char* pDeviceName;
19540  ma_ptr handle;
19541  int openFlags = 0;
19542  struct ma_sio_cap caps;
19543  struct ma_sio_par par;
19544  ma_device_id* pDeviceID;
19545  ma_format format;
19547  ma_uint32 sampleRate;
19549  ma_uint32 internalChannels;
19550  ma_uint32 internalSampleRate;
19551  ma_uint32 internalBufferSizeInFrames;
19552  ma_uint32 internalPeriods;
19553 
19554  ma_assert(pContext != NULL);
19555  ma_assert(pConfig != NULL);
19556  ma_assert(deviceType != ma_device_type_duplex);
19557  ma_assert(pDevice != NULL);
19558 
19559  if (deviceType == ma_device_type_capture) {
19560  openFlags = MA_SIO_REC;
19561  pDeviceID = pConfig->capture.pDeviceID;
19562  format = pConfig->capture.format;
19563  channels = pConfig->capture.channels;
19564  sampleRate = pConfig->sampleRate;
19565  } else {
19566  openFlags = MA_SIO_PLAY;
19567  pDeviceID = pConfig->playback.pDeviceID;
19568  format = pConfig->playback.format;
19569  channels = pConfig->playback.channels;
19570  sampleRate = pConfig->sampleRate;
19571  }
19572 
19573  pDeviceName = MA_SIO_DEVANY;
19574  if (pDeviceID != NULL) {
19575  pDeviceName = pDeviceID->sndio;
19576  }
19577 
19578  handle = (ma_ptr)((ma_sio_open_proc)pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
19579  if (handle == NULL) {
19580  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
19581  }
19582 
19583  /* We need to retrieve the device caps to determine the most appropriate format to use. */
19584  if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
19585  ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
19586  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.", MA_ERROR);
19587  }
19588 
19589  /*
19590  Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
19591  way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
19592  to the requested channels, regardless of whether or not the default channel count is requested.
19593 
19594  For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
19595  value returned by ma_find_best_channels_from_sio_cap__sndio().
19596  */
19597  if (deviceType == ma_device_type_capture) {
19598  if (pDevice->capture.usingDefaultFormat) {
19599  format = ma_find_best_format_from_sio_cap__sndio(&caps);
19600  }
19601  if (pDevice->capture.usingDefaultChannels) {
19602  if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
19603  channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
19604  }
19605  }
19606  } else {
19607  if (pDevice->playback.usingDefaultFormat) {
19608  format = ma_find_best_format_from_sio_cap__sndio(&caps);
19609  }
19610  if (pDevice->playback.usingDefaultChannels) {
19611  if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
19612  channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
19613  }
19614  }
19615  }
19616 
19617  if (pDevice->usingDefaultSampleRate) {
19618  sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
19619  }
19620 
19621 
19622  ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
19623  par.msb = 0;
19624  par.le = ma_is_little_endian();
19625 
19626  switch (format) {
19627  case ma_format_u8:
19628  {
19629  par.bits = 8;
19630  par.bps = 1;
19631  par.sig = 0;
19632  } break;
19633 
19634  case ma_format_s24:
19635  {
19636  par.bits = 24;
19637  par.bps = 3;
19638  par.sig = 1;
19639  } break;
19640 
19641  case ma_format_s32:
19642  {
19643  par.bits = 32;
19644  par.bps = 4;
19645  par.sig = 1;
19646  } break;
19647 
19648  case ma_format_s16:
19649  case ma_format_f32:
19650  default:
19651  {
19652  par.bits = 16;
19653  par.bps = 2;
19654  par.sig = 1;
19655  } break;
19656  }
19657 
19658  if (deviceType == ma_device_type_capture) {
19659  par.rchan = channels;
19660  } else {
19661  par.pchan = channels;
19662  }
19663 
19664  par.rate = sampleRate;
19665 
19666  internalBufferSizeInFrames = pConfig->bufferSizeInFrames;
19667  if (internalBufferSizeInFrames == 0) {
19668  internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, par.rate);
19669  }
19670 
19671  par.round = internalBufferSizeInFrames / pConfig->periods;
19672  par.appbufsz = par.round * pConfig->periods;
19673 
19674  if (((ma_sio_setpar_proc)pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
19675  ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
19676  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MA_FORMAT_NOT_SUPPORTED);
19677  }
19678  if (((ma_sio_getpar_proc)pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
19679  ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
19680  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MA_FORMAT_NOT_SUPPORTED);
19681  }
19682 
19683  internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
19684  internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
19685  internalSampleRate = par.rate;
19686  internalPeriods = par.appbufsz / par.round;
19687  internalBufferSizeInFrames = par.appbufsz;
19688 
19689  if (deviceType == ma_device_type_capture) {
19690  pDevice->sndio.handleCapture = handle;
19691  pDevice->capture.internalFormat = internalFormat;
19692  pDevice->capture.internalChannels = internalChannels;
19693  pDevice->capture.internalSampleRate = internalSampleRate;
19694  ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
19695  pDevice->capture.internalBufferSizeInFrames = internalBufferSizeInFrames;
19696  pDevice->capture.internalPeriods = internalPeriods;
19697  } else {
19698  pDevice->sndio.handlePlayback = handle;
19699  pDevice->playback.internalFormat = internalFormat;
19700  pDevice->playback.internalChannels = internalChannels;
19701  pDevice->playback.internalSampleRate = internalSampleRate;
19702  ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
19703  pDevice->playback.internalBufferSizeInFrames = internalBufferSizeInFrames;
19704  pDevice->playback.internalPeriods = internalPeriods;
19705  }
19706 
19707 #ifdef MA_DEBUG_OUTPUT
19708  printf("DEVICE INFO\n");
19709  printf(" Format: %s\n", ma_get_format_name(internalFormat));
19710  printf(" Channels: %d\n", internalChannels);
19711  printf(" Sample Rate: %d\n", internalSampleRate);
19712  printf(" Buffer Size: %d\n", internalBufferSizeInFrames);
19713  printf(" Periods: %d\n", internalPeriods);
19714  printf(" appbufsz: %d\n", par.appbufsz);
19715  printf(" round: %d\n", par.round);
19716 #endif
19717 
19718  return MA_SUCCESS;
19719 }
19720 
19721 ma_result ma_device_init__sndio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
19722 {
19723  ma_assert(pDevice != NULL);
19724 
19725  ma_zero_object(&pDevice->sndio);
19726 
19727  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
19728  ma_result result = ma_device_init_handle__sndio(pContext, pConfig, ma_device_type_capture, pDevice);
19729  if (result != MA_SUCCESS) {
19730  return result;
19731  }
19732  }
19733 
19734  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
19735  ma_result result = ma_device_init_handle__sndio(pContext, pConfig, ma_device_type_playback, pDevice);
19736  if (result != MA_SUCCESS) {
19737  return result;
19738  }
19739  }
19740 
19741  return MA_SUCCESS;
19742 }
19743 
19744 ma_result ma_device_stop__sndio(ma_device* pDevice)
19745 {
19746  ma_assert(pDevice != NULL);
19747 
19748  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19749  ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
19750  ma_atomic_exchange_32(&pDevice->sndio.isStartedCapture, MA_FALSE);
19751  }
19752 
19753  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
19754  ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
19755  ma_atomic_exchange_32(&pDevice->sndio.isStartedPlayback, MA_FALSE);
19756  }
19757 
19758  return MA_SUCCESS;
19759 }
19760 
19761 ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount)
19762 {
19763  int result;
19764 
19765  if (!pDevice->sndio.isStartedPlayback) {
19766  ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */
19767  ma_atomic_exchange_32(&pDevice->sndio.isStartedPlayback, MA_TRUE);
19768  }
19769 
19770  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));
19771  if (result == 0) {
19772  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
19773  }
19774 
19775  return MA_SUCCESS;
19776 }
19777 
19778 ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount)
19779 {
19780  int result;
19781 
19782  if (!pDevice->sndio.isStartedCapture) {
19783  ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); /* <-- Doesn't actually playback until data is written. */
19784  ma_atomic_exchange_32(&pDevice->sndio.isStartedCapture, MA_TRUE);
19785  }
19786 
19787  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));
19788  if (result == 0) {
19789  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
19790  }
19791 
19792  return MA_SUCCESS;
19793 }
19794 
19795 ma_result ma_context_uninit__sndio(ma_context* pContext)
19796 {
19797  ma_assert(pContext != NULL);
19798  ma_assert(pContext->backend == ma_backend_sndio);
19799 
19800  (void)pContext;
19801  return MA_SUCCESS;
19802 }
19803 
19804 ma_result ma_context_init__sndio(const ma_context_config* pConfig, ma_context* pContext)
19805 {
19806 #ifndef MA_NO_RUNTIME_LINKING
19807  const char* libsndioNames[] = {
19808  "libsndio.so"
19809  };
19810  size_t i;
19811 
19812  for (i = 0; i < ma_countof(libsndioNames); ++i) {
19813  pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]);
19814  if (pContext->sndio.sndioSO != NULL) {
19815  break;
19816  }
19817  }
19818 
19819  if (pContext->sndio.sndioSO == NULL) {
19820  return MA_NO_BACKEND;
19821  }
19822 
19823  pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open");
19824  pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close");
19825  pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar");
19826  pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar");
19827  pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap");
19828  pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write");
19829  pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read");
19830  pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start");
19831  pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop");
19832  pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar");
19833 #else
19834  pContext->sndio.sio_open = sio_open;
19835  pContext->sndio.sio_close = sio_close;
19836  pContext->sndio.sio_setpar = sio_setpar;
19837  pContext->sndio.sio_getpar = sio_getpar;
19838  pContext->sndio.sio_getcap = sio_getcap;
19839  pContext->sndio.sio_write = sio_write;
19840  pContext->sndio.sio_read = sio_read;
19841  pContext->sndio.sio_start = sio_start;
19842  pContext->sndio.sio_stop = sio_stop;
19843  pContext->sndio.sio_initpar = sio_initpar;
19844 #endif
19845 
19846  pContext->onUninit = ma_context_uninit__sndio;
19847  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__sndio;
19848  pContext->onEnumDevices = ma_context_enumerate_devices__sndio;
19849  pContext->onGetDeviceInfo = ma_context_get_device_info__sndio;
19850  pContext->onDeviceInit = ma_device_init__sndio;
19851  pContext->onDeviceUninit = ma_device_uninit__sndio;
19852  pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
19853  pContext->onDeviceStop = ma_device_stop__sndio;
19854  pContext->onDeviceWrite = ma_device_write__sndio;
19855  pContext->onDeviceRead = ma_device_read__sndio;
19856 
19857  (void)pConfig;
19858  return MA_SUCCESS;
19859 }
19860 #endif /* sndio */
19861 
19862 
19863 
19864 /******************************************************************************
19865 
19866 audio(4) Backend
19867 
19868 ******************************************************************************/
19869 #ifdef MA_HAS_AUDIO4
19870 #include <fcntl.h>
19871 #include <poll.h>
19872 #include <errno.h>
19873 #include <sys/stat.h>
19874 #include <sys/types.h>
19875 #include <sys/ioctl.h>
19876 #include <sys/audioio.h>
19877 
19878 #if defined(__OpenBSD__)
19879  #include <sys/param.h>
19880  #if defined(OpenBSD) && OpenBSD >= 201709
19881  #define MA_AUDIO4_USE_NEW_API
19882  #endif
19883 #endif
19884 
19885 void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
19886 {
19887  size_t baseLen;
19888 
19889  ma_assert(id != NULL);
19890  ma_assert(idSize > 0);
19891  ma_assert(deviceIndex >= 0);
19892 
19893  baseLen = strlen(base);
19894  ma_assert(idSize > baseLen);
19895 
19896  ma_strcpy_s(id, idSize, base);
19897  ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
19898 }
19899 
19900 ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
19901 {
19902  size_t idLen;
19903  size_t baseLen;
19904  const char* deviceIndexStr;
19905 
19906  ma_assert(id != NULL);
19907  ma_assert(base != NULL);
19908  ma_assert(pIndexOut != NULL);
19909 
19910  idLen = strlen(id);
19911  baseLen = strlen(base);
19912  if (idLen <= baseLen) {
19913  return MA_ERROR; /* Doesn't look like the id starts with the base. */
19914  }
19915 
19916  if (strncmp(id, base, baseLen) != 0) {
19917  return MA_ERROR; /* ID does not begin with base. */
19918  }
19919 
19920  deviceIndexStr = id + baseLen;
19921  if (deviceIndexStr[0] == '\0') {
19922  return MA_ERROR; /* No index specified in the ID. */
19923  }
19924 
19925  if (pIndexOut) {
19926  *pIndexOut = atoi(deviceIndexStr);
19927  }
19928 
19929  return MA_SUCCESS;
19930 }
19931 
19932 ma_bool32 ma_context_is_device_id_equal__audio4(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
19933 {
19934  ma_assert(pContext != NULL);
19935  ma_assert(pID0 != NULL);
19936  ma_assert(pID1 != NULL);
19937  (void)pContext;
19938 
19939  return ma_strcmp(pID0->audio4, pID1->audio4) == 0;
19940 }
19941 
19942 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
19943 ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
19944 {
19945  if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
19946  return ma_format_u8;
19947  } else {
19948  if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
19949  if (precision == 16) {
19950  return ma_format_s16;
19951  } else if (precision == 24) {
19952  return ma_format_s24;
19953  } else if (precision == 32) {
19954  return ma_format_s32;
19955  }
19956  } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
19957  if (precision == 16) {
19958  return ma_format_s16;
19959  } else if (precision == 24) {
19960  return ma_format_s24;
19961  } else if (precision == 32) {
19962  return ma_format_s32;
19963  }
19964  }
19965  }
19966 
19967  return ma_format_unknown; /* Encoding not supported. */
19968 }
19969 
19970 void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
19971 {
19972  ma_assert(format != ma_format_unknown);
19973  ma_assert(pEncoding != NULL);
19974  ma_assert(pPrecision != NULL);
19975 
19976  switch (format)
19977  {
19978  case ma_format_u8:
19979  {
19980  *pEncoding = AUDIO_ENCODING_ULINEAR;
19981  *pPrecision = 8;
19982  } break;
19983 
19984  case ma_format_s24:
19985  {
19986  *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
19987  *pPrecision = 24;
19988  } break;
19989 
19990  case ma_format_s32:
19991  {
19992  *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
19993  *pPrecision = 32;
19994  } break;
19995 
19996  case ma_format_s16:
19997  case ma_format_f32:
19998  default:
19999  {
20000  *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
20001  *pPrecision = 16;
20002  } break;
20003  }
20004 }
20005 
20006 ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
20007 {
20008  return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
20009 }
20010 #else
20011 ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
20012 {
20013  if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
20014  return ma_format_u8;
20015  }
20016  if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
20017  return ma_format_s16;
20018  }
20019  if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
20020  return ma_format_s24;
20021  }
20022  if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
20023  return ma_format_f32;
20024  }
20025 
20026  /* Format not supported. */
20027  return ma_format_unknown;
20028 }
20029 #endif
20030 
20031 ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pInfoOut)
20032 {
20033  audio_device_t fdDevice;
20034 #if !defined(MA_AUDIO4_USE_NEW_API)
20035  int counter = 0;
20036  audio_info_t fdInfo;
20037 #else
20038  struct audio_swpar fdPar;
20039  ma_format format;
20040 #endif
20041 
20042  ma_assert(pContext != NULL);
20043  ma_assert(fd >= 0);
20044  ma_assert(pInfoOut != NULL);
20045 
20046  (void)pContext;
20047  (void)deviceType;
20048 
20049  if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
20050  return MA_ERROR; /* Failed to retrieve device info. */
20051  }
20052 
20053  /* Name. */
20054  ma_strcpy_s(pInfoOut->name, sizeof(pInfoOut->name), fdDevice.name);
20055 
20056 #if !defined(MA_AUDIO4_USE_NEW_API)
20057  /* Supported formats. We get this by looking at the encodings. */
20058  for (;;) {
20059  audio_encoding_t encoding;
20060  ma_format format;
20061 
20062  ma_zero_object(&encoding);
20063  encoding.index = counter;
20064  if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
20065  break;
20066  }
20067 
20068  format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
20069  if (format != ma_format_unknown) {
20070  pInfoOut->formats[pInfoOut->formatCount++] = format;
20071  }
20072 
20073  counter += 1;
20074  }
20075 
20076  if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
20077  return MA_ERROR;
20078  }
20079 
20080  if (deviceType == ma_device_type_playback) {
20081  pInfoOut->minChannels = fdInfo.play.channels;
20082  pInfoOut->maxChannels = fdInfo.play.channels;
20083  pInfoOut->minSampleRate = fdInfo.play.sample_rate;
20084  pInfoOut->maxSampleRate = fdInfo.play.sample_rate;
20085  } else {
20086  pInfoOut->minChannels = fdInfo.record.channels;
20087  pInfoOut->maxChannels = fdInfo.record.channels;
20088  pInfoOut->minSampleRate = fdInfo.record.sample_rate;
20089  pInfoOut->maxSampleRate = fdInfo.record.sample_rate;
20090  }
20091 #else
20092  if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
20093  return MA_ERROR;
20094  }
20095 
20096  format = ma_format_from_swpar__audio4(&fdPar);
20097  if (format == ma_format_unknown) {
20098  return MA_FORMAT_NOT_SUPPORTED;
20099  }
20100  pInfoOut->formats[pInfoOut->formatCount++] = format;
20101 
20102  if (deviceType == ma_device_type_playback) {
20103  pInfoOut->minChannels = fdPar.pchan;
20104  pInfoOut->maxChannels = fdPar.pchan;
20105  } else {
20106  pInfoOut->minChannels = fdPar.rchan;
20107  pInfoOut->maxChannels = fdPar.rchan;
20108  }
20109 
20110  pInfoOut->minSampleRate = fdPar.rate;
20111  pInfoOut->maxSampleRate = fdPar.rate;
20112 #endif
20113 
20114  return MA_SUCCESS;
20115 }
20116 
20117 ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
20118 {
20119  const int maxDevices = 64;
20120  char devpath[256];
20121  int iDevice;
20122 
20123  ma_assert(pContext != NULL);
20124  ma_assert(callback != NULL);
20125 
20126  /*
20127  Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
20128  version here since we can open it even when another process has control of the "/dev/audioN" device.
20129  */
20130  for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
20131  struct stat st;
20132  int fd;
20133  ma_bool32 isTerminating = MA_FALSE;
20134 
20135  ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
20136  ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
20137 
20138  if (stat(devpath, &st) < 0) {
20139  break;
20140  }
20141 
20142  /* The device exists, but we need to check if it's usable as playback and/or capture. */
20143 
20144  /* Playback. */
20145  if (!isTerminating) {
20146  fd = open(devpath, O_RDONLY, 0);
20147  if (fd >= 0) {
20148  /* Supports playback. */
20149  ma_device_info deviceInfo;
20150  ma_zero_object(&deviceInfo);
20151  ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
20152  if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
20153  isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
20154  }
20155 
20156  close(fd);
20157  }
20158  }
20159 
20160  /* Capture. */
20161  if (!isTerminating) {
20162  fd = open(devpath, O_WRONLY, 0);
20163  if (fd >= 0) {
20164  /* Supports capture. */
20165  ma_device_info deviceInfo;
20166  ma_zero_object(&deviceInfo);
20167  ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
20168  if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
20169  isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
20170  }
20171 
20172  close(fd);
20173  }
20174  }
20175 
20176  if (isTerminating) {
20177  break;
20178  }
20179  }
20180 
20181  return MA_SUCCESS;
20182 }
20183 
20184 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)
20185 {
20186  int fd = -1;
20187  int deviceIndex = -1;
20188  char ctlid[256];
20189  ma_result result;
20190 
20191  ma_assert(pContext != NULL);
20192  (void)shareMode;
20193 
20194  /*
20195  We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
20196  from the device ID which will be in "/dev/audioN" format.
20197  */
20198  if (pDeviceID == NULL) {
20199  /* Default device. */
20200  ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
20201  } else {
20202  /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */
20203  result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
20204  if (result != MA_SUCCESS) {
20205  return result;
20206  }
20207 
20208  ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
20209  }
20210 
20211  fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
20212  if (fd == -1) {
20213  return MA_NO_DEVICE;
20214  }
20215 
20216  if (deviceIndex == -1) {
20217  ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
20218  } else {
20219  ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
20220  }
20221 
20222  result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
20223 
20224  close(fd);
20225  return result;
20226 }
20227 
20228 void ma_device_uninit__audio4(ma_device* pDevice)
20229 {
20230  ma_assert(pDevice != NULL);
20231 
20232  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20233  close(pDevice->audio4.fdCapture);
20234  }
20235 
20236  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20237  close(pDevice->audio4.fdPlayback);
20238  }
20239 }
20240 
20241 ma_result ma_device_init_fd__audio4(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
20242 {
20243  const char* pDefaultDeviceNames[] = {
20244  "/dev/audio",
20245  "/dev/audio0"
20246  };
20247  int fd;
20248  int fdFlags = 0;
20249 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
20250  audio_info_t fdInfo;
20251 #else
20252  struct audio_swpar fdPar;
20253 #endif
20255  ma_uint32 internalChannels;
20256  ma_uint32 internalSampleRate;
20257  ma_uint32 internalBufferSizeInFrames;
20258  ma_uint32 internalPeriods;
20259 
20260  ma_assert(pContext != NULL);
20261  ma_assert(pConfig != NULL);
20262  ma_assert(deviceType != ma_device_type_duplex);
20263  ma_assert(pDevice != NULL);
20264 
20265  (void)pContext;
20266 
20267  /* The first thing to do is open the file. */
20268  if (deviceType == ma_device_type_capture) {
20269  fdFlags = O_RDONLY;
20270  } else {
20271  fdFlags = O_WRONLY;
20272  }
20273  fdFlags |= O_NONBLOCK;
20274 
20275  if ((deviceType == ma_device_type_capture && pConfig->capture.pDeviceID == NULL) || (deviceType == ma_device_type_playback && pConfig->playback.pDeviceID == NULL)) {
20276  /* Default device. */
20277  size_t iDevice;
20278  for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) {
20279  fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0);
20280  if (fd != -1) {
20281  break;
20282  }
20283  }
20284  } else {
20285  /* Specific device. */
20286  fd = open((deviceType == ma_device_type_capture) ? pConfig->capture.pDeviceID->audio4 : pConfig->playback.pDeviceID->audio4, fdFlags, 0);
20287  }
20288 
20289  if (fd == -1) {
20290  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
20291  }
20292 
20293 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
20294  AUDIO_INITINFO(&fdInfo);
20295 
20296  /* We get the driver to do as much of the data conversion as possible. */
20297  if (deviceType == ma_device_type_capture) {
20298  fdInfo.mode = AUMODE_RECORD;
20299  ma_encoding_from_format__audio4(pConfig->capture.format, &fdInfo.record.encoding, &fdInfo.record.precision);
20300  fdInfo.record.channels = pConfig->capture.channels;
20301  fdInfo.record.sample_rate = pConfig->sampleRate;
20302  } else {
20303  fdInfo.mode = AUMODE_PLAY;
20304  ma_encoding_from_format__audio4(pConfig->playback.format, &fdInfo.play.encoding, &fdInfo.play.precision);
20305  fdInfo.play.channels = pConfig->playback.channels;
20306  fdInfo.play.sample_rate = pConfig->sampleRate;
20307  }
20308 
20309  if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
20310  close(fd);
20311  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
20312  }
20313 
20314  if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
20315  close(fd);
20316  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
20317  }
20318 
20319  if (deviceType == ma_device_type_capture) {
20320  internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
20321  internalChannels = fdInfo.record.channels;
20322  internalSampleRate = fdInfo.record.sample_rate;
20323  } else {
20324  internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
20325  internalChannels = fdInfo.play.channels;
20326  internalSampleRate = fdInfo.play.sample_rate;
20327  }
20328 
20330  close(fd);
20331  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);
20332  }
20333 
20334  /* Buffer. */
20335  {
20336  ma_uint32 internalBufferSizeInBytes;
20337 
20338  internalBufferSizeInFrames = pConfig->bufferSizeInFrames;
20339  if (internalBufferSizeInFrames == 0) {
20340  internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, internalSampleRate);
20341  }
20342 
20343  internalBufferSizeInBytes = internalBufferSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
20344  if (internalBufferSizeInBytes < 16) {
20345  internalBufferSizeInBytes = 16;
20346  }
20347 
20348  internalPeriods = pConfig->periods;
20349  if (internalPeriods < 2) {
20350  internalPeriods = 2;
20351  }
20352 
20353  /* What miniaudio calls a fragment, audio4 calls a block. */
20354  AUDIO_INITINFO(&fdInfo);
20355  fdInfo.hiwat = internalPeriods;
20356  fdInfo.lowat = internalPeriods-1;
20357  fdInfo.blocksize = internalBufferSizeInBytes / internalPeriods;
20358  if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
20359  close(fd);
20360  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
20361  }
20362 
20363  internalPeriods = fdInfo.hiwat;
20364  internalBufferSizeInFrames = (fdInfo.blocksize * fdInfo.hiwat) / ma_get_bytes_per_frame(internalFormat, internalChannels);
20365  }
20366 #else
20367  /* 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. */
20368  if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
20369  close(fd);
20370  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.", MA_FORMAT_NOT_SUPPORTED);
20371  }
20372 
20373  internalFormat = ma_format_from_swpar__audio4(&fdPar);
20374  internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
20375  internalSampleRate = fdPar.rate;
20376 
20378  close(fd);
20379  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);
20380  }
20381 
20382  /* Buffer. */
20383  {
20384  ma_uint32 internalBufferSizeInBytes;
20385 
20386  internalBufferSizeInFrames = pConfig->bufferSizeInFrames;
20387  if (internalBufferSizeInFrames == 0) {
20388  internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, internalSampleRate);
20389  }
20390 
20391  /* What miniaudio calls a fragment, audio4 calls a block. */
20392  internalBufferSizeInBytes = internalBufferSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
20393  if (internalBufferSizeInBytes < 16) {
20394  internalBufferSizeInBytes = 16;
20395  }
20396 
20397  fdPar.nblks = pConfig->periods;
20398  fdPar.round = internalBufferSizeInBytes / fdPar.nblks;
20399 
20400  if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
20401  close(fd);
20402  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.", MA_FORMAT_NOT_SUPPORTED);
20403  }
20404 
20405  if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
20406  close(fd);
20407  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.", MA_FORMAT_NOT_SUPPORTED);
20408  }
20409  }
20410 
20411  internalFormat = ma_format_from_swpar__audio4(&fdPar);
20412  internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
20413  internalSampleRate = fdPar.rate;
20414  internalPeriods = fdPar.nblks;
20415  internalBufferSizeInFrames = (fdPar.nblks * fdPar.round) / ma_get_bytes_per_frame(internalFormat, internalChannels);
20416 #endif
20417 
20419  close(fd);
20420  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);
20421  }
20422 
20423  if (deviceType == ma_device_type_capture) {
20424  pDevice->audio4.fdCapture = fd;
20425  pDevice->capture.internalFormat = internalFormat;
20426  pDevice->capture.internalChannels = internalChannels;
20427  pDevice->capture.internalSampleRate = internalSampleRate;
20428  ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDevice->capture.internalChannelMap);
20429  pDevice->capture.internalBufferSizeInFrames = internalBufferSizeInFrames;
20430  pDevice->capture.internalPeriods = internalPeriods;
20431  } else {
20432  pDevice->audio4.fdPlayback = fd;
20433  pDevice->playback.internalFormat = internalFormat;
20434  pDevice->playback.internalChannels = internalChannels;
20435  pDevice->playback.internalSampleRate = internalSampleRate;
20436  ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDevice->playback.internalChannelMap);
20437  pDevice->playback.internalBufferSizeInFrames = internalBufferSizeInFrames;
20438  pDevice->playback.internalPeriods = internalPeriods;
20439  }
20440 
20441  return MA_SUCCESS;
20442 }
20443 
20444 ma_result ma_device_init__audio4(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
20445 {
20446  ma_assert(pDevice != NULL);
20447 
20448  ma_zero_object(&pDevice->audio4);
20449 
20450  pDevice->audio4.fdCapture = -1;
20451  pDevice->audio4.fdPlayback = -1;
20452 
20453  /*
20454  The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD
20455  introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as
20456  I'm aware.
20457  */
20458 #if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
20459  /* NetBSD 8.0+ */
20463  }
20464 #else
20465  /* All other flavors. */
20466 #endif
20467 
20468  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
20469  ma_result result = ma_device_init_fd__audio4(pContext, pConfig, ma_device_type_capture, pDevice);
20470  if (result != MA_SUCCESS) {
20471  return result;
20472  }
20473  }
20474 
20475  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
20476  ma_result result = ma_device_init_fd__audio4(pContext, pConfig, ma_device_type_playback, pDevice);
20477  if (result != MA_SUCCESS) {
20478  if (pConfig->deviceType == ma_device_type_duplex) {
20479  close(pDevice->audio4.fdCapture);
20480  }
20481  return result;
20482  }
20483  }
20484 
20485  return MA_SUCCESS;
20486 }
20487 
20488 #if 0
20489 ma_result ma_device_start__audio4(ma_device* pDevice)
20490 {
20491  ma_assert(pDevice != NULL);
20492 
20493  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20494  if (pDevice->audio4.fdCapture == -1) {
20495  return MA_INVALID_ARGS;
20496  }
20497  }
20498 
20499  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20500  if (pDevice->audio4.fdPlayback == -1) {
20501  return MA_INVALID_ARGS;
20502  }
20503  }
20504 
20505  return MA_SUCCESS;
20506 }
20507 #endif
20508 
20509 ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
20510 {
20511  if (fd == -1) {
20512  return MA_INVALID_ARGS;
20513  }
20514 
20515 #if !defined(MA_AUDIO4_USE_NEW_API)
20516  if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
20517  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
20518  }
20519 #else
20520  if (ioctl(fd, AUDIO_STOP, 0) < 0) {
20521  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
20522  }
20523 #endif
20524 
20525  return MA_SUCCESS;
20526 }
20527 
20528 ma_result ma_device_stop__audio4(ma_device* pDevice)
20529 {
20530  ma_assert(pDevice != NULL);
20531 
20532  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20533  ma_result result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
20534  if (result != MA_SUCCESS) {
20535  return result;
20536  }
20537  }
20538 
20539  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20540  ma_result result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
20541  if (result != MA_SUCCESS) {
20542  return result;
20543  }
20544  }
20545 
20546  return MA_SUCCESS;
20547 }
20548 
20549 ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount)
20550 {
20551  int result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
20552  if (result < 0) {
20553  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
20554  }
20555 
20556  return MA_SUCCESS;
20557 }
20558 
20559 ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount)
20560 {
20561  int result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
20562  if (result < 0) {
20563  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
20564  }
20565 
20566  return MA_SUCCESS;
20567 }
20568 
20569 ma_result ma_context_uninit__audio4(ma_context* pContext)
20570 {
20571  ma_assert(pContext != NULL);
20572  ma_assert(pContext->backend == ma_backend_audio4);
20573 
20574  (void)pContext;
20575  return MA_SUCCESS;
20576 }
20577 
20578 ma_result ma_context_init__audio4(const ma_context_config* pConfig, ma_context* pContext)
20579 {
20580  ma_assert(pContext != NULL);
20581 
20582  (void)pConfig;
20583 
20584  pContext->onUninit = ma_context_uninit__audio4;
20585  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__audio4;
20586  pContext->onEnumDevices = ma_context_enumerate_devices__audio4;
20587  pContext->onGetDeviceInfo = ma_context_get_device_info__audio4;
20588  pContext->onDeviceInit = ma_device_init__audio4;
20589  pContext->onDeviceUninit = ma_device_uninit__audio4;
20590  pContext->onDeviceStart = NULL;
20591  pContext->onDeviceStop = ma_device_stop__audio4;
20592  pContext->onDeviceWrite = ma_device_write__audio4;
20593  pContext->onDeviceRead = ma_device_read__audio4;
20594 
20595  return MA_SUCCESS;
20596 }
20597 #endif /* audio4 */
20598 
20599 
20600 /******************************************************************************
20601 
20602 OSS Backend
20603 
20604 ******************************************************************************/
20605 #ifdef MA_HAS_OSS
20606 #include <sys/ioctl.h>
20607 #include <unistd.h>
20608 #include <fcntl.h>
20609 #include <sys/soundcard.h>
20610 
20611 #ifndef SNDCTL_DSP_HALT
20612 #define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
20613 #endif
20614 
20615 int ma_open_temp_device__oss()
20616 {
20617  /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */
20618  int fd = open("/dev/mixer", O_RDONLY, 0);
20619  if (fd >= 0) {
20620  return fd;
20621  }
20622 
20623  return -1;
20624 }
20625 
20626 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)
20627 {
20628  const char* deviceName;
20629  int flags;
20630 
20631  ma_assert(pContext != NULL);
20632  ma_assert(pfd != NULL);
20633  (void)pContext;
20634 
20635  *pfd = -1;
20636 
20637  /* This function should only be called for playback or capture, not duplex. */
20638  if (deviceType == ma_device_type_duplex) {
20639  return MA_INVALID_ARGS;
20640  }
20641 
20642  deviceName = "/dev/dsp";
20643  if (pDeviceID != NULL) {
20644  deviceName = pDeviceID->oss;
20645  }
20646 
20647  flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
20648  if (shareMode == ma_share_mode_exclusive) {
20649  flags |= O_EXCL;
20650  }
20651 
20652  *pfd = open(deviceName, flags, 0);
20653  if (*pfd == -1) {
20655  }
20656 
20657  return MA_SUCCESS;
20658 }
20659 
20660 ma_bool32 ma_context_is_device_id_equal__oss(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
20661 {
20662  ma_assert(pContext != NULL);
20663  ma_assert(pID0 != NULL);
20664  ma_assert(pID1 != NULL);
20665  (void)pContext;
20666 
20667  return ma_strcmp(pID0->oss, pID1->oss) == 0;
20668 }
20669 
20670 ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
20671 {
20672  int fd;
20673  oss_sysinfo si;
20674  int result;
20675 
20676  ma_assert(pContext != NULL);
20677  ma_assert(callback != NULL);
20678 
20679  fd = ma_open_temp_device__oss();
20680  if (fd == -1) {
20681  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);
20682  }
20683 
20684  result = ioctl(fd, SNDCTL_SYSINFO, &si);
20685  if (result != -1) {
20686  int iAudioDevice;
20687  for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
20688  oss_audioinfo ai;
20689  ai.dev = iAudioDevice;
20690  result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
20691  if (result != -1) {
20692  if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
20693  ma_device_info deviceInfo;
20694  ma_bool32 isTerminating = MA_FALSE;
20695 
20696  ma_zero_object(&deviceInfo);
20697 
20698  /* ID */
20699  ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
20700 
20701  /*
20702  The human readable device name should be in the "ai.handle" variable, but it can
20703  sometimes be empty in which case we just fall back to "ai.name" which is less user
20704  friendly, but usually has a value.
20705  */
20706  if (ai.handle[0] != '\0') {
20707  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
20708  } else {
20709  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
20710  }
20711 
20712  /* The device can be both playback and capture. */
20713  if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
20714  isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
20715  }
20716  if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
20717  isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
20718  }
20719 
20720  if (isTerminating) {
20721  break;
20722  }
20723  }
20724  }
20725  }
20726  } else {
20727  close(fd);
20728  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
20729  }
20730 
20731  close(fd);
20732  return MA_SUCCESS;
20733 }
20734 
20735 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)
20736 {
20737  ma_bool32 foundDevice;
20738  int fdTemp;
20739  oss_sysinfo si;
20740  int result;
20741 
20742  ma_assert(pContext != NULL);
20743  (void)shareMode;
20744 
20745  /* Handle the default device a little differently. */
20746  if (pDeviceID == NULL) {
20747  if (deviceType == ma_device_type_playback) {
20748  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
20749  } else {
20750  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
20751  }
20752 
20753  return MA_SUCCESS;
20754  }
20755 
20756 
20757  /* If we get here it means we are _not_ using the default device. */
20758  foundDevice = MA_FALSE;
20759 
20760  fdTemp = ma_open_temp_device__oss();
20761  if (fdTemp == -1) {
20762  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);
20763  }
20764 
20765  result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
20766  if (result != -1) {
20767  int iAudioDevice;
20768  for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
20769  oss_audioinfo ai;
20770  ai.dev = iAudioDevice;
20771  result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
20772  if (result != -1) {
20773  if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
20774  /* It has the same name, so now just confirm the type. */
20775  if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
20776  (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
20777  unsigned int formatMask;
20778 
20779  /* ID */
20780  ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
20781 
20782  /*
20783  The human readable device name should be in the "ai.handle" variable, but it can
20784  sometimes be empty in which case we just fall back to "ai.name" which is less user
20785  friendly, but usually has a value.
20786  */
20787  if (ai.handle[0] != '\0') {
20788  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
20789  } else {
20790  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
20791  }
20792 
20793  pDeviceInfo->minChannels = ai.min_channels;
20794  pDeviceInfo->maxChannels = ai.max_channels;
20795  pDeviceInfo->minSampleRate = ai.min_rate;
20796  pDeviceInfo->maxSampleRate = ai.max_rate;
20797  pDeviceInfo->formatCount = 0;
20798 
20799  if (deviceType == ma_device_type_playback) {
20800  formatMask = ai.oformats;
20801  } else {
20802  formatMask = ai.iformats;
20803  }
20804 
20805  if ((formatMask & AFMT_U8) != 0) {
20806  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_u8;
20807  }
20808  if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
20809  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s16;
20810  }
20811  if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
20812  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s32;
20813  }
20814 
20815  foundDevice = MA_TRUE;
20816  break;
20817  }
20818  }
20819  }
20820  }
20821  } else {
20822  close(fdTemp);
20823  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
20824  }
20825 
20826 
20827  close(fdTemp);
20828 
20829  if (!foundDevice) {
20830  return MA_NO_DEVICE;
20831  }
20832 
20833  return MA_SUCCESS;
20834 }
20835 
20836 void ma_device_uninit__oss(ma_device* pDevice)
20837 {
20838  ma_assert(pDevice != NULL);
20839 
20840  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20841  close(pDevice->oss.fdCapture);
20842  }
20843 
20844  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20845  close(pDevice->oss.fdPlayback);
20846  }
20847 }
20848 
20849 int ma_format_to_oss(ma_format format)
20850 {
20851  int ossFormat = AFMT_U8;
20852  switch (format) {
20853  case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
20854  case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
20855  case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
20856  case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
20857  case ma_format_u8:
20858  default: ossFormat = AFMT_U8; break;
20859  }
20860 
20861  return ossFormat;
20862 }
20863 
20864 ma_format ma_format_from_oss(int ossFormat)
20865 {
20866  if (ossFormat == AFMT_U8) {
20867  return ma_format_u8;
20868  } else {
20869  if (ma_is_little_endian()) {
20870  switch (ossFormat) {
20871  case AFMT_S16_LE: return ma_format_s16;
20872  case AFMT_S32_LE: return ma_format_s32;
20873  default: return ma_format_unknown;
20874  }
20875  } else {
20876  switch (ossFormat) {
20877  case AFMT_S16_BE: return ma_format_s16;
20878  case AFMT_S32_BE: return ma_format_s32;
20879  default: return ma_format_unknown;
20880  }
20881  }
20882  }
20883 
20884  return ma_format_unknown;
20885 }
20886 
20887 ma_result ma_device_init_fd__oss(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
20888 {
20889  ma_result result;
20890  int ossResult;
20891  int fd;
20892  const ma_device_id* pDeviceID = NULL;
20893  ma_share_mode shareMode;
20894  int ossFormat;
20895  int ossChannels;
20896  int ossSampleRate;
20897  int ossFragment;
20898 
20899  ma_assert(pContext != NULL);
20900  ma_assert(pConfig != NULL);
20901  ma_assert(deviceType != ma_device_type_duplex);
20902  ma_assert(pDevice != NULL);
20903 
20904  (void)pContext;
20905 
20906  if (deviceType == ma_device_type_capture) {
20907  pDeviceID = pConfig->capture.pDeviceID;
20908  shareMode = pConfig->capture.shareMode;
20909  ossFormat = ma_format_to_oss(pConfig->capture.format);
20910  ossChannels = (int)pConfig->capture.channels;
20911  ossSampleRate = (int)pConfig->sampleRate;
20912  } else {
20913  pDeviceID = pConfig->playback.pDeviceID;
20914  shareMode = pConfig->playback.shareMode;
20915  ossFormat = ma_format_to_oss(pConfig->playback.format);
20916  ossChannels = (int)pConfig->playback.channels;
20917  ossSampleRate = (int)pConfig->sampleRate;
20918  }
20919 
20920  result = ma_context_open_device__oss(pContext, deviceType, pDeviceID, shareMode, &fd);
20921  if (result != MA_SUCCESS) {
20922  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
20923  }
20924 
20925  /*
20926  The OSS documantation is very clear about the order we should be initializing the device's properties:
20927  1) Format
20928  2) Channels
20929  3) Sample rate.
20930  */
20931 
20932  /* Format. */
20933  ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
20934  if (ossResult == -1) {
20935  close(fd);
20936  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MA_FORMAT_NOT_SUPPORTED);
20937  }
20938 
20939  /* Channels. */
20940  ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
20941  if (ossResult == -1) {
20942  close(fd);
20943  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.", MA_FORMAT_NOT_SUPPORTED);
20944  }
20945 
20946  /* Sample Rate. */
20947  ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
20948  if (ossResult == -1) {
20949  close(fd);
20950  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.", MA_FORMAT_NOT_SUPPORTED);
20951  }
20952 
20953  /*
20954  Buffer.
20955 
20956  The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
20957  it should be done before or after format/channels/rate.
20958 
20959  OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
20960  value.
20961  */
20962  {
20963  ma_uint32 fragmentSizeInBytes;
20964  ma_uint32 bufferSizeInFrames;
20965  ma_uint32 ossFragmentSizePower;
20966 
20967  bufferSizeInFrames = pConfig->bufferSizeInFrames;
20968  if (bufferSizeInFrames == 0) {
20969  bufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, (ma_uint32)ossSampleRate);
20970  }
20971 
20972  fragmentSizeInBytes = ma_round_to_power_of_2((bufferSizeInFrames / pConfig->periods) * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
20973  if (fragmentSizeInBytes < 16) {
20974  fragmentSizeInBytes = 16;
20975  }
20976 
20977  ossFragmentSizePower = 4;
20978  fragmentSizeInBytes >>= 4;
20979  while (fragmentSizeInBytes >>= 1) {
20980  ossFragmentSizePower += 1;
20981  }
20982 
20983  ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
20984  ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
20985  if (ossResult == -1) {
20986  close(fd);
20987  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.", MA_FORMAT_NOT_SUPPORTED);
20988  }
20989  }
20990 
20991  /* Internal settings. */
20992  if (deviceType == ma_device_type_capture) {
20993  pDevice->oss.fdCapture = fd;
20994  pDevice->capture.internalFormat = ma_format_from_oss(ossFormat);
20995  pDevice->capture.internalChannels = ossChannels;
20996  pDevice->capture.internalSampleRate = ossSampleRate;
20997  ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
20998  pDevice->capture.internalPeriods = (ma_uint32)(ossFragment >> 16);
20999  pDevice->capture.internalBufferSizeInFrames = (((ma_uint32)(1 << (ossFragment & 0xFFFF))) * pDevice->capture.internalPeriods) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
21000 
21001  if (pDevice->capture.internalFormat == ma_format_unknown) {
21002  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
21003  }
21004  } else {
21005  pDevice->oss.fdPlayback = fd;
21006  pDevice->playback.internalFormat = ma_format_from_oss(ossFormat);
21007  pDevice->playback.internalChannels = ossChannels;
21008  pDevice->playback.internalSampleRate = ossSampleRate;
21009  ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
21010  pDevice->playback.internalPeriods = (ma_uint32)(ossFragment >> 16);
21011  pDevice->playback.internalBufferSizeInFrames = (((ma_uint32)(1 << (ossFragment & 0xFFFF))) * pDevice->playback.internalPeriods) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
21012 
21013  if (pDevice->playback.internalFormat == ma_format_unknown) {
21014  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
21015  }
21016  }
21017 
21018  return MA_SUCCESS;
21019 }
21020 
21021 ma_result ma_device_init__oss(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
21022 {
21023  ma_assert(pContext != NULL);
21024  ma_assert(pConfig != NULL);
21025  ma_assert(pDevice != NULL);
21026 
21027  ma_zero_object(&pDevice->oss);
21028 
21029  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
21030  ma_result result = ma_device_init_fd__oss(pContext, pConfig, ma_device_type_capture, pDevice);
21031  if (result != MA_SUCCESS) {
21032  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
21033  }
21034  }
21035 
21036  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
21037  ma_result result = ma_device_init_fd__oss(pContext, pConfig, ma_device_type_playback, pDevice);
21038  if (result != MA_SUCCESS) {
21039  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
21040  }
21041  }
21042 
21043  return MA_SUCCESS;
21044 }
21045 
21046 ma_result ma_device_stop__oss(ma_device* pDevice)
21047 {
21048  ma_assert(pDevice != NULL);
21049 
21050  /*
21051  We want to use SNDCTL_DSP_HALT. From the documentation:
21052 
21053  In multithreaded applications SNDCTL_DSP_HALT (SNDCTL_DSP_RESET) must only be called by the thread
21054  that actually reads/writes the audio device. It must not be called by some master thread to kill the
21055  audio thread. The audio thread will not stop or get any kind of notification that the device was
21056  stopped by the master thread. The device gets stopped but the next read or write call will silently
21057  restart the device.
21058 
21059  This is actually safe in our case, because this function is only ever called from within our worker
21060  thread anyway. Just keep this in mind, though...
21061  */
21062 
21063  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21064  int result = ioctl(pDevice->oss.fdCapture, SNDCTL_DSP_HALT, 0);
21065  if (result == -1) {
21066  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
21067  }
21068  }
21069 
21070  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21071  int result = ioctl(pDevice->oss.fdPlayback, SNDCTL_DSP_HALT, 0);
21072  if (result == -1) {
21073  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
21074  }
21075  }
21076 
21077  return MA_SUCCESS;
21078 }
21079 
21080 ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount)
21081 {
21082  int resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
21083  if (resultOSS < 0) {
21084  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
21085  }
21086 
21087  return MA_SUCCESS;
21088 }
21089 
21090 ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount)
21091 {
21092  int resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
21093  if (resultOSS < 0) {
21094  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
21095  }
21096 
21097  return MA_SUCCESS;
21098 }
21099 
21100 ma_result ma_context_uninit__oss(ma_context* pContext)
21101 {
21102  ma_assert(pContext != NULL);
21103  ma_assert(pContext->backend == ma_backend_oss);
21104 
21105  (void)pContext;
21106  return MA_SUCCESS;
21107 }
21108 
21109 ma_result ma_context_init__oss(const ma_context_config* pConfig, ma_context* pContext)
21110 {
21111  int fd;
21112  int ossVersion;
21113  int result;
21114 
21115  ma_assert(pContext != NULL);
21116 
21117  (void)pConfig;
21118 
21119  /* Try opening a temporary device first so we can get version information. This is closed at the end. */
21120  fd = ma_open_temp_device__oss();
21121  if (fd == -1) {
21122  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. */
21123  }
21124 
21125  /* Grab the OSS version. */
21126  ossVersion = 0;
21127  result = ioctl(fd, OSS_GETVERSION, &ossVersion);
21128  if (result == -1) {
21129  close(fd);
21130  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.", MA_NO_BACKEND);
21131  }
21132 
21133  pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
21134  pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
21135 
21136  pContext->onUninit = ma_context_uninit__oss;
21137  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__oss;
21138  pContext->onEnumDevices = ma_context_enumerate_devices__oss;
21139  pContext->onGetDeviceInfo = ma_context_get_device_info__oss;
21140  pContext->onDeviceInit = ma_device_init__oss;
21141  pContext->onDeviceUninit = ma_device_uninit__oss;
21142  pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
21143  pContext->onDeviceStop = ma_device_stop__oss;
21144  pContext->onDeviceWrite = ma_device_write__oss;
21145  pContext->onDeviceRead = ma_device_read__oss;
21146 
21147  close(fd);
21148  return MA_SUCCESS;
21149 }
21150 #endif /* OSS */
21151 
21152 
21153 /******************************************************************************
21154 
21155 AAudio Backend
21156 
21157 ******************************************************************************/
21158 #ifdef MA_HAS_AAUDIO
21159 /*#include <AAudio/AAudio.h>*/
21160 
21161 #define MA_AAUDIO_UNSPECIFIED 0
21162 
21163 typedef int32_t ma_aaudio_result_t;
21164 typedef int32_t ma_aaudio_direction_t;
21165 typedef int32_t ma_aaudio_sharing_mode_t;
21166 typedef int32_t ma_aaudio_format_t;
21167 typedef int32_t ma_aaudio_stream_state_t;
21168 typedef int32_t ma_aaudio_performance_mode_t;
21169 typedef int32_t ma_aaudio_data_callback_result_t;
21170 
21171 /* Result codes. miniaudio only cares about the success code. */
21172 #define MA_AAUDIO_OK 0
21173 
21174 /* Directions. */
21175 #define MA_AAUDIO_DIRECTION_OUTPUT 0
21176 #define MA_AAUDIO_DIRECTION_INPUT 1
21177 
21178 /* Sharing modes. */
21179 #define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
21180 #define MA_AAUDIO_SHARING_MODE_SHARED 1
21181 
21182 /* Formats. */
21183 #define MA_AAUDIO_FORMAT_PCM_I16 1
21184 #define MA_AAUDIO_FORMAT_PCM_FLOAT 2
21185 
21186 /* Stream states. */
21187 #define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
21188 #define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
21189 #define MA_AAUDIO_STREAM_STATE_OPEN 2
21190 #define MA_AAUDIO_STREAM_STATE_STARTING 3
21191 #define MA_AAUDIO_STREAM_STATE_STARTED 4
21192 #define MA_AAUDIO_STREAM_STATE_PAUSING 5
21193 #define MA_AAUDIO_STREAM_STATE_PAUSED 6
21194 #define MA_AAUDIO_STREAM_STATE_FLUSHING 7
21195 #define MA_AAUDIO_STREAM_STATE_FLUSHED 8
21196 #define MA_AAUDIO_STREAM_STATE_STOPPING 9
21197 #define MA_AAUDIO_STREAM_STATE_STOPPED 10
21198 #define MA_AAUDIO_STREAM_STATE_CLOSING 11
21199 #define MA_AAUDIO_STREAM_STATE_CLOSED 12
21200 #define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
21201 
21202 /* Performance modes. */
21203 #define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
21204 #define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
21205 #define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
21206 
21207 /* Callback results. */
21208 #define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
21209 #define MA_AAUDIO_CALLBACK_RESULT_STOP 1
21210 
21211 /* Objects. */
21212 typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
21213 typedef struct ma_AAudioStream_t* ma_AAudioStream;
21214 
21215 typedef ma_aaudio_data_callback_result_t (*ma_AAudioStream_dataCallback)(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
21216 
21217 typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
21218 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
21219 typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
21220 typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
21221 typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
21222 typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
21223 typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
21224 typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
21225 typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
21226 typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
21227 typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
21228 typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
21229 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
21230 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
21231 typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
21232 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);
21233 typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
21234 typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
21235 typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
21236 typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
21237 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
21238 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
21239 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
21240 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
21241 
21242 ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
21243 {
21244  switch (resultAA)
21245  {
21246  case MA_AAUDIO_OK: return MA_SUCCESS;
21247  default: break;
21248  }
21249 
21250  return MA_ERROR;
21251 }
21252 
21253 ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
21254 {
21255  ma_device* pDevice = (ma_device*)pUserData;
21256  ma_assert(pDevice != NULL);
21257 
21258  if (pDevice->type == ma_device_type_duplex) {
21259  ma_device__handle_duplex_callback_capture(pDevice, frameCount, pAudioData, &pDevice->aaudio.duplexRB);
21260  } else {
21261  ma_device__send_frames_to_client(pDevice, frameCount, pAudioData); /* Send directly to the client. */
21262  }
21263 
21264  (void)pStream;
21265  return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
21266 }
21267 
21268 ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
21269 {
21270  ma_device* pDevice = (ma_device*)pUserData;
21271  ma_assert(pDevice != NULL);
21272 
21273  if (pDevice->type == ma_device_type_duplex) {
21274  ma_device__handle_duplex_callback_playback(pDevice, frameCount, pAudioData, &pDevice->aaudio.duplexRB);
21275  } else {
21276  ma_device__read_frames_from_client(pDevice, frameCount, pAudioData); /* Read directly from the client. */
21277  }
21278 
21279  (void)pStream;
21280  return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
21281 }
21282 
21283 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)
21284 {
21285  ma_AAudioStreamBuilder* pBuilder;
21286  ma_aaudio_result_t resultAA;
21287 
21288  ma_assert(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */
21289 
21290  *ppStream = NULL;
21291 
21292  resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
21293  if (resultAA != MA_AAUDIO_OK) {
21294  return ma_result_from_aaudio(resultAA);
21295  }
21296 
21297  if (pDeviceID != NULL) {
21298  ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
21299  }
21300 
21301  ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
21302  ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
21303 
21304  if (pConfig != NULL) {
21305  ma_uint32 bufferCapacityInFrames;
21306 
21307  if (pDevice == NULL || !pDevice->usingDefaultSampleRate) {
21308  ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pConfig->sampleRate);
21309  }
21310 
21311  if (deviceType == ma_device_type_capture) {
21312  if (pDevice == NULL || !pDevice->capture.usingDefaultChannels) {
21313  ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->capture.channels);
21314  }
21315  if (pDevice == NULL || !pDevice->capture.usingDefaultFormat) {
21316  ((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);
21317  }
21318  } else {
21319  if (pDevice == NULL || !pDevice->playback.usingDefaultChannels) {
21320  ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->playback.channels);
21321  }
21322  if (pDevice == NULL || !pDevice->playback.usingDefaultFormat) {
21323  ((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);
21324  }
21325  }
21326 
21327  bufferCapacityInFrames = pConfig->bufferSizeInFrames;
21328  if (bufferCapacityInFrames == 0) {
21330  }
21331  ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
21332 
21333  ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pConfig->periods);
21334 
21335  if (deviceType == ma_device_type_capture) {
21336  ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
21337  } else {
21338  ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
21339  }
21340 
21341  /* 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. */
21342  ((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);
21343  }
21344 
21345  resultAA = ((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream);
21346  if (resultAA != MA_AAUDIO_OK) {
21347  *ppStream = NULL;
21348  ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
21349  return ma_result_from_aaudio(resultAA);
21350  }
21351 
21352  ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
21353  return MA_SUCCESS;
21354 }
21355 
21356 ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
21357 {
21358  return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
21359 }
21360 
21361 ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
21362 {
21363  /* The only way to know this is to try creating a stream. */
21364  ma_AAudioStream* pStream;
21365  ma_result result = ma_open_stream__aaudio(pContext, deviceType, NULL, ma_share_mode_shared, NULL, NULL, &pStream);
21366  if (result != MA_SUCCESS) {
21367  return MA_FALSE;
21368  }
21369 
21370  ma_close_stream__aaudio(pContext, pStream);
21371  return MA_TRUE;
21372 }
21373 
21374 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)
21375 {
21376  ma_aaudio_stream_state_t actualNewState;
21377  ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
21378  if (resultAA != MA_AAUDIO_OK) {
21379  return ma_result_from_aaudio(resultAA);
21380  }
21381 
21382  if (newState != actualNewState) {
21383  return MA_ERROR; /* Failed to transition into the expected state. */
21384  }
21385 
21386  return MA_SUCCESS;
21387 }
21388 
21389 
21390 ma_bool32 ma_context_is_device_id_equal__aaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
21391 {
21392  ma_assert(pContext != NULL);
21393  ma_assert(pID0 != NULL);
21394  ma_assert(pID1 != NULL);
21395  (void)pContext;
21396 
21397  return pID0->aaudio == pID1->aaudio;
21398 }
21399 
21400 ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
21401 {
21402  ma_bool32 cbResult = MA_TRUE;
21403 
21404  ma_assert(pContext != NULL);
21405  ma_assert(callback != NULL);
21406 
21407  /* 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. */
21408 
21409  /* Playback. */
21410  if (cbResult) {
21411  ma_device_info deviceInfo;
21412  ma_zero_object(&deviceInfo);
21413  deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
21414  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
21415 
21416  if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
21417  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
21418  }
21419  }
21420 
21421  /* Capture. */
21422  if (cbResult) {
21423  ma_device_info deviceInfo;
21424  ma_zero_object(&deviceInfo);
21425  deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
21426  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
21427 
21428  if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
21429  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
21430  }
21431  }
21432 
21433  return MA_SUCCESS;
21434 }
21435 
21436 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)
21437 {
21438  ma_AAudioStream* pStream;
21439  ma_result result;
21440 
21441  ma_assert(pContext != NULL);
21442 
21443  /* No exclusive mode with AAudio. */
21444  if (shareMode == ma_share_mode_exclusive) {
21446  }
21447 
21448  /* ID */
21449  if (pDeviceID != NULL) {
21450  pDeviceInfo->id.aaudio = pDeviceID->aaudio;
21451  } else {
21452  pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
21453  }
21454 
21455  /* Name */
21456  if (deviceType == ma_device_type_playback) {
21457  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
21458  } else {
21459  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
21460  }
21461 
21462 
21463  /* We'll need to open the device to get accurate sample rate and channel count information. */
21464  result = ma_open_stream__aaudio(pContext, deviceType, pDeviceID, shareMode, NULL, NULL, &pStream);
21465  if (result != MA_SUCCESS) {
21466  return result;
21467  }
21468 
21469  pDeviceInfo->minChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
21470  pDeviceInfo->maxChannels = pDeviceInfo->minChannels;
21471  pDeviceInfo->minSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
21472  pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
21473 
21474  ma_close_stream__aaudio(pContext, pStream);
21475  pStream = NULL;
21476 
21477 
21478  /* AAudio supports s16 and f32. */
21479  pDeviceInfo->formatCount = 2;
21480  pDeviceInfo->formats[0] = ma_format_s16;
21481  pDeviceInfo->formats[1] = ma_format_f32;
21482 
21483  return MA_SUCCESS;
21484 }
21485 
21486 
21487 void ma_device_uninit__aaudio(ma_device* pDevice)
21488 {
21489  ma_assert(pDevice != NULL);
21490 
21491  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21492  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
21493  pDevice->aaudio.pStreamCapture = NULL;
21494  }
21495 
21496  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21497  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
21498  pDevice->aaudio.pStreamPlayback = NULL;
21499  }
21500 
21501  if (pDevice->type == ma_device_type_duplex) {
21502  ma_pcm_rb_uninit(&pDevice->aaudio.duplexRB);
21503  }
21504 }
21505 
21506 ma_result ma_device_init__aaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
21507 {
21508  ma_result result;
21509 
21510  ma_assert(pDevice != NULL);
21511 
21512  /* No exclusive mode with AAudio. */
21516  }
21517 
21518  /* We first need to try opening the stream. */
21519  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
21520  int32_t framesPerPeriod;
21521 
21522  result = ma_open_stream__aaudio(pContext, ma_device_type_capture, pConfig->capture.pDeviceID, pConfig->capture.shareMode, pConfig, pDevice, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
21523  if (result != MA_SUCCESS) {
21524  return result; /* Failed to open the AAudio stream. */
21525  }
21526 
21527  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;
21528  pDevice->capture.internalChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
21529  pDevice->capture.internalSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
21530  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. */
21531  pDevice->capture.internalBufferSizeInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pContext->aaudio.AAudioStream_getBufferCapacityInFrames)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
21532 
21533  /*
21534  TODO: When synchronous reading and writing is supported, use AAudioStream_getFramesPerBurst() instead of AAudioStream_getFramesPerDataCallback(). Keep
21535  using AAudioStream_getFramesPerDataCallback() for asynchronous mode, though.
21536  */
21537  framesPerPeriod = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pContext->aaudio.AAudioStream_getFramesPerDataCallback)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
21538  if (framesPerPeriod > 0) {
21539  pDevice->capture.internalPeriods = 1;
21540  } else {
21541  pDevice->capture.internalPeriods = pDevice->capture.internalBufferSizeInFrames / framesPerPeriod;
21542  }
21543  }
21544 
21545  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
21546  int32_t framesPerPeriod;
21547 
21548  result = ma_open_stream__aaudio(pContext, ma_device_type_playback, pConfig->playback.pDeviceID, pConfig->playback.shareMode, pConfig, pDevice, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
21549  if (result != MA_SUCCESS) {
21550  return result; /* Failed to open the AAudio stream. */
21551  }
21552 
21553  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;
21554  pDevice->playback.internalChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
21555  pDevice->playback.internalSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
21556  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. */
21557  pDevice->playback.internalBufferSizeInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pContext->aaudio.AAudioStream_getBufferCapacityInFrames)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
21558 
21559  framesPerPeriod = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pContext->aaudio.AAudioStream_getFramesPerDataCallback)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
21560  if (framesPerPeriod > 0) {
21561  pDevice->playback.internalPeriods = 1;
21562  } else {
21563  pDevice->playback.internalPeriods = pDevice->playback.internalBufferSizeInFrames / framesPerPeriod;
21564  }
21565  }
21566 
21567  if (pConfig->deviceType == ma_device_type_duplex) {
21568  ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalBufferSizeInFrames);
21569  ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->aaudio.duplexRB);
21570  if (result != MA_SUCCESS) {
21571  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21572  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
21573  }
21574  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21575  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
21576  }
21577  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[AAudio] Failed to initialize ring buffer.", result);
21578  }
21579  }
21580 
21581  return MA_SUCCESS;
21582 }
21583 
21584 ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
21585 {
21586  ma_aaudio_result_t resultAA;
21587  ma_aaudio_stream_state_t currentState;
21588 
21589  ma_assert(pDevice != NULL);
21590 
21591  resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
21592  if (resultAA != MA_AAUDIO_OK) {
21593  return ma_result_from_aaudio(resultAA);
21594  }
21595 
21596  /* Do we actually need to wait for the device to transition into it's started state? */
21597 
21598  /* 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. */
21599  currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
21600  if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
21601  ma_result result;
21602 
21603  if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
21604  return MA_ERROR; /* Expecting the stream to be a starting or started state. */
21605  }
21606 
21607  result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
21608  if (result != MA_SUCCESS) {
21609  return result;
21610  }
21611  }
21612 
21613  return MA_SUCCESS;
21614 }
21615 
21616 ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
21617 {
21618  ma_aaudio_result_t resultAA;
21619  ma_aaudio_stream_state_t currentState;
21620 
21621  ma_assert(pDevice != NULL);
21622 
21623  resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
21624  if (resultAA != MA_AAUDIO_OK) {
21625  return ma_result_from_aaudio(resultAA);
21626  }
21627 
21628  /* 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. */
21629  currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
21630  if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
21631  ma_result result;
21632 
21633  if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
21634  return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */
21635  }
21636 
21637  result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
21638  if (result != MA_SUCCESS) {
21639  return result;
21640  }
21641  }
21642 
21643  return MA_SUCCESS;
21644 }
21645 
21646 ma_result ma_device_start__aaudio(ma_device* pDevice)
21647 {
21648  ma_assert(pDevice != NULL);
21649 
21650  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21651  ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
21652  if (result != MA_SUCCESS) {
21653  return result;
21654  }
21655  }
21656 
21657  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21658  ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
21659  if (result != MA_SUCCESS) {
21660  if (pDevice->type == ma_device_type_duplex) {
21661  ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
21662  }
21663  return result;
21664  }
21665  }
21666 
21667  return MA_SUCCESS;
21668 }
21669 
21670 ma_result ma_device_stop__aaudio(ma_device* pDevice)
21671 {
21672  ma_stop_proc onStop;
21673 
21674  ma_assert(pDevice != NULL);
21675 
21676  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21677  ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
21678  if (result != MA_SUCCESS) {
21679  return result;
21680  }
21681  }
21682 
21683  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21684  ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
21685  if (result != MA_SUCCESS) {
21686  return result;
21687  }
21688  }
21689 
21690  onStop = pDevice->onStop;
21691  if (onStop) {
21692  onStop(pDevice);
21693  }
21694 
21695  return MA_SUCCESS;
21696 }
21697 
21698 
21699 ma_result ma_context_uninit__aaudio(ma_context* pContext)
21700 {
21701  ma_assert(pContext != NULL);
21702  ma_assert(pContext->backend == ma_backend_aaudio);
21703 
21704  ma_dlclose(pContext, pContext->aaudio.hAAudio);
21705  pContext->aaudio.hAAudio = NULL;
21706 
21707  return MA_SUCCESS;
21708 }
21709 
21710 ma_result ma_context_init__aaudio(const ma_context_config* pConfig, ma_context* pContext)
21711 {
21712  const char* libNames[] = {
21713  "libaaudio.so"
21714  };
21715  size_t i;
21716 
21717  for (i = 0; i < ma_countof(libNames); ++i) {
21718  pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]);
21719  if (pContext->aaudio.hAAudio != NULL) {
21720  break;
21721  }
21722  }
21723 
21724  if (pContext->aaudio.hAAudio == NULL) {
21726  }
21727 
21728  pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
21729  pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
21730  pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
21731  pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
21732  pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
21733  pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
21734  pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
21735  pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
21736  pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
21737  pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
21738  pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
21739  pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
21740  pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
21741  pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close");
21742  pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState");
21743  pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
21744  pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat");
21745  pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
21746  pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
21747  pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
21748  pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
21749  pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
21750  pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart");
21751  pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop");
21752 
21753  pContext->isBackendAsynchronous = MA_TRUE;
21754 
21755  pContext->onUninit = ma_context_uninit__aaudio;
21756  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__aaudio;
21757  pContext->onEnumDevices = ma_context_enumerate_devices__aaudio;
21758  pContext->onGetDeviceInfo = ma_context_get_device_info__aaudio;
21759  pContext->onDeviceInit = ma_device_init__aaudio;
21760  pContext->onDeviceUninit = ma_device_uninit__aaudio;
21761  pContext->onDeviceStart = ma_device_start__aaudio;
21762  pContext->onDeviceStop = ma_device_stop__aaudio;
21763 
21764  (void)pConfig;
21765  return MA_SUCCESS;
21766 }
21767 #endif /* AAudio */
21768 
21769 
21770 /******************************************************************************
21771 
21772 OpenSL|ES Backend
21773 
21774 ******************************************************************************/
21775 #ifdef MA_HAS_OPENSL
21776 #include <SLES/OpenSLES.h>
21777 #ifdef MA_ANDROID
21778 #include <SLES/OpenSLES_Android.h>
21779 #endif
21780 
21781 /* OpenSL|ES has one-per-application objects :( */
21782 SLObjectItf g_maEngineObjectSL = NULL;
21783 SLEngineItf g_maEngineSL = NULL;
21784 ma_uint32 g_maOpenSLInitCounter = 0;
21785 
21786 #define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
21787 #define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
21788 #define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
21789 #define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
21790 
21791 #ifdef MA_ANDROID
21792 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
21793 #else
21794 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
21795 #endif
21796 
21797 /* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
21798 ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
21799 {
21800  switch (id)
21801  {
21802  case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
21803  case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
21804  case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
21805  case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
21806  case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
21807  case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
21808  case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
21809  case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
21810  case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
21811  case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
21812  case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
21813  case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
21814  case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
21815  case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
21816  case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
21817  case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
21818  case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
21819  case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
21820  default: return 0;
21821  }
21822 }
21823 
21824 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */
21825 SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
21826 {
21827  switch (id)
21828  {
21829  case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
21830  case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
21831  case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
21832  case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
21833  case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
21834  case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
21835  case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
21836  case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
21837  case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
21838  case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
21839  case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
21840  case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
21841  case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
21842  case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
21843  case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
21844  case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
21845  case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
21846  case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
21847  case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
21848  default: return 0;
21849  }
21850 }
21851 
21852 /* Converts a channel mapping to an OpenSL-style channel mask. */
21853 SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel channelMap[MA_MAX_CHANNELS], ma_uint32 channels)
21854 {
21855  SLuint32 channelMask = 0;
21856  ma_uint32 iChannel;
21857  for (iChannel = 0; iChannel < channels; ++iChannel) {
21858  channelMask |= ma_channel_id_to_opensl(channelMap[iChannel]);
21859  }
21860 
21861  return channelMask;
21862 }
21863 
21864 /* Converts an OpenSL-style channel mask to a miniaudio channel map. */
21865 void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
21866 {
21867  if (channels == 1 && channelMask == 0) {
21868  channelMap[0] = MA_CHANNEL_MONO;
21869  } else if (channels == 2 && channelMask == 0) {
21870  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
21871  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
21872  } else {
21873  if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
21874  channelMap[0] = MA_CHANNEL_MONO;
21875  } else {
21876  /* Just iterate over each bit. */
21877  ma_uint32 iChannel = 0;
21878  ma_uint32 iBit;
21879  for (iBit = 0; iBit < 32; ++iBit) {
21880  SLuint32 bitValue = (channelMask & (1UL << iBit));
21881  if (bitValue != 0) {
21882  /* The bit is set. */
21883  channelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
21884  iChannel += 1;
21885  }
21886  }
21887  }
21888  }
21889 }
21890 
21891 SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
21892 {
21893  if (samplesPerSec <= SL_SAMPLINGRATE_8) {
21894  return SL_SAMPLINGRATE_8;
21895  }
21896  if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
21897  return SL_SAMPLINGRATE_11_025;
21898  }
21899  if (samplesPerSec <= SL_SAMPLINGRATE_12) {
21900  return SL_SAMPLINGRATE_12;
21901  }
21902  if (samplesPerSec <= SL_SAMPLINGRATE_16) {
21903  return SL_SAMPLINGRATE_16;
21904  }
21905  if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
21906  return SL_SAMPLINGRATE_22_05;
21907  }
21908  if (samplesPerSec <= SL_SAMPLINGRATE_24) {
21909  return SL_SAMPLINGRATE_24;
21910  }
21911  if (samplesPerSec <= SL_SAMPLINGRATE_32) {
21912  return SL_SAMPLINGRATE_32;
21913  }
21914  if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
21915  return SL_SAMPLINGRATE_44_1;
21916  }
21917  if (samplesPerSec <= SL_SAMPLINGRATE_48) {
21918  return SL_SAMPLINGRATE_48;
21919  }
21920 
21921  /* Android doesn't support more than 48000. */
21922 #ifndef MA_ANDROID
21923  if (samplesPerSec <= SL_SAMPLINGRATE_64) {
21924  return SL_SAMPLINGRATE_64;
21925  }
21926  if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
21927  return SL_SAMPLINGRATE_88_2;
21928  }
21929  if (samplesPerSec <= SL_SAMPLINGRATE_96) {
21930  return SL_SAMPLINGRATE_96;
21931  }
21932  if (samplesPerSec <= SL_SAMPLINGRATE_192) {
21933  return SL_SAMPLINGRATE_192;
21934  }
21935 #endif
21936 
21937  return SL_SAMPLINGRATE_16;
21938 }
21939 
21940 
21941 ma_bool32 ma_context_is_device_id_equal__opensl(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
21942 {
21943  ma_assert(pContext != NULL);
21944  ma_assert(pID0 != NULL);
21945  ma_assert(pID1 != NULL);
21946  (void)pContext;
21947 
21948  return pID0->opensl == pID1->opensl;
21949 }
21950 
21951 ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
21952 {
21953  ma_bool32 cbResult;
21954 
21955  ma_assert(pContext != NULL);
21956  ma_assert(callback != NULL);
21957 
21958  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. */
21959  if (g_maOpenSLInitCounter == 0) {
21960  return MA_INVALID_OPERATION;
21961  }
21962 
21963  /*
21964  TODO: Test Me.
21965 
21966  This is currently untested, so for now we are just returning default devices.
21967  */
21968 #if 0 && !defined(MA_ANDROID)
21969  ma_bool32 isTerminated = MA_FALSE;
21970 
21971  SLuint32 pDeviceIDs[128];
21972  SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
21973 
21974  SLAudioIODeviceCapabilitiesItf deviceCaps;
21975  SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
21976  if (resultSL != SL_RESULT_SUCCESS) {
21977  /* The interface may not be supported so just report a default device. */
21978  goto return_default_device;
21979  }
21980 
21981  /* Playback */
21982  if (!isTerminated) {
21983  resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
21984  if (resultSL != SL_RESULT_SUCCESS) {
21985  return MA_NO_DEVICE;
21986  }
21987 
21988  for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
21989  ma_device_info deviceInfo;
21990  ma_zero_object(&deviceInfo);
21991  deviceInfo.id.opensl = pDeviceIDs[iDevice];
21992 
21993  SLAudioOutputDescriptor desc;
21994  resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
21995  if (resultSL == SL_RESULT_SUCCESS) {
21996  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
21997 
21998  ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
21999  if (cbResult == MA_FALSE) {
22000  isTerminated = MA_TRUE;
22001  break;
22002  }
22003  }
22004  }
22005  }
22006 
22007  /* Capture */
22008  if (!isTerminated) {
22009  resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
22010  if (resultSL != SL_RESULT_SUCCESS) {
22011  return MA_NO_DEVICE;
22012  }
22013 
22014  for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
22015  ma_device_info deviceInfo;
22016  ma_zero_object(&deviceInfo);
22017  deviceInfo.id.opensl = pDeviceIDs[iDevice];
22018 
22019  SLAudioInputDescriptor desc;
22020  resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
22021  if (resultSL == SL_RESULT_SUCCESS) {
22022  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
22023 
22024  ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
22025  if (cbResult == MA_FALSE) {
22026  isTerminated = MA_TRUE;
22027  break;
22028  }
22029  }
22030  }
22031  }
22032 
22033  return MA_SUCCESS;
22034 #else
22035  goto return_default_device;
22036 #endif
22037 
22038 return_default_device:;
22039  cbResult = MA_TRUE;
22040 
22041  /* Playback. */
22042  if (cbResult) {
22043  ma_device_info deviceInfo;
22044  ma_zero_object(&deviceInfo);
22045  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
22046  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
22047  }
22048 
22049  /* Capture. */
22050  if (cbResult) {
22051  ma_device_info deviceInfo;
22052  ma_zero_object(&deviceInfo);
22053  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
22054  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
22055  }
22056 
22057  return MA_SUCCESS;
22058 }
22059 
22060 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)
22061 {
22062  ma_assert(pContext != NULL);
22063 
22064  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. */
22065  if (g_maOpenSLInitCounter == 0) {
22066  return MA_INVALID_OPERATION;
22067  }
22068 
22069  /* No exclusive mode with OpenSL|ES. */
22070  if (shareMode == ma_share_mode_exclusive) {
22072  }
22073 
22074  /*
22075  TODO: Test Me.
22076 
22077  This is currently untested, so for now we are just returning default devices.
22078  */
22079 #if 0 && !defined(MA_ANDROID)
22080  SLAudioIODeviceCapabilitiesItf deviceCaps;
22081  SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
22082  if (resultSL != SL_RESULT_SUCCESS) {
22083  /* The interface may not be supported so just report a default device. */
22084  goto return_default_device;
22085  }
22086 
22087  if (deviceType == ma_device_type_playback) {
22088  SLAudioOutputDescriptor desc;
22089  resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
22090  if (resultSL != SL_RESULT_SUCCESS) {
22091  return MA_NO_DEVICE;
22092  }
22093 
22094  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
22095  } else {
22096  SLAudioInputDescriptor desc;
22097  resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
22098  if (resultSL != SL_RESULT_SUCCESS) {
22099  return MA_NO_DEVICE;
22100  }
22101 
22102  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
22103  }
22104 
22105  goto return_detailed_info;
22106 #else
22107  goto return_default_device;
22108 #endif
22109 
22110 return_default_device:
22111  if (pDeviceID != NULL) {
22112  if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
22113  (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
22114  return MA_NO_DEVICE; /* Don't know the device. */
22115  }
22116  }
22117 
22118  /* Name / Description */
22119  if (deviceType == ma_device_type_playback) {
22120  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
22121  } else {
22122  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
22123  }
22124 
22125  goto return_detailed_info;
22126 
22127 
22128 return_detailed_info:
22129 
22130  /*
22131  For now we're just outputting a set of values that are supported by the API but not necessarily supported
22132  by the device natively. Later on we should work on this so that it more closely reflects the device's
22133  actual native format.
22134  */
22135  pDeviceInfo->minChannels = 1;
22136  pDeviceInfo->maxChannels = 2;
22137  pDeviceInfo->minSampleRate = 8000;
22138  pDeviceInfo->maxSampleRate = 48000;
22139  pDeviceInfo->formatCount = 2;
22140  pDeviceInfo->formats[0] = ma_format_u8;
22141  pDeviceInfo->formats[1] = ma_format_s16;
22142 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
22143  pDeviceInfo->formats[pDeviceInfo->formatCount] = ma_format_f32;
22144  pDeviceInfo->formatCount += 1;
22145 #endif
22146 
22147  return MA_SUCCESS;
22148 }
22149 
22150 
22151 #ifdef MA_ANDROID
22152 /*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
22153 void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
22154 {
22155  ma_device* pDevice = (ma_device*)pUserData;
22156  size_t periodSizeInBytes;
22157  ma_uint8* pBuffer;
22158  SLresult resultSL;
22159 
22160  ma_assert(pDevice != NULL);
22161 
22162  (void)pBufferQueue;
22163 
22164  /*
22165  For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
22166  OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
22167  but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
22168  */
22169 
22170  /* Don't do anything if the device is not started. */
22171  if (pDevice->state != MA_STATE_STARTED) {
22172  return;
22173  }
22174 
22175  periodSizeInBytes = (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods) * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
22176  pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
22177 
22178  if (pDevice->type == ma_device_type_duplex) {
22179  ma_device__handle_duplex_callback_capture(pDevice, (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods), pBuffer, &pDevice->opensl.duplexRB);
22180  } else {
22181  ma_device__send_frames_to_client(pDevice, (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods), pBuffer);
22182  }
22183 
22184  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
22185  if (resultSL != SL_RESULT_SUCCESS) {
22186  return;
22187  }
22188 
22189  pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
22190 }
22191 
22192 void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
22193 {
22194  ma_device* pDevice = (ma_device*)pUserData;
22195  size_t periodSizeInBytes;
22196  ma_uint8* pBuffer;
22197  SLresult resultSL;
22198 
22199  ma_assert(pDevice != NULL);
22200 
22201  (void)pBufferQueue;
22202 
22203  /* Don't do anything if the device is not started. */
22204  if (pDevice->state != MA_STATE_STARTED) {
22205  return;
22206  }
22207 
22208  periodSizeInBytes = (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods) * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22209  pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
22210 
22211  if (pDevice->type == ma_device_type_duplex) {
22212  ma_device__handle_duplex_callback_playback(pDevice, (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods), pBuffer, &pDevice->opensl.duplexRB);
22213  } else {
22214  ma_device__read_frames_from_client(pDevice, (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods), pBuffer);
22215  }
22216 
22217  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
22218  if (resultSL != SL_RESULT_SUCCESS) {
22219  return;
22220  }
22221 
22222  pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
22223 }
22224 #endif
22225 
22226 void ma_device_uninit__opensl(ma_device* pDevice)
22227 {
22228  ma_assert(pDevice != NULL);
22229 
22230  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. */
22231  if (g_maOpenSLInitCounter == 0) {
22232  return;
22233  }
22234 
22235  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22236  if (pDevice->opensl.pAudioRecorderObj) {
22237  MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
22238  }
22239 
22240  ma_free(pDevice->opensl.pBufferCapture);
22241  }
22242 
22243  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22244  if (pDevice->opensl.pAudioPlayerObj) {
22245  MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
22246  }
22247  if (pDevice->opensl.pOutputMixObj) {
22248  MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
22249  }
22250 
22251  ma_free(pDevice->opensl.pBufferPlayback);
22252  }
22253 
22254  if (pDevice->type == ma_device_type_duplex) {
22255  ma_pcm_rb_uninit(&pDevice->opensl.duplexRB);
22256  }
22257 }
22258 
22259 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
22260 typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
22261 #else
22262 typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
22263 #endif
22264 
22265 ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat)
22266 {
22267 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
22268  if (format == ma_format_f32) {
22269  pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
22270  pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
22271  } else {
22272  pDataFormat->formatType = SL_DATAFORMAT_PCM;
22273  }
22274 #else
22275  pDataFormat->formatType = SL_DATAFORMAT_PCM;
22276 #endif
22277 
22278  pDataFormat->numChannels = channels;
22279  ((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 */
22280  pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format)*8;
22281  pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
22282  pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
22283 
22284  /*
22285  Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
22286  - Only mono and stereo is supported.
22287  - Only u8 and s16 formats are supported.
22288  - Maximum sample rate of 48000.
22289  */
22290 #ifdef MA_ANDROID
22291  if (pDataFormat->numChannels > 2) {
22292  pDataFormat->numChannels = 2;
22293  }
22294 #if __ANDROID_API__ >= 21
22295  if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
22296  /* It's floating point. */
22297  ma_assert(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
22298  if (pDataFormat->bitsPerSample > 32) {
22299  pDataFormat->bitsPerSample = 32;
22300  }
22301  } else {
22302  if (pDataFormat->bitsPerSample > 16) {
22303  pDataFormat->bitsPerSample = 16;
22304  }
22305  }
22306 #else
22307  if (pDataFormat->bitsPerSample > 16) {
22308  pDataFormat->bitsPerSample = 16;
22309  }
22310 #endif
22311  if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
22312  ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
22313  }
22314 #endif
22315 
22316  pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */
22317 
22318  return MA_SUCCESS;
22319 }
22320 
22321 ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap)
22322 {
22323  ma_bool32 isFloatingPoint = MA_FALSE;
22324 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
22325  if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
22326  ma_assert(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
22327  isFloatingPoint = MA_TRUE;
22328  }
22329 #endif
22330  if (isFloatingPoint) {
22331  if (pDataFormat->bitsPerSample == 32) {
22332  *pFormat = ma_format_f32;
22333  }
22334  } else {
22335  if (pDataFormat->bitsPerSample == 8) {
22336  *pFormat = ma_format_u8;
22337  } else if (pDataFormat->bitsPerSample == 16) {
22338  *pFormat = ma_format_s16;
22339  } else if (pDataFormat->bitsPerSample == 24) {
22340  *pFormat = ma_format_s24;
22341  } else if (pDataFormat->bitsPerSample == 32) {
22342  *pFormat = ma_format_s32;
22343  }
22344  }
22345 
22346  *pChannels = pDataFormat->numChannels;
22347  *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
22348  ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, pDataFormat->numChannels, pChannelMap);
22349 
22350  return MA_SUCCESS;
22351 }
22352 
22353 ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
22354 {
22355 #ifdef MA_ANDROID
22356  SLDataLocator_AndroidSimpleBufferQueue queue;
22357  SLresult resultSL;
22358  ma_uint32 bufferSizeInFrames;
22359  size_t bufferSizeInBytes;
22360  const SLInterfaceID itfIDs1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
22361  const SLboolean itfIDsRequired1[] = {SL_BOOLEAN_TRUE};
22362 #endif
22363 
22364  (void)pContext;
22365 
22366  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. */
22367  if (g_maOpenSLInitCounter == 0) {
22368  return MA_INVALID_OPERATION;
22369  }
22370 
22371  /*
22372  For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
22373  been able to test with and I currently depend on Android-specific extensions (simple buffer
22374  queues).
22375  */
22376 #ifdef MA_ANDROID
22377  /* No exclusive mode with OpenSL|ES. */
22381  }
22382 
22383  /* Now we can start initializing the device properly. */
22384  ma_assert(pDevice != NULL);
22385  ma_zero_object(&pDevice->opensl);
22386 
22387  queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
22388  queue.numBuffers = pConfig->periods;
22389 
22390 
22391  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
22392  ma_SLDataFormat_PCM pcm;
22393  SLDataLocator_IODevice locatorDevice;
22394  SLDataSource source;
22395  SLDataSink sink;
22396 
22397  ma_SLDataFormat_PCM_init__opensl(pConfig->capture.format, pConfig->capture.channels, pConfig->sampleRate, pConfig->capture.channelMap, &pcm);
22398 
22399  locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
22400  locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
22401  locatorDevice.deviceID = (pConfig->capture.pDeviceID == NULL) ? SL_DEFAULTDEVICEID_AUDIOINPUT : pConfig->capture.pDeviceID->opensl;
22402  locatorDevice.device = NULL;
22403 
22404  source.pLocator = &locatorDevice;
22405  source.pFormat = NULL;
22406 
22407  sink.pLocator = &queue;
22408  sink.pFormat = (SLDataFormat_PCM*)&pcm;
22409 
22410  resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
22411  if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
22412  /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
22413  pcm.formatType = SL_DATAFORMAT_PCM;
22414  pcm.numChannels = 1;
22415  ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
22416  pcm.bitsPerSample = 16;
22417  pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
22418  pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
22419  resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
22420  }
22421 
22422  if (resultSL != SL_RESULT_SUCCESS) {
22423  ma_device_uninit__opensl(pDevice);
22424  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22425  }
22426 
22427  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) {
22428  ma_device_uninit__opensl(pDevice);
22429  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22430  }
22431 
22432  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_RECORD, &pDevice->opensl.pAudioRecorder) != SL_RESULT_SUCCESS) {
22433  ma_device_uninit__opensl(pDevice);
22434  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22435  }
22436 
22437  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture) != SL_RESULT_SUCCESS) {
22438  ma_device_uninit__opensl(pDevice);
22439  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22440  }
22441 
22442  if (MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice) != SL_RESULT_SUCCESS) {
22443  ma_device_uninit__opensl(pDevice);
22444  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22445  }
22446 
22447  /* The internal format is determined by the "pcm" object. */
22448  ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->capture.internalFormat, &pDevice->capture.internalChannels, &pDevice->capture.internalSampleRate, pDevice->capture.internalChannelMap);
22449 
22450  /* Buffer. */
22451  bufferSizeInFrames = pConfig->bufferSizeInFrames;
22452  if (bufferSizeInFrames == 0) {
22453  bufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, pDevice->capture.internalSampleRate);
22454  }
22455  pDevice->capture.internalPeriods = pConfig->periods;
22456  pDevice->capture.internalBufferSizeInFrames = (bufferSizeInFrames / pDevice->capture.internalPeriods) * pDevice->capture.internalPeriods;
22457  pDevice->opensl.currentBufferIndexCapture = 0;
22458 
22459  bufferSizeInBytes = pDevice->capture.internalBufferSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
22460  pDevice->opensl.pBufferCapture = (ma_uint8*)ma_malloc(bufferSizeInBytes);
22461  if (pDevice->opensl.pBufferCapture == NULL) {
22462  ma_device_uninit__opensl(pDevice);
22463  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
22464  }
22465  MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
22466  }
22467 
22468  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
22469  ma_SLDataFormat_PCM pcm;
22470  SLDataSource source;
22471  SLDataLocator_OutputMix outmixLocator;
22472  SLDataSink sink;
22473 
22474  ma_SLDataFormat_PCM_init__opensl(pConfig->playback.format, pConfig->playback.channels, pConfig->sampleRate, pConfig->playback.channelMap, &pcm);
22475 
22476  resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
22477  if (resultSL != SL_RESULT_SUCCESS) {
22478  ma_device_uninit__opensl(pDevice);
22479  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22480  }
22481 
22482  if (MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE)) {
22483  ma_device_uninit__opensl(pDevice);
22484  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22485  }
22486 
22487  if (MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix) != SL_RESULT_SUCCESS) {
22488  ma_device_uninit__opensl(pDevice);
22489  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22490  }
22491 
22492  /* Set the output device. */
22493  if (pConfig->playback.pDeviceID != NULL) {
22494  SLuint32 deviceID_OpenSL = pConfig->playback.pDeviceID->opensl;
22495  MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
22496  }
22497 
22498  source.pLocator = &queue;
22499  source.pFormat = (SLDataFormat_PCM*)&pcm;
22500 
22501  outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
22502  outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
22503 
22504  sink.pLocator = &outmixLocator;
22505  sink.pFormat = NULL;
22506 
22507  resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
22508  if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
22509  /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
22510  pcm.formatType = SL_DATAFORMAT_PCM;
22511  pcm.numChannels = 2;
22512  ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
22513  pcm.bitsPerSample = 16;
22514  pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
22515  pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
22516  resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
22517  }
22518 
22519  if (resultSL != SL_RESULT_SUCCESS) {
22520  ma_device_uninit__opensl(pDevice);
22521  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22522  }
22523 
22524  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) {
22525  ma_device_uninit__opensl(pDevice);
22526  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22527  }
22528 
22529  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_PLAY, &pDevice->opensl.pAudioPlayer) != SL_RESULT_SUCCESS) {
22530  ma_device_uninit__opensl(pDevice);
22531  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22532  }
22533 
22534  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback) != SL_RESULT_SUCCESS) {
22535  ma_device_uninit__opensl(pDevice);
22536  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22537  }
22538 
22539  if (MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice) != SL_RESULT_SUCCESS) {
22540  ma_device_uninit__opensl(pDevice);
22541  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22542  }
22543 
22544  /* The internal format is determined by the "pcm" object. */
22545  ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->playback.internalFormat, &pDevice->playback.internalChannels, &pDevice->playback.internalSampleRate, pDevice->playback.internalChannelMap);
22546 
22547  /* Buffer. */
22548  bufferSizeInFrames = pConfig->bufferSizeInFrames;
22549  if (bufferSizeInFrames == 0) {
22550  bufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, pDevice->playback.internalSampleRate);
22551  }
22552  pDevice->playback.internalPeriods = pConfig->periods;
22553  pDevice->playback.internalBufferSizeInFrames = (bufferSizeInFrames / pDevice->playback.internalPeriods) * pDevice->playback.internalPeriods;
22554  pDevice->opensl.currentBufferIndexPlayback = 0;
22555 
22556  bufferSizeInBytes = pDevice->playback.internalBufferSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22557  pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_malloc(bufferSizeInBytes);
22558  if (pDevice->opensl.pBufferPlayback == NULL) {
22559  ma_device_uninit__opensl(pDevice);
22560  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
22561  }
22562  MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
22563  }
22564 
22565  if (pConfig->deviceType == ma_device_type_duplex) {
22566  ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalBufferSizeInFrames);
22567  ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->opensl.duplexRB);
22568  if (result != MA_SUCCESS) {
22569  ma_device_uninit__opensl(pDevice);
22570  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to initialize ring buffer.", result);
22571  }
22572  }
22573 
22574  return MA_SUCCESS;
22575 #else
22576  return MA_NO_BACKEND; /* Non-Android implementations are not supported. */
22577 #endif
22578 }
22579 
22580 ma_result ma_device_start__opensl(ma_device* pDevice)
22581 {
22582  SLresult resultSL;
22583  size_t periodSizeInBytes;
22584  ma_uint32 iPeriod;
22585 
22586  ma_assert(pDevice != NULL);
22587 
22588  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. */
22589  if (g_maOpenSLInitCounter == 0) {
22590  return MA_INVALID_OPERATION;
22591  }
22592 
22593  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22594  resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
22595  if (resultSL != SL_RESULT_SUCCESS) {
22596  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
22597  }
22598 
22599  periodSizeInBytes = (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods) * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
22600  for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
22601  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
22602  if (resultSL != SL_RESULT_SUCCESS) {
22603  MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
22604  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
22605  }
22606  }
22607  }
22608 
22609  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22610  resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
22611  if (resultSL != SL_RESULT_SUCCESS) {
22612  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
22613  }
22614 
22615  /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */
22616  if (pDevice->type == ma_device_type_duplex) {
22617  MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalBufferSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
22618  } else {
22619  ma_device__read_frames_from_client(pDevice, pDevice->playback.internalBufferSizeInFrames, pDevice->opensl.pBufferPlayback);
22620  }
22621 
22622  periodSizeInBytes = (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods) * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22623  for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
22624  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
22625  if (resultSL != SL_RESULT_SUCCESS) {
22626  MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
22627  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
22628  }
22629  }
22630  }
22631 
22632  return MA_SUCCESS;
22633 }
22634 
22635 ma_result ma_device_stop__opensl(ma_device* pDevice)
22636 {
22637  SLresult resultSL;
22638  ma_stop_proc onStop;
22639 
22640  ma_assert(pDevice != NULL);
22641 
22642  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. */
22643  if (g_maOpenSLInitCounter == 0) {
22644  return MA_INVALID_OPERATION;
22645  }
22646 
22647  /* TODO: Wait until all buffers have been processed. Hint: Maybe SLAndroidSimpleBufferQueue::GetState() could be used in a loop? */
22648 
22649  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22650  resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
22651  if (resultSL != SL_RESULT_SUCCESS) {
22652  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
22653  }
22654 
22655  MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
22656  }
22657 
22658  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22659  resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
22660  if (resultSL != SL_RESULT_SUCCESS) {
22661  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
22662  }
22663 
22664  MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
22665  }
22666 
22667  /* 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. */
22668  onStop = pDevice->onStop;
22669  if (onStop) {
22670  onStop(pDevice);
22671  }
22672 
22673  return MA_SUCCESS;
22674 }
22675 
22676 
22677 ma_result ma_context_uninit__opensl(ma_context* pContext)
22678 {
22679  ma_assert(pContext != NULL);
22680  ma_assert(pContext->backend == ma_backend_opensl);
22681  (void)pContext;
22682 
22683  /* Uninit global data. */
22684  if (g_maOpenSLInitCounter > 0) {
22685  if (ma_atomic_decrement_32(&g_maOpenSLInitCounter) == 0) {
22686  (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
22687  }
22688  }
22689 
22690  return MA_SUCCESS;
22691 }
22692 
22693 ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_context* pContext)
22694 {
22695  ma_assert(pContext != NULL);
22696 
22697  (void)pConfig;
22698 
22699  /* Initialize global data first if applicable. */
22700  if (ma_atomic_increment_32(&g_maOpenSLInitCounter) == 1) {
22701  SLresult resultSL = slCreateEngine(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
22702  if (resultSL != SL_RESULT_SUCCESS) {
22703  ma_atomic_decrement_32(&g_maOpenSLInitCounter);
22704  return MA_NO_BACKEND;
22705  }
22706 
22707  (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
22708 
22709  resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_ENGINE, &g_maEngineSL);
22710  if (resultSL != SL_RESULT_SUCCESS) {
22711  (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
22712  ma_atomic_decrement_32(&g_maOpenSLInitCounter);
22713  return MA_NO_BACKEND;
22714  }
22715  }
22716 
22717  pContext->isBackendAsynchronous = MA_TRUE;
22718 
22719  pContext->onUninit = ma_context_uninit__opensl;
22720  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__opensl;
22721  pContext->onEnumDevices = ma_context_enumerate_devices__opensl;
22722  pContext->onGetDeviceInfo = ma_context_get_device_info__opensl;
22723  pContext->onDeviceInit = ma_device_init__opensl;
22724  pContext->onDeviceUninit = ma_device_uninit__opensl;
22725  pContext->onDeviceStart = ma_device_start__opensl;
22726  pContext->onDeviceStop = ma_device_stop__opensl;
22727 
22728  return MA_SUCCESS;
22729 }
22730 #endif /* OpenSL|ES */
22731 
22732 
22733 /******************************************************************************
22734 
22735 Web Audio Backend
22736 
22737 ******************************************************************************/
22738 #ifdef MA_HAS_WEBAUDIO
22739 #include <emscripten/emscripten.h>
22740 
22741 ma_bool32 ma_is_capture_supported__webaudio()
22742 {
22743  return EM_ASM_INT({
22744  return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
22745  }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
22746 }
22747 
22748 #ifdef __cplusplus
22749 extern "C" {
22750 #endif
22751 EMSCRIPTEN_KEEPALIVE void ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
22752 {
22753  if (pDevice->type == ma_device_type_duplex) {
22754  ma_device__handle_duplex_callback_capture(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB);
22755  } else {
22756  ma_device__send_frames_to_client(pDevice, (ma_uint32)frameCount, pFrames); /* Send directly to the client. */
22757  }
22758 }
22759 
22760 EMSCRIPTEN_KEEPALIVE void ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
22761 {
22762  if (pDevice->type == ma_device_type_duplex) {
22763  ma_device__handle_duplex_callback_playback(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB);
22764  } else {
22765  ma_device__read_frames_from_client(pDevice, (ma_uint32)frameCount, pFrames); /* Read directly from the device. */
22766  }
22767 }
22768 #ifdef __cplusplus
22769 }
22770 #endif
22771 
22772 ma_bool32 ma_context_is_device_id_equal__webaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
22773 {
22774  ma_assert(pContext != NULL);
22775  ma_assert(pID0 != NULL);
22776  ma_assert(pID1 != NULL);
22777  (void)pContext;
22778 
22779  return ma_strcmp(pID0->webaudio, pID1->webaudio) == 0;
22780 }
22781 
22782 ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
22783 {
22784  ma_bool32 cbResult = MA_TRUE;
22785 
22786  ma_assert(pContext != NULL);
22787  ma_assert(callback != NULL);
22788 
22789  /* Only supporting default devices for now. */
22790 
22791  /* Playback. */
22792  if (cbResult) {
22793  ma_device_info deviceInfo;
22794  ma_zero_object(&deviceInfo);
22795  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
22796  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
22797  }
22798 
22799  /* Capture. */
22800  if (cbResult) {
22801  if (ma_is_capture_supported__webaudio()) {
22802  ma_device_info deviceInfo;
22803  ma_zero_object(&deviceInfo);
22804  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
22805  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
22806  }
22807  }
22808 
22809  return MA_SUCCESS;
22810 }
22811 
22812 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)
22813 {
22814  ma_assert(pContext != NULL);
22815 
22816  /* No exclusive mode with Web Audio. */
22817  if (shareMode == ma_share_mode_exclusive) {
22819  }
22820 
22821  if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
22822  return MA_NO_DEVICE;
22823  }
22824 
22825 
22826  ma_zero_memory(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
22827 
22828  /* Only supporting default devices for now. */
22829  if (deviceType == ma_device_type_playback) {
22830  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
22831  } else {
22832  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
22833  }
22834 
22835  /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
22836  pDeviceInfo->minChannels = 1;
22837  pDeviceInfo->maxChannels = MA_MAX_CHANNELS;
22838  if (pDeviceInfo->maxChannels > 32) {
22839  pDeviceInfo->maxChannels = 32; /* Maximum output channel count is 32 for createScriptProcessor() (JavaScript). */
22840  }
22841 
22842  /* We can query the sample rate by just using a temporary audio context. */
22843  pDeviceInfo->minSampleRate = EM_ASM_INT({
22844  try {
22845  var temp = new (window.AudioContext || window.webkitAudioContext)();
22846  var sampleRate = temp.sampleRate;
22847  temp.close();
22848  return sampleRate;
22849  } catch(e) {
22850  return 0;
22851  }
22852  }, 0); /* Must pass in a dummy argument for C99 compatibility. */
22853  pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
22854  if (pDeviceInfo->minSampleRate == 0) {
22855  return MA_NO_DEVICE;
22856  }
22857 
22858  /* Web Audio only supports f32. */
22859  pDeviceInfo->formatCount = 1;
22860  pDeviceInfo->formats[0] = ma_format_f32;
22861 
22862  return MA_SUCCESS;
22863 }
22864 
22865 
22866 void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex)
22867 {
22868  ma_assert(pDevice != NULL);
22869 
22870  EM_ASM({
22871  var device = miniaudio.get_device_by_index($0);
22872 
22873  /* Make sure all nodes are disconnected and marked for collection. */
22874  if (device.scriptNode !== undefined) {
22875  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... */
22876  device.scriptNode.disconnect();
22877  device.scriptNode = undefined;
22878  }
22879  if (device.streamNode !== undefined) {
22880  device.streamNode.disconnect();
22881  device.streamNode = undefined;
22882  }
22883 
22884  /*
22885  Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
22886  to clear the callback before closing.
22887  */
22888  device.webaudio.close();
22889  device.webaudio = undefined;
22890 
22891  /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */
22892  if (device.intermediaryBuffer !== undefined) {
22893  Module._free(device.intermediaryBuffer);
22894  device.intermediaryBuffer = undefined;
22895  device.intermediaryBufferView = undefined;
22896  device.intermediaryBufferSizeInBytes = undefined;
22897  }
22898 
22899  /* Make sure the device is untracked so the slot can be reused later. */
22900  miniaudio.untrack_device_by_index($0);
22901  }, deviceIndex, deviceType);
22902 }
22903 
22904 void ma_device_uninit__webaudio(ma_device* pDevice)
22905 {
22906  ma_assert(pDevice != NULL);
22907 
22908  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22909  ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
22910  }
22911 
22912  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22913  ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
22914  }
22915 
22916  if (pDevice->type == ma_device_type_duplex) {
22917  ma_pcm_rb_uninit(&pDevice->webaudio.duplexRB);
22918  }
22919 }
22920 
22921 ma_result ma_device_init_by_type__webaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
22922 {
22923  int deviceIndex;
22924  ma_uint32 internalBufferSizeInFrames;
22925 
22926  ma_assert(pContext != NULL);
22927  ma_assert(pConfig != NULL);
22928  ma_assert(deviceType != ma_device_type_duplex);
22929  ma_assert(pDevice != NULL);
22930 
22931  if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
22932  return MA_NO_DEVICE;
22933  }
22934 
22935  /* Try calculating an appropriate buffer size. */
22936  internalBufferSizeInFrames = pConfig->bufferSizeInFrames;
22937  if (internalBufferSizeInFrames == 0) {
22938  internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, pConfig->sampleRate);
22939  }
22940 
22941  /* The size of the buffer must be a power of 2 and between 256 and 16384. */
22942  if (internalBufferSizeInFrames < 256) {
22943  internalBufferSizeInFrames = 256;
22944  } else if (internalBufferSizeInFrames > 16384) {
22945  internalBufferSizeInFrames = 16384;
22946  } else {
22947  internalBufferSizeInFrames = ma_next_power_of_2(internalBufferSizeInFrames);
22948  }
22949 
22950  /* 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. */
22951  deviceIndex = EM_ASM_INT({
22952  var channels = $0;
22953  var sampleRate = $1;
22954  var bufferSize = $2; /* In PCM frames. */
22955  var isCapture = $3;
22956  var pDevice = $4;
22957 
22958  if (typeof(miniaudio) === 'undefined') {
22959  return -1; /* Context not initialized. */
22960  }
22961 
22962  var device = {};
22963 
22964  /* The AudioContext must be created in a suspended state. */
22965  device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate});
22966  device.webaudio.suspend();
22967 
22968  /*
22969  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
22970  JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free().
22971  */
22972  device.intermediaryBufferSizeInBytes = channels * bufferSize * 4;
22973  device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes);
22974  device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
22975 
22976  /*
22977  Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations.
22978 
22979  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
22980  that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of
22981  something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to
22982  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
22983  implementation. I'll be avoiding that insane AudioWorklet API like the plague...
22984 
22985  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
22986  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
22987  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
22988  been unable to figure out how to get this as raw PCM. The closes I can think is to use the MIME type for WAV files and just parse it, but I don't know
22989  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
22990  this for now. If anything knows how I could get raw PCM data using the MediaRecorder API please let me know!
22991  */
22992  device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channels, channels);
22993 
22994  if (isCapture) {
22995  device.scriptNode.onaudioprocess = function(e) {
22996  if (device.intermediaryBuffer === undefined) {
22997  return; /* This means the device has been uninitialized. */
22998  }
22999 
23000  /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */
23001  for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
23002  e.outputBuffer.getChannelData(iChannel).fill(0.0);
23003  }
23004 
23005  /* There are some situations where we may want to send silence to the client. */
23006  var sendSilence = false;
23007  if (device.streamNode === undefined) {
23008  sendSilence = true;
23009  }
23010 
23011  /* Sanity check. This will never happen, right? */
23012  if (e.inputBuffer.numberOfChannels != channels) {
23013  console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence.");
23014  sendSilence = true;
23015  }
23016 
23017  /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */
23018  var totalFramesProcessed = 0;
23019  while (totalFramesProcessed < e.inputBuffer.length) {
23020  var framesRemaining = e.inputBuffer.length - totalFramesProcessed;
23021  var framesToProcess = framesRemaining;
23022  if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
23023  framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
23024  }
23025 
23026  /* 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. */
23027  if (sendSilence) {
23028  device.intermediaryBufferView.fill(0.0);
23029  } else {
23030  for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
23031  for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
23032  device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame];
23033  }
23034  }
23035  }
23036 
23037  /* Send data to the client from our intermediary buffer. */
23038  ccall("ma_device_process_pcm_frames_capture__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
23039 
23040  totalFramesProcessed += framesToProcess;
23041  }
23042  };
23043 
23044  navigator.mediaDevices.getUserMedia({audio:true, video:false})
23045  .then(function(stream) {
23046  device.streamNode = device.webaudio.createMediaStreamSource(stream);
23047  device.streamNode.connect(device.scriptNode);
23048  device.scriptNode.connect(device.webaudio.destination);
23049  })
23050  .catch(function(error) {
23051  /* I think this should output silence... */
23052  device.scriptNode.connect(device.webaudio.destination);
23053  });
23054  } else {
23055  device.scriptNode.onaudioprocess = function(e) {
23056  if (device.intermediaryBuffer === undefined) {
23057  return; /* This means the device has been uninitialized. */
23058  }
23059 
23060  var outputSilence = false;
23061 
23062  /* Sanity check. This will never happen, right? */
23063  if (e.outputBuffer.numberOfChannels != channels) {
23064  console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence.");
23065  outputSilence = true;
23066  return;
23067  }
23068 
23069  /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */
23070  var totalFramesProcessed = 0;
23071  while (totalFramesProcessed < e.outputBuffer.length) {
23072  var framesRemaining = e.outputBuffer.length - totalFramesProcessed;
23073  var framesToProcess = framesRemaining;
23074  if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
23075  framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
23076  }
23077 
23078  /* Read data from the client into our intermediary buffer. */
23079  ccall("ma_device_process_pcm_frames_playback__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
23080 
23081  /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */
23082  if (outputSilence) {
23083  for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
23084  e.outputBuffer.getChannelData(iChannel).fill(0.0);
23085  }
23086  } else {
23087  for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
23088  for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
23089  e.outputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame] = device.intermediaryBufferView[iFrame*channels + iChannel];
23090  }
23091  }
23092  }
23093 
23094  totalFramesProcessed += framesToProcess;
23095  }
23096  };
23097 
23098  device.scriptNode.connect(device.webaudio.destination);
23099  }
23100 
23101  return miniaudio.track_device(device);
23102  }, (deviceType == ma_device_type_capture) ? pConfig->capture.channels : pConfig->playback.channels, pConfig->sampleRate, internalBufferSizeInFrames, deviceType == ma_device_type_capture, pDevice);
23103 
23104  if (deviceIndex < 0) {
23106  }
23107 
23108  if (deviceType == ma_device_type_capture) {
23109  pDevice->webaudio.indexCapture = deviceIndex;
23110  pDevice->capture.internalFormat = ma_format_f32;
23111  pDevice->capture.internalChannels = pConfig->capture.channels;
23112  ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
23113  pDevice->capture.internalSampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
23114  pDevice->capture.internalBufferSizeInFrames = internalBufferSizeInFrames;
23115  pDevice->capture.internalPeriods = 1;
23116  } else {
23117  pDevice->webaudio.indexPlayback = deviceIndex;
23118  pDevice->playback.internalFormat = ma_format_f32;
23119  pDevice->playback.internalChannels = pConfig->playback.channels;
23120  ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
23121  pDevice->playback.internalSampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
23122  pDevice->playback.internalBufferSizeInFrames = internalBufferSizeInFrames;
23123  pDevice->playback.internalPeriods = 1;
23124  }
23125 
23126  return MA_SUCCESS;
23127 }
23128 
23129 ma_result ma_device_init__webaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
23130 {
23131  ma_result result;
23132 
23133  /* No exclusive mode with Web Audio. */
23137  }
23138 
23139  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
23140  result = ma_device_init_by_type__webaudio(pContext, pConfig, ma_device_type_capture, pDevice);
23141  if (result != MA_SUCCESS) {
23142  return result;
23143  }
23144  }
23145 
23146  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
23147  result = ma_device_init_by_type__webaudio(pContext, pConfig, ma_device_type_playback, pDevice);
23148  if (result != MA_SUCCESS) {
23149  if (pConfig->deviceType == ma_device_type_duplex) {
23150  ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
23151  }
23152  return result;
23153  }
23154  }
23155 
23156  /*
23157  We need a ring buffer for moving data from the capture device to the playback device. The capture callback is the producer
23158  and the playback callback is the consumer. The buffer needs to be large enough to hold internalBufferSizeInFrames based on
23159  the external sample rate.
23160  */
23161  if (pConfig->deviceType == ma_device_type_duplex) {
23162  ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalBufferSizeInFrames) * 2;
23163  result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->webaudio.duplexRB);
23164  if (result != MA_SUCCESS) {
23165  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23166  ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
23167  }
23168  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23169  ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
23170  }
23171  return result;
23172  }
23173  }
23174 
23175  return MA_SUCCESS;
23176 }
23177 
23178 ma_result ma_device_start__webaudio(ma_device* pDevice)
23179 {
23180  ma_assert(pDevice != NULL);
23181 
23182  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23183  EM_ASM({
23184  miniaudio.get_device_by_index($0).webaudio.resume();
23185  }, pDevice->webaudio.indexCapture);
23186  }
23187 
23188  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23189  EM_ASM({
23190  miniaudio.get_device_by_index($0).webaudio.resume();
23191  }, pDevice->webaudio.indexPlayback);
23192  }
23193 
23194  return MA_SUCCESS;
23195 }
23196 
23197 ma_result ma_device_stop__webaudio(ma_device* pDevice)
23198 {
23199  ma_assert(pDevice != NULL);
23200 
23201  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23202  EM_ASM({
23203  miniaudio.get_device_by_index($0).webaudio.suspend();
23204  }, pDevice->webaudio.indexCapture);
23205  }
23206 
23207  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23208  EM_ASM({
23209  miniaudio.get_device_by_index($0).webaudio.suspend();
23210  }, pDevice->webaudio.indexPlayback);
23211  }
23212 
23213  ma_stop_proc onStop = pDevice->onStop;
23214  if (onStop) {
23215  onStop(pDevice);
23216  }
23217 
23218  return MA_SUCCESS;
23219 }
23220 
23221 ma_result ma_context_uninit__webaudio(ma_context* pContext)
23222 {
23223  ma_assert(pContext != NULL);
23224  ma_assert(pContext->backend == ma_backend_webaudio);
23225 
23226  /* Nothing needs to be done here. */
23227  (void)pContext;
23228 
23229  return MA_SUCCESS;
23230 }
23231 
23232 ma_result ma_context_init__webaudio(const ma_context_config* pConfig, ma_context* pContext)
23233 {
23234  int resultFromJS;
23235 
23236  ma_assert(pContext != NULL);
23237 
23238  /* Here is where our global JavaScript object is initialized. */
23239  resultFromJS = EM_ASM_INT({
23240  if ((window.AudioContext || window.webkitAudioContext) === undefined) {
23241  return 0; /* Web Audio not supported. */
23242  }
23243 
23244  if (typeof(miniaudio) === 'undefined') {
23245  miniaudio = {};
23246  miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */
23247 
23248  miniaudio.track_device = function(device) {
23249  /* Try inserting into a free slot first. */
23250  for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
23251  if (miniaudio.devices[iDevice] == null) {
23252  miniaudio.devices[iDevice] = device;
23253  return iDevice;
23254  }
23255  }
23256 
23257  /* Getting here means there is no empty slots in the array so we just push to the end. */
23258  miniaudio.devices.push(device);
23259  return miniaudio.devices.length - 1;
23260  };
23261 
23262  miniaudio.untrack_device_by_index = function(deviceIndex) {
23263  /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */
23264  miniaudio.devices[deviceIndex] = null;
23265 
23266  /* Trim the array if possible. */
23267  while (miniaudio.devices.length > 0) {
23268  if (miniaudio.devices[miniaudio.devices.length-1] == null) {
23269  miniaudio.devices.pop();
23270  } else {
23271  break;
23272  }
23273  }
23274  };
23275 
23276  miniaudio.untrack_device = function(device) {
23277  for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
23278  if (miniaudio.devices[iDevice] == device) {
23279  return miniaudio.untrack_device_by_index(iDevice);
23280  }
23281  }
23282  };
23283 
23284  miniaudio.get_device_by_index = function(deviceIndex) {
23285  return miniaudio.devices[deviceIndex];
23286  };
23287  }
23288 
23289  return 1;
23290  }, 0); /* Must pass in a dummy argument for C99 compatibility. */
23291 
23292  if (resultFromJS != 1) {
23294  }
23295 
23296 
23297  pContext->isBackendAsynchronous = MA_TRUE;
23298 
23299  pContext->onUninit = ma_context_uninit__webaudio;
23300  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__webaudio;
23301  pContext->onEnumDevices = ma_context_enumerate_devices__webaudio;
23302  pContext->onGetDeviceInfo = ma_context_get_device_info__webaudio;
23303  pContext->onDeviceInit = ma_device_init__webaudio;
23304  pContext->onDeviceUninit = ma_device_uninit__webaudio;
23305  pContext->onDeviceStart = ma_device_start__webaudio;
23306  pContext->onDeviceStop = ma_device_stop__webaudio;
23307 
23308  (void)pConfig; /* Unused. */
23309  return MA_SUCCESS;
23310 }
23311 #endif /* Web Audio */
23312 
23313 
23314 
23315 ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels)
23316 {
23317  /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */
23318  if (channelMap[0] != MA_CHANNEL_NONE) {
23319  ma_uint32 iChannel;
23320 
23321  if (channels == 0) {
23322  return MA_FALSE; /* No channels. */
23323  }
23324 
23325  /* A channel cannot be present in the channel map more than once. */
23326  for (iChannel = 0; iChannel < channels; ++iChannel) {
23327  ma_uint32 jChannel;
23328  for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
23329  if (channelMap[iChannel] == channelMap[jChannel]) {
23330  return MA_FALSE;
23331  }
23332  }
23333  }
23334  }
23335 
23336  return MA_TRUE;
23337 }
23338 
23339 
23340 void ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
23341 {
23342  ma_assert(pDevice != NULL);
23343 
23344  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
23345  if (pDevice->capture.usingDefaultFormat) {
23346  pDevice->capture.format = pDevice->capture.internalFormat;
23347  }
23348  if (pDevice->capture.usingDefaultChannels) {
23349  pDevice->capture.channels = pDevice->capture.internalChannels;
23350  }
23351  if (pDevice->capture.usingDefaultChannelMap) {
23352  if (pDevice->capture.internalChannels == pDevice->capture.channels) {
23353  ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);
23354  } else {
23355  ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.channels, pDevice->capture.channelMap);
23356  }
23357  }
23358  }
23359 
23360  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
23361  if (pDevice->playback.usingDefaultFormat) {
23362  pDevice->playback.format = pDevice->playback.internalFormat;
23363  }
23364  if (pDevice->playback.usingDefaultChannels) {
23365  pDevice->playback.channels = pDevice->playback.internalChannels;
23366  }
23367  if (pDevice->playback.usingDefaultChannelMap) {
23368  if (pDevice->playback.internalChannels == pDevice->playback.channels) {
23369  ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);
23370  } else {
23371  ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.channels, pDevice->playback.channelMap);
23372  }
23373  }
23374  }
23375 
23376  if (pDevice->usingDefaultSampleRate) {
23377  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
23378  pDevice->sampleRate = pDevice->capture.internalSampleRate;
23379  } else {
23380  pDevice->sampleRate = pDevice->playback.internalSampleRate;
23381  }
23382  }
23383 
23384  /* PCM converters. */
23385  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
23386  /* Converting from internal device format to public format. */
23388  converterConfig.neverConsumeEndOfInput = MA_TRUE;
23389  converterConfig.pUserData = pDevice;
23390  converterConfig.formatIn = pDevice->capture.internalFormat;
23391  converterConfig.channelsIn = pDevice->capture.internalChannels;
23392  converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
23393  ma_channel_map_copy(converterConfig.channelMapIn, pDevice->capture.internalChannelMap, pDevice->capture.internalChannels);
23394  converterConfig.formatOut = pDevice->capture.format;
23395  converterConfig.channelsOut = pDevice->capture.channels;
23396  converterConfig.sampleRateOut = pDevice->sampleRate;
23397  ma_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, pDevice->capture.channels);
23398  converterConfig.onRead = ma_device__pcm_converter__on_read_from_buffer_capture;
23399  ma_pcm_converter_init(&converterConfig, &pDevice->capture.converter);
23400  }
23401 
23402  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
23403  /* Converting from public format to device format. */
23405  converterConfig.neverConsumeEndOfInput = MA_TRUE;
23406  converterConfig.pUserData = pDevice;
23407  converterConfig.formatIn = pDevice->playback.format;
23408  converterConfig.channelsIn = pDevice->playback.channels;
23409  converterConfig.sampleRateIn = pDevice->sampleRate;
23410  ma_channel_map_copy(converterConfig.channelMapIn, pDevice->playback.channelMap, pDevice->playback.channels);
23411  converterConfig.formatOut = pDevice->playback.internalFormat;
23412  converterConfig.channelsOut = pDevice->playback.internalChannels;
23413  converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
23414  ma_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, pDevice->playback.internalChannels);
23415  if (deviceType == ma_device_type_playback) {
23416  if (pDevice->type == ma_device_type_playback) {
23417  converterConfig.onRead = ma_device__on_read_from_client;
23418  } else {
23419  converterConfig.onRead = ma_device__pcm_converter__on_read_from_buffer_playback;
23420  }
23421  } else {
23422  converterConfig.onRead = ma_device__pcm_converter__on_read_from_buffer_playback;
23423  }
23424  ma_pcm_converter_init(&converterConfig, &pDevice->playback.converter);
23425  }
23426 }
23427 
23428 
23429 ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
23430 {
23431  ma_device* pDevice = (ma_device*)pData;
23432  ma_assert(pDevice != NULL);
23433 
23434 #ifdef MA_WIN32
23435  ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
23436 #endif
23437 
23438  /*
23439  When the device is being initialized it's initial state is set to MA_STATE_UNINITIALIZED. Before returning from
23440  ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
23441  after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
23442  thread to signal an event to know when the worker thread is ready for action.
23443  */
23444  ma_device__set_state(pDevice, MA_STATE_STOPPED);
23445  ma_event_signal(&pDevice->stopEvent);
23446 
23447  for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
23448  ma_stop_proc onStop;
23449 
23450  /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
23451  ma_event_wait(&pDevice->wakeupEvent);
23452 
23453  /* Default result code. */
23454  pDevice->workResult = MA_SUCCESS;
23455 
23456  /* If the reason for the wake up is that we are terminating, just break from the loop. */
23457  if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
23458  break;
23459  }
23460 
23461  /*
23462  Getting to this point means the device is wanting to get started. The function that has requested that the device
23463  be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
23464  in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
23465  */
23466  ma_assert(ma_device__get_state(pDevice) == MA_STATE_STARTING);
23467 
23468  /* Make sure the state is set appropriately. */
23469  ma_device__set_state(pDevice, MA_STATE_STARTED);
23470  ma_event_signal(&pDevice->startEvent);
23471 
23472  if (pDevice->pContext->onDeviceMainLoop != NULL) {
23473  pDevice->pContext->onDeviceMainLoop(pDevice);
23474  } else {
23475  ma_uint32 periodSizeInFrames;
23476 
23477  /* When a device is using miniaudio's generic worker thread they must implement onDeviceRead or onDeviceWrite, depending on the device type. */
23478  ma_assert(
23479  (pDevice->type == ma_device_type_playback && pDevice->pContext->onDeviceWrite != NULL) ||
23480  (pDevice->type == ma_device_type_capture && pDevice->pContext->onDeviceRead != NULL) ||
23481  (pDevice->type == ma_device_type_duplex && pDevice->pContext->onDeviceWrite != NULL && pDevice->pContext->onDeviceRead != NULL)
23482  );
23483 
23484  if (pDevice->type == ma_device_type_capture) {
23485  ma_assert(pDevice->capture.internalBufferSizeInFrames >= pDevice->capture.internalPeriods);
23486  periodSizeInFrames = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
23487  } else if (pDevice->type == ma_device_type_playback) {
23488  ma_assert(pDevice->playback.internalBufferSizeInFrames >= pDevice->playback.internalPeriods);
23489  periodSizeInFrames = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
23490  } else {
23491  ma_assert(pDevice->capture.internalBufferSizeInFrames >= pDevice->capture.internalPeriods);
23492  ma_assert(pDevice->playback.internalBufferSizeInFrames >= pDevice->playback.internalPeriods);
23493  periodSizeInFrames = ma_min(
23494  pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods,
23495  pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods
23496  );
23497  }
23498 
23499  /*
23500  With the blocking API, the device is started automatically in read()/write(). All we need to do is enter the loop and just keep reading
23501  or writing based on the period size.
23502  */
23503 
23504  /* Main Loop */
23505  ma_assert(periodSizeInFrames >= 1);
23506  while (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
23507  ma_result result = MA_SUCCESS;
23508  ma_uint32 totalFramesProcessed = 0;
23509 
23510  if (pDevice->type == ma_device_type_duplex) {
23511  /* The process is device_read -> convert -> callback -> convert -> device_write. */
23512  ma_uint8 captureDeviceData[4096];
23513  ma_uint32 captureDeviceDataCapInFrames = sizeof(captureDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
23514 
23515  while (totalFramesProcessed < periodSizeInFrames) {
23516  ma_device_callback_proc onData;
23517  ma_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed;
23518  ma_uint32 framesToProcess = framesRemaining;
23519  if (framesToProcess > captureDeviceDataCapInFrames) {
23520  framesToProcess = captureDeviceDataCapInFrames;
23521  }
23522 
23523  result = pDevice->pContext->onDeviceRead(pDevice, captureDeviceData, framesToProcess);
23524  if (result != MA_SUCCESS) {
23525  break;
23526  }
23527 
23528  onData = pDevice->onData;
23529  if (onData != NULL) {
23530  pDevice->capture._dspFrameCount = framesToProcess;
23531  pDevice->capture._dspFrames = captureDeviceData;
23532 
23533  /* We need to process every input frame. */
23534  for (;;) {
23535  ma_uint8 capturedData[4096]; /* In capture.format/channels format */
23536  ma_uint8 playbackData[4096]; /* In playback.format/channels format */
23537 
23538  ma_uint32 capturedDataCapInFrames = sizeof(capturedData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
23539  ma_uint32 playbackDataCapInFrames = sizeof(playbackData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
23540 
23541  ma_uint32 capturedFramesToTryProcessing = ma_min(capturedDataCapInFrames, playbackDataCapInFrames);
23542  ma_uint32 capturedFramesToProcess = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, capturedData, capturedFramesToTryProcessing);
23543  if (capturedFramesToProcess == 0) {
23544  break; /* Don't fire the data callback with zero frames. */
23545  }
23546 
23547  onData(pDevice, playbackData, capturedData, capturedFramesToProcess);
23548 
23549  /* At this point the playbackData buffer should be holding data that needs to be written to the device. */
23550  pDevice->playback._dspFrameCount = capturedFramesToProcess;
23551  pDevice->playback._dspFrames = playbackData;
23552  for (;;) {
23553  ma_uint8 playbackDeviceData[4096];
23554 
23555  ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
23556  ma_uint32 playbackDeviceFramesCount = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, playbackDeviceData, playbackDeviceDataCapInFrames);
23557  if (playbackDeviceFramesCount == 0) {
23558  break;
23559  }
23560 
23561  result = pDevice->pContext->onDeviceWrite(pDevice, playbackDeviceData, playbackDeviceFramesCount);
23562  if (result != MA_SUCCESS) {
23563  break;
23564  }
23565 
23566  if (playbackDeviceFramesCount < playbackDeviceDataCapInFrames) {
23567  break;
23568  }
23569  }
23570 
23571  if (capturedFramesToProcess < capturedFramesToTryProcessing) {
23572  break;
23573  }
23574 
23575  /* In case an error happened from onDeviceWrite()... */
23576  if (result != MA_SUCCESS) {
23577  break;
23578  }
23579  }
23580  }
23581 
23582  totalFramesProcessed += framesToProcess;
23583  }
23584  } else {
23585  ma_uint8 buffer[4096];
23586  ma_uint32 bufferSizeInFrames;
23587  if (pDevice->type == ma_device_type_capture) {
23588  bufferSizeInFrames = sizeof(buffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
23589  } else {
23590  bufferSizeInFrames = sizeof(buffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
23591  }
23592 
23593  while (totalFramesProcessed < periodSizeInFrames) {
23594  ma_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed;
23595  ma_uint32 framesToProcess = framesRemaining;
23596  if (framesToProcess > bufferSizeInFrames) {
23597  framesToProcess = bufferSizeInFrames;
23598  }
23599 
23600  if (pDevice->type == ma_device_type_playback) {
23601  ma_device__read_frames_from_client(pDevice, framesToProcess, buffer);
23602  result = pDevice->pContext->onDeviceWrite(pDevice, buffer, framesToProcess);
23603  } else {
23604  result = pDevice->pContext->onDeviceRead(pDevice, buffer, framesToProcess);
23605  ma_device__send_frames_to_client(pDevice, framesToProcess, buffer);
23606  }
23607 
23608  totalFramesProcessed += framesToProcess;
23609  }
23610  }
23611 
23612  /* Get out of the loop if read()/write() returned an error. It probably means the device has been stopped. */
23613  if (result != MA_SUCCESS) {
23614  break;
23615  }
23616  }
23617  }
23618 
23619  /*
23620  Getting here means we have broken from the main loop which happens the application has requested that device be stopped. Note that this
23621  may have actually already happened above if the device was lost and miniaudio has attempted to re-initialize the device. In this case we
23622  don't want to be doing this a second time.
23623  */
23624  if (ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED) {
23625  if (pDevice->pContext->onDeviceStop) {
23626  pDevice->pContext->onDeviceStop(pDevice);
23627  }
23628  }
23629 
23630  /* After the device has stopped, make sure an event is posted. */
23631  onStop = pDevice->onStop;
23632  if (onStop) {
23633  onStop(pDevice);
23634  }
23635 
23636  /*
23637  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
23638  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
23639  uninitialized state to stopped state.
23640  */
23641  if (ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED) {
23642  ma_device__set_state(pDevice, MA_STATE_STOPPED);
23643  ma_event_signal(&pDevice->stopEvent);
23644  }
23645  }
23646 
23647  /* Make sure we aren't continuously waiting on a stop event. */
23648  ma_event_signal(&pDevice->stopEvent); /* <-- Is this still needed? */
23649 
23650 #ifdef MA_WIN32
23651  ma_CoUninitialize(pDevice->pContext);
23652 #endif
23653 
23654  return (ma_thread_result)0;
23655 }
23656 
23657 
23658 /* Helper for determining whether or not the given device is initialized. */
23659 ma_bool32 ma_device__is_initialized(ma_device* pDevice)
23660 {
23661  if (pDevice == NULL) {
23662  return MA_FALSE;
23663  }
23664 
23665  return ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED;
23666 }
23667 
23668 
23669 #ifdef MA_WIN32
23670 ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
23671 {
23672  ma_CoUninitialize(pContext);
23673  ma_dlclose(pContext, pContext->win32.hUser32DLL);
23674  ma_dlclose(pContext, pContext->win32.hOle32DLL);
23675  ma_dlclose(pContext, pContext->win32.hAdvapi32DLL);
23676 
23677  return MA_SUCCESS;
23678 }
23679 
23680 ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
23681 {
23682 #ifdef MA_WIN32_DESKTOP
23683  /* Ole32.dll */
23684  pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll");
23685  if (pContext->win32.hOle32DLL == NULL) {
23687  }
23688 
23689  pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx");
23690  pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize");
23691  pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance");
23692  pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree");
23693  pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear");
23694  pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2");
23695 
23696 
23697  /* User32.dll */
23698  pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll");
23699  if (pContext->win32.hUser32DLL == NULL) {
23701  }
23702 
23703  pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow");
23704  pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow");
23705 
23706 
23707  /* Advapi32.dll */
23708  pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll");
23709  if (pContext->win32.hAdvapi32DLL == NULL) {
23711  }
23712 
23713  pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
23714  pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey");
23715  pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
23716 #endif
23717 
23718  ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
23719  return MA_SUCCESS;
23720 }
23721 #else
23722 ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
23723 {
23724 #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
23725  ma_dlclose(pContext, pContext->posix.pthreadSO);
23726 #else
23727  (void)pContext;
23728 #endif
23729 
23730  return MA_SUCCESS;
23731 }
23732 
23733 ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
23734 {
23735  /* pthread */
23736 #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
23737  const char* libpthreadFileNames[] = {
23738  "libpthread.so",
23739  "libpthread.so.0",
23740  "libpthread.dylib"
23741  };
23742  size_t i;
23743 
23744  for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) {
23745  pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]);
23746  if (pContext->posix.pthreadSO != NULL) {
23747  break;
23748  }
23749  }
23750 
23751  if (pContext->posix.pthreadSO == NULL) {
23753  }
23754 
23755  pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create");
23756  pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join");
23757  pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init");
23758  pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy");
23759  pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock");
23760  pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock");
23761  pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init");
23762  pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy");
23763  pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait");
23764  pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal");
23765  pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init");
23766  pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy");
23767  pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy");
23768  pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam");
23769  pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam");
23770 #else
23771  pContext->posix.pthread_create = (ma_proc)pthread_create;
23772  pContext->posix.pthread_join = (ma_proc)pthread_join;
23773  pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init;
23774  pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy;
23775  pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock;
23776  pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock;
23777  pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init;
23778  pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy;
23779  pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait;
23780  pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
23781  pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
23782  pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
23783 #if !defined(__EMSCRIPTEN__)
23784  pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
23785  pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
23786  pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
23787 #endif
23788 #endif
23789 
23790  return MA_SUCCESS;
23791 }
23792 #endif
23793 
23794 ma_result ma_context_init_backend_apis(ma_context* pContext)
23795 {
23796  ma_result result;
23797 #ifdef MA_WIN32
23798  result = ma_context_init_backend_apis__win32(pContext);
23799 #else
23800  result = ma_context_init_backend_apis__nix(pContext);
23801 #endif
23802 
23803  return result;
23804 }
23805 
23806 ma_result ma_context_uninit_backend_apis(ma_context* pContext)
23807 {
23808  ma_result result;
23809 #ifdef MA_WIN32
23810  result = ma_context_uninit_backend_apis__win32(pContext);
23811 #else
23812  result = ma_context_uninit_backend_apis__nix(pContext);
23813 #endif
23814 
23815  return result;
23816 }
23817 
23818 
23819 ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
23820 {
23821  return pContext->isBackendAsynchronous;
23822 }
23823 
23824 ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
23825 {
23826  ma_result result;
23827  ma_context_config config;
23828  ma_backend defaultBackends[ma_backend_null+1];
23829  ma_uint32 iBackend;
23830  ma_backend* pBackendsToIterate;
23831  ma_uint32 backendsToIterateCount;
23832 
23833  if (pContext == NULL) {
23834  return MA_INVALID_ARGS;
23835  }
23836 
23837  ma_zero_object(pContext);
23838 
23839  /* Always make sure the config is set first to ensure properties are available as soon as possible. */
23840  if (pConfig != NULL) {
23841  config = *pConfig;
23842  } else {
23843  config = ma_context_config_init();
23844  }
23845 
23846  pContext->logCallback = config.logCallback;
23847  pContext->threadPriority = config.threadPriority;
23848  pContext->pUserData = config.pUserData;
23849 
23850  /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
23851  result = ma_context_init_backend_apis(pContext);
23852  if (result != MA_SUCCESS) {
23853  return result;
23854  }
23855 
23856  for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
23857  defaultBackends[iBackend] = (ma_backend)iBackend;
23858  }
23859 
23860  pBackendsToIterate = (ma_backend*)backends;
23861  backendsToIterateCount = backendCount;
23862  if (pBackendsToIterate == NULL) {
23863  pBackendsToIterate = (ma_backend*)defaultBackends;
23864  backendsToIterateCount = ma_countof(defaultBackends);
23865  }
23866 
23867  ma_assert(pBackendsToIterate != NULL);
23868 
23869  for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
23870  ma_backend backend = pBackendsToIterate[iBackend];
23871 
23872  result = MA_NO_BACKEND;
23873  switch (backend) {
23874  #ifdef MA_HAS_WASAPI
23875  case ma_backend_wasapi:
23876  {
23877  result = ma_context_init__wasapi(&config, pContext);
23878  } break;
23879  #endif
23880  #ifdef MA_HAS_DSOUND
23881  case ma_backend_dsound:
23882  {
23883  result = ma_context_init__dsound(&config, pContext);
23884  } break;
23885  #endif
23886  #ifdef MA_HAS_WINMM
23887  case ma_backend_winmm:
23888  {
23889  result = ma_context_init__winmm(&config, pContext);
23890  } break;
23891  #endif
23892  #ifdef MA_HAS_ALSA
23893  case ma_backend_alsa:
23894  {
23895  result = ma_context_init__alsa(&config, pContext);
23896  } break;
23897  #endif
23898  #ifdef MA_HAS_PULSEAUDIO
23899  case ma_backend_pulseaudio:
23900  {
23901  result = ma_context_init__pulse(&config, pContext);
23902  } break;
23903  #endif
23904  #ifdef MA_HAS_JACK
23905  case ma_backend_jack:
23906  {
23907  result = ma_context_init__jack(&config, pContext);
23908  } break;
23909  #endif
23910  #ifdef MA_HAS_COREAUDIO
23911  case ma_backend_coreaudio:
23912  {
23913  result = ma_context_init__coreaudio(&config, pContext);
23914  } break;
23915  #endif
23916  #ifdef MA_HAS_SNDIO
23917  case ma_backend_sndio:
23918  {
23919  result = ma_context_init__sndio(&config, pContext);
23920  } break;
23921  #endif
23922  #ifdef MA_HAS_AUDIO4
23923  case ma_backend_audio4:
23924  {
23925  result = ma_context_init__audio4(&config, pContext);
23926  } break;
23927  #endif
23928  #ifdef MA_HAS_OSS
23929  case ma_backend_oss:
23930  {
23931  result = ma_context_init__oss(&config, pContext);
23932  } break;
23933  #endif
23934  #ifdef MA_HAS_AAUDIO
23935  case ma_backend_aaudio:
23936  {
23937  result = ma_context_init__aaudio(&config, pContext);
23938  } break;
23939  #endif
23940  #ifdef MA_HAS_OPENSL
23941  case ma_backend_opensl:
23942  {
23943  result = ma_context_init__opensl(&config, pContext);
23944  } break;
23945  #endif
23946  #ifdef MA_HAS_WEBAUDIO
23947  case ma_backend_webaudio:
23948  {
23949  result = ma_context_init__webaudio(&config, pContext);
23950  } break;
23951  #endif
23952  #ifdef MA_HAS_NULL
23953  case ma_backend_null:
23954  {
23955  result = ma_context_init__null(&config, pContext);
23956  } break;
23957  #endif
23958 
23959  default: break;
23960  }
23961 
23962  /* If this iteration was successful, return. */
23963  if (result == MA_SUCCESS) {
23964  result = ma_mutex_init(pContext, &pContext->deviceEnumLock);
23965  if (result != MA_SUCCESS) {
23966  ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.", MA_FAILED_TO_CREATE_MUTEX);
23967  }
23968  result = ma_mutex_init(pContext, &pContext->deviceInfoLock);
23969  if (result != MA_SUCCESS) {
23970  ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.", MA_FAILED_TO_CREATE_MUTEX);
23971  }
23972 
23973 #ifdef MA_DEBUG_OUTPUT
23974  printf("[miniaudio] Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
23975  printf("[miniaudio] SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
23976  printf("[miniaudio] AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
23977  printf("[miniaudio] AVX512F: %s\n", ma_has_avx512f() ? "YES" : "NO");
23978  printf("[miniaudio] NEON: %s\n", ma_has_neon() ? "YES" : "NO");
23979 #endif
23980 
23981  pContext->backend = backend;
23982  return result;
23983  }
23984  }
23985 
23986  /* If we get here it means an error occurred. */
23987  ma_zero_object(pContext); /* Safety. */
23988  return MA_NO_BACKEND;
23989 }
23990 
23992 {
23993  if (pContext == NULL) {
23994  return MA_INVALID_ARGS;
23995  }
23996 
23997  pContext->onUninit(pContext);
23998 
23999  ma_mutex_uninit(&pContext->deviceEnumLock);
24000  ma_mutex_uninit(&pContext->deviceInfoLock);
24001  ma_free(pContext->pDeviceInfos);
24002  ma_context_uninit_backend_apis(pContext);
24003 
24004  return MA_SUCCESS;
24005 }
24006 
24007 
24009 {
24010  ma_result result;
24011 
24012  if (pContext == NULL || pContext->onEnumDevices == NULL || callback == NULL) {
24013  return MA_INVALID_ARGS;
24014  }
24015 
24016  ma_mutex_lock(&pContext->deviceEnumLock);
24017  {
24018  result = pContext->onEnumDevices(pContext, callback, pUserData);
24019  }
24020  ma_mutex_unlock(&pContext->deviceEnumLock);
24021 
24022  return result;
24023 }
24024 
24025 
24026 ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
24027 {
24028  /*
24029  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
24030  it's just appended to the end. If it's a playback device it's inserted just before the first capture device.
24031  */
24032 
24033  /*
24034  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
24035  simple fixed size increment for buffer expansion.
24036  */
24037  const ma_uint32 bufferExpansionCount = 2;
24038  const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
24039 
24040  if (pContext->deviceInfoCapacity >= totalDeviceInfoCount) {
24041  ma_uint32 newCapacity = totalDeviceInfoCount + bufferExpansionCount;
24042  ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity);
24043  if (pNewInfos == NULL) {
24044  return MA_FALSE; /* Out of memory. */
24045  }
24046 
24047  pContext->pDeviceInfos = pNewInfos;
24048  pContext->deviceInfoCapacity = newCapacity;
24049  }
24050 
24051  if (deviceType == ma_device_type_playback) {
24052  /* Playback. Insert just before the first capture device. */
24053 
24054  /* The first thing to do is move all of the capture devices down a slot. */
24055  ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
24056  size_t iCaptureDevice;
24057  for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
24058  pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
24059  }
24060 
24061  /* Now just insert where the first capture device was before moving it down a slot. */
24062  pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
24063  pContext->playbackDeviceInfoCount += 1;
24064  } else {
24065  /* Capture. Insert at the end. */
24066  pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
24067  pContext->captureDeviceInfoCount += 1;
24068  }
24069 
24070  (void)pUserData;
24071  return MA_TRUE;
24072 }
24073 
24074 ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount)
24075 {
24076  ma_result result;
24077 
24078  /* Safety. */
24079  if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
24080  if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
24081  if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
24082  if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
24083 
24084  if (pContext == NULL || pContext->onEnumDevices == NULL) {
24085  return MA_INVALID_ARGS;
24086  }
24087 
24088  /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
24089  ma_mutex_lock(&pContext->deviceEnumLock);
24090  {
24091  /* Reset everything first. */
24092  pContext->playbackDeviceInfoCount = 0;
24093  pContext->captureDeviceInfoCount = 0;
24094 
24095  /* Now enumerate over available devices. */
24096  result = pContext->onEnumDevices(pContext, ma_context_get_devices__enum_callback, NULL);
24097  if (result == MA_SUCCESS) {
24098  /* Playback devices. */
24099  if (ppPlaybackDeviceInfos != NULL) {
24100  *ppPlaybackDeviceInfos = pContext->pDeviceInfos;
24101  }
24102  if (pPlaybackDeviceCount != NULL) {
24103  *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
24104  }
24105 
24106  /* Capture devices. */
24107  if (ppCaptureDeviceInfos != NULL) {
24108  *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; /* Capture devices come after playback devices. */
24109  }
24110  if (pCaptureDeviceCount != NULL) {
24111  *pCaptureDeviceCount = pContext->captureDeviceInfoCount;
24112  }
24113  }
24114  }
24115  ma_mutex_unlock(&pContext->deviceEnumLock);
24116 
24117  return result;
24118 }
24119 
24120 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)
24121 {
24122  ma_device_info deviceInfo;
24123 
24124  /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
24125  if (pContext == NULL || pDeviceInfo == NULL) {
24126  return MA_INVALID_ARGS;
24127  }
24128 
24129  ma_zero_object(&deviceInfo);
24130 
24131  /* Help the backend out by copying over the device ID if we have one. */
24132  if (pDeviceID != NULL) {
24133  ma_copy_memory(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
24134  }
24135 
24136  /* The backend may have an optimized device info retrieval function. If so, try that first. */
24137  if (pContext->onGetDeviceInfo != NULL) {
24138  ma_result result;
24139  ma_mutex_lock(&pContext->deviceInfoLock);
24140  {
24141  result = pContext->onGetDeviceInfo(pContext, deviceType, pDeviceID, shareMode, &deviceInfo);
24142  }
24143  ma_mutex_unlock(&pContext->deviceInfoLock);
24144 
24145  /* Clamp ranges. */
24146  deviceInfo.minChannels = ma_max(deviceInfo.minChannels, MA_MIN_CHANNELS);
24147  deviceInfo.maxChannels = ma_min(deviceInfo.maxChannels, MA_MAX_CHANNELS);
24148  deviceInfo.minSampleRate = ma_max(deviceInfo.minSampleRate, MA_MIN_SAMPLE_RATE);
24149  deviceInfo.maxSampleRate = ma_min(deviceInfo.maxSampleRate, MA_MAX_SAMPLE_RATE);
24150 
24151  *pDeviceInfo = deviceInfo;
24152  return result;
24153  }
24154 
24155  /* Getting here means onGetDeviceInfo has not been set. */
24156  return MA_ERROR;
24157 }
24158 
24159 
24160 ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
24161 {
24162  ma_result result;
24163  ma_device_config config;
24164 
24165  if (pContext == NULL) {
24166  return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
24167  }
24168  if (pDevice == NULL) {
24169  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
24170  }
24171  if (pConfig == NULL) {
24172  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pConfig == NULL).", MA_INVALID_ARGS);
24173  }
24174 
24175  /* We need to make a copy of the config so we can set default values if they were left unset in the input config. */
24176  config = *pConfig;
24177 
24178  /* Basic config validation. */
24180  return ma_post_error(pDevice, 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);
24181  }
24182 
24184  if (config.capture.channels > MA_MAX_CHANNELS) {
24185  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Capture channel count cannot exceed 32.", MA_INVALID_DEVICE_CONFIG);
24186  }
24187  if (!ma__is_channel_map_valid(config.capture.channelMap, config.capture.channels)) {
24188  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid config. Capture channel map is invalid.", MA_INVALID_DEVICE_CONFIG);
24189  }
24190  }
24191 
24193  if (config.playback.channels > MA_MAX_CHANNELS) {
24194  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Playback channel count cannot exceed 32.", MA_INVALID_DEVICE_CONFIG);
24195  }
24196  if (!ma__is_channel_map_valid(config.playback.channelMap, config.playback.channels)) {
24197  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid config. Playback channel map is invalid.", MA_INVALID_DEVICE_CONFIG);
24198  }
24199  }
24200 
24201 
24202  ma_zero_object(pDevice);
24203  pDevice->pContext = pContext;
24204 
24205  /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
24206  pDevice->pUserData = config.pUserData;
24207  pDevice->onData = config.dataCallback;
24208  pDevice->onStop = config.stopCallback;
24209 
24210  if (((ma_uintptr)pDevice % sizeof(pDevice)) != 0) {
24211  if (pContext->logCallback) {
24212  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.");
24213  }
24214  }
24215 
24216  /*
24217  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
24218  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.
24219  */
24220  if (config.sampleRate == 0) {
24221  config.sampleRate = MA_DEFAULT_SAMPLE_RATE;
24222  pDevice->usingDefaultSampleRate = MA_TRUE;
24223  }
24224 
24225  if (config.capture.format == ma_format_unknown) {
24226  config.capture.format = MA_DEFAULT_FORMAT;
24227  pDevice->capture.usingDefaultFormat = MA_TRUE;
24228  }
24229  if (config.capture.channels == 0) {
24230  config.capture.channels = MA_DEFAULT_CHANNELS;
24231  pDevice->capture.usingDefaultChannels = MA_TRUE;
24232  }
24233  if (config.capture.channelMap[0] == MA_CHANNEL_NONE) {
24234  pDevice->capture.usingDefaultChannelMap = MA_TRUE;
24235  }
24236 
24237  if (config.playback.format == ma_format_unknown) {
24238  config.playback.format = MA_DEFAULT_FORMAT;
24239  pDevice->playback.usingDefaultFormat = MA_TRUE;
24240  }
24241  if (config.playback.channels == 0) {
24242  config.playback.channels = MA_DEFAULT_CHANNELS;
24243  pDevice->playback.usingDefaultChannels = MA_TRUE;
24244  }
24245  if (config.playback.channelMap[0] == MA_CHANNEL_NONE) {
24246  pDevice->playback.usingDefaultChannelMap = MA_TRUE;
24247  }
24248 
24249 
24250  /* Default buffer size. */
24251  if (config.bufferSizeInMilliseconds == 0 && config.bufferSizeInFrames == 0) {
24252  config.bufferSizeInMilliseconds = (config.performanceProfile == ma_performance_profile_low_latency) ? MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY : MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE;
24253  pDevice->usingDefaultBufferSize = MA_TRUE;
24254  }
24255 
24256  /* Default periods. */
24257  if (config.periods == 0) {
24258  config.periods = MA_DEFAULT_PERIODS;
24259  pDevice->usingDefaultPeriods = MA_TRUE;
24260  }
24261 
24262  /*
24263  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
24264  periods acting as a buffer in case the capture and playback devices get's slightly out of sync.
24265  */
24266  if (config.deviceType == ma_device_type_duplex && config.periods < 3) {
24267  config.periods = 3;
24268  }
24269 
24270 
24271  pDevice->type = config.deviceType;
24272  pDevice->sampleRate = config.sampleRate;
24273 
24274  pDevice->capture.shareMode = config.capture.shareMode;
24275  pDevice->capture.format = config.capture.format;
24276  pDevice->capture.channels = config.capture.channels;
24277  ma_channel_map_copy(pDevice->capture.channelMap, config.capture.channelMap, config.capture.channels);
24278 
24279  pDevice->playback.shareMode = config.playback.shareMode;
24280  pDevice->playback.format = config.playback.format;
24281  pDevice->playback.channels = config.playback.channels;
24282  ma_channel_map_copy(pDevice->playback.channelMap, config.playback.channelMap, config.playback.channels);
24283 
24284 
24285  /* The internal format, channel count and sample rate can be modified by the backend. */
24286  pDevice->capture.internalFormat = pDevice->capture.format;
24287  pDevice->capture.internalChannels = pDevice->capture.channels;
24288  pDevice->capture.internalSampleRate = pDevice->sampleRate;
24289  ma_channel_map_copy(pDevice->capture.internalChannelMap, pDevice->capture.channelMap, pDevice->capture.channels);
24290 
24291  pDevice->playback.internalFormat = pDevice->playback.format;
24292  pDevice->playback.internalChannels = pDevice->playback.channels;
24293  pDevice->playback.internalSampleRate = pDevice->sampleRate;
24294  ma_channel_map_copy(pDevice->playback.internalChannelMap, pDevice->playback.channelMap, pDevice->playback.channels);
24295 
24296 
24297  if (ma_mutex_init(pContext, &pDevice->lock) != MA_SUCCESS) {
24298  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to create mutex.", MA_FAILED_TO_CREATE_MUTEX);
24299  }
24300 
24301  /*
24302  When the device is started, the worker thread is the one that does the actual startup of the backend device. We
24303  use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
24304 
24305  Each of these semaphores is released internally by the worker thread when the work is completed. The start
24306  semaphore is also used to wake up the worker thread.
24307  */
24308  if (ma_event_init(pContext, &pDevice->wakeupEvent) != MA_SUCCESS) {
24309  ma_mutex_uninit(&pDevice->lock);
24310  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to create worker thread wakeup event.", MA_FAILED_TO_CREATE_EVENT);
24311  }
24312  if (ma_event_init(pContext, &pDevice->startEvent) != MA_SUCCESS) {
24313  ma_event_uninit(&pDevice->wakeupEvent);
24314  ma_mutex_uninit(&pDevice->lock);
24315  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to create worker thread start event.", MA_FAILED_TO_CREATE_EVENT);
24316  }
24317  if (ma_event_init(pContext, &pDevice->stopEvent) != MA_SUCCESS) {
24318  ma_event_uninit(&pDevice->startEvent);
24319  ma_event_uninit(&pDevice->wakeupEvent);
24320  ma_mutex_uninit(&pDevice->lock);
24321  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to create worker thread stop event.", MA_FAILED_TO_CREATE_EVENT);
24322  }
24323 
24324 
24325  result = pContext->onDeviceInit(pContext, &config, pDevice);
24326  if (result != MA_SUCCESS) {
24327  return MA_NO_BACKEND; /* The error message will have been posted with ma_post_error() by the source of the error so don't bother calling it here. */
24328  }
24329 
24330  ma_device__post_init_setup(pDevice, pConfig->deviceType);
24331 
24332 
24333  /* If the backend did not fill out a name for the device, try a generic method. */
24334  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24335  if (pDevice->capture.name[0] == '\0') {
24336  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) {
24337  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), (config.capture.pDeviceID == NULL) ? MA_DEFAULT_CAPTURE_DEVICE_NAME : "Capture Device", (size_t)-1);
24338  }
24339  }
24340  }
24341  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24342  if (pDevice->playback.name[0] == '\0') {
24343  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) {
24344  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), (config.playback.pDeviceID == NULL) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : "Playback Device", (size_t)-1);
24345  }
24346  }
24347  }
24348 
24349 
24350  /* Some backends don't require the worker thread. */
24351  if (!ma_context_is_backend_asynchronous(pContext)) {
24352  /* The worker thread. */
24353  if (ma_thread_create(pContext, &pDevice->thread, ma_worker_thread, pDevice) != MA_SUCCESS) {
24354  ma_device_uninit(pDevice);
24355  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to create worker thread.", MA_FAILED_TO_CREATE_THREAD);
24356  }
24357 
24358  /* Wait for the worker thread to put the device into it's stopped state for real. */
24359  ma_event_wait(&pDevice->stopEvent);
24360  } else {
24361  ma_device__set_state(pDevice, MA_STATE_STOPPED);
24362  }
24363 
24364 
24365 #ifdef MA_DEBUG_OUTPUT
24366  printf("[%s]\n", ma_get_backend_name(pDevice->pContext->backend));
24367  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24368  printf(" %s (%s)\n", pDevice->capture.name, "Capture");
24369  printf(" Format: %s -> %s\n", ma_get_format_name(pDevice->capture.format), ma_get_format_name(pDevice->capture.internalFormat));
24370  printf(" Channels: %d -> %d\n", pDevice->capture.channels, pDevice->capture.internalChannels);
24371  printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->capture.internalSampleRate);
24372  printf(" Buffer Size: %d/%d (%d)\n", pDevice->capture.internalBufferSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods));
24373  printf(" Conversion:\n");
24374  printf(" Pre Format Conversion: %s\n", pDevice->capture.converter.isPreFormatConversionRequired ? "YES" : "NO");
24375  printf(" Post Format Conversion: %s\n", pDevice->capture.converter.isPostFormatConversionRequired ? "YES" : "NO");
24376  printf(" Channel Routing: %s\n", pDevice->capture.converter.isChannelRoutingRequired ? "YES" : "NO");
24377  printf(" SRC: %s\n", pDevice->capture.converter.isSRCRequired ? "YES" : "NO");
24378  printf(" Channel Routing at Start: %s\n", pDevice->capture.converter.isChannelRoutingAtStart ? "YES" : "NO");
24379  printf(" Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
24380  }
24381  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24382  printf(" %s (%s)\n", pDevice->playback.name, "Playback");
24383  printf(" Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat));
24384  printf(" Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
24385  printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
24386  printf(" Buffer Size: %d/%d (%d)\n", pDevice->playback.internalBufferSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods));
24387  printf(" Conversion:\n");
24388  printf(" Pre Format Conversion: %s\n", pDevice->playback.converter.isPreFormatConversionRequired ? "YES" : "NO");
24389  printf(" Post Format Conversion: %s\n", pDevice->playback.converter.isPostFormatConversionRequired ? "YES" : "NO");
24390  printf(" Channel Routing: %s\n", pDevice->playback.converter.isChannelRoutingRequired ? "YES" : "NO");
24391  printf(" SRC: %s\n", pDevice->playback.converter.isSRCRequired ? "YES" : "NO");
24392  printf(" Channel Routing at Start: %s\n", pDevice->playback.converter.isChannelRoutingAtStart ? "YES" : "NO");
24393  printf(" Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
24394  }
24395 #endif
24396 
24397 
24398  ma_assert(ma_device__get_state(pDevice) == MA_STATE_STOPPED);
24399  return MA_SUCCESS;
24400 }
24401 
24402 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)
24403 {
24404  ma_result result;
24405  ma_context* pContext;
24406  ma_backend defaultBackends[ma_backend_null+1];
24407  ma_uint32 iBackend;
24408  ma_backend* pBackendsToIterate;
24409  ma_uint32 backendsToIterateCount;
24410 
24411  if (pConfig == NULL) {
24412  return MA_INVALID_ARGS;
24413  }
24414 
24415  pContext = (ma_context*)ma_malloc(sizeof(*pContext));
24416  if (pContext == NULL) {
24417  return MA_OUT_OF_MEMORY;
24418  }
24419 
24420  for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
24421  defaultBackends[iBackend] = (ma_backend)iBackend;
24422  }
24423 
24424  pBackendsToIterate = (ma_backend*)backends;
24425  backendsToIterateCount = backendCount;
24426  if (pBackendsToIterate == NULL) {
24427  pBackendsToIterate = (ma_backend*)defaultBackends;
24428  backendsToIterateCount = ma_countof(defaultBackends);
24429  }
24430 
24431  result = MA_NO_BACKEND;
24432 
24433  for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
24434  result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
24435  if (result == MA_SUCCESS) {
24436  result = ma_device_init(pContext, pConfig, pDevice);
24437  if (result == MA_SUCCESS) {
24438  break; /* Success. */
24439  } else {
24440  ma_context_uninit(pContext); /* Failure. */
24441  }
24442  }
24443  }
24444 
24445  if (result != MA_SUCCESS) {
24446  ma_free(pContext);
24447  return result;
24448  }
24449 
24450  pDevice->isOwnerOfContext = MA_TRUE;
24451  return result;
24452 }
24453 
24454 void ma_device_uninit(ma_device* pDevice)
24455 {
24456  if (!ma_device__is_initialized(pDevice)) {
24457  return;
24458  }
24459 
24460  /* 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. */
24461  if (ma_device_is_started(pDevice)) {
24462  ma_device_stop(pDevice);
24463  }
24464 
24465  /* Putting the device into an uninitialized state will make the worker thread return. */
24466  ma_device__set_state(pDevice, MA_STATE_UNINITIALIZED);
24467 
24468  /* Wake up the worker thread and wait for it to properly terminate. */
24469  if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
24470  ma_event_signal(&pDevice->wakeupEvent);
24471  ma_thread_wait(&pDevice->thread);
24472  }
24473 
24474  pDevice->pContext->onDeviceUninit(pDevice);
24475 
24476  ma_event_uninit(&pDevice->stopEvent);
24477  ma_event_uninit(&pDevice->startEvent);
24478  ma_event_uninit(&pDevice->wakeupEvent);
24479  ma_mutex_uninit(&pDevice->lock);
24480 
24481  if (pDevice->isOwnerOfContext) {
24482  ma_context_uninit(pDevice->pContext);
24483  ma_free(pDevice->pContext);
24484  }
24485 
24486  ma_zero_object(pDevice);
24487 }
24488 
24490 {
24491  if (pDevice == NULL) {
24492  return;
24493  }
24494 
24495  ma_atomic_exchange_ptr(&pDevice->onStop, proc);
24496 }
24497 
24499 {
24500  ma_result result;
24501 
24502  if (pDevice == NULL) {
24503  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
24504  }
24505 
24506  if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
24507  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
24508  }
24509 
24510  /*
24511  Starting the device doesn't do anything in synchronous mode because in that case it's started automatically with
24512  ma_device_write() and ma_device_read(). It's best to return an error so that the application can be aware that
24513  it's not doing it right.
24514  */
24515  if (!ma_device__is_async(pDevice)) {
24516  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called in synchronous mode. This should only be used in asynchronous/callback mode.", MA_DEVICE_NOT_INITIALIZED);
24517  }
24518 
24519  result = MA_ERROR;
24520  ma_mutex_lock(&pDevice->lock);
24521  {
24522  /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
24523  ma_assert(ma_device__get_state(pDevice) == MA_STATE_STOPPED);
24524 
24525  ma_device__set_state(pDevice, MA_STATE_STARTING);
24526 
24527  /* Asynchronous backends need to be handled differently. */
24528  if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
24529  result = pDevice->pContext->onDeviceStart(pDevice);
24530  if (result == MA_SUCCESS) {
24531  ma_device__set_state(pDevice, MA_STATE_STARTED);
24532  }
24533  } else {
24534  /*
24535  Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
24536  thread and then wait for the start event.
24537  */
24538  ma_event_signal(&pDevice->wakeupEvent);
24539 
24540  /*
24541  Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
24542  into the started state. Don't call ma_device__set_state() here.
24543  */
24544  ma_event_wait(&pDevice->startEvent);
24545  result = pDevice->workResult;
24546  }
24547  }
24548  ma_mutex_unlock(&pDevice->lock);
24549 
24550  return result;
24551 }
24552 
24554 {
24555  ma_result result;
24556 
24557  if (pDevice == NULL) {
24558  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
24559  }
24560 
24561  if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
24562  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
24563  }
24564 
24565  /*
24566  Stopping is slightly different for synchronous mode. In this case it just tells the driver to stop the internal processing of the device. Also,
24567  stopping in synchronous mode does not require state checking.
24568  */
24569  if (!ma_device__is_async(pDevice)) {
24570  if (pDevice->pContext->onDeviceStop) {
24571  return pDevice->pContext->onDeviceStop(pDevice);
24572  }
24573  }
24574 
24575  result = MA_ERROR;
24576  ma_mutex_lock(&pDevice->lock);
24577  {
24578  /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
24579  ma_assert(ma_device__get_state(pDevice) == MA_STATE_STARTED);
24580 
24581  ma_device__set_state(pDevice, MA_STATE_STOPPING);
24582 
24583  /* There's no need to wake up the thread like we do when starting. */
24584 
24585  /* Asynchronous backends need to be handled differently. */
24586  if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
24587  if (pDevice->pContext->onDeviceStop) {
24588  result = pDevice->pContext->onDeviceStop(pDevice);
24589  } else {
24590  result = MA_SUCCESS;
24591  }
24592 
24593  ma_device__set_state(pDevice, MA_STATE_STOPPED);
24594  } else {
24595  /* Synchronous backends. */
24596 
24597  /*
24598  We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
24599  the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
24600  */
24601  ma_event_wait(&pDevice->stopEvent);
24602  result = MA_SUCCESS;
24603  }
24604  }
24605  ma_mutex_unlock(&pDevice->lock);
24606 
24607  return result;
24608 }
24609 
24611 {
24612  if (pDevice == NULL) {
24613  return MA_FALSE;
24614  }
24615 
24616  return ma_device__get_state(pDevice) == MA_STATE_STARTED;
24617 }
24618 
24619 
24621 {
24622  ma_context_config config;
24623  ma_zero_object(&config);
24624 
24625  return config;
24626 }
24627 
24629 {
24630  ma_device_config config;
24631  ma_zero_object(&config);
24632  config.deviceType = deviceType;
24633 
24634  return config;
24635 }
24636 #endif /* MA_NO_DEVICE_IO */
24637 
24638 
24639 void ma_get_standard_channel_map_microsoft(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
24640 {
24641  /* Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */
24642  switch (channels)
24643  {
24644  case 1:
24645  {
24646  channelMap[0] = MA_CHANNEL_MONO;
24647  } break;
24648 
24649  case 2:
24650  {
24651  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24652  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24653  } break;
24654 
24655  case 3: /* Not defined, but best guess. */
24656  {
24657  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24658  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24659  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24660  } break;
24661 
24662  case 4:
24663  {
24664 #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
24665  /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
24666  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24667  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24668  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24669  channelMap[3] = MA_CHANNEL_BACK_CENTER;
24670 #else
24671  /* Quad. */
24672  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24673  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24674  channelMap[2] = MA_CHANNEL_BACK_LEFT;
24675  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
24676 #endif
24677  } break;
24678 
24679  case 5: /* Not defined, but best guess. */
24680  {
24681  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24682  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24683  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24684  channelMap[3] = MA_CHANNEL_BACK_LEFT;
24685  channelMap[4] = MA_CHANNEL_BACK_RIGHT;
24686  } break;
24687 
24688  case 6:
24689  {
24690  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24691  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24692  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24693  channelMap[3] = MA_CHANNEL_LFE;
24694  channelMap[4] = MA_CHANNEL_SIDE_LEFT;
24695  channelMap[5] = MA_CHANNEL_SIDE_RIGHT;
24696  } break;
24697 
24698  case 7: /* Not defined, but best guess. */
24699  {
24700  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24701  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24702  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24703  channelMap[3] = MA_CHANNEL_LFE;
24704  channelMap[4] = MA_CHANNEL_BACK_CENTER;
24705  channelMap[5] = MA_CHANNEL_SIDE_LEFT;
24706  channelMap[6] = MA_CHANNEL_SIDE_RIGHT;
24707  } break;
24708 
24709  case 8:
24710  default:
24711  {
24712  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24713  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24714  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24715  channelMap[3] = MA_CHANNEL_LFE;
24716  channelMap[4] = MA_CHANNEL_BACK_LEFT;
24717  channelMap[5] = MA_CHANNEL_BACK_RIGHT;
24718  channelMap[6] = MA_CHANNEL_SIDE_LEFT;
24719  channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
24720  } break;
24721  }
24722 
24723  /* Remainder. */
24724  if (channels > 8) {
24725  ma_uint32 iChannel;
24726  for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
24727  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
24728  }
24729  }
24730 }
24731 
24732 void ma_get_standard_channel_map_alsa(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
24733 {
24734  switch (channels)
24735  {
24736  case 1:
24737  {
24738  channelMap[0] = MA_CHANNEL_MONO;
24739  } break;
24740 
24741  case 2:
24742  {
24743  channelMap[0] = MA_CHANNEL_LEFT;
24744  channelMap[1] = MA_CHANNEL_RIGHT;
24745  } break;
24746 
24747  case 3:
24748  {
24749  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24750  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24751  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24752  } break;
24753 
24754  case 4:
24755  {
24756  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24757  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24758  channelMap[2] = MA_CHANNEL_BACK_LEFT;
24759  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
24760  } break;
24761 
24762  case 5:
24763  {
24764  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24765  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24766  channelMap[2] = MA_CHANNEL_BACK_LEFT;
24767  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
24768  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
24769  } break;
24770 
24771  case 6:
24772  {
24773  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24774  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24775  channelMap[2] = MA_CHANNEL_BACK_LEFT;
24776  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
24777  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
24778  channelMap[5] = MA_CHANNEL_LFE;
24779  } break;
24780 
24781  case 7:
24782  {
24783  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24784  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24785  channelMap[2] = MA_CHANNEL_BACK_LEFT;
24786  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
24787  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
24788  channelMap[5] = MA_CHANNEL_LFE;
24789  channelMap[6] = MA_CHANNEL_BACK_CENTER;
24790  } break;
24791 
24792  case 8:
24793  default:
24794  {
24795  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24796  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24797  channelMap[2] = MA_CHANNEL_BACK_LEFT;
24798  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
24799  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
24800  channelMap[5] = MA_CHANNEL_LFE;
24801  channelMap[6] = MA_CHANNEL_SIDE_LEFT;
24802  channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
24803  } break;
24804  }
24805 
24806  /* Remainder. */
24807  if (channels > 8) {
24808  ma_uint32 iChannel;
24809  for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
24810  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
24811  }
24812  }
24813 }
24814 
24815 void ma_get_standard_channel_map_rfc3551(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
24816 {
24817  switch (channels)
24818  {
24819  case 1:
24820  {
24821  channelMap[0] = MA_CHANNEL_MONO;
24822  } break;
24823 
24824  case 2:
24825  {
24826  channelMap[0] = MA_CHANNEL_LEFT;
24827  channelMap[1] = MA_CHANNEL_RIGHT;
24828  } break;
24829 
24830  case 3:
24831  {
24832  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24833  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24834  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24835  } break;
24836 
24837  case 4:
24838  {
24839  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24840  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
24841  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
24842  channelMap[3] = MA_CHANNEL_BACK_CENTER;
24843  } break;
24844 
24845  case 5:
24846  {
24847  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24848  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24849  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24850  channelMap[3] = MA_CHANNEL_BACK_LEFT;
24851  channelMap[4] = MA_CHANNEL_BACK_RIGHT;
24852  } break;
24853 
24854  case 6:
24855  {
24856  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24857  channelMap[1] = MA_CHANNEL_SIDE_LEFT;
24858  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24859  channelMap[3] = MA_CHANNEL_FRONT_RIGHT;
24860  channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
24861  channelMap[5] = MA_CHANNEL_BACK_CENTER;
24862  } break;
24863  }
24864 
24865  /* Remainder. */
24866  if (channels > 8) {
24867  ma_uint32 iChannel;
24868  for (iChannel = 6; iChannel < MA_MAX_CHANNELS; ++iChannel) {
24869  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
24870  }
24871  }
24872 }
24873 
24874 void ma_get_standard_channel_map_flac(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
24875 {
24876  switch (channels)
24877  {
24878  case 1:
24879  {
24880  channelMap[0] = MA_CHANNEL_MONO;
24881  } break;
24882 
24883  case 2:
24884  {
24885  channelMap[0] = MA_CHANNEL_LEFT;
24886  channelMap[1] = MA_CHANNEL_RIGHT;
24887  } break;
24888 
24889  case 3:
24890  {
24891  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24892  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24893  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24894  } break;
24895 
24896  case 4:
24897  {
24898  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24899  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24900  channelMap[2] = MA_CHANNEL_BACK_LEFT;
24901  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
24902  } break;
24903 
24904  case 5:
24905  {
24906  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24907  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24908  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24909  channelMap[3] = MA_CHANNEL_BACK_LEFT;
24910  channelMap[4] = MA_CHANNEL_BACK_RIGHT;
24911  } break;
24912 
24913  case 6:
24914  {
24915  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24916  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24917  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24918  channelMap[3] = MA_CHANNEL_LFE;
24919  channelMap[4] = MA_CHANNEL_BACK_LEFT;
24920  channelMap[5] = MA_CHANNEL_BACK_RIGHT;
24921  } break;
24922 
24923  case 7:
24924  {
24925  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24926  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24927  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24928  channelMap[3] = MA_CHANNEL_LFE;
24929  channelMap[4] = MA_CHANNEL_BACK_CENTER;
24930  channelMap[5] = MA_CHANNEL_SIDE_LEFT;
24931  channelMap[6] = MA_CHANNEL_SIDE_RIGHT;
24932  } break;
24933 
24934  case 8:
24935  default:
24936  {
24937  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24938  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24939  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
24940  channelMap[3] = MA_CHANNEL_LFE;
24941  channelMap[4] = MA_CHANNEL_BACK_LEFT;
24942  channelMap[5] = MA_CHANNEL_BACK_RIGHT;
24943  channelMap[6] = MA_CHANNEL_SIDE_LEFT;
24944  channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
24945  } break;
24946  }
24947 
24948  /* Remainder. */
24949  if (channels > 8) {
24950  ma_uint32 iChannel;
24951  for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
24952  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
24953  }
24954  }
24955 }
24956 
24957 void ma_get_standard_channel_map_vorbis(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
24958 {
24959  /* 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?! */
24960  switch (channels)
24961  {
24962  case 1:
24963  {
24964  channelMap[0] = MA_CHANNEL_MONO;
24965  } break;
24966 
24967  case 2:
24968  {
24969  channelMap[0] = MA_CHANNEL_LEFT;
24970  channelMap[1] = MA_CHANNEL_RIGHT;
24971  } break;
24972 
24973  case 3:
24974  {
24975  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24976  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
24977  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
24978  } break;
24979 
24980  case 4:
24981  {
24982  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24983  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
24984  channelMap[2] = MA_CHANNEL_BACK_LEFT;
24985  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
24986  } break;
24987 
24988  case 5:
24989  {
24990  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
24991  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
24992  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
24993  channelMap[3] = MA_CHANNEL_BACK_LEFT;
24994  channelMap[4] = MA_CHANNEL_BACK_RIGHT;
24995  } break;
24996 
24997  case 6:
24998  {
24999  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25000  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
25001  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
25002  channelMap[3] = MA_CHANNEL_BACK_LEFT;
25003  channelMap[4] = MA_CHANNEL_BACK_RIGHT;
25004  channelMap[5] = MA_CHANNEL_LFE;
25005  } break;
25006 
25007  case 7:
25008  {
25009  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25010  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
25011  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
25012  channelMap[3] = MA_CHANNEL_SIDE_LEFT;
25013  channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
25014  channelMap[5] = MA_CHANNEL_BACK_CENTER;
25015  channelMap[6] = MA_CHANNEL_LFE;
25016  } break;
25017 
25018  case 8:
25019  default:
25020  {
25021  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25022  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
25023  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
25024  channelMap[3] = MA_CHANNEL_SIDE_LEFT;
25025  channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
25026  channelMap[5] = MA_CHANNEL_BACK_LEFT;
25027  channelMap[6] = MA_CHANNEL_BACK_RIGHT;
25028  channelMap[7] = MA_CHANNEL_LFE;
25029  } break;
25030  }
25031 
25032  /* Remainder. */
25033  if (channels > 8) {
25034  ma_uint32 iChannel;
25035  for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
25036  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
25037  }
25038  }
25039 }
25040 
25041 void ma_get_standard_channel_map_sound4(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
25042 {
25043  switch (channels)
25044  {
25045  case 1:
25046  {
25047  channelMap[0] = MA_CHANNEL_MONO;
25048  } break;
25049 
25050  case 2:
25051  {
25052  channelMap[0] = MA_CHANNEL_LEFT;
25053  channelMap[1] = MA_CHANNEL_RIGHT;
25054  } break;
25055 
25056  case 3:
25057  {
25058  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25059  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
25060  channelMap[2] = MA_CHANNEL_BACK_CENTER;
25061  } break;
25062 
25063  case 4:
25064  {
25065  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25066  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
25067  channelMap[2] = MA_CHANNEL_BACK_LEFT;
25068  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
25069  } break;
25070 
25071  case 5:
25072  {
25073  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25074  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
25075  channelMap[2] = MA_CHANNEL_BACK_LEFT;
25076  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
25077  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
25078  } break;
25079 
25080  case 6:
25081  {
25082  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25083  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
25084  channelMap[2] = MA_CHANNEL_BACK_LEFT;
25085  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
25086  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
25087  channelMap[5] = MA_CHANNEL_LFE;
25088  } break;
25089 
25090  case 7:
25091  {
25092  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25093  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
25094  channelMap[2] = MA_CHANNEL_BACK_LEFT;
25095  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
25096  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
25097  channelMap[5] = MA_CHANNEL_BACK_CENTER;
25098  channelMap[6] = MA_CHANNEL_LFE;
25099  } break;
25100 
25101  case 8:
25102  default:
25103  {
25104  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25105  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
25106  channelMap[2] = MA_CHANNEL_BACK_LEFT;
25107  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
25108  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
25109  channelMap[5] = MA_CHANNEL_LFE;
25110  channelMap[6] = MA_CHANNEL_SIDE_LEFT;
25111  channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
25112  } break;
25113  }
25114 
25115  /* Remainder. */
25116  if (channels > 8) {
25117  ma_uint32 iChannel;
25118  for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
25119  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
25120  }
25121  }
25122 }
25123 
25124 void ma_get_standard_channel_map_sndio(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
25125 {
25126  switch (channels)
25127  {
25128  case 1:
25129  {
25130  channelMap[0] = MA_CHANNEL_MONO;
25131  } break;
25132 
25133  case 2:
25134  {
25135  channelMap[0] = MA_CHANNEL_LEFT;
25136  channelMap[1] = MA_CHANNEL_RIGHT;
25137  } break;
25138 
25139  case 3:
25140  {
25141  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25142  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
25143  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
25144  } break;
25145 
25146  case 4:
25147  {
25148  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25149  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
25150  channelMap[2] = MA_CHANNEL_BACK_LEFT;
25151  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
25152  } break;
25153 
25154  case 5:
25155  {
25156  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25157  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
25158  channelMap[2] = MA_CHANNEL_BACK_LEFT;
25159  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
25160  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
25161  } break;
25162 
25163  case 6:
25164  default:
25165  {
25166  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
25167  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
25168  channelMap[2] = MA_CHANNEL_BACK_LEFT;
25169  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
25170  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
25171  channelMap[5] = MA_CHANNEL_LFE;
25172  } break;
25173  }
25174 
25175  /* Remainder. */
25176  if (channels > 6) {
25177  ma_uint32 iChannel;
25178  for (iChannel = 6; iChannel < MA_MAX_CHANNELS; ++iChannel) {
25179  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
25180  }
25181  }
25182 }
25183 
25185 {
25186  switch (standardChannelMap)
25187  {
25189  {
25190  ma_get_standard_channel_map_alsa(channels, channelMap);
25191  } break;
25192 
25194  {
25195  ma_get_standard_channel_map_rfc3551(channels, channelMap);
25196  } break;
25197 
25199  {
25200  ma_get_standard_channel_map_flac(channels, channelMap);
25201  } break;
25202 
25204  {
25205  ma_get_standard_channel_map_vorbis(channels, channelMap);
25206  } break;
25207 
25209  {
25210  ma_get_standard_channel_map_sound4(channels, channelMap);
25211  } break;
25212 
25214  {
25215  ma_get_standard_channel_map_sndio(channels, channelMap);
25216  } break;
25217 
25219  default:
25220  {
25221  ma_get_standard_channel_map_microsoft(channels, channelMap);
25222  } break;
25223  }
25224 }
25225 
25227 {
25228  if (pOut != NULL && pIn != NULL && channels > 0) {
25229  ma_copy_memory(pOut, pIn, sizeof(*pOut) * channels);
25230  }
25231 }
25232 
25234 {
25235  if (channelMap == NULL) {
25236  return MA_FALSE;
25237  }
25238 
25239  /* A channel count of 0 is invalid. */
25240  if (channels == 0) {
25241  return MA_FALSE;
25242  }
25243 
25244  /* It does not make sense to have a mono channel when there is more than 1 channel. */
25245  if (channels > 1) {
25246  ma_uint32 iChannel;
25247  for (iChannel = 0; iChannel < channels; ++iChannel) {
25248  if (channelMap[iChannel] == MA_CHANNEL_MONO) {
25249  return MA_FALSE;
25250  }
25251  }
25252  }
25253 
25254  return MA_TRUE;
25255 }
25256 
25258 {
25259  ma_uint32 iChannel;
25260 
25261  if (channelMapA == channelMapB) {
25262  return MA_FALSE;
25263  }
25264 
25265  if (channels == 0 || channels > MA_MAX_CHANNELS) {
25266  return MA_FALSE;
25267  }
25268 
25269  for (iChannel = 0; iChannel < channels; ++iChannel) {
25270  if (channelMapA[iChannel] != channelMapB[iChannel]) {
25271  return MA_FALSE;
25272  }
25273  }
25274 
25275  return MA_TRUE;
25276 }
25277 
25279 {
25280  ma_uint32 iChannel;
25281 
25282  for (iChannel = 0; iChannel < channels; ++iChannel) {
25283  if (channelMap[iChannel] != MA_CHANNEL_NONE) {
25284  return MA_FALSE;
25285  }
25286  }
25287 
25288  return MA_TRUE;
25289 }
25290 
25292 {
25293  ma_uint32 iChannel;
25294  for (iChannel = 0; iChannel < channels; ++iChannel) {
25295  if (channelMap[iChannel] == channelPosition) {
25296  return MA_TRUE;
25297  }
25298  }
25299 
25300  return MA_FALSE;
25301 }
25302 
25303 
25304 
25305 
25306 /**************************************************************************************************************************************************************
25307 
25308 Format Conversion.
25309 
25310 **************************************************************************************************************************************************************/
25311 void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
25312 {
25313 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
25314  ma_copy_memory(dst, src, (size_t)sizeInBytes);
25315 #else
25316  while (sizeInBytes > 0) {
25317  ma_uint64 bytesToCopyNow = sizeInBytes;
25318  if (bytesToCopyNow > MA_SIZE_MAX) {
25319  bytesToCopyNow = MA_SIZE_MAX;
25320  }
25321 
25322  ma_copy_memory(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */
25323 
25324  sizeInBytes -= bytesToCopyNow;
25325  dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow);
25326  src = (const void*)((const ma_uint8*)src + bytesToCopyNow);
25327  }
25328 #endif
25329 }
25330 
25331 void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)
25332 {
25333 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
25334  ma_zero_memory(dst, (size_t)sizeInBytes);
25335 #else
25336  while (sizeInBytes > 0) {
25337  ma_uint64 bytesToZeroNow = sizeInBytes;
25338  if (bytesToZeroNow > MA_SIZE_MAX) {
25339  bytesToZeroNow = MA_SIZE_MAX;
25340  }
25341 
25342  ma_zero_memory(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */
25343 
25344  sizeInBytes -= bytesToZeroNow;
25345  dst = (void*)((ma_uint8*)dst + bytesToZeroNow);
25346  }
25347 #endif
25348 }
25349 
25350 
25351 /* u8 */
25352 void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25353 {
25354  (void)ditherMode;
25355  ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
25356 }
25357 
25358 
25359 void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25360 {
25361  ma_int16* dst_s16 = (ma_int16*)dst;
25362  const ma_uint8* src_u8 = (const ma_uint8*)src;
25363 
25364  ma_uint64 i;
25365  for (i = 0; i < count; i += 1) {
25366  ma_int16 x = src_u8[i];
25367  x = x - 128;
25368  x = x << 8;
25369  dst_s16[i] = x;
25370  }
25371 
25372  (void)ditherMode;
25373 }
25374 
25375 void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25376 {
25377  ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
25378 }
25379 
25380 #if defined(MA_SUPPORT_SSE2)
25381 void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25382 {
25383  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
25384 }
25385 #endif
25386 #if defined(MA_SUPPORT_AVX2)
25387 void ma_pcm_u8_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25388 {
25389  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
25390 }
25391 #endif
25392 #if defined(MA_SUPPORT_AVX512)
25393 void ma_pcm_u8_to_s16__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25394 {
25395  ma_pcm_u8_to_s16__avx2(dst, src, count, ditherMode);
25396 }
25397 #endif
25398 #if defined(MA_SUPPORT_NEON)
25399 void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25400 {
25401  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
25402 }
25403 #endif
25404 
25405 void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25406 {
25407 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25408  ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
25409 #else
25410  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
25411 #endif
25412 }
25413 
25414 
25415 void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25416 {
25417  ma_uint8* dst_s24 = (ma_uint8*)dst;
25418  const ma_uint8* src_u8 = (const ma_uint8*)src;
25419 
25420  ma_uint64 i;
25421  for (i = 0; i < count; i += 1) {
25422  ma_int16 x = src_u8[i];
25423  x = x - 128;
25424 
25425  dst_s24[i*3+0] = 0;
25426  dst_s24[i*3+1] = 0;
25427  dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
25428  }
25429 
25430  (void)ditherMode;
25431 }
25432 
25433 void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25434 {
25435  ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
25436 }
25437 
25438 #if defined(MA_SUPPORT_SSE2)
25439 void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25440 {
25441  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
25442 }
25443 #endif
25444 #if defined(MA_SUPPORT_AVX2)
25445 void ma_pcm_u8_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25446 {
25447  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
25448 }
25449 #endif
25450 #if defined(MA_SUPPORT_AVX512)
25451 void ma_pcm_u8_to_s24__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25452 {
25453  ma_pcm_u8_to_s24__avx2(dst, src, count, ditherMode);
25454 }
25455 #endif
25456 #if defined(MA_SUPPORT_NEON)
25457 void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25458 {
25459  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
25460 }
25461 #endif
25462 
25463 void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25464 {
25465 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25466  ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
25467 #else
25468  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
25469 #endif
25470 }
25471 
25472 
25473 void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25474 {
25475  ma_int32* dst_s32 = (ma_int32*)dst;
25476  const ma_uint8* src_u8 = (const ma_uint8*)src;
25477 
25478  ma_uint64 i;
25479  for (i = 0; i < count; i += 1) {
25480  ma_int32 x = src_u8[i];
25481  x = x - 128;
25482  x = x << 24;
25483  dst_s32[i] = x;
25484  }
25485 
25486  (void)ditherMode;
25487 }
25488 
25489 void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25490 {
25491  ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
25492 }
25493 
25494 #if defined(MA_SUPPORT_SSE2)
25495 void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25496 {
25497  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
25498 }
25499 #endif
25500 #if defined(MA_SUPPORT_AVX2)
25501 void ma_pcm_u8_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25502 {
25503  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
25504 }
25505 #endif
25506 #if defined(MA_SUPPORT_AVX512)
25507 void ma_pcm_u8_to_s32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25508 {
25509  ma_pcm_u8_to_s32__avx2(dst, src, count, ditherMode);
25510 }
25511 #endif
25512 #if defined(MA_SUPPORT_NEON)
25513 void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25514 {
25515  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
25516 }
25517 #endif
25518 
25519 void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25520 {
25521 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25522  ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
25523 #else
25524  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
25525 #endif
25526 }
25527 
25528 
25529 void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25530 {
25531  float* dst_f32 = (float*)dst;
25532  const ma_uint8* src_u8 = (const ma_uint8*)src;
25533 
25534  ma_uint64 i;
25535  for (i = 0; i < count; i += 1) {
25536  float x = (float)src_u8[i];
25537  x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
25538  x = x - 1; /* 0..2 to -1..1 */
25539 
25540  dst_f32[i] = x;
25541  }
25542 
25543  (void)ditherMode;
25544 }
25545 
25546 void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25547 {
25548  ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
25549 }
25550 
25551 #if defined(MA_SUPPORT_SSE2)
25552 void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25553 {
25554  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
25555 }
25556 #endif
25557 #if defined(MA_SUPPORT_AVX2)
25558 void ma_pcm_u8_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25559 {
25560  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
25561 }
25562 #endif
25563 #if defined(MA_SUPPORT_AVX512)
25564 void ma_pcm_u8_to_f32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25565 {
25566  ma_pcm_u8_to_f32__avx2(dst, src, count, ditherMode);
25567 }
25568 #endif
25569 #if defined(MA_SUPPORT_NEON)
25570 void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25571 {
25572  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
25573 }
25574 #endif
25575 
25576 void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25577 {
25578 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25579  ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
25580 #else
25581  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
25582 #endif
25583 }
25584 
25585 
25586 
25587 void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
25588 {
25589  ma_uint8* dst_u8 = (ma_uint8*)dst;
25590  const ma_uint8** src_u8 = (const ma_uint8**)src;
25591 
25592  ma_uint64 iFrame;
25593  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
25594  ma_uint32 iChannel;
25595  for (iChannel = 0; iChannel < channels; iChannel += 1) {
25596  dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
25597  }
25598  }
25599 }
25600 
25601 void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
25602 {
25603  ma_uint8* dst_u8 = (ma_uint8*)dst;
25604  const ma_uint8** src_u8 = (const ma_uint8**)src;
25605 
25606  if (channels == 1) {
25607  ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
25608  } else if (channels == 2) {
25609  ma_uint64 iFrame;
25610  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
25611  dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
25612  dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
25613  }
25614  } else {
25615  ma_uint64 iFrame;
25616  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
25617  ma_uint32 iChannel;
25618  for (iChannel = 0; iChannel < channels; iChannel += 1) {
25619  dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
25620  }
25621  }
25622  }
25623 }
25624 
25625 void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
25626 {
25627 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25628  ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
25629 #else
25630  ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
25631 #endif
25632 }
25633 
25634 
25635 void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
25636 {
25637  ma_uint8** dst_u8 = (ma_uint8**)dst;
25638  const ma_uint8* src_u8 = (const ma_uint8*)src;
25639 
25640  ma_uint64 iFrame;
25641  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
25642  ma_uint32 iChannel;
25643  for (iChannel = 0; iChannel < channels; iChannel += 1) {
25644  dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
25645  }
25646  }
25647 }
25648 
25649 void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
25650 {
25651  ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
25652 }
25653 
25654 void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
25655 {
25656 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25657  ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
25658 #else
25659  ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
25660 #endif
25661 }
25662 
25663 
25664 /* s16 */
25665 void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25666 {
25667  ma_uint8* dst_u8 = (ma_uint8*)dst;
25668  const ma_int16* src_s16 = (const ma_int16*)src;
25669 
25670  if (ditherMode == ma_dither_mode_none) {
25671  ma_uint64 i;
25672  for (i = 0; i < count; i += 1) {
25673  ma_int16 x = src_s16[i];
25674  x = x >> 8;
25675  x = x + 128;
25676  dst_u8[i] = (ma_uint8)x;
25677  }
25678  } else {
25679  ma_uint64 i;
25680  for (i = 0; i < count; i += 1) {
25681  ma_int16 x = src_s16[i];
25682 
25683  /* Dither. Don't overflow. */
25684  ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
25685  if ((x + dither) <= 0x7FFF) {
25686  x = (ma_int16)(x + dither);
25687  } else {
25688  x = 0x7FFF;
25689  }
25690 
25691  x = x >> 8;
25692  x = x + 128;
25693  dst_u8[i] = (ma_uint8)x;
25694  }
25695  }
25696 }
25697 
25698 void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25699 {
25700  ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
25701 }
25702 
25703 #if defined(MA_SUPPORT_SSE2)
25704 void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25705 {
25706  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
25707 }
25708 #endif
25709 #if defined(MA_SUPPORT_AVX2)
25710 void ma_pcm_s16_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25711 {
25712  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
25713 }
25714 #endif
25715 #if defined(MA_SUPPORT_AVX512)
25716 void ma_pcm_s16_to_u8__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25717 {
25718  ma_pcm_s16_to_u8__avx2(dst, src, count, ditherMode);
25719 }
25720 #endif
25721 #if defined(MA_SUPPORT_NEON)
25722 void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25723 {
25724  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
25725 }
25726 #endif
25727 
25728 void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25729 {
25730 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25731  ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
25732 #else
25733  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
25734 #endif
25735 }
25736 
25737 
25738 void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25739 {
25740  (void)ditherMode;
25741  ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
25742 }
25743 
25744 
25745 void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25746 {
25747  ma_uint8* dst_s24 = (ma_uint8*)dst;
25748  const ma_int16* src_s16 = (const ma_int16*)src;
25749 
25750  ma_uint64 i;
25751  for (i = 0; i < count; i += 1) {
25752  dst_s24[i*3+0] = 0;
25753  dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
25754  dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
25755  }
25756 
25757  (void)ditherMode;
25758 }
25759 
25760 void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25761 {
25762  ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
25763 }
25764 
25765 #if defined(MA_SUPPORT_SSE2)
25766 void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25767 {
25768  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
25769 }
25770 #endif
25771 #if defined(MA_SUPPORT_AVX2)
25772 void ma_pcm_s16_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25773 {
25774  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
25775 }
25776 #endif
25777 #if defined(MA_SUPPORT_AVX512)
25778 void ma_pcm_s16_to_s24__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25779 {
25780  ma_pcm_s16_to_s24__avx2(dst, src, count, ditherMode);
25781 }
25782 #endif
25783 #if defined(MA_SUPPORT_NEON)
25784 void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25785 {
25786  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
25787 }
25788 #endif
25789 
25790 void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25791 {
25792 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25793  ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
25794 #else
25795  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
25796 #endif
25797 }
25798 
25799 
25800 void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25801 {
25802  ma_int32* dst_s32 = (ma_int32*)dst;
25803  const ma_int16* src_s16 = (const ma_int16*)src;
25804 
25805  ma_uint64 i;
25806  for (i = 0; i < count; i += 1) {
25807  dst_s32[i] = src_s16[i] << 16;
25808  }
25809 
25810  (void)ditherMode;
25811 }
25812 
25813 void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25814 {
25815  ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
25816 }
25817 
25818 #if defined(MA_SUPPORT_SSE2)
25819 void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25820 {
25821  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
25822 }
25823 #endif
25824 #if defined(MA_SUPPORT_AVX2)
25825 void ma_pcm_s16_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25826 {
25827  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
25828 }
25829 #endif
25830 #if defined(MA_SUPPORT_AVX512)
25831 void ma_pcm_s16_to_s32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25832 {
25833  ma_pcm_s16_to_s32__avx2(dst, src, count, ditherMode);
25834 }
25835 #endif
25836 #if defined(MA_SUPPORT_NEON)
25837 void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25838 {
25839  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
25840 }
25841 #endif
25842 
25843 void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25844 {
25845 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25846  ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
25847 #else
25848  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
25849 #endif
25850 }
25851 
25852 
25853 void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25854 {
25855  float* dst_f32 = (float*)dst;
25856  const ma_int16* src_s16 = (const ma_int16*)src;
25857 
25858  ma_uint64 i;
25859  for (i = 0; i < count; i += 1) {
25860  float x = (float)src_s16[i];
25861 
25862 #if 0
25863  /* The accurate way. */
25864  x = x + 32768.0f; /* -32768..32767 to 0..65535 */
25865  x = x * 0.00003051804379339284f; /* 0..65536 to 0..2 */
25866  x = x - 1; /* 0..2 to -1..1 */
25867 #else
25868  /* The fast way. */
25869  x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
25870 #endif
25871 
25872  dst_f32[i] = x;
25873  }
25874 
25875  (void)ditherMode;
25876 }
25877 
25878 void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25879 {
25880  ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
25881 }
25882 
25883 #if defined(MA_SUPPORT_SSE2)
25884 void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25885 {
25886  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
25887 }
25888 #endif
25889 #if defined(MA_SUPPORT_AVX2)
25890 void ma_pcm_s16_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25891 {
25892  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
25893 }
25894 #endif
25895 #if defined(MA_SUPPORT_AVX512)
25896 void ma_pcm_s16_to_f32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25897 {
25898  ma_pcm_s16_to_f32__avx2(dst, src, count, ditherMode);
25899 }
25900 #endif
25901 #if defined(MA_SUPPORT_NEON)
25902 void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25903 {
25904  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
25905 }
25906 #endif
25907 
25908 void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25909 {
25910 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25911  ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
25912 #else
25913  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
25914 #endif
25915 }
25916 
25917 
25918 void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
25919 {
25920  ma_int16* dst_s16 = (ma_int16*)dst;
25921  const ma_int16** src_s16 = (const ma_int16**)src;
25922 
25923  ma_uint64 iFrame;
25924  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
25925  ma_uint32 iChannel;
25926  for (iChannel = 0; iChannel < channels; iChannel += 1) {
25927  dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
25928  }
25929  }
25930 }
25931 
25932 void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
25933 {
25934  ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
25935 }
25936 
25937 void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
25938 {
25939 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25940  ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
25941 #else
25942  ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
25943 #endif
25944 }
25945 
25946 
25947 void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
25948 {
25949  ma_int16** dst_s16 = (ma_int16**)dst;
25950  const ma_int16* src_s16 = (const ma_int16*)src;
25951 
25952  ma_uint64 iFrame;
25953  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
25954  ma_uint32 iChannel;
25955  for (iChannel = 0; iChannel < channels; iChannel += 1) {
25956  dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
25957  }
25958  }
25959 }
25960 
25961 void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
25962 {
25963  ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
25964 }
25965 
25966 void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
25967 {
25968 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
25969  ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
25970 #else
25971  ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
25972 #endif
25973 }
25974 
25975 
25976 /* s24 */
25977 void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
25978 {
25979  ma_uint8* dst_u8 = (ma_uint8*)dst;
25980  const ma_uint8* src_s24 = (const ma_uint8*)src;
25981 
25982  if (ditherMode == ma_dither_mode_none) {
25983  ma_uint64 i;
25984  for (i = 0; i < count; i += 1) {
25985  ma_int8 x = (ma_int8)src_s24[i*3 + 2] + 128;
25986  dst_u8[i] = (ma_uint8)x;
25987  }
25988  } else {
25989  ma_uint64 i;
25990  for (i = 0; i < count; i += 1) {
25991  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);
25992 
25993  /* Dither. Don't overflow. */
25994  ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
25995  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
25996  x = x + dither;
25997  } else {
25998  x = 0x7FFFFFFF;
25999  }
26000 
26001  x = x >> 24;
26002  x = x + 128;
26003  dst_u8[i] = (ma_uint8)x;
26004  }
26005  }
26006 }
26007 
26008 void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26009 {
26010  ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
26011 }
26012 
26013 #if defined(MA_SUPPORT_SSE2)
26014 void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26015 {
26016  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
26017 }
26018 #endif
26019 #if defined(MA_SUPPORT_AVX2)
26020 void ma_pcm_s24_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26021 {
26022  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
26023 }
26024 #endif
26025 #if defined(MA_SUPPORT_AVX512)
26026 void ma_pcm_s24_to_u8__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26027 {
26028  ma_pcm_s24_to_u8__avx2(dst, src, count, ditherMode);
26029 }
26030 #endif
26031 #if defined(MA_SUPPORT_NEON)
26032 void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26033 {
26034  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
26035 }
26036 #endif
26037 
26038 void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26039 {
26040 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26041  ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
26042 #else
26043  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
26044 #endif
26045 }
26046 
26047 
26048 void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26049 {
26050  ma_int16* dst_s16 = (ma_int16*)dst;
26051  const ma_uint8* src_s24 = (const ma_uint8*)src;
26052 
26053  if (ditherMode == ma_dither_mode_none) {
26054  ma_uint64 i;
26055  for (i = 0; i < count; i += 1) {
26056  ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
26057  ma_uint16 dst_hi = ((ma_uint16)src_s24[i*3 + 2]) << 8;
26058  dst_s16[i] = (ma_int16)dst_lo | dst_hi;
26059  }
26060  } else {
26061  ma_uint64 i;
26062  for (i = 0; i < count; i += 1) {
26063  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);
26064 
26065  /* Dither. Don't overflow. */
26066  ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
26067  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
26068  x = x + dither;
26069  } else {
26070  x = 0x7FFFFFFF;
26071  }
26072 
26073  x = x >> 16;
26074  dst_s16[i] = (ma_int16)x;
26075  }
26076  }
26077 }
26078 
26079 void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26080 {
26081  ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
26082 }
26083 
26084 #if defined(MA_SUPPORT_SSE2)
26085 void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26086 {
26087  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
26088 }
26089 #endif
26090 #if defined(MA_SUPPORT_AVX2)
26091 void ma_pcm_s24_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26092 {
26093  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
26094 }
26095 #endif
26096 #if defined(MA_SUPPORT_AVX512)
26097 void ma_pcm_s24_to_s16__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26098 {
26099  ma_pcm_s24_to_s16__avx2(dst, src, count, ditherMode);
26100 }
26101 #endif
26102 #if defined(MA_SUPPORT_NEON)
26103 void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26104 {
26105  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
26106 }
26107 #endif
26108 
26109 void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26110 {
26111 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26112  ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
26113 #else
26114  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
26115 #endif
26116 }
26117 
26118 
26119 void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26120 {
26121  (void)ditherMode;
26122 
26123  ma_copy_memory_64(dst, src, count * 3);
26124 }
26125 
26126 
26127 void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26128 {
26129  ma_int32* dst_s32 = (ma_int32*)dst;
26130  const ma_uint8* src_s24 = (const ma_uint8*)src;
26131 
26132  ma_uint64 i;
26133  for (i = 0; i < count; i += 1) {
26134  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);
26135  }
26136 
26137  (void)ditherMode;
26138 }
26139 
26140 void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26141 {
26142  ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
26143 }
26144 
26145 #if defined(MA_SUPPORT_SSE2)
26146 void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26147 {
26148  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
26149 }
26150 #endif
26151 #if defined(MA_SUPPORT_AVX2)
26152 void ma_pcm_s24_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26153 {
26154  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
26155 }
26156 #endif
26157 #if defined(MA_SUPPORT_AVX512)
26158 void ma_pcm_s24_to_s32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26159 {
26160  ma_pcm_s24_to_s32__avx2(dst, src, count, ditherMode);
26161 }
26162 #endif
26163 #if defined(MA_SUPPORT_NEON)
26164 void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26165 {
26166  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
26167 }
26168 #endif
26169 
26170 void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26171 {
26172 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26173  ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
26174 #else
26175  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
26176 #endif
26177 }
26178 
26179 
26180 void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26181 {
26182  float* dst_f32 = (float*)dst;
26183  const ma_uint8* src_s24 = (const ma_uint8*)src;
26184 
26185  ma_uint64 i;
26186  for (i = 0; i < count; i += 1) {
26187  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);
26188 
26189 #if 0
26190  /* The accurate way. */
26191  x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */
26192  x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */
26193  x = x - 1; /* 0..2 to -1..1 */
26194 #else
26195  /* The fast way. */
26196  x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */
26197 #endif
26198 
26199  dst_f32[i] = x;
26200  }
26201 
26202  (void)ditherMode;
26203 }
26204 
26205 void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26206 {
26207  ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
26208 }
26209 
26210 #if defined(MA_SUPPORT_SSE2)
26211 void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26212 {
26213  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
26214 }
26215 #endif
26216 #if defined(MA_SUPPORT_AVX2)
26217 void ma_pcm_s24_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26218 {
26219  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
26220 }
26221 #endif
26222 #if defined(MA_SUPPORT_AVX512)
26223 void ma_pcm_s24_to_f32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26224 {
26225  ma_pcm_s24_to_f32__avx2(dst, src, count, ditherMode);
26226 }
26227 #endif
26228 #if defined(MA_SUPPORT_NEON)
26229 void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26230 {
26231  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
26232 }
26233 #endif
26234 
26235 void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26236 {
26237 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26238  ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
26239 #else
26240  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
26241 #endif
26242 }
26243 
26244 
26245 void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
26246 {
26247  ma_uint8* dst8 = (ma_uint8*)dst;
26248  const ma_uint8** src8 = (const ma_uint8**)src;
26249 
26250  ma_uint64 iFrame;
26251  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
26252  ma_uint32 iChannel;
26253  for (iChannel = 0; iChannel < channels; iChannel += 1) {
26254  dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
26255  dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
26256  dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
26257  }
26258  }
26259 }
26260 
26261 void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
26262 {
26263  ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
26264 }
26265 
26266 void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
26267 {
26268 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26269  ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
26270 #else
26271  ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
26272 #endif
26273 }
26274 
26275 
26276 void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
26277 {
26278  ma_uint8** dst8 = (ma_uint8**)dst;
26279  const ma_uint8* src8 = (const ma_uint8*)src;
26280 
26281  ma_uint32 iFrame;
26282  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
26283  ma_uint32 iChannel;
26284  for (iChannel = 0; iChannel < channels; iChannel += 1) {
26285  dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
26286  dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
26287  dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
26288  }
26289  }
26290 }
26291 
26292 void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
26293 {
26294  ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
26295 }
26296 
26297 void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
26298 {
26299 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26300  ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
26301 #else
26302  ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
26303 #endif
26304 }
26305 
26306 
26307 
26308 /* s32 */
26309 void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26310 {
26311  ma_uint8* dst_u8 = (ma_uint8*)dst;
26312  const ma_int32* src_s32 = (const ma_int32*)src;
26313 
26314  if (ditherMode == ma_dither_mode_none) {
26315  ma_uint64 i;
26316  for (i = 0; i < count; i += 1) {
26317  ma_int32 x = src_s32[i];
26318  x = x >> 24;
26319  x = x + 128;
26320  dst_u8[i] = (ma_uint8)x;
26321  }
26322  } else {
26323  ma_uint64 i;
26324  for (i = 0; i < count; i += 1) {
26325  ma_int32 x = src_s32[i];
26326 
26327  /* Dither. Don't overflow. */
26328  ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
26329  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
26330  x = x + dither;
26331  } else {
26332  x = 0x7FFFFFFF;
26333  }
26334 
26335  x = x >> 24;
26336  x = x + 128;
26337  dst_u8[i] = (ma_uint8)x;
26338  }
26339  }
26340 }
26341 
26342 void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26343 {
26344  ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
26345 }
26346 
26347 #if defined(MA_SUPPORT_SSE2)
26348 void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26349 {
26350  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
26351 }
26352 #endif
26353 #if defined(MA_SUPPORT_AVX2)
26354 void ma_pcm_s32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26355 {
26356  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
26357 }
26358 #endif
26359 #if defined(MA_SUPPORT_AVX512)
26360 void ma_pcm_s32_to_u8__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26361 {
26362  ma_pcm_s32_to_u8__avx2(dst, src, count, ditherMode);
26363 }
26364 #endif
26365 #if defined(MA_SUPPORT_NEON)
26366 void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26367 {
26368  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
26369 }
26370 #endif
26371 
26372 void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26373 {
26374 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26375  ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
26376 #else
26377  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
26378 #endif
26379 }
26380 
26381 
26382 void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26383 {
26384  ma_int16* dst_s16 = (ma_int16*)dst;
26385  const ma_int32* src_s32 = (const ma_int32*)src;
26386 
26387  if (ditherMode == ma_dither_mode_none) {
26388  ma_uint64 i;
26389  for (i = 0; i < count; i += 1) {
26390  ma_int32 x = src_s32[i];
26391  x = x >> 16;
26392  dst_s16[i] = (ma_int16)x;
26393  }
26394  } else {
26395  ma_uint64 i;
26396  for (i = 0; i < count; i += 1) {
26397  ma_int32 x = src_s32[i];
26398 
26399  /* Dither. Don't overflow. */
26400  ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
26401  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
26402  x = x + dither;
26403  } else {
26404  x = 0x7FFFFFFF;
26405  }
26406 
26407  x = x >> 16;
26408  dst_s16[i] = (ma_int16)x;
26409  }
26410  }
26411 }
26412 
26413 void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26414 {
26415  ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
26416 }
26417 
26418 #if defined(MA_SUPPORT_SSE2)
26419 void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26420 {
26421  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
26422 }
26423 #endif
26424 #if defined(MA_SUPPORT_AVX2)
26425 void ma_pcm_s32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26426 {
26427  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
26428 }
26429 #endif
26430 #if defined(MA_SUPPORT_AVX512)
26431 void ma_pcm_s32_to_s16__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26432 {
26433  ma_pcm_s32_to_s16__avx2(dst, src, count, ditherMode);
26434 }
26435 #endif
26436 #if defined(MA_SUPPORT_NEON)
26437 void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26438 {
26439  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
26440 }
26441 #endif
26442 
26443 void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26444 {
26445 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26446  ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
26447 #else
26448  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
26449 #endif
26450 }
26451 
26452 
26453 void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26454 {
26455  ma_uint8* dst_s24 = (ma_uint8*)dst;
26456  const ma_int32* src_s32 = (const ma_int32*)src;
26457 
26458  ma_uint64 i;
26459  for (i = 0; i < count; i += 1) {
26460  ma_uint32 x = (ma_uint32)src_s32[i];
26461  dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
26462  dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
26463  dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
26464  }
26465 
26466  (void)ditherMode; /* No dithering for s32 -> s24. */
26467 }
26468 
26469 void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26470 {
26471  ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
26472 }
26473 
26474 #if defined(MA_SUPPORT_SSE2)
26475 void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26476 {
26477  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
26478 }
26479 #endif
26480 #if defined(MA_SUPPORT_AVX2)
26481 void ma_pcm_s32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26482 {
26483  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
26484 }
26485 #endif
26486 #if defined(MA_SUPPORT_AVX512)
26487 void ma_pcm_s32_to_s24__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26488 {
26489  ma_pcm_s32_to_s24__avx2(dst, src, count, ditherMode);
26490 }
26491 #endif
26492 #if defined(MA_SUPPORT_NEON)
26493 void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26494 {
26495  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
26496 }
26497 #endif
26498 
26499 void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26500 {
26501 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26502  ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
26503 #else
26504  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
26505 #endif
26506 }
26507 
26508 
26509 void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26510 {
26511  (void)ditherMode;
26512 
26513  ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
26514 }
26515 
26516 
26517 void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26518 {
26519  float* dst_f32 = (float*)dst;
26520  const ma_int32* src_s32 = (const ma_int32*)src;
26521 
26522  ma_uint64 i;
26523  for (i = 0; i < count; i += 1) {
26524  double x = src_s32[i];
26525 
26526 #if 0
26527  x = x + 2147483648.0;
26528  x = x * 0.0000000004656612873077392578125;
26529  x = x - 1;
26530 #else
26531  x = x / 2147483648.0;
26532 #endif
26533 
26534  dst_f32[i] = (float)x;
26535  }
26536 
26537  (void)ditherMode; /* No dithering for s32 -> f32. */
26538 }
26539 
26540 void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26541 {
26542  ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
26543 }
26544 
26545 #if defined(MA_SUPPORT_SSE2)
26546 void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26547 {
26548  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
26549 }
26550 #endif
26551 #if defined(MA_SUPPORT_AVX2)
26552 void ma_pcm_s32_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26553 {
26554  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
26555 }
26556 #endif
26557 #if defined(MA_SUPPORT_AVX512)
26558 void ma_pcm_s32_to_f32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26559 {
26560  ma_pcm_s32_to_f32__avx2(dst, src, count, ditherMode);
26561 }
26562 #endif
26563 #if defined(MA_SUPPORT_NEON)
26564 void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26565 {
26566  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
26567 }
26568 #endif
26569 
26570 void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26571 {
26572 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26573  ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
26574 #else
26575  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
26576 #endif
26577 }
26578 
26579 
26580 void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
26581 {
26582  ma_int32* dst_s32 = (ma_int32*)dst;
26583  const ma_int32** src_s32 = (const ma_int32**)src;
26584 
26585  ma_uint64 iFrame;
26586  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
26587  ma_uint32 iChannel;
26588  for (iChannel = 0; iChannel < channels; iChannel += 1) {
26589  dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
26590  }
26591  }
26592 }
26593 
26594 void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
26595 {
26596  ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
26597 }
26598 
26599 void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
26600 {
26601 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26602  ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
26603 #else
26604  ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
26605 #endif
26606 }
26607 
26608 
26609 void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
26610 {
26611  ma_int32** dst_s32 = (ma_int32**)dst;
26612  const ma_int32* src_s32 = (const ma_int32*)src;
26613 
26614  ma_uint64 iFrame;
26615  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
26616  ma_uint32 iChannel;
26617  for (iChannel = 0; iChannel < channels; iChannel += 1) {
26618  dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
26619  }
26620  }
26621 }
26622 
26623 void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
26624 {
26625  ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
26626 }
26627 
26628 void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
26629 {
26630 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26631  ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
26632 #else
26633  ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
26634 #endif
26635 }
26636 
26637 
26638 /* f32 */
26639 void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26640 {
26641  ma_uint64 i;
26642 
26643  ma_uint8* dst_u8 = (ma_uint8*)dst;
26644  const float* src_f32 = (const float*)src;
26645 
26646  float ditherMin = 0;
26647  float ditherMax = 0;
26648  if (ditherMode != ma_dither_mode_none) {
26649  ditherMin = 1.0f / -128;
26650  ditherMax = 1.0f / 127;
26651  }
26652 
26653  for (i = 0; i < count; i += 1) {
26654  float x = src_f32[i];
26655  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
26656  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
26657  x = x + 1; /* -1..1 to 0..2 */
26658  x = x * 127.5f; /* 0..2 to 0..255 */
26659 
26660  dst_u8[i] = (ma_uint8)x;
26661  }
26662 }
26663 
26664 void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26665 {
26666  ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
26667 }
26668 
26669 #if defined(MA_SUPPORT_SSE2)
26670 void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26671 {
26672  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
26673 }
26674 #endif
26675 #if defined(MA_SUPPORT_AVX2)
26676 void ma_pcm_f32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26677 {
26678  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
26679 }
26680 #endif
26681 #if defined(MA_SUPPORT_AVX512)
26682 void ma_pcm_f32_to_u8__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26683 {
26684  ma_pcm_f32_to_u8__avx2(dst, src, count, ditherMode);
26685 }
26686 #endif
26687 #if defined(MA_SUPPORT_NEON)
26688 void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26689 {
26690  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
26691 }
26692 #endif
26693 
26694 void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26695 {
26696 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
26697  ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
26698 #else
26699  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
26700 #endif
26701 }
26702 
26703 
26704 void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26705 {
26706  ma_uint64 i;
26707 
26708  ma_int16* dst_s16 = (ma_int16*)dst;
26709  const float* src_f32 = (const float*)src;
26710 
26711  float ditherMin = 0;
26712  float ditherMax = 0;
26713  if (ditherMode != ma_dither_mode_none) {
26714  ditherMin = 1.0f / -32768;
26715  ditherMax = 1.0f / 32767;
26716  }
26717 
26718  for (i = 0; i < count; i += 1) {
26719  float x = src_f32[i];
26720  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
26721  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
26722 
26723 #if 0
26724  /* The accurate way. */
26725  x = x + 1; /* -1..1 to 0..2 */
26726  x = x * 32767.5f; /* 0..2 to 0..65535 */
26727  x = x - 32768.0f; /* 0...65535 to -32768..32767 */
26728 #else
26729  /* The fast way. */
26730  x = x * 32767.0f; /* -1..1 to -32767..32767 */
26731 #endif
26732 
26733  dst_s16[i] = (ma_int16)x;
26734  }
26735 }
26736 
26737 void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26738 {
26739  ma_uint64 i;
26740  ma_uint64 i4;
26741  ma_uint64 count4;
26742 
26743  ma_int16* dst_s16 = (ma_int16*)dst;
26744  const float* src_f32 = (const float*)src;
26745 
26746  float ditherMin = 0;
26747  float ditherMax = 0;
26748  if (ditherMode != ma_dither_mode_none) {
26749  ditherMin = 1.0f / -32768;
26750  ditherMax = 1.0f / 32767;
26751  }
26752 
26753  /* Unrolled. */
26754  i = 0;
26755  count4 = count >> 2;
26756  for (i4 = 0; i4 < count4; i4 += 1) {
26757  float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
26758  float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
26759  float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
26760  float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
26761 
26762  float x0 = src_f32[i+0];
26763  float x1 = src_f32[i+1];
26764  float x2 = src_f32[i+2];
26765  float x3 = src_f32[i+3];
26766 
26767  x0 = x0 + d0;
26768  x1 = x1 + d1;
26769  x2 = x2 + d2;
26770  x3 = x3 + d3;
26771 
26772  x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
26773  x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
26774  x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
26775  x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
26776 
26777  x0 = x0 * 32767.0f;
26778  x1 = x1 * 32767.0f;
26779  x2 = x2 * 32767.0f;
26780  x3 = x3 * 32767.0f;
26781 
26782  dst_s16[i+0] = (ma_int16)x0;
26783  dst_s16[i+1] = (ma_int16)x1;
26784  dst_s16[i+2] = (ma_int16)x2;
26785  dst_s16[i+3] = (ma_int16)x3;
26786 
26787  i += 4;
26788  }
26789 
26790  /* Leftover. */
26791  for (; i < count; i += 1) {
26792  float x = src_f32[i];
26793  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
26794  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
26795  x = x * 32767.0f; /* -1..1 to -32767..32767 */
26796 
26797  dst_s16[i] = (ma_int16)x;
26798  }
26799 }
26800 
26801 #if defined(MA_SUPPORT_SSE2)
26802 void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26803 {
26804  ma_uint64 i;
26805  ma_uint64 i8;
26806  ma_uint64 count8;
26807  ma_int16* dst_s16;
26808  const float* src_f32;
26809  float ditherMin;
26810  float ditherMax;
26811 
26812  /* Both the input and output buffers need to be aligned to 16 bytes. */
26813  if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
26814  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
26815  return;
26816  }
26817 
26818  dst_s16 = (ma_int16*)dst;
26819  src_f32 = (const float*)src;
26820 
26821  ditherMin = 0;
26822  ditherMax = 0;
26823  if (ditherMode != ma_dither_mode_none) {
26824  ditherMin = 1.0f / -32768;
26825  ditherMax = 1.0f / 32767;
26826  }
26827 
26828  i = 0;
26829 
26830  /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
26831  count8 = count >> 3;
26832  for (i8 = 0; i8 < count8; i8 += 1) {
26833  __m128 d0;
26834  __m128 d1;
26835  __m128 x0;
26836  __m128 x1;
26837 
26838  if (ditherMode == ma_dither_mode_none) {
26839  d0 = _mm_set1_ps(0);
26840  d1 = _mm_set1_ps(0);
26841  } else if (ditherMode == ma_dither_mode_rectangle) {
26842  d0 = _mm_set_ps(
26843  ma_dither_f32_rectangle(ditherMin, ditherMax),
26844  ma_dither_f32_rectangle(ditherMin, ditherMax),
26845  ma_dither_f32_rectangle(ditherMin, ditherMax),
26846  ma_dither_f32_rectangle(ditherMin, ditherMax)
26847  );
26848  d1 = _mm_set_ps(
26849  ma_dither_f32_rectangle(ditherMin, ditherMax),
26850  ma_dither_f32_rectangle(ditherMin, ditherMax),
26851  ma_dither_f32_rectangle(ditherMin, ditherMax),
26852  ma_dither_f32_rectangle(ditherMin, ditherMax)
26853  );
26854  } else {
26855  d0 = _mm_set_ps(
26856  ma_dither_f32_triangle(ditherMin, ditherMax),
26857  ma_dither_f32_triangle(ditherMin, ditherMax),
26858  ma_dither_f32_triangle(ditherMin, ditherMax),
26859  ma_dither_f32_triangle(ditherMin, ditherMax)
26860  );
26861  d1 = _mm_set_ps(
26862  ma_dither_f32_triangle(ditherMin, ditherMax),
26863  ma_dither_f32_triangle(ditherMin, ditherMax),
26864  ma_dither_f32_triangle(ditherMin, ditherMax),
26865  ma_dither_f32_triangle(ditherMin, ditherMax)
26866  );
26867  }
26868 
26869  x0 = *((__m128*)(src_f32 + i) + 0);
26870  x1 = *((__m128*)(src_f32 + i) + 1);
26871 
26872  x0 = _mm_add_ps(x0, d0);
26873  x1 = _mm_add_ps(x1, d1);
26874 
26875  x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
26876  x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
26877 
26878  _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
26879 
26880  i += 8;
26881  }
26882 
26883 
26884  /* Leftover. */
26885  for (; i < count; i += 1) {
26886  float x = src_f32[i];
26887  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
26888  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
26889  x = x * 32767.0f; /* -1..1 to -32767..32767 */
26890 
26891  dst_s16[i] = (ma_int16)x;
26892  }
26893 }
26894 #endif
26895 #if defined(MA_SUPPORT_AVX2)
26896 void ma_pcm_f32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
26897 {
26898  ma_uint64 i;
26899  ma_uint64 i16;
26900  ma_uint64 count16;
26901  ma_int16* dst_s16;
26902  const float* src_f32;
26903  float ditherMin;
26904  float ditherMax;
26905 
26906  /* Both the input and output buffers need to be aligned to 32 bytes. */
26907  if ((((ma_uintptr)dst & 31) != 0) || (((ma_uintptr)src & 31) != 0)) {
26908  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
26909  return;
26910  }
26911 
26912  dst_s16 = (ma_int16*)dst;
26913  src_f32 = (const float*)src;
26914 
26915  ditherMin = 0;
26916  ditherMax = 0;
26917  if (ditherMode != ma_dither_mode_none) {
26918  ditherMin = 1.0f / -32768;
26919  ditherMax = 1.0f / 32767;
26920  }
26921 
26922  i = 0;
26923 
26924  /* AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. */
26925  count16 = count >> 4;
26926  for (i16 = 0; i16 < count16; i16 += 1) {
26927  __m256 d0;
26928  __m256 d1;
26929  __m256 x0;
26930  __m256 x1;
26931  __m256i i0;
26932  __m256i i1;
26933  __m256i p0;
26934  __m256i p1;
26935  __m256i r;
26936 
26937  if (ditherMode == ma_dither_mode_none) {
26938  d0 = _mm256_set1_ps(0);
26939  d1 = _mm256_set1_ps(0);
26940  } else if (ditherMode == ma_dither_mode_rectangle) {
26941  d0 = _mm256_set_ps(
26942  ma_dither_f32_rectangle(ditherMin, ditherMax),
26943  ma_dither_f32_rectangle(ditherMin, ditherMax),
26944  ma_dither_f32_rectangle(ditherMin, ditherMax),
26945  ma_dither_f32_rectangle(ditherMin, ditherMax),
26946  ma_dither_f32_rectangle(ditherMin, ditherMax),
26947  ma_dither_f32_rectangle(ditherMin, ditherMax),
26948  ma_dither_f32_rectangle(ditherMin, ditherMax),
26949  ma_dither_f32_rectangle(ditherMin, ditherMax)
26950  );
26951  d1 = _mm256_set_ps(
26952  ma_dither_f32_rectangle(ditherMin, ditherMax),
26953  ma_dither_f32_rectangle(ditherMin, ditherMax),
26954  ma_dither_f32_rectangle(ditherMin, ditherMax),
26955  ma_dither_f32_rectangle(ditherMin, ditherMax),
26956  ma_dither_f32_rectangle(ditherMin, ditherMax),
26957  ma_dither_f32_rectangle(ditherMin, ditherMax),
26958  ma_dither_f32_rectangle(ditherMin, ditherMax),
26959  ma_dither_f32_rectangle(ditherMin, ditherMax)
26960  );
26961  } else {
26962  d0 = _mm256_set_ps(
26963  ma_dither_f32_triangle(ditherMin, ditherMax),
26964  ma_dither_f32_triangle(ditherMin, ditherMax),
26965  ma_dither_f32_triangle(ditherMin, ditherMax),
26966  ma_dither_f32_triangle(ditherMin, ditherMax),
26967  ma_dither_f32_triangle(ditherMin, ditherMax),
26968  ma_dither_f32_triangle(ditherMin, ditherMax),
26969  ma_dither_f32_triangle(ditherMin, ditherMax),
26970  ma_dither_f32_triangle(ditherMin, ditherMax)
26971  );
26972  d1 = _mm256_set_ps(
26973  ma_dither_f32_triangle(ditherMin, ditherMax),
26974  ma_dither_f32_triangle(ditherMin, ditherMax),
26975  ma_dither_f32_triangle(ditherMin, ditherMax),
26976  ma_dither_f32_triangle(ditherMin, ditherMax),
26977  ma_dither_f32_triangle(ditherMin, ditherMax),
26978  ma_dither_f32_triangle(ditherMin, ditherMax),
26979  ma_dither_f32_triangle(ditherMin, ditherMax),
26980  ma_dither_f32_triangle(ditherMin, ditherMax)
26981  );
26982  }
26983 
26984  x0 = *((__m256*)(src_f32 + i) + 0);
26985  x1 = *((__m256*)(src_f32 + i) + 1);
26986 
26987  x0 = _mm256_add_ps(x0, d0);
26988  x1 = _mm256_add_ps(x1, d1);
26989 
26990  x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f));
26991  x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f));
26992 
26993  /* Computing the final result is a little more complicated for AVX2 than SSE2. */
26994  i0 = _mm256_cvttps_epi32(x0);
26995  i1 = _mm256_cvttps_epi32(x1);
26996  p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32);
26997  p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48);
26998  r = _mm256_packs_epi32(p0, p1);
26999 
27000  _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r);
27001 
27002  i += 16;
27003  }
27004 
27005 
27006  /* Leftover. */
27007  for (; i < count; i += 1) {
27008  float x = src_f32[i];
27009  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
27010  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
27011  x = x * 32767.0f; /* -1..1 to -32767..32767 */
27012 
27013  dst_s16[i] = (ma_int16)x;
27014  }
27015 }
27016 #endif
27017 #if defined(MA_SUPPORT_AVX512)
27018 void ma_pcm_f32_to_s16__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27019 {
27020  /* TODO: Convert this from AVX to AVX-512. */
27021  ma_pcm_f32_to_s16__avx2(dst, src, count, ditherMode);
27022 }
27023 #endif
27024 #if defined(MA_SUPPORT_NEON)
27025 void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27026 {
27027  ma_uint64 i;
27028  ma_uint64 i8;
27029  ma_uint64 count8;
27030  ma_int16* dst_s16;
27031  const float* src_f32;
27032  float ditherMin;
27033  float ditherMax;
27034 
27035  /* Both the input and output buffers need to be aligned to 16 bytes. */
27036  if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
27037  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
27038  return;
27039  }
27040 
27041  dst_s16 = (ma_int16*)dst;
27042  src_f32 = (const float*)src;
27043 
27044  ditherMin = 0;
27045  ditherMax = 0;
27046  if (ditherMode != ma_dither_mode_none) {
27047  ditherMin = 1.0f / -32768;
27048  ditherMax = 1.0f / 32767;
27049  }
27050 
27051  i = 0;
27052 
27053  /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
27054  count8 = count >> 3;
27055  for (i8 = 0; i8 < count8; i8 += 1) {
27056  float32x4_t d0;
27057  float32x4_t d1;
27058  float32x4_t x0;
27059  float32x4_t x1;
27060  int32x4_t i0;
27061  int32x4_t i1;
27062 
27063  if (ditherMode == ma_dither_mode_none) {
27064  d0 = vmovq_n_f32(0);
27065  d1 = vmovq_n_f32(0);
27066  } else if (ditherMode == ma_dither_mode_rectangle) {
27067  float d0v[4];
27068  d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
27069  d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
27070  d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
27071  d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
27072  d0 = vld1q_f32(d0v);
27073 
27074  float d1v[4];
27075  d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
27076  d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
27077  d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
27078  d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
27079  d1 = vld1q_f32(d1v);
27080  } else {
27081  float d0v[4];
27082  d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
27083  d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
27084  d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
27085  d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
27086  d0 = vld1q_f32(d0v);
27087 
27088  float d1v[4];
27089  d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
27090  d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
27091  d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
27092  d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
27093  d1 = vld1q_f32(d1v);
27094  }
27095 
27096  x0 = *((float32x4_t*)(src_f32 + i) + 0);
27097  x1 = *((float32x4_t*)(src_f32 + i) + 1);
27098 
27099  x0 = vaddq_f32(x0, d0);
27100  x1 = vaddq_f32(x1, d1);
27101 
27102  x0 = vmulq_n_f32(x0, 32767.0f);
27103  x1 = vmulq_n_f32(x1, 32767.0f);
27104 
27105  i0 = vcvtq_s32_f32(x0);
27106  i1 = vcvtq_s32_f32(x1);
27107  *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
27108 
27109  i += 8;
27110  }
27111 
27112 
27113  /* Leftover. */
27114  for (; i < count; i += 1) {
27115  float x = src_f32[i];
27116  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
27117  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
27118  x = x * 32767.0f; /* -1..1 to -32767..32767 */
27119 
27120  dst_s16[i] = (ma_int16)x;
27121  }
27122 }
27123 #endif
27124 
27125 void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27126 {
27127 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27128  ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
27129 #else
27130  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
27131 #endif
27132 }
27133 
27134 
27135 void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27136 {
27137  ma_uint8* dst_s24 = (ma_uint8*)dst;
27138  const float* src_f32 = (const float*)src;
27139 
27140  ma_uint64 i;
27141  for (i = 0; i < count; i += 1) {
27142  ma_int32 r;
27143  float x = src_f32[i];
27144  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
27145 
27146 #if 0
27147  /* The accurate way. */
27148  x = x + 1; /* -1..1 to 0..2 */
27149  x = x * 8388607.5f; /* 0..2 to 0..16777215 */
27150  x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */
27151 #else
27152  /* The fast way. */
27153  x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */
27154 #endif
27155 
27156  r = (ma_int32)x;
27157  dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
27158  dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
27159  dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
27160  }
27161 
27162  (void)ditherMode; /* No dithering for f32 -> s24. */
27163 }
27164 
27165 void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27166 {
27167  ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
27168 }
27169 
27170 #if defined(MA_SUPPORT_SSE2)
27171 void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27172 {
27173  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
27174 }
27175 #endif
27176 #if defined(MA_SUPPORT_AVX2)
27177 void ma_pcm_f32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27178 {
27179  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
27180 }
27181 #endif
27182 #if defined(MA_SUPPORT_AVX512)
27183 void ma_pcm_f32_to_s24__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27184 {
27185  ma_pcm_f32_to_s24__avx2(dst, src, count, ditherMode);
27186 }
27187 #endif
27188 #if defined(MA_SUPPORT_NEON)
27189 void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27190 {
27191  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
27192 }
27193 #endif
27194 
27195 void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27196 {
27197 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27198  ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
27199 #else
27200  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
27201 #endif
27202 }
27203 
27204 
27205 void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27206 {
27207  ma_int32* dst_s32 = (ma_int32*)dst;
27208  const float* src_f32 = (const float*)src;
27209 
27210  ma_uint32 i;
27211  for (i = 0; i < count; i += 1) {
27212  double x = src_f32[i];
27213  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
27214 
27215 #if 0
27216  /* The accurate way. */
27217  x = x + 1; /* -1..1 to 0..2 */
27218  x = x * 2147483647.5; /* 0..2 to 0..4294967295 */
27219  x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */
27220 #else
27221  /* The fast way. */
27222  x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */
27223 #endif
27224 
27225  dst_s32[i] = (ma_int32)x;
27226  }
27227 
27228  (void)ditherMode; /* No dithering for f32 -> s32. */
27229 }
27230 
27231 void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27232 {
27233  ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
27234 }
27235 
27236 #if defined(MA_SUPPORT_SSE2)
27237 void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27238 {
27239  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
27240 }
27241 #endif
27242 #if defined(MA_SUPPORT_AVX2)
27243 void ma_pcm_f32_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27244 {
27245  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
27246 }
27247 #endif
27248 #if defined(MA_SUPPORT_AVX512)
27249 void ma_pcm_f32_to_s32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27250 {
27251  ma_pcm_f32_to_s32__avx2(dst, src, count, ditherMode);
27252 }
27253 #endif
27254 #if defined(MA_SUPPORT_NEON)
27255 void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27256 {
27257  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
27258 }
27259 #endif
27260 
27261 void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27262 {
27263 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27264  ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
27265 #else
27266  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
27267 #endif
27268 }
27269 
27270 
27271 void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27272 {
27273  (void)ditherMode;
27274 
27275  ma_copy_memory_64(dst, src, count * sizeof(float));
27276 }
27277 
27278 
27279 void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
27280 {
27281  float* dst_f32 = (float*)dst;
27282  const float** src_f32 = (const float**)src;
27283 
27284  ma_uint64 iFrame;
27285  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
27286  ma_uint32 iChannel;
27287  for (iChannel = 0; iChannel < channels; iChannel += 1) {
27288  dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
27289  }
27290  }
27291 }
27292 
27293 void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
27294 {
27295  ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
27296 }
27297 
27298 void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
27299 {
27300 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27301  ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
27302 #else
27303  ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
27304 #endif
27305 }
27306 
27307 
27308 void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
27309 {
27310  float** dst_f32 = (float**)dst;
27311  const float* src_f32 = (const float*)src;
27312 
27313  ma_uint64 iFrame;
27314  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
27315  ma_uint32 iChannel;
27316  for (iChannel = 0; iChannel < channels; iChannel += 1) {
27317  dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
27318  }
27319  }
27320 }
27321 
27322 void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
27323 {
27324  ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
27325 }
27326 
27327 void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
27328 {
27329 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27330  ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
27331 #else
27332  ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
27333 #endif
27334 }
27335 
27336 
27337 void ma_format_converter_init_callbacks__default(ma_format_converter* pConverter)
27338 {
27339  ma_assert(pConverter != NULL);
27340 
27341  switch (pConverter->config.formatIn)
27342  {
27343  case ma_format_u8:
27344  {
27345  if (pConverter->config.formatOut == ma_format_u8) {
27346  pConverter->onConvertPCM = ma_pcm_u8_to_u8;
27347  } else if (pConverter->config.formatOut == ma_format_s16) {
27348  pConverter->onConvertPCM = ma_pcm_u8_to_s16;
27349  } else if (pConverter->config.formatOut == ma_format_s24) {
27350  pConverter->onConvertPCM = ma_pcm_u8_to_s24;
27351  } else if (pConverter->config.formatOut == ma_format_s32) {
27352  pConverter->onConvertPCM = ma_pcm_u8_to_s32;
27353  } else if (pConverter->config.formatOut == ma_format_f32) {
27354  pConverter->onConvertPCM = ma_pcm_u8_to_f32;
27355  }
27356  } break;
27357 
27358  case ma_format_s16:
27359  {
27360  if (pConverter->config.formatOut == ma_format_u8) {
27361  pConverter->onConvertPCM = ma_pcm_s16_to_u8;
27362  } else if (pConverter->config.formatOut == ma_format_s16) {
27363  pConverter->onConvertPCM = ma_pcm_s16_to_s16;
27364  } else if (pConverter->config.formatOut == ma_format_s24) {
27365  pConverter->onConvertPCM = ma_pcm_s16_to_s24;
27366  } else if (pConverter->config.formatOut == ma_format_s32) {
27367  pConverter->onConvertPCM = ma_pcm_s16_to_s32;
27368  } else if (pConverter->config.formatOut == ma_format_f32) {
27369  pConverter->onConvertPCM = ma_pcm_s16_to_f32;
27370  }
27371  } break;
27372 
27373  case ma_format_s24:
27374  {
27375  if (pConverter->config.formatOut == ma_format_u8) {
27376  pConverter->onConvertPCM = ma_pcm_s24_to_u8;
27377  } else if (pConverter->config.formatOut == ma_format_s16) {
27378  pConverter->onConvertPCM = ma_pcm_s24_to_s16;
27379  } else if (pConverter->config.formatOut == ma_format_s24) {
27380  pConverter->onConvertPCM = ma_pcm_s24_to_s24;
27381  } else if (pConverter->config.formatOut == ma_format_s32) {
27382  pConverter->onConvertPCM = ma_pcm_s24_to_s32;
27383  } else if (pConverter->config.formatOut == ma_format_f32) {
27384  pConverter->onConvertPCM = ma_pcm_s24_to_f32;
27385  }
27386  } break;
27387 
27388  case ma_format_s32:
27389  {
27390  if (pConverter->config.formatOut == ma_format_u8) {
27391  pConverter->onConvertPCM = ma_pcm_s32_to_u8;
27392  } else if (pConverter->config.formatOut == ma_format_s16) {
27393  pConverter->onConvertPCM = ma_pcm_s32_to_s16;
27394  } else if (pConverter->config.formatOut == ma_format_s24) {
27395  pConverter->onConvertPCM = ma_pcm_s32_to_s24;
27396  } else if (pConverter->config.formatOut == ma_format_s32) {
27397  pConverter->onConvertPCM = ma_pcm_s32_to_s32;
27398  } else if (pConverter->config.formatOut == ma_format_f32) {
27399  pConverter->onConvertPCM = ma_pcm_s32_to_f32;
27400  }
27401  } break;
27402 
27403  case ma_format_f32:
27404  default:
27405  {
27406  if (pConverter->config.formatOut == ma_format_u8) {
27407  pConverter->onConvertPCM = ma_pcm_f32_to_u8;
27408  } else if (pConverter->config.formatOut == ma_format_s16) {
27409  pConverter->onConvertPCM = ma_pcm_f32_to_s16;
27410  } else if (pConverter->config.formatOut == ma_format_s24) {
27411  pConverter->onConvertPCM = ma_pcm_f32_to_s24;
27412  } else if (pConverter->config.formatOut == ma_format_s32) {
27413  pConverter->onConvertPCM = ma_pcm_f32_to_s32;
27414  } else if (pConverter->config.formatOut == ma_format_f32) {
27415  pConverter->onConvertPCM = ma_pcm_f32_to_f32;
27416  }
27417  } break;
27418  }
27419 }
27420 
27421 #if defined(MA_SUPPORT_SSE2)
27422 void ma_format_converter_init_callbacks__sse2(ma_format_converter* pConverter)
27423 {
27424  ma_assert(pConverter != NULL);
27425 
27426  switch (pConverter->config.formatIn)
27427  {
27428  case ma_format_u8:
27429  {
27430  if (pConverter->config.formatOut == ma_format_u8) {
27431  pConverter->onConvertPCM = ma_pcm_u8_to_u8;
27432  } else if (pConverter->config.formatOut == ma_format_s16) {
27433  pConverter->onConvertPCM = ma_pcm_u8_to_s16__sse2;
27434  } else if (pConverter->config.formatOut == ma_format_s24) {
27435  pConverter->onConvertPCM = ma_pcm_u8_to_s24__sse2;
27436  } else if (pConverter->config.formatOut == ma_format_s32) {
27437  pConverter->onConvertPCM = ma_pcm_u8_to_s32__sse2;
27438  } else if (pConverter->config.formatOut == ma_format_f32) {
27439  pConverter->onConvertPCM = ma_pcm_u8_to_f32__sse2;
27440  }
27441  } break;
27442 
27443  case ma_format_s16:
27444  {
27445  if (pConverter->config.formatOut == ma_format_u8) {
27446  pConverter->onConvertPCM = ma_pcm_s16_to_u8__sse2;
27447  } else if (pConverter->config.formatOut == ma_format_s16) {
27448  pConverter->onConvertPCM = ma_pcm_s16_to_s16;
27449  } else if (pConverter->config.formatOut == ma_format_s24) {
27450  pConverter->onConvertPCM = ma_pcm_s16_to_s24__sse2;
27451  } else if (pConverter->config.formatOut == ma_format_s32) {
27452  pConverter->onConvertPCM = ma_pcm_s16_to_s32__sse2;
27453  } else if (pConverter->config.formatOut == ma_format_f32) {
27454  pConverter->onConvertPCM = ma_pcm_s16_to_f32__sse2;
27455  }
27456  } break;
27457 
27458  case ma_format_s24:
27459  {
27460  if (pConverter->config.formatOut == ma_format_u8) {
27461  pConverter->onConvertPCM = ma_pcm_s24_to_u8__sse2;
27462  } else if (pConverter->config.formatOut == ma_format_s16) {
27463  pConverter->onConvertPCM = ma_pcm_s24_to_s16__sse2;
27464  } else if (pConverter->config.formatOut == ma_format_s24) {
27465  pConverter->onConvertPCM = ma_pcm_s24_to_s24;
27466  } else if (pConverter->config.formatOut == ma_format_s32) {
27467  pConverter->onConvertPCM = ma_pcm_s24_to_s32__sse2;
27468  } else if (pConverter->config.formatOut == ma_format_f32) {
27469  pConverter->onConvertPCM = ma_pcm_s24_to_f32__sse2;
27470  }
27471  } break;
27472 
27473  case ma_format_s32:
27474  {
27475  if (pConverter->config.formatOut == ma_format_u8) {
27476  pConverter->onConvertPCM = ma_pcm_s32_to_u8__sse2;
27477  } else if (pConverter->config.formatOut == ma_format_s16) {
27478  pConverter->onConvertPCM = ma_pcm_s32_to_s16__sse2;
27479  } else if (pConverter->config.formatOut == ma_format_s24) {
27480  pConverter->onConvertPCM = ma_pcm_s32_to_s24__sse2;
27481  } else if (pConverter->config.formatOut == ma_format_s32) {
27482  pConverter->onConvertPCM = ma_pcm_s32_to_s32;
27483  } else if (pConverter->config.formatOut == ma_format_f32) {
27484  pConverter->onConvertPCM = ma_pcm_s32_to_f32__sse2;
27485  }
27486  } break;
27487 
27488  case ma_format_f32:
27489  default:
27490  {
27491  if (pConverter->config.formatOut == ma_format_u8) {
27492  pConverter->onConvertPCM = ma_pcm_f32_to_u8__sse2;
27493  } else if (pConverter->config.formatOut == ma_format_s16) {
27494  pConverter->onConvertPCM = ma_pcm_f32_to_s16__sse2;
27495  } else if (pConverter->config.formatOut == ma_format_s24) {
27496  pConverter->onConvertPCM = ma_pcm_f32_to_s24__sse2;
27497  } else if (pConverter->config.formatOut == ma_format_s32) {
27498  pConverter->onConvertPCM = ma_pcm_f32_to_s32__sse2;
27499  } else if (pConverter->config.formatOut == ma_format_f32) {
27500  pConverter->onConvertPCM = ma_pcm_f32_to_f32;
27501  }
27502  } break;
27503  }
27504 }
27505 #endif
27506 
27507 #if defined(MA_SUPPORT_AVX2)
27508 void ma_format_converter_init_callbacks__avx2(ma_format_converter* pConverter)
27509 {
27510  ma_assert(pConverter != NULL);
27511 
27512  switch (pConverter->config.formatIn)
27513  {
27514  case ma_format_u8:
27515  {
27516  if (pConverter->config.formatOut == ma_format_u8) {
27517  pConverter->onConvertPCM = ma_pcm_u8_to_u8;
27518  } else if (pConverter->config.formatOut == ma_format_s16) {
27519  pConverter->onConvertPCM = ma_pcm_u8_to_s16__avx2;
27520  } else if (pConverter->config.formatOut == ma_format_s24) {
27521  pConverter->onConvertPCM = ma_pcm_u8_to_s24__avx2;
27522  } else if (pConverter->config.formatOut == ma_format_s32) {
27523  pConverter->onConvertPCM = ma_pcm_u8_to_s32__avx2;
27524  } else if (pConverter->config.formatOut == ma_format_f32) {
27525  pConverter->onConvertPCM = ma_pcm_u8_to_f32__avx2;
27526  }
27527  } break;
27528 
27529  case ma_format_s16:
27530  {
27531  if (pConverter->config.formatOut == ma_format_u8) {
27532  pConverter->onConvertPCM = ma_pcm_s16_to_u8__avx2;
27533  } else if (pConverter->config.formatOut == ma_format_s16) {
27534  pConverter->onConvertPCM = ma_pcm_s16_to_s16;
27535  } else if (pConverter->config.formatOut == ma_format_s24) {
27536  pConverter->onConvertPCM = ma_pcm_s16_to_s24__avx2;
27537  } else if (pConverter->config.formatOut == ma_format_s32) {
27538  pConverter->onConvertPCM = ma_pcm_s16_to_s32__avx2;
27539  } else if (pConverter->config.formatOut == ma_format_f32) {
27540  pConverter->onConvertPCM = ma_pcm_s16_to_f32__avx2;
27541  }
27542  } break;
27543 
27544  case ma_format_s24:
27545  {
27546  if (pConverter->config.formatOut == ma_format_u8) {
27547  pConverter->onConvertPCM = ma_pcm_s24_to_u8__avx2;
27548  } else if (pConverter->config.formatOut == ma_format_s16) {
27549  pConverter->onConvertPCM = ma_pcm_s24_to_s16__avx2;
27550  } else if (pConverter->config.formatOut == ma_format_s24) {
27551  pConverter->onConvertPCM = ma_pcm_s24_to_s24;
27552  } else if (pConverter->config.formatOut == ma_format_s32) {
27553  pConverter->onConvertPCM = ma_pcm_s24_to_s32__avx2;
27554  } else if (pConverter->config.formatOut == ma_format_f32) {
27555  pConverter->onConvertPCM = ma_pcm_s24_to_f32__avx2;
27556  }
27557  } break;
27558 
27559  case ma_format_s32:
27560  {
27561  if (pConverter->config.formatOut == ma_format_u8) {
27562  pConverter->onConvertPCM = ma_pcm_s32_to_u8__avx2;
27563  } else if (pConverter->config.formatOut == ma_format_s16) {
27564  pConverter->onConvertPCM = ma_pcm_s32_to_s16__avx2;
27565  } else if (pConverter->config.formatOut == ma_format_s24) {
27566  pConverter->onConvertPCM = ma_pcm_s32_to_s24__avx2;
27567  } else if (pConverter->config.formatOut == ma_format_s32) {
27568  pConverter->onConvertPCM = ma_pcm_s32_to_s32;
27569  } else if (pConverter->config.formatOut == ma_format_f32) {
27570  pConverter->onConvertPCM = ma_pcm_s32_to_f32__avx2;
27571  }
27572  } break;
27573 
27574  case ma_format_f32:
27575  default:
27576  {
27577  if (pConverter->config.formatOut == ma_format_u8) {
27578  pConverter->onConvertPCM = ma_pcm_f32_to_u8__avx2;
27579  } else if (pConverter->config.formatOut == ma_format_s16) {
27580  pConverter->onConvertPCM = ma_pcm_f32_to_s16__avx2;
27581  } else if (pConverter->config.formatOut == ma_format_s24) {
27582  pConverter->onConvertPCM = ma_pcm_f32_to_s24__avx2;
27583  } else if (pConverter->config.formatOut == ma_format_s32) {
27584  pConverter->onConvertPCM = ma_pcm_f32_to_s32__avx2;
27585  } else if (pConverter->config.formatOut == ma_format_f32) {
27586  pConverter->onConvertPCM = ma_pcm_f32_to_f32;
27587  }
27588  } break;
27589  }
27590 }
27591 #endif
27592 
27593 #if defined(MA_SUPPORT_AVX512)
27594 void ma_format_converter_init_callbacks__avx512(ma_format_converter* pConverter)
27595 {
27596  ma_assert(pConverter != NULL);
27597 
27598  switch (pConverter->config.formatIn)
27599  {
27600  case ma_format_u8:
27601  {
27602  if (pConverter->config.formatOut == ma_format_u8) {
27603  pConverter->onConvertPCM = ma_pcm_u8_to_u8;
27604  } else if (pConverter->config.formatOut == ma_format_s16) {
27605  pConverter->onConvertPCM = ma_pcm_u8_to_s16__avx512;
27606  } else if (pConverter->config.formatOut == ma_format_s24) {
27607  pConverter->onConvertPCM = ma_pcm_u8_to_s24__avx512;
27608  } else if (pConverter->config.formatOut == ma_format_s32) {
27609  pConverter->onConvertPCM = ma_pcm_u8_to_s32__avx512;
27610  } else if (pConverter->config.formatOut == ma_format_f32) {
27611  pConverter->onConvertPCM = ma_pcm_u8_to_f32__avx512;
27612  }
27613  } break;
27614 
27615  case ma_format_s16:
27616  {
27617  if (pConverter->config.formatOut == ma_format_u8) {
27618  pConverter->onConvertPCM = ma_pcm_s16_to_u8__avx512;
27619  } else if (pConverter->config.formatOut == ma_format_s16) {
27620  pConverter->onConvertPCM = ma_pcm_s16_to_s16;
27621  } else if (pConverter->config.formatOut == ma_format_s24) {
27622  pConverter->onConvertPCM = ma_pcm_s16_to_s24__avx512;
27623  } else if (pConverter->config.formatOut == ma_format_s32) {
27624  pConverter->onConvertPCM = ma_pcm_s16_to_s32__avx512;
27625  } else if (pConverter->config.formatOut == ma_format_f32) {
27626  pConverter->onConvertPCM = ma_pcm_s16_to_f32__avx512;
27627  }
27628  } break;
27629 
27630  case ma_format_s24:
27631  {
27632  if (pConverter->config.formatOut == ma_format_u8) {
27633  pConverter->onConvertPCM = ma_pcm_s24_to_u8__avx512;
27634  } else if (pConverter->config.formatOut == ma_format_s16) {
27635  pConverter->onConvertPCM = ma_pcm_s24_to_s16__avx512;
27636  } else if (pConverter->config.formatOut == ma_format_s24) {
27637  pConverter->onConvertPCM = ma_pcm_s24_to_s24;
27638  } else if (pConverter->config.formatOut == ma_format_s32) {
27639  pConverter->onConvertPCM = ma_pcm_s24_to_s32__avx512;
27640  } else if (pConverter->config.formatOut == ma_format_f32) {
27641  pConverter->onConvertPCM = ma_pcm_s24_to_f32__avx512;
27642  }
27643  } break;
27644 
27645  case ma_format_s32:
27646  {
27647  if (pConverter->config.formatOut == ma_format_u8) {
27648  pConverter->onConvertPCM = ma_pcm_s32_to_u8__avx512;
27649  } else if (pConverter->config.formatOut == ma_format_s16) {
27650  pConverter->onConvertPCM = ma_pcm_s32_to_s16__avx512;
27651  } else if (pConverter->config.formatOut == ma_format_s24) {
27652  pConverter->onConvertPCM = ma_pcm_s32_to_s24__avx512;
27653  } else if (pConverter->config.formatOut == ma_format_s32) {
27654  pConverter->onConvertPCM = ma_pcm_s32_to_s32;
27655  } else if (pConverter->config.formatOut == ma_format_f32) {
27656  pConverter->onConvertPCM = ma_pcm_s32_to_f32__avx512;
27657  }
27658  } break;
27659 
27660  case ma_format_f32:
27661  default:
27662  {
27663  if (pConverter->config.formatOut == ma_format_u8) {
27664  pConverter->onConvertPCM = ma_pcm_f32_to_u8__avx512;
27665  } else if (pConverter->config.formatOut == ma_format_s16) {
27666  pConverter->onConvertPCM = ma_pcm_f32_to_s16__avx512;
27667  } else if (pConverter->config.formatOut == ma_format_s24) {
27668  pConverter->onConvertPCM = ma_pcm_f32_to_s24__avx512;
27669  } else if (pConverter->config.formatOut == ma_format_s32) {
27670  pConverter->onConvertPCM = ma_pcm_f32_to_s32__avx512;
27671  } else if (pConverter->config.formatOut == ma_format_f32) {
27672  pConverter->onConvertPCM = ma_pcm_f32_to_f32;
27673  }
27674  } break;
27675  }
27676 }
27677 #endif
27678 
27679 #if defined(MA_SUPPORT_NEON)
27680 void ma_format_converter_init_callbacks__neon(ma_format_converter* pConverter)
27681 {
27682  ma_assert(pConverter != NULL);
27683 
27684  switch (pConverter->config.formatIn)
27685  {
27686  case ma_format_u8:
27687  {
27688  if (pConverter->config.formatOut == ma_format_u8) {
27689  pConverter->onConvertPCM = ma_pcm_u8_to_u8;
27690  } else if (pConverter->config.formatOut == ma_format_s16) {
27691  pConverter->onConvertPCM = ma_pcm_u8_to_s16__neon;
27692  } else if (pConverter->config.formatOut == ma_format_s24) {
27693  pConverter->onConvertPCM = ma_pcm_u8_to_s24__neon;
27694  } else if (pConverter->config.formatOut == ma_format_s32) {
27695  pConverter->onConvertPCM = ma_pcm_u8_to_s32__neon;
27696  } else if (pConverter->config.formatOut == ma_format_f32) {
27697  pConverter->onConvertPCM = ma_pcm_u8_to_f32__neon;
27698  }
27699  } break;
27700 
27701  case ma_format_s16:
27702  {
27703  if (pConverter->config.formatOut == ma_format_u8) {
27704  pConverter->onConvertPCM = ma_pcm_s16_to_u8__neon;
27705  } else if (pConverter->config.formatOut == ma_format_s16) {
27706  pConverter->onConvertPCM = ma_pcm_s16_to_s16;
27707  } else if (pConverter->config.formatOut == ma_format_s24) {
27708  pConverter->onConvertPCM = ma_pcm_s16_to_s24__neon;
27709  } else if (pConverter->config.formatOut == ma_format_s32) {
27710  pConverter->onConvertPCM = ma_pcm_s16_to_s32__neon;
27711  } else if (pConverter->config.formatOut == ma_format_f32) {
27712  pConverter->onConvertPCM = ma_pcm_s16_to_f32__neon;
27713  }
27714  } break;
27715 
27716  case ma_format_s24:
27717  {
27718  if (pConverter->config.formatOut == ma_format_u8) {
27719  pConverter->onConvertPCM = ma_pcm_s24_to_u8__neon;
27720  } else if (pConverter->config.formatOut == ma_format_s16) {
27721  pConverter->onConvertPCM = ma_pcm_s24_to_s16__neon;
27722  } else if (pConverter->config.formatOut == ma_format_s24) {
27723  pConverter->onConvertPCM = ma_pcm_s24_to_s24;
27724  } else if (pConverter->config.formatOut == ma_format_s32) {
27725  pConverter->onConvertPCM = ma_pcm_s24_to_s32__neon;
27726  } else if (pConverter->config.formatOut == ma_format_f32) {
27727  pConverter->onConvertPCM = ma_pcm_s24_to_f32__neon;
27728  }
27729  } break;
27730 
27731  case ma_format_s32:
27732  {
27733  if (pConverter->config.formatOut == ma_format_u8) {
27734  pConverter->onConvertPCM = ma_pcm_s32_to_u8__neon;
27735  } else if (pConverter->config.formatOut == ma_format_s16) {
27736  pConverter->onConvertPCM = ma_pcm_s32_to_s16__neon;
27737  } else if (pConverter->config.formatOut == ma_format_s24) {
27738  pConverter->onConvertPCM = ma_pcm_s32_to_s24__neon;
27739  } else if (pConverter->config.formatOut == ma_format_s32) {
27740  pConverter->onConvertPCM = ma_pcm_s32_to_s32;
27741  } else if (pConverter->config.formatOut == ma_format_f32) {
27742  pConverter->onConvertPCM = ma_pcm_s32_to_f32__neon;
27743  }
27744  } break;
27745 
27746  case ma_format_f32:
27747  default:
27748  {
27749  if (pConverter->config.formatOut == ma_format_u8) {
27750  pConverter->onConvertPCM = ma_pcm_f32_to_u8__neon;
27751  } else if (pConverter->config.formatOut == ma_format_s16) {
27752  pConverter->onConvertPCM = ma_pcm_f32_to_s16__neon;
27753  } else if (pConverter->config.formatOut == ma_format_s24) {
27754  pConverter->onConvertPCM = ma_pcm_f32_to_s24__neon;
27755  } else if (pConverter->config.formatOut == ma_format_s32) {
27756  pConverter->onConvertPCM = ma_pcm_f32_to_s32__neon;
27757  } else if (pConverter->config.formatOut == ma_format_f32) {
27758  pConverter->onConvertPCM = ma_pcm_f32_to_f32;
27759  }
27760  } break;
27761  }
27762 }
27763 #endif
27764 
27766 {
27767  if (pConverter == NULL) {
27768  return MA_INVALID_ARGS;
27769  }
27770  ma_zero_object(pConverter);
27771 
27772  if (pConfig == NULL) {
27773  return MA_INVALID_ARGS;
27774  }
27775 
27776  pConverter->config = *pConfig;
27777 
27778  /* SIMD */
27779  pConverter->useSSE2 = ma_has_sse2() && !pConfig->noSSE2;
27780  pConverter->useAVX2 = ma_has_avx2() && !pConfig->noAVX2;
27781  pConverter->useAVX512 = ma_has_avx512f() && !pConfig->noAVX512;
27782  pConverter->useNEON = ma_has_neon() && !pConfig->noNEON;
27783 
27784 #if defined(MA_SUPPORT_AVX512)
27785  if (pConverter->useAVX512) {
27786  ma_format_converter_init_callbacks__avx512(pConverter);
27787  } else
27788 #endif
27789 #if defined(MA_SUPPORT_AVX2)
27790  if (pConverter->useAVX2) {
27791  ma_format_converter_init_callbacks__avx2(pConverter);
27792  } else
27793 #endif
27794 #if defined(MA_SUPPORT_SSE2)
27795  if (pConverter->useSSE2) {
27796  ma_format_converter_init_callbacks__sse2(pConverter);
27797  } else
27798 #endif
27799 #if defined(MA_SUPPORT_NEON)
27800  if (pConverter->useNEON) {
27801  ma_format_converter_init_callbacks__neon(pConverter);
27802  } else
27803 #endif
27804  {
27805  ma_format_converter_init_callbacks__default(pConverter);
27806  }
27807 
27808  switch (pConfig->formatOut)
27809  {
27810  case ma_format_u8:
27811  {
27812  pConverter->onInterleavePCM = ma_pcm_interleave_u8;
27813  pConverter->onDeinterleavePCM = ma_pcm_deinterleave_u8;
27814  } break;
27815  case ma_format_s16:
27816  {
27817  pConverter->onInterleavePCM = ma_pcm_interleave_s16;
27818  pConverter->onDeinterleavePCM = ma_pcm_deinterleave_s16;
27819  } break;
27820  case ma_format_s24:
27821  {
27822  pConverter->onInterleavePCM = ma_pcm_interleave_s24;
27823  pConverter->onDeinterleavePCM = ma_pcm_deinterleave_s24;
27824  } break;
27825  case ma_format_s32:
27826  {
27827  pConverter->onInterleavePCM = ma_pcm_interleave_s32;
27828  pConverter->onDeinterleavePCM = ma_pcm_deinterleave_s32;
27829  } break;
27830  case ma_format_f32:
27831  default:
27832  {
27833  pConverter->onInterleavePCM = ma_pcm_interleave_f32;
27834  pConverter->onDeinterleavePCM = ma_pcm_deinterleave_f32;
27835  } break;
27836  }
27837 
27838  return MA_SUCCESS;
27839 }
27840 
27841 ma_uint64 ma_format_converter_read(ma_format_converter* pConverter, ma_uint64 frameCount, void* pFramesOut, void* pUserData)
27842 {
27843  ma_uint64 totalFramesRead;
27844  ma_uint32 sampleSizeIn;
27845  ma_uint32 sampleSizeOut;
27846  ma_uint32 frameSizeOut;
27847  ma_uint8* pNextFramesOut;
27848 
27849  if (pConverter == NULL || pFramesOut == NULL) {
27850  return 0;
27851  }
27852 
27853  totalFramesRead = 0;
27854  sampleSizeIn = ma_get_bytes_per_sample(pConverter->config.formatIn);
27855  sampleSizeOut = ma_get_bytes_per_sample(pConverter->config.formatOut);
27856  /*frameSizeIn = sampleSizeIn * pConverter->config.channels;*/
27857  frameSizeOut = sampleSizeOut * pConverter->config.channels;
27858  pNextFramesOut = (ma_uint8*)pFramesOut;
27859 
27860  if (pConverter->config.onRead != NULL) {
27861  /* Input data is interleaved. */
27862  if (pConverter->config.formatIn == pConverter->config.formatOut) {
27863  /* Pass through. */
27864  while (totalFramesRead < frameCount) {
27865  ma_uint32 framesJustRead;
27866  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
27867  ma_uint64 framesToReadRightNow = framesRemaining;
27868  if (framesToReadRightNow > 0xFFFFFFFF) {
27869  framesToReadRightNow = 0xFFFFFFFF;
27870  }
27871 
27872  framesJustRead = (ma_uint32)pConverter->config.onRead(pConverter, (ma_uint32)framesToReadRightNow, pNextFramesOut, pUserData);
27873  if (framesJustRead == 0) {
27874  break;
27875  }
27876 
27877  totalFramesRead += framesJustRead;
27878  pNextFramesOut += framesJustRead * frameSizeOut;
27879 
27880  if (framesJustRead < framesToReadRightNow) {
27881  break;
27882  }
27883  }
27884  } else {
27885  /* Conversion required. */
27886  ma_uint32 maxFramesToReadAtATime;
27887 
27889  ma_assert(sizeof(temp) <= 0xFFFFFFFF);
27890 
27891  maxFramesToReadAtATime = sizeof(temp) / sampleSizeIn / pConverter->config.channels;
27892 
27893  while (totalFramesRead < frameCount) {
27894  ma_uint32 framesJustRead;
27895  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
27896  ma_uint64 framesToReadRightNow = framesRemaining;
27897  if (framesToReadRightNow > maxFramesToReadAtATime) {
27898  framesToReadRightNow = maxFramesToReadAtATime;
27899  }
27900 
27901  framesJustRead = (ma_uint32)pConverter->config.onRead(pConverter, (ma_uint32)framesToReadRightNow, temp, pUserData);
27902  if (framesJustRead == 0) {
27903  break;
27904  }
27905 
27906  pConverter->onConvertPCM(pNextFramesOut, temp, framesJustRead*pConverter->config.channels, pConverter->config.ditherMode);
27907 
27908  totalFramesRead += framesJustRead;
27909  pNextFramesOut += framesJustRead * frameSizeOut;
27910 
27911  if (framesJustRead < framesToReadRightNow) {
27912  break;
27913  }
27914  }
27915  }
27916  } else {
27917  /* Input data is deinterleaved. If a conversion is required we need to do an intermediary step. */
27918  void* ppTempSamplesOfOutFormat[MA_MAX_CHANNELS];
27919  size_t splitBufferSizeOut;
27920  ma_uint32 maxFramesToReadAtATime;
27921 
27923  ma_assert(sizeof(tempSamplesOfOutFormat) <= 0xFFFFFFFF);
27924 
27925  ma_split_buffer(tempSamplesOfOutFormat, sizeof(tempSamplesOfOutFormat), pConverter->config.channels, MA_SIMD_ALIGNMENT, (void**)&ppTempSamplesOfOutFormat, &splitBufferSizeOut);
27926 
27927  maxFramesToReadAtATime = (ma_uint32)(splitBufferSizeOut / sampleSizeIn);
27928 
27929  while (totalFramesRead < frameCount) {
27930  ma_uint32 framesJustRead;
27931  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
27932  ma_uint64 framesToReadRightNow = framesRemaining;
27933  if (framesToReadRightNow > maxFramesToReadAtATime) {
27934  framesToReadRightNow = maxFramesToReadAtATime;
27935  }
27936 
27937  if (pConverter->config.formatIn == pConverter->config.formatOut) {
27938  /* Only interleaving. */
27939  framesJustRead = (ma_uint32)pConverter->config.onReadDeinterleaved(pConverter, (ma_uint32)framesToReadRightNow, ppTempSamplesOfOutFormat, pUserData);
27940  if (framesJustRead == 0) {
27941  break;
27942  }
27943  } else {
27944  /* Interleaving + Conversion. Convert first, then interleave. */
27945  void* ppTempSamplesOfInFormat[MA_MAX_CHANNELS];
27946  size_t splitBufferSizeIn;
27947  ma_uint32 iChannel;
27948 
27950 
27951  ma_split_buffer(tempSamplesOfInFormat, sizeof(tempSamplesOfInFormat), pConverter->config.channels, MA_SIMD_ALIGNMENT, (void**)&ppTempSamplesOfInFormat, &splitBufferSizeIn);
27952 
27953  if (framesToReadRightNow > (splitBufferSizeIn / sampleSizeIn)) {
27954  framesToReadRightNow = (splitBufferSizeIn / sampleSizeIn);
27955  }
27956 
27957  framesJustRead = (ma_uint32)pConverter->config.onReadDeinterleaved(pConverter, (ma_uint32)framesToReadRightNow, ppTempSamplesOfInFormat, pUserData);
27958  if (framesJustRead == 0) {
27959  break;
27960  }
27961 
27962  for (iChannel = 0; iChannel < pConverter->config.channels; iChannel += 1) {
27963  pConverter->onConvertPCM(ppTempSamplesOfOutFormat[iChannel], ppTempSamplesOfInFormat[iChannel], framesJustRead, pConverter->config.ditherMode);
27964  }
27965  }
27966 
27967  pConverter->onInterleavePCM(pNextFramesOut, (const void**)ppTempSamplesOfOutFormat, framesJustRead, pConverter->config.channels);
27968 
27969  totalFramesRead += framesJustRead;
27970  pNextFramesOut += framesJustRead * frameSizeOut;
27971 
27972  if (framesJustRead < framesToReadRightNow) {
27973  break;
27974  }
27975  }
27976  }
27977 
27978  return totalFramesRead;
27979 }
27980 
27981 ma_uint64 ma_format_converter_read_deinterleaved(ma_format_converter* pConverter, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
27982 {
27983  ma_uint64 totalFramesRead;
27984  ma_uint32 sampleSizeIn;
27985  ma_uint32 sampleSizeOut;
27986  ma_uint8* ppNextSamplesOut[MA_MAX_CHANNELS];
27987 
27988  if (pConverter == NULL || ppSamplesOut == NULL) {
27989  return 0;
27990  }
27991 
27992  totalFramesRead = 0;
27993  sampleSizeIn = ma_get_bytes_per_sample(pConverter->config.formatIn);
27994  sampleSizeOut = ma_get_bytes_per_sample(pConverter->config.formatOut);
27995 
27996  ma_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pConverter->config.channels);
27997 
27998  if (pConverter->config.onRead != NULL) {
27999  /* Input data is interleaved. */
28000  ma_uint32 maxFramesToReadAtATime;
28001 
28003  ma_assert(sizeof(tempSamplesOfOutFormat) <= 0xFFFFFFFF);
28004 
28005  maxFramesToReadAtATime = sizeof(tempSamplesOfOutFormat) / sampleSizeIn / pConverter->config.channels;
28006 
28007  while (totalFramesRead < frameCount) {
28008  ma_uint32 iChannel;
28009  ma_uint32 framesJustRead;
28010  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
28011  ma_uint64 framesToReadRightNow = framesRemaining;
28012  if (framesToReadRightNow > maxFramesToReadAtATime) {
28013  framesToReadRightNow = maxFramesToReadAtATime;
28014  }
28015 
28016  if (pConverter->config.formatIn == pConverter->config.formatOut) {
28017  /* Only de-interleaving. */
28018  framesJustRead = (ma_uint32)pConverter->config.onRead(pConverter, (ma_uint32)framesToReadRightNow, tempSamplesOfOutFormat, pUserData);
28019  if (framesJustRead == 0) {
28020  break;
28021  }
28022  } else {
28023  /* De-interleaving + Conversion. Convert first, then de-interleave. */
28024  MA_ALIGN(MA_SIMD_ALIGNMENT) ma_uint8 tempSamplesOfInFormat[sizeof(tempSamplesOfOutFormat)];
28025 
28026  framesJustRead = (ma_uint32)pConverter->config.onRead(pConverter, (ma_uint32)framesToReadRightNow, tempSamplesOfInFormat, pUserData);
28027  if (framesJustRead == 0) {
28028  break;
28029  }
28030 
28031  pConverter->onConvertPCM(tempSamplesOfOutFormat, tempSamplesOfInFormat, framesJustRead * pConverter->config.channels, pConverter->config.ditherMode);
28032  }
28033 
28034  pConverter->onDeinterleavePCM((void**)ppNextSamplesOut, tempSamplesOfOutFormat, framesJustRead, pConverter->config.channels);
28035 
28036  totalFramesRead += framesJustRead;
28037  for (iChannel = 0; iChannel < pConverter->config.channels; ++iChannel) {
28038  ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut;
28039  }
28040 
28041  if (framesJustRead < framesToReadRightNow) {
28042  break;
28043  }
28044  }
28045  } else {
28046  /* Input data is deinterleaved. */
28047  if (pConverter->config.formatIn == pConverter->config.formatOut) {
28048  /* Pass through. */
28049  while (totalFramesRead < frameCount) {
28050  ma_uint32 iChannel;
28051  ma_uint32 framesJustRead;
28052  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
28053  ma_uint64 framesToReadRightNow = framesRemaining;
28054  if (framesToReadRightNow > 0xFFFFFFFF) {
28055  framesToReadRightNow = 0xFFFFFFFF;
28056  }
28057 
28058  framesJustRead = (ma_uint32)pConverter->config.onReadDeinterleaved(pConverter, (ma_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pUserData);
28059  if (framesJustRead == 0) {
28060  break;
28061  }
28062 
28063  totalFramesRead += framesJustRead;
28064  for (iChannel = 0; iChannel < pConverter->config.channels; ++iChannel) {
28065  ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut;
28066  }
28067 
28068  if (framesJustRead < framesToReadRightNow) {
28069  break;
28070  }
28071  }
28072  } else {
28073  /* Conversion required. */
28074  void* ppTemp[MA_MAX_CHANNELS];
28075  size_t splitBufferSize;
28076  ma_uint32 maxFramesToReadAtATime;
28077 
28079  ma_assert(sizeof(temp) <= 0xFFFFFFFF);
28080 
28081  ma_split_buffer(temp, sizeof(temp), pConverter->config.channels, MA_SIMD_ALIGNMENT, (void**)&ppTemp, &splitBufferSize);
28082 
28083  maxFramesToReadAtATime = (ma_uint32)(splitBufferSize / sampleSizeIn);
28084 
28085  while (totalFramesRead < frameCount) {
28086  ma_uint32 iChannel;
28087  ma_uint32 framesJustRead;
28088  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
28089  ma_uint64 framesToReadRightNow = framesRemaining;
28090  if (framesToReadRightNow > maxFramesToReadAtATime) {
28091  framesToReadRightNow = maxFramesToReadAtATime;
28092  }
28093 
28094  framesJustRead = (ma_uint32)pConverter->config.onReadDeinterleaved(pConverter, (ma_uint32)framesToReadRightNow, ppTemp, pUserData);
28095  if (framesJustRead == 0) {
28096  break;
28097  }
28098 
28099  for (iChannel = 0; iChannel < pConverter->config.channels; iChannel += 1) {
28100  pConverter->onConvertPCM(ppNextSamplesOut[iChannel], ppTemp[iChannel], framesJustRead, pConverter->config.ditherMode);
28101  ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut;
28102  }
28103 
28104  totalFramesRead += framesJustRead;
28105 
28106  if (framesJustRead < framesToReadRightNow) {
28107  break;
28108  }
28109  }
28110  }
28111  }
28112 
28113  return totalFramesRead;
28114 }
28115 
28116 
28118 {
28120  ma_zero_object(&config);
28121 
28122  return config;
28123 }
28124 
28126 {
28128  config.formatIn = formatIn;
28129  config.formatOut = formatOut;
28130  config.channels = channels;
28131  config.onRead = onRead;
28132  config.onReadDeinterleaved = NULL;
28133  config.pUserData = pUserData;
28134 
28135  return config;
28136 }
28137 
28139 {
28140  ma_format_converter_config config = ma_format_converter_config_init(formatIn, formatOut, channels, NULL, pUserData);
28141  config.onReadDeinterleaved = onReadDeinterleaved;
28142 
28143  return config;
28144 }
28145 
28146 
28147 
28148 /**************************************************************************************************************************************************************
28149 
28150 Channel Routing
28151 
28152 **************************************************************************************************************************************************************/
28153 
28154 /*
28155 -X = Left, +X = Right
28156 -Y = Bottom, +Y = Top
28157 -Z = Front, +Z = Back
28158 */
28159 typedef struct
28160 {
28161  float x;
28162  float y;
28163  float z;
28164 } ma_vec3;
28165 
28166 static MA_INLINE ma_vec3 ma_vec3f(float x, float y, float z)
28167 {
28168  ma_vec3 r;
28169  r.x = x;
28170  r.y = y;
28171  r.z = z;
28172 
28173  return r;
28174 }
28175 
28176 static MA_INLINE ma_vec3 ma_vec3_add(ma_vec3 a, ma_vec3 b)
28177 {
28178  return ma_vec3f(
28179  a.x + b.x,
28180  a.y + b.y,
28181  a.z + b.z
28182  );
28183 }
28184 
28185 static MA_INLINE ma_vec3 ma_vec3_sub(ma_vec3 a, ma_vec3 b)
28186 {
28187  return ma_vec3f(
28188  a.x - b.x,
28189  a.y - b.y,
28190  a.z - b.z
28191  );
28192 }
28193 
28194 static MA_INLINE ma_vec3 ma_vec3_mul(ma_vec3 a, ma_vec3 b)
28195 {
28196  return ma_vec3f(
28197  a.x * b.x,
28198  a.y * b.y,
28199  a.z * b.z
28200  );
28201 }
28202 
28203 static MA_INLINE ma_vec3 ma_vec3_div(ma_vec3 a, ma_vec3 b)
28204 {
28205  return ma_vec3f(
28206  a.x / b.x,
28207  a.y / b.y,
28208  a.z / b.z
28209  );
28210 }
28211 
28212 static MA_INLINE float ma_vec3_dot(ma_vec3 a, ma_vec3 b)
28213 {
28214  return a.x*b.x + a.y*b.y + a.z*b.z;
28215 }
28216 
28217 static MA_INLINE float ma_vec3_length2(ma_vec3 a)
28218 {
28219  return ma_vec3_dot(a, a);
28220 }
28221 
28222 static MA_INLINE float ma_vec3_length(ma_vec3 a)
28223 {
28224  return (float)sqrt(ma_vec3_length2(a));
28225 }
28226 
28227 static MA_INLINE ma_vec3 ma_vec3_normalize(ma_vec3 a)
28228 {
28229  float len = 1 / ma_vec3_length(a);
28230 
28231  ma_vec3 r;
28232  r.x = a.x * len;
28233  r.y = a.y * len;
28234  r.z = a.z * len;
28235 
28236  return r;
28237 }
28238 
28239 static MA_INLINE float ma_vec3_distance(ma_vec3 a, ma_vec3 b)
28240 {
28241  return ma_vec3_length(ma_vec3_sub(a, b));
28242 }
28243 
28244 
28245 #define MA_PLANE_LEFT 0
28246 #define MA_PLANE_RIGHT 1
28247 #define MA_PLANE_FRONT 2
28248 #define MA_PLANE_BACK 3
28249 #define MA_PLANE_BOTTOM 4
28250 #define MA_PLANE_TOP 5
28251 
28252 float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
28253  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */
28254  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */
28255  { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */
28256  { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */
28257  { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */
28258  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */
28259  { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */
28260  { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */
28261  { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */
28262  { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
28263  { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */
28264  { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */
28265  { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */
28266  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */
28267  { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */
28268  { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */
28269  { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */
28270  { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */
28271  { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */
28272  { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */
28273  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */
28274  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */
28275  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */
28276  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */
28277  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */
28278  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */
28279  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */
28280  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */
28281  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */
28282  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */
28283  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */
28284  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */
28285  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */
28286  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */
28287  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */
28288  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */
28289  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */
28290  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */
28291  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */
28292  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */
28293  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */
28294  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */
28295  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */
28296  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */
28297  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */
28298  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */
28299  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */
28300  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */
28301  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */
28302  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */
28303  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */
28304  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
28305 };
28306 
28307 float ma_calculate_channel_position_planar_weight(ma_channel channelPositionA, ma_channel channelPositionB)
28308 {
28309  /*
28310  Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
28311  the following output configuration:
28312 
28313  - front/left
28314  - side/left
28315  - back/left
28316 
28317  The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount
28318  of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
28319 
28320  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
28321  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
28322  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
28323  receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
28324  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
28325  across 3 spatial dimensions.
28326 
28327  The first thing to do is figure out how each speaker's volume is spread over each of plane:
28328  - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane
28329  - side/left: 1 plane (left only) = 1/1 = entire volume from left plane
28330  - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane
28331  - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane
28332 
28333  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
28334  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
28335  taken by the other to produce the final contribution.
28336  */
28337 
28338  /* Contribution = Sum(Volume to Give * Volume to Take) */
28339  float contribution =
28340  g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
28341  g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
28342  g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
28343  g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
28344  g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
28345  g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
28346 
28347  return contribution;
28348 }
28349 
28350 float ma_channel_router__calculate_input_channel_planar_weight(const ma_channel_router* pRouter, ma_channel channelPositionIn, ma_channel channelPositionOut)
28351 {
28352  ma_assert(pRouter != NULL);
28353  (void)pRouter;
28354 
28355  return ma_calculate_channel_position_planar_weight(channelPositionIn, channelPositionOut);
28356 }
28357 
28358 ma_bool32 ma_channel_router__is_spatial_channel_position(const ma_channel_router* pRouter, ma_channel channelPosition)
28359 {
28360  int i;
28361 
28362  ma_assert(pRouter != NULL);
28363  (void)pRouter;
28364 
28365  if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
28366  return MA_FALSE;
28367  }
28368 
28369  for (i = 0; i < 6; ++i) {
28370  if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
28371  return MA_TRUE;
28372  }
28373  }
28374 
28375  return MA_FALSE;
28376 }
28377 
28379 {
28380  ma_uint32 iChannelIn;
28381  ma_uint32 iChannelOut;
28382 
28383  if (pRouter == NULL) {
28384  return MA_INVALID_ARGS;
28385  }
28386 
28387  ma_zero_object(pRouter);
28388 
28389  if (pConfig == NULL) {
28390  return MA_INVALID_ARGS;
28391  }
28392  if (pConfig->onReadDeinterleaved == NULL) {
28393  return MA_INVALID_ARGS;
28394  }
28395 
28396  if (!ma_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) {
28397  return MA_INVALID_ARGS; /* Invalid input channel map. */
28398  }
28399  if (!ma_channel_map_valid(pConfig->channelsOut, pConfig->channelMapOut)) {
28400  return MA_INVALID_ARGS; /* Invalid output channel map. */
28401  }
28402 
28403  pRouter->config = *pConfig;
28404 
28405  /* SIMD */
28406  pRouter->useSSE2 = ma_has_sse2() && !pConfig->noSSE2;
28407  pRouter->useAVX2 = ma_has_avx2() && !pConfig->noAVX2;
28408  pRouter->useAVX512 = ma_has_avx512f() && !pConfig->noAVX512;
28409  pRouter->useNEON = ma_has_neon() && !pConfig->noNEON;
28410 
28411  /* If the input and output channels and channel maps are the same we should use a passthrough. */
28412  if (pRouter->config.channelsIn == pRouter->config.channelsOut) {
28413  if (ma_channel_map_equal(pRouter->config.channelsIn, pRouter->config.channelMapIn, pRouter->config.channelMapOut)) {
28414  pRouter->isPassthrough = MA_TRUE;
28415  }
28417  pRouter->isPassthrough = MA_TRUE;
28418  }
28419  }
28420 
28421  /*
28422  Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules:
28423 
28424  1) If it's a passthrough, do nothing - it's just a simple memcpy().
28425  2) If the channel counts are the same and every channel position in the input map is present in the output map, use a
28426  simple shuffle. An example might be different 5.1 channel layouts.
28427  3) Otherwise channels are blended based on spatial locality.
28428  */
28429  if (!pRouter->isPassthrough) {
28430  if (pRouter->config.channelsIn == pRouter->config.channelsOut) {
28431  ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
28432  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
28433  ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
28434  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
28435  if (pRouter->config.channelMapIn[iChannelIn] == pRouter->config.channelMapOut[iChannelOut]) {
28436  isInputChannelPositionInOutput = MA_TRUE;
28437  break;
28438  }
28439  }
28440 
28441  if (!isInputChannelPositionInOutput) {
28442  areAllChannelPositionsPresent = MA_FALSE;
28443  break;
28444  }
28445  }
28446 
28447  if (areAllChannelPositionsPresent) {
28448  pRouter->isSimpleShuffle = MA_TRUE;
28449 
28450  /*
28451  All the router will be doing is rearranging channels which means all we need to do is use a shuffling table which is just
28452  a mapping between the index of the input channel to the index of the output channel.
28453  */
28454  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
28455  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
28456  if (pRouter->config.channelMapIn[iChannelIn] == pRouter->config.channelMapOut[iChannelOut]) {
28457  pRouter->shuffleTable[iChannelIn] = (ma_uint8)iChannelOut;
28458  break;
28459  }
28460  }
28461  }
28462  }
28463  }
28464  }
28465 
28466 
28467  /*
28468  Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple
28469  shuffling. We use different algorithms for calculating weights depending on our mixing mode.
28470 
28471  In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just
28472  map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel
28473  map, nothing will be heard!
28474  */
28475 
28476  /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */
28477  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
28478  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
28479 
28480  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
28481  ma_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
28482 
28483  if (channelPosIn == channelPosOut) {
28484  pRouter->config.weights[iChannelIn][iChannelOut] = 1;
28485  }
28486  }
28487  }
28488 
28489  /*
28490  The mono channel is accumulated on all other channels, except LFE. Make sure in this loop we exclude output mono channels since
28491  they were handled in the pass above.
28492  */
28493  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
28494  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
28495 
28496  if (channelPosIn == MA_CHANNEL_MONO) {
28497  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
28498  ma_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
28499 
28500  if (channelPosOut != MA_CHANNEL_NONE && channelPosOut != MA_CHANNEL_MONO && channelPosOut != MA_CHANNEL_LFE) {
28501  pRouter->config.weights[iChannelIn][iChannelOut] = 1;
28502  }
28503  }
28504  }
28505  }
28506 
28507  /* The output mono channel is the average of all non-none, non-mono and non-lfe input channels. */
28508  {
28509  ma_uint32 len = 0;
28510  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
28511  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
28512 
28513  if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
28514  len += 1;
28515  }
28516  }
28517 
28518  if (len > 0) {
28519  float monoWeight = 1.0f / len;
28520 
28521  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
28522  ma_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
28523 
28524  if (channelPosOut == MA_CHANNEL_MONO) {
28525  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
28526  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
28527 
28528  if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
28529  pRouter->config.weights[iChannelIn][iChannelOut] += monoWeight;
28530  }
28531  }
28532  }
28533  }
28534  }
28535  }
28536 
28537 
28538  /* Input and output channels that are not present on the other side need to be blended in based on spatial locality. */
28539  switch (pRouter->config.mixingMode)
28540  {
28542  {
28543  /* Unmapped input channels. */
28544  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
28545  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
28546 
28547  if (ma_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
28548  if (!ma_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) {
28549  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
28550  ma_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
28551 
28552  if (ma_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
28553  float weight = 0;
28555  weight = ma_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
28556  }
28557 
28558  /* Only apply the weight if we haven't already got some contribution from the respective channels. */
28559  if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) {
28560  pRouter->config.weights[iChannelIn][iChannelOut] = weight;
28561  }
28562  }
28563  }
28564  }
28565  }
28566  }
28567 
28568  /* Unmapped output channels. */
28569  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
28570  ma_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
28571 
28572  if (ma_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
28573  if (!ma_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) {
28574  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
28575  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
28576 
28577  if (ma_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
28578  float weight = 0;
28580  weight = ma_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
28581  }
28582 
28583  /* Only apply the weight if we haven't already got some contribution from the respective channels. */
28584  if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) {
28585  pRouter->config.weights[iChannelIn][iChannelOut] = weight;
28586  }
28587  }
28588  }
28589  }
28590  }
28591  }
28592  } break;
28593 
28596  default:
28597  {
28598  /* Fallthrough. */
28599  } break;
28600  }
28601 
28602  return MA_SUCCESS;
28603 }
28604 
28605 static MA_INLINE ma_bool32 ma_channel_router__can_use_sse2(ma_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn)
28606 {
28607  return pRouter->useSSE2 && (((ma_uintptr)pSamplesOut & 15) == 0) && (((ma_uintptr)pSamplesIn & 15) == 0);
28608 }
28609 
28610 static MA_INLINE ma_bool32 ma_channel_router__can_use_avx2(ma_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn)
28611 {
28612  return pRouter->useAVX2 && (((ma_uintptr)pSamplesOut & 31) == 0) && (((ma_uintptr)pSamplesIn & 31) == 0);
28613 }
28614 
28615 static MA_INLINE ma_bool32 ma_channel_router__can_use_avx512(ma_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn)
28616 {
28617  return pRouter->useAVX512 && (((ma_uintptr)pSamplesOut & 63) == 0) && (((ma_uintptr)pSamplesIn & 63) == 0);
28618 }
28619 
28620 static MA_INLINE ma_bool32 ma_channel_router__can_use_neon(ma_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn)
28621 {
28622  return pRouter->useNEON && (((ma_uintptr)pSamplesOut & 15) == 0) && (((ma_uintptr)pSamplesIn & 15) == 0);
28623 }
28624 
28625 void ma_channel_router__do_routing(ma_channel_router* pRouter, ma_uint64 frameCount, float** ppSamplesOut, const float** ppSamplesIn)
28626 {
28627  ma_uint32 iChannelIn;
28628  ma_uint32 iChannelOut;
28629 
28630  ma_assert(pRouter != NULL);
28631  ma_assert(pRouter->isPassthrough == MA_FALSE);
28632 
28633  if (pRouter->isSimpleShuffle) {
28634  /* A shuffle is just a re-arrangement of channels and does not require any arithmetic. */
28635  ma_assert(pRouter->config.channelsIn == pRouter->config.channelsOut);
28636  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
28637  iChannelOut = pRouter->shuffleTable[iChannelIn];
28638  ma_copy_memory_64(ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn], frameCount * sizeof(float));
28639  }
28640  } else {
28641  /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
28642 
28643  /* Clear. */
28644  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
28645  ma_zero_memory_64(ppSamplesOut[iChannelOut], frameCount * sizeof(float));
28646  }
28647 
28648  /* Accumulate. */
28649  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
28650  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
28651  ma_uint64 iFrame = 0;
28652 #if defined(MA_SUPPORT_NEON)
28653  if (ma_channel_router__can_use_neon(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
28654  float32x4_t weight = vmovq_n_f32(pRouter->config.weights[iChannelIn][iChannelOut]);
28655  ma_uint64 frameCount4 = frameCount/4;
28656  ma_uint64 iFrame4;
28657 
28658  for (iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
28659  float32x4_t* pO = (float32x4_t*)ppSamplesOut[iChannelOut] + iFrame4;
28660  float32x4_t* pI = (float32x4_t*)ppSamplesIn [iChannelIn ] + iFrame4;
28661  *pO = vaddq_f32(*pO, vmulq_f32(*pI, weight));
28662  }
28663 
28664  iFrame += frameCount4*4;
28665  }
28666  else
28667 #endif
28668 #if defined(MA_SUPPORT_AVX512)
28669  if (ma_channel_router__can_use_avx512(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
28670  __m512 weight = _mm512_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
28671  ma_uint64 frameCount16 = frameCount/16;
28672  ma_uint64 iFrame16;
28673 
28674  for (iFrame16 = 0; iFrame16 < frameCount16; iFrame16 += 1) {
28675  __m512* pO = (__m512*)ppSamplesOut[iChannelOut] + iFrame16;
28676  __m512* pI = (__m512*)ppSamplesIn [iChannelIn ] + iFrame16;
28677  *pO = _mm512_add_ps(*pO, _mm512_mul_ps(*pI, weight));
28678  }
28679 
28680  iFrame += frameCount16*16;
28681  }
28682  else
28683 #endif
28684 #if defined(MA_SUPPORT_AVX2)
28685  if (ma_channel_router__can_use_avx2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
28686  __m256 weight = _mm256_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
28687  ma_uint64 frameCount8 = frameCount/8;
28688  ma_uint64 iFrame8;
28689 
28690  for (iFrame8 = 0; iFrame8 < frameCount8; iFrame8 += 1) {
28691  __m256* pO = (__m256*)ppSamplesOut[iChannelOut] + iFrame8;
28692  __m256* pI = (__m256*)ppSamplesIn [iChannelIn ] + iFrame8;
28693  *pO = _mm256_add_ps(*pO, _mm256_mul_ps(*pI, weight));
28694  }
28695 
28696  iFrame += frameCount8*8;
28697  }
28698  else
28699 #endif
28700 #if defined(MA_SUPPORT_SSE2)
28701  if (ma_channel_router__can_use_sse2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
28702  __m128 weight = _mm_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
28703  ma_uint64 frameCount4 = frameCount/4;
28704  ma_uint64 iFrame4;
28705 
28706  for (iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
28707  __m128* pO = (__m128*)ppSamplesOut[iChannelOut] + iFrame4;
28708  __m128* pI = (__m128*)ppSamplesIn [iChannelIn ] + iFrame4;
28709  *pO = _mm_add_ps(*pO, _mm_mul_ps(*pI, weight));
28710  }
28711 
28712  iFrame += frameCount4*4;
28713  } else
28714 #endif
28715  { /* Reference. */
28716  float weight0 = pRouter->config.weights[iChannelIn][iChannelOut];
28717  float weight1 = pRouter->config.weights[iChannelIn][iChannelOut];
28718  float weight2 = pRouter->config.weights[iChannelIn][iChannelOut];
28719  float weight3 = pRouter->config.weights[iChannelIn][iChannelOut];
28720  ma_uint64 frameCount4 = frameCount/4;
28721  ma_uint64 iFrame4;
28722 
28723  for (iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
28724  ppSamplesOut[iChannelOut][iFrame+0] += ppSamplesIn[iChannelIn][iFrame+0] * weight0;
28725  ppSamplesOut[iChannelOut][iFrame+1] += ppSamplesIn[iChannelIn][iFrame+1] * weight1;
28726  ppSamplesOut[iChannelOut][iFrame+2] += ppSamplesIn[iChannelIn][iFrame+2] * weight2;
28727  ppSamplesOut[iChannelOut][iFrame+3] += ppSamplesIn[iChannelIn][iFrame+3] * weight3;
28728  iFrame += 4;
28729  }
28730  }
28731 
28732  /* Leftover. */
28733  for (; iFrame < frameCount; ++iFrame) {
28734  ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * pRouter->config.weights[iChannelIn][iChannelOut];
28735  }
28736  }
28737  }
28738  }
28739 }
28740 
28741 ma_uint64 ma_channel_router_read_deinterleaved(ma_channel_router* pRouter, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
28742 {
28743  if (pRouter == NULL || ppSamplesOut == NULL) {
28744  return 0;
28745  }
28746 
28747  /* Fast path for a passthrough. */
28748  if (pRouter->isPassthrough) {
28749  if (frameCount <= 0xFFFFFFFF) {
28750  return (ma_uint32)pRouter->config.onReadDeinterleaved(pRouter, (ma_uint32)frameCount, ppSamplesOut, pUserData);
28751  } else {
28752  float* ppNextSamplesOut[MA_MAX_CHANNELS];
28753  ma_uint64 totalFramesRead;
28754 
28755  ma_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(float*) * pRouter->config.channelsOut);
28756 
28757  totalFramesRead = 0;
28758  while (totalFramesRead < frameCount) {
28759  ma_uint32 iChannel;
28760  ma_uint32 framesJustRead;
28761  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
28762  ma_uint64 framesToReadRightNow = framesRemaining;
28763  if (framesToReadRightNow > 0xFFFFFFFF) {
28764  framesToReadRightNow = 0xFFFFFFFF;
28765  }
28766 
28767  framesJustRead = (ma_uint32)pRouter->config.onReadDeinterleaved(pRouter, (ma_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pUserData);
28768  if (framesJustRead == 0) {
28769  break;
28770  }
28771 
28772  totalFramesRead += framesJustRead;
28773 
28774  if (framesJustRead < framesToReadRightNow) {
28775  break;
28776  }
28777 
28778  for (iChannel = 0; iChannel < pRouter->config.channelsOut; ++iChannel) {
28779  ppNextSamplesOut[iChannel] += framesJustRead;
28780  }
28781  }
28782 
28783  return totalFramesRead;
28784  }
28785  }
28786 
28787  /* Slower path for a non-passthrough. */
28788  {
28789  float* ppNextSamplesOut[MA_MAX_CHANNELS];
28790  float* ppTemp[MA_MAX_CHANNELS];
28791  size_t maxBytesToReadPerFrameEachIteration;
28792  size_t maxFramesToReadEachIteration;
28793  ma_uint64 totalFramesRead;
28794  MA_ALIGN(MA_SIMD_ALIGNMENT) float temp[MA_MAX_CHANNELS * 256];
28795 
28796  ma_assert(sizeof(temp) <= 0xFFFFFFFF);
28797  ma_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(float*) * pRouter->config.channelsOut);
28798 
28799 
28800  ma_split_buffer(temp, sizeof(temp), pRouter->config.channelsIn, MA_SIMD_ALIGNMENT, (void**)&ppTemp, &maxBytesToReadPerFrameEachIteration);
28801 
28802  maxFramesToReadEachIteration = maxBytesToReadPerFrameEachIteration/sizeof(float);
28803 
28804  totalFramesRead = 0;
28805  while (totalFramesRead < frameCount) {
28806  ma_uint32 iChannel;
28807  ma_uint32 framesJustRead;
28808  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
28809  ma_uint64 framesToReadRightNow = framesRemaining;
28810  if (framesToReadRightNow > maxFramesToReadEachIteration) {
28811  framesToReadRightNow = maxFramesToReadEachIteration;
28812  }
28813 
28814  framesJustRead = pRouter->config.onReadDeinterleaved(pRouter, (ma_uint32)framesToReadRightNow, (void**)ppTemp, pUserData);
28815  if (framesJustRead == 0) {
28816  break;
28817  }
28818 
28819  ma_channel_router__do_routing(pRouter, framesJustRead, (float**)ppNextSamplesOut, (const float**)ppTemp); /* <-- Real work is done here. */
28820 
28821  totalFramesRead += framesJustRead;
28822  if (totalFramesRead < frameCount) {
28823  for (iChannel = 0; iChannel < pRouter->config.channelsIn; iChannel += 1) {
28824  ppNextSamplesOut[iChannel] += framesJustRead;
28825  }
28826  }
28827 
28828  if (framesJustRead < framesToReadRightNow) {
28829  break;
28830  }
28831  }
28832 
28833  return totalFramesRead;
28834  }
28835 }
28836 
28837 ma_channel_router_config ma_channel_router_config_init(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, ma_channel_router_read_deinterleaved_proc onRead, void* pUserData)
28838 {
28839  ma_channel_router_config config;
28840  ma_uint32 iChannel;
28841 
28842  ma_zero_object(&config);
28843 
28844  config.channelsIn = channelsIn;
28845  for (iChannel = 0; iChannel < channelsIn; ++iChannel) {
28846  config.channelMapIn[iChannel] = channelMapIn[iChannel];
28847  }
28848 
28849  config.channelsOut = channelsOut;
28850  for (iChannel = 0; iChannel < channelsOut; ++iChannel) {
28851  config.channelMapOut[iChannel] = channelMapOut[iChannel];
28852  }
28853 
28854  config.mixingMode = mixingMode;
28855  config.onReadDeinterleaved = onRead;
28856  config.pUserData = pUserData;
28857 
28858  return config;
28859 }
28860 
28861 
28862 
28863 /**************************************************************************************************************************************************************
28864 
28865 SRC
28866 
28867 **************************************************************************************************************************************************************/
28868 #define ma_floorf(x) ((float)floor((double)(x)))
28869 #define ma_sinf(x) ((float)sin((double)(x)))
28870 #define ma_cosf(x) ((float)cos((double)(x)))
28871 
28872 static MA_INLINE double ma_sinc(double x)
28873 {
28874  if (x != 0) {
28875  return sin(MA_PI_D*x) / (MA_PI_D*x);
28876  } else {
28877  return 1;
28878  }
28879 }
28880 
28881 #define ma_sincf(x) ((float)ma_sinc((double)(x)))
28882 
28883 
28884 ma_uint64 ma_src_read_deinterleaved__passthrough(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
28885 ma_uint64 ma_src_read_deinterleaved__linear(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
28886 ma_uint64 ma_src_read_deinterleaved__sinc(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
28887 
28888 void ma_src__build_sinc_table__sinc(ma_src* pSRC)
28889 {
28890  ma_uint32 i;
28891 
28892  ma_assert(pSRC != NULL);
28893 
28894  pSRC->sinc.table[0] = 1.0f;
28895  for (i = 1; i < ma_countof(pSRC->sinc.table); i += 1) {
28896  double x = i*MA_PI_D / MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION;
28897  pSRC->sinc.table[i] = (float)(sin(x)/x);
28898  }
28899 }
28900 
28901 void ma_src__build_sinc_table__rectangular(ma_src* pSRC)
28902 {
28903  /* This is the same as the base sinc table. */
28904  ma_src__build_sinc_table__sinc(pSRC);
28905 }
28906 
28907 void ma_src__build_sinc_table__hann(ma_src* pSRC)
28908 {
28909  ma_uint32 i;
28910 
28911  ma_src__build_sinc_table__sinc(pSRC);
28912 
28913  for (i = 0; i < ma_countof(pSRC->sinc.table); i += 1) {
28914  double x = pSRC->sinc.table[i];
28915  double N = MA_SRC_SINC_MAX_WINDOW_WIDTH*2;
28917  double w = 0.5 * (1 - cos((2*MA_PI_D*n) / (N)));
28918 
28919  pSRC->sinc.table[i] = (float)(x * w);
28920  }
28921 }
28922 
28923 ma_result ma_src_init(const ma_src_config* pConfig, ma_src* pSRC)
28924 {
28925  if (pSRC == NULL) {
28926  return MA_INVALID_ARGS;
28927  }
28928 
28929  ma_zero_object(pSRC);
28930 
28931  if (pConfig == NULL || pConfig->onReadDeinterleaved == NULL) {
28932  return MA_INVALID_ARGS;
28933  }
28934  if (pConfig->channels == 0 || pConfig->channels > MA_MAX_CHANNELS) {
28935  return MA_INVALID_ARGS;
28936  }
28937 
28938  pSRC->config = *pConfig;
28939 
28940  /* SIMD */
28941  pSRC->useSSE2 = ma_has_sse2() && !pConfig->noSSE2;
28942  pSRC->useAVX2 = ma_has_avx2() && !pConfig->noAVX2;
28943  pSRC->useAVX512 = ma_has_avx512f() && !pConfig->noAVX512;
28944  pSRC->useNEON = ma_has_neon() && !pConfig->noNEON;
28945 
28946  if (pSRC->config.algorithm == ma_src_algorithm_sinc) {
28947  /* Make sure the window width within bounds. */
28948  if (pSRC->config.sinc.windowWidth == 0) {
28949  pSRC->config.sinc.windowWidth = MA_SRC_SINC_DEFAULT_WINDOW_WIDTH;
28950  }
28951  if (pSRC->config.sinc.windowWidth < MA_SRC_SINC_MIN_WINDOW_WIDTH) {
28952  pSRC->config.sinc.windowWidth = MA_SRC_SINC_MIN_WINDOW_WIDTH;
28953  }
28954  if (pSRC->config.sinc.windowWidth > MA_SRC_SINC_MAX_WINDOW_WIDTH) {
28955  pSRC->config.sinc.windowWidth = MA_SRC_SINC_MAX_WINDOW_WIDTH;
28956  }
28957 
28958  /* Set up the lookup table. */
28959  switch (pSRC->config.sinc.windowFunction) {
28960  case ma_src_sinc_window_function_hann: ma_src__build_sinc_table__hann(pSRC); break;
28961  case ma_src_sinc_window_function_rectangular: ma_src__build_sinc_table__rectangular(pSRC); break;
28962  default: return MA_INVALID_ARGS; /* <-- Hitting this means the window function is unknown to miniaudio. */
28963  }
28964  }
28965 
28966  return MA_SUCCESS;
28967 }
28968 
28969 ma_result ma_src_set_sample_rate(ma_src* pSRC, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
28970 {
28971  if (pSRC == NULL) {
28972  return MA_INVALID_ARGS;
28973  }
28974 
28975  /* Must have a sample rate of > 0. */
28976  if (sampleRateIn == 0 || sampleRateOut == 0) {
28977  return MA_INVALID_ARGS;
28978  }
28979 
28980  ma_atomic_exchange_32(&pSRC->config.sampleRateIn, sampleRateIn);
28981  ma_atomic_exchange_32(&pSRC->config.sampleRateOut, sampleRateOut);
28982 
28983  return MA_SUCCESS;
28984 }
28985 
28986 ma_uint64 ma_src_read_deinterleaved(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
28987 {
28988  ma_src_algorithm algorithm;
28989 
28990  if (pSRC == NULL || frameCount == 0 || ppSamplesOut == NULL) {
28991  return 0;
28992  }
28993 
28994  algorithm = pSRC->config.algorithm;
28995 
28996  /* Can use a function pointer for this. */
28997  switch (algorithm) {
28998  case ma_src_algorithm_none: return ma_src_read_deinterleaved__passthrough(pSRC, frameCount, ppSamplesOut, pUserData);
28999  case ma_src_algorithm_linear: return ma_src_read_deinterleaved__linear( pSRC, frameCount, ppSamplesOut, pUserData);
29000  case ma_src_algorithm_sinc: return ma_src_read_deinterleaved__sinc( pSRC, frameCount, ppSamplesOut, pUserData);
29001  default: break;
29002  }
29003 
29004  /* Should never get here. */
29005  return 0;
29006 }
29007 
29008 ma_uint64 ma_src_read_deinterleaved__passthrough(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
29009 {
29010  if (frameCount <= 0xFFFFFFFF) {
29011  return pSRC->config.onReadDeinterleaved(pSRC, (ma_uint32)frameCount, ppSamplesOut, pUserData);
29012  } else {
29013  ma_uint32 iChannel;
29014  ma_uint64 totalFramesRead;
29015  float* ppNextSamplesOut[MA_MAX_CHANNELS];
29016 
29017  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
29018  ppNextSamplesOut[iChannel] = (float*)ppSamplesOut[iChannel];
29019  }
29020 
29021  totalFramesRead = 0;
29022  while (totalFramesRead < frameCount) {
29023  ma_uint32 framesJustRead;
29024  ma_uint64 framesRemaining = frameCount - totalFramesRead;
29025  ma_uint64 framesToReadRightNow = framesRemaining;
29026  if (framesToReadRightNow > 0xFFFFFFFF) {
29027  framesToReadRightNow = 0xFFFFFFFF;
29028  }
29029 
29030  framesJustRead = (ma_uint32)pSRC->config.onReadDeinterleaved(pSRC, (ma_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pUserData);
29031  if (framesJustRead == 0) {
29032  break;
29033  }
29034 
29035  totalFramesRead += framesJustRead;
29036  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
29037  ppNextSamplesOut[iChannel] += framesJustRead;
29038  }
29039 
29040  if (framesJustRead < framesToReadRightNow) {
29041  break;
29042  }
29043  }
29044 
29045  return totalFramesRead;
29046  }
29047 }
29048 
29049 ma_uint64 ma_src_read_deinterleaved__linear(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
29050 {
29051  float* ppNextSamplesOut[MA_MAX_CHANNELS];
29052  float factor;
29053  ma_uint32 maxFrameCountPerChunkIn;
29054  ma_uint64 totalFramesRead;
29055 
29056  ma_assert(pSRC != NULL);
29057  ma_assert(frameCount > 0);
29058  ma_assert(ppSamplesOut != NULL);
29059 
29060  ma_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pSRC->config.channels);
29061 
29062  factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
29063  maxFrameCountPerChunkIn = ma_countof(pSRC->linear.input[0]);
29064 
29065  totalFramesRead = 0;
29066  while (totalFramesRead < frameCount) {
29067  ma_uint32 iChannel;
29068  float tBeg;
29069  float tEnd;
29070  float tAvailable;
29071  float tNext;
29072  float* ppSamplesFromClient[MA_MAX_CHANNELS];
29073  ma_uint32 iNextFrame;
29074  ma_uint32 maxOutputFramesToRead;
29075  ma_uint32 maxOutputFramesToRead4;
29076  ma_uint32 framesToReadFromClient;
29077  ma_uint32 framesReadFromClient;
29078  ma_uint64 framesRemaining = frameCount - totalFramesRead;
29079  ma_uint64 framesToRead = framesRemaining;
29080  if (framesToRead > 16384) {
29081  framesToRead = 16384; /* <-- Keep this small because we're using 32-bit floats for calculating sample positions and I don't want to run out of precision with huge sample counts. */
29082  }
29083 
29084 
29085  /* Read Input Data */
29086  tBeg = pSRC->linear.timeIn;
29087  tEnd = tBeg + ((ma_int64)framesToRead*factor); /* Cast to int64 required for VC6. */
29088 
29089  framesToReadFromClient = (ma_uint32)(tEnd) + 1 + 1; /* +1 to make tEnd 1-based and +1 because we always need to an extra sample for interpolation. */
29090  if (framesToReadFromClient >= maxFrameCountPerChunkIn) {
29091  framesToReadFromClient = maxFrameCountPerChunkIn;
29092  }
29093 
29094  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
29095  ppSamplesFromClient[iChannel] = pSRC->linear.input[iChannel] + pSRC->linear.leftoverFrames;
29096  }
29097 
29098  framesReadFromClient = 0;
29099  if (framesToReadFromClient > pSRC->linear.leftoverFrames) {
29100  framesReadFromClient = (ma_uint32)pSRC->config.onReadDeinterleaved(pSRC, (ma_uint32)framesToReadFromClient - pSRC->linear.leftoverFrames, (void**)ppSamplesFromClient, pUserData);
29101  }
29102 
29103  framesReadFromClient += pSRC->linear.leftoverFrames; /* <-- You can sort of think of it as though we've re-read the leftover samples from the client. */
29104  if (framesReadFromClient < 2) {
29105  break;
29106  }
29107 
29108  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
29109  ppSamplesFromClient[iChannel] = pSRC->linear.input[iChannel];
29110  }
29111 
29112 
29113  /* Write Output Data */
29114 
29115  /*
29116  At this point we have a bunch of frames that the client has given to us for processing. From this we can determine the maximum number of output frames
29117  that can be processed from this input. We want to output as many samples as possible from our input data.
29118  */
29119  tAvailable = framesReadFromClient - tBeg - 1; /* Subtract 1 because the last input sample is needed for interpolation and cannot be included in the output sample count calculation. */
29120 
29121  maxOutputFramesToRead = (ma_uint32)(tAvailable / factor);
29122  if (maxOutputFramesToRead == 0) {
29123  maxOutputFramesToRead = 1;
29124  }
29125  if (maxOutputFramesToRead > framesToRead) {
29126  maxOutputFramesToRead = (ma_uint32)framesToRead;
29127  }
29128 
29129  /* Output frames are always read in groups of 4 because I'm planning on using this as a reference for some SIMD-y stuff later. */
29130  maxOutputFramesToRead4 = maxOutputFramesToRead/4;
29131  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
29132  ma_uint32 iFrameOut;
29133  float t0 = pSRC->linear.timeIn + factor*0;
29134  float t1 = pSRC->linear.timeIn + factor*1;
29135  float t2 = pSRC->linear.timeIn + factor*2;
29136  float t3 = pSRC->linear.timeIn + factor*3;
29137  float t;
29138 
29139  for (iFrameOut = 0; iFrameOut < maxOutputFramesToRead4; iFrameOut += 1) {
29140  float iPrevSample0 = (float)floor(t0);
29141  float iPrevSample1 = (float)floor(t1);
29142  float iPrevSample2 = (float)floor(t2);
29143  float iPrevSample3 = (float)floor(t3);
29144 
29145  float iNextSample0 = iPrevSample0 + 1;
29146  float iNextSample1 = iPrevSample1 + 1;
29147  float iNextSample2 = iPrevSample2 + 1;
29148  float iNextSample3 = iPrevSample3 + 1;
29149 
29150  float alpha0 = t0 - iPrevSample0;
29151  float alpha1 = t1 - iPrevSample1;
29152  float alpha2 = t2 - iPrevSample2;
29153  float alpha3 = t3 - iPrevSample3;
29154 
29155  float prevSample0 = ppSamplesFromClient[iChannel][(ma_uint32)iPrevSample0];
29156  float prevSample1 = ppSamplesFromClient[iChannel][(ma_uint32)iPrevSample1];
29157  float prevSample2 = ppSamplesFromClient[iChannel][(ma_uint32)iPrevSample2];
29158  float prevSample3 = ppSamplesFromClient[iChannel][(ma_uint32)iPrevSample3];
29159 
29160  float nextSample0 = ppSamplesFromClient[iChannel][(ma_uint32)iNextSample0];
29161  float nextSample1 = ppSamplesFromClient[iChannel][(ma_uint32)iNextSample1];
29162  float nextSample2 = ppSamplesFromClient[iChannel][(ma_uint32)iNextSample2];
29163  float nextSample3 = ppSamplesFromClient[iChannel][(ma_uint32)iNextSample3];
29164 
29165  ppNextSamplesOut[iChannel][iFrameOut*4 + 0] = ma_mix_f32_fast(prevSample0, nextSample0, alpha0);
29166  ppNextSamplesOut[iChannel][iFrameOut*4 + 1] = ma_mix_f32_fast(prevSample1, nextSample1, alpha1);
29167  ppNextSamplesOut[iChannel][iFrameOut*4 + 2] = ma_mix_f32_fast(prevSample2, nextSample2, alpha2);
29168  ppNextSamplesOut[iChannel][iFrameOut*4 + 3] = ma_mix_f32_fast(prevSample3, nextSample3, alpha3);
29169 
29170  t0 += factor*4;
29171  t1 += factor*4;
29172  t2 += factor*4;
29173  t3 += factor*4;
29174  }
29175 
29176  t = pSRC->linear.timeIn + (factor*maxOutputFramesToRead4*4);
29177  for (iFrameOut = (maxOutputFramesToRead4*4); iFrameOut < maxOutputFramesToRead; iFrameOut += 1) {
29178  float iPrevSample = (float)floor(t);
29179  float iNextSample = iPrevSample + 1;
29180  float alpha = t - iPrevSample;
29181  float prevSample;
29182  float nextSample;
29183 
29184  ma_assert(iPrevSample < ma_countof(pSRC->linear.input[iChannel]));
29185  ma_assert(iNextSample < ma_countof(pSRC->linear.input[iChannel]));
29186 
29187  prevSample = ppSamplesFromClient[iChannel][(ma_uint32)iPrevSample];
29188  nextSample = ppSamplesFromClient[iChannel][(ma_uint32)iNextSample];
29189 
29190  ppNextSamplesOut[iChannel][iFrameOut] = ma_mix_f32_fast(prevSample, nextSample, alpha);
29191 
29192  t += factor;
29193  }
29194 
29195  ppNextSamplesOut[iChannel] += maxOutputFramesToRead;
29196  }
29197 
29198  totalFramesRead += maxOutputFramesToRead;
29199 
29200 
29201  /* Residual */
29202  tNext = pSRC->linear.timeIn + (maxOutputFramesToRead*factor);
29203 
29204  pSRC->linear.timeIn = tNext;
29205  ma_assert(tNext <= framesReadFromClient+1);
29206 
29207  iNextFrame = (ma_uint32)floor(tNext);
29208  pSRC->linear.leftoverFrames = framesReadFromClient - iNextFrame;
29209  pSRC->linear.timeIn = tNext - iNextFrame;
29210 
29211  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
29212  ma_uint32 iFrame;
29213  for (iFrame = 0; iFrame < pSRC->linear.leftoverFrames; ++iFrame) {
29214  float sample = ppSamplesFromClient[iChannel][framesReadFromClient-pSRC->linear.leftoverFrames + iFrame];
29215  ppSamplesFromClient[iChannel][iFrame] = sample;
29216  }
29217  }
29218 
29219 
29220  /* Exit the loop if we've found everything from the client. */
29221  if (framesReadFromClient < framesToReadFromClient) {
29222  break;
29223  }
29224  }
29225 
29226  return totalFramesRead;
29227 }
29228 
29229 
29231 {
29232  ma_src_config config;
29233  ma_zero_object(&config);
29234 
29235  return config;
29236 }
29237 
29238 ma_src_config ma_src_config_init(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint32 channels, ma_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData)
29239 {
29241  config.sampleRateIn = sampleRateIn;
29242  config.sampleRateOut = sampleRateOut;
29243  config.channels = channels;
29244  config.onReadDeinterleaved = onReadDeinterleaved;
29245  config.pUserData = pUserData;
29246 
29247  return config;
29248 }
29249 
29250 
29251 /**************************************************************************************************************************************************************
29252 
29253 Sinc Sample Rate Conversion
29254 ===========================
29255 
29256 The sinc SRC algorithm uses a windowed sinc to perform interpolation of samples. Currently, miniaudio's implementation supports rectangular and Hann window
29257 methods.
29258 
29259 Whenever an output sample is being computed, it looks at a sub-section of the input samples. I've called this sub-section in the code below the "window",
29260 which I realize is a bit ambigous with the mathematical "window", but it works for me when I need to conceptualize things in my head. The window is made up
29261 of two halves. The first half contains past input samples (initialized to zero), and the second half contains future input samples. As time moves forward
29262 and input samples are consumed, the window moves forward. The larger the window, the better the quality at the expense of slower processing. The window is
29263 limited the range [MA_SRC_SINC_MIN_WINDOW_WIDTH, MA_SRC_SINC_MAX_WINDOW_WIDTH] and defaults to MA_SRC_SINC_DEFAULT_WINDOW_WIDTH.
29264 
29265 Input samples are cached for efficiency (to prevent frequently requesting tiny numbers of samples from the client). When the window gets to the end of the
29266 cache, it's moved back to the start, and more samples are read from the client. If the client has no more data to give, the cache is filled with zeros and
29267 the last of the input samples will be consumed. Once the last of the input samples have been consumed, no more samples will be output.
29268 
29269 
29270 When reading output samples, we always first read whatever is already in the input cache. Only when the cache has been fully consumed do we read more data
29271 from the client.
29272 
29273 To access samples in the input buffer you do so relative to the window. When the window itself is at position 0, the first item in the buffer is accessed
29274 with "windowPos + windowWidth". Generally, to access any sample relative to the window you do "windowPos + windowWidth + sampleIndexRelativeToWindow".
29275 
29276 **************************************************************************************************************************************************************/
29277 
29278 /* Comment this to disable interpolation of table lookups. Less accurate, but faster. */
29279 #define MA_USE_SINC_TABLE_INTERPOLATION
29280 
29281 /* Retrieves a sample from the input buffer's window. Values >= 0 retrieve future samples. Negative values return past samples. */
29282 static MA_INLINE float ma_src_sinc__get_input_sample_from_window(const ma_src* pSRC, ma_uint32 channel, ma_uint32 windowPosInSamples, ma_int32 sampleIndex)
29283 {
29284  ma_assert(pSRC != NULL);
29285  ma_assert(channel < pSRC->config.channels);
29286  ma_assert(sampleIndex >= -(ma_int32)pSRC->config.sinc.windowWidth);
29287  ma_assert(sampleIndex < (ma_int32)pSRC->config.sinc.windowWidth);
29288 
29289  /* The window should always be contained within the input cache. */
29290  ma_assert(windowPosInSamples < ma_countof(pSRC->sinc.input[0]) - pSRC->config.sinc.windowWidth);
29291 
29292  return pSRC->sinc.input[channel][windowPosInSamples + pSRC->config.sinc.windowWidth + sampleIndex];
29293 }
29294 
29295 static MA_INLINE float ma_src_sinc__interpolation_factor(const ma_src* pSRC, float x)
29296 {
29297  float xabs;
29298  ma_int32 ixabs;
29299 
29300  ma_assert(pSRC != NULL);
29301 
29302  xabs = (float)fabs(x);
29304  ixabs = (ma_int32)xabs;
29305 
29306 #if defined(MA_USE_SINC_TABLE_INTERPOLATION)
29307  {
29308  float a = xabs - ixabs;
29309  return ma_mix_f32_fast(pSRC->sinc.table[ixabs], pSRC->sinc.table[ixabs+1], a);
29310  }
29311 #else
29312  return pSRC->sinc.table[ixabs];
29313 #endif
29314 }
29315 
29316 #if defined(MA_SUPPORT_SSE2)
29317 static MA_INLINE __m128 ma_fabsf_sse2(__m128 x)
29318 {
29319  return _mm_and_ps(_mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF)), x);
29320 }
29321 
29322 static MA_INLINE __m128 ma_truncf_sse2(__m128 x)
29323 {
29324  return _mm_cvtepi32_ps(_mm_cvttps_epi32(x));
29325 }
29326 
29327 static MA_INLINE __m128 ma_src_sinc__interpolation_factor__sse2(const ma_src* pSRC, __m128 x)
29328 {
29329  __m128 resolution128;
29330  __m128 xabs;
29331  __m128i ixabs;
29332  __m128 lo;
29333  __m128 hi;
29334  __m128 a;
29335  __m128 r;
29336  int* ixabsv;
29337 
29338  resolution128 = _mm_set1_ps(MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION);
29339  xabs = ma_fabsf_sse2(x);
29340  xabs = _mm_mul_ps(xabs, resolution128);
29341  ixabs = _mm_cvttps_epi32(xabs);
29342 
29343  ixabsv = (int*)&ixabs;
29344 
29345  lo = _mm_set_ps(
29346  pSRC->sinc.table[ixabsv[3]],
29347  pSRC->sinc.table[ixabsv[2]],
29348  pSRC->sinc.table[ixabsv[1]],
29349  pSRC->sinc.table[ixabsv[0]]
29350  );
29351 
29352  hi = _mm_set_ps(
29353  pSRC->sinc.table[ixabsv[3]+1],
29354  pSRC->sinc.table[ixabsv[2]+1],
29355  pSRC->sinc.table[ixabsv[1]+1],
29356  pSRC->sinc.table[ixabsv[0]+1]
29357  );
29358 
29359  a = _mm_sub_ps(xabs, _mm_cvtepi32_ps(ixabs));
29360  r = ma_mix_f32_fast__sse2(lo, hi, a);
29361 
29362  return r;
29363 }
29364 #endif
29365 
29366 #if defined(MA_SUPPORT_AVX2)
29367 static MA_INLINE __m256 ma_fabsf_avx2(__m256 x)
29368 {
29369  return _mm256_and_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0x7FFFFFFF)), x);
29370 }
29371 
29372 #if 0
29373 static MA_INLINE __m256 ma_src_sinc__interpolation_factor__avx2(const ma_src* pSRC, __m256 x)
29374 {
29375  __m256 resolution256 = _mm256_set1_ps(MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION);
29376  __m256 xabs = ma_fabsf_avx2(x);
29377 
29378  xabs = _mm256_mul_ps(xabs, resolution256);
29379 
29380  __m256i ixabs = _mm256_cvttps_epi32(xabs);
29381  __m256 a = _mm256_sub_ps(xabs, _mm256_cvtepi32_ps(ixabs));
29382 
29383 
29384  int* ixabsv = (int*)&ixabs;
29385 
29386  __m256 lo = _mm256_set_ps(
29387  pSRC->sinc.table[ixabsv[7]],
29388  pSRC->sinc.table[ixabsv[6]],
29389  pSRC->sinc.table[ixabsv[5]],
29390  pSRC->sinc.table[ixabsv[4]],
29391  pSRC->sinc.table[ixabsv[3]],
29392  pSRC->sinc.table[ixabsv[2]],
29393  pSRC->sinc.table[ixabsv[1]],
29394  pSRC->sinc.table[ixabsv[0]]
29395  );
29396 
29397  __m256 hi = _mm256_set_ps(
29398  pSRC->sinc.table[ixabsv[7]+1],
29399  pSRC->sinc.table[ixabsv[6]+1],
29400  pSRC->sinc.table[ixabsv[5]+1],
29401  pSRC->sinc.table[ixabsv[4]+1],
29402  pSRC->sinc.table[ixabsv[3]+1],
29403  pSRC->sinc.table[ixabsv[2]+1],
29404  pSRC->sinc.table[ixabsv[1]+1],
29405  pSRC->sinc.table[ixabsv[0]+1]
29406  );
29407 
29408  __m256 r = ma_mix_f32_fast__avx2(lo, hi, a);
29409 
29410  return r;
29411 }
29412 #endif
29413 
29414 #endif
29415 
29416 #if defined(MA_SUPPORT_NEON)
29417 static MA_INLINE float32x4_t ma_fabsf_neon(float32x4_t x)
29418 {
29419  return vabdq_f32(vmovq_n_f32(0), x);
29420 }
29421 
29422 static MA_INLINE float32x4_t ma_src_sinc__interpolation_factor__neon(const ma_src* pSRC, float32x4_t x)
29423 {
29424  float32x4_t xabs;
29425  int32x4_t ixabs;
29426  float32x4_t a
29427  float32x4_t r
29428  int* ixabsv;
29429  float lo[4];
29430  float hi[4];
29431 
29432  xabs = ma_fabsf_neon(x);
29433  xabs = vmulq_n_f32(xabs, MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION);
29434  ixabs = vcvtq_s32_f32(xabs);
29435 
29436  ixabsv = (int*)&ixabs;
29437 
29438  lo[0] = pSRC->sinc.table[ixabsv[0]];
29439  lo[1] = pSRC->sinc.table[ixabsv[1]];
29440  lo[2] = pSRC->sinc.table[ixabsv[2]];
29441  lo[3] = pSRC->sinc.table[ixabsv[3]];
29442 
29443  hi[0] = pSRC->sinc.table[ixabsv[0]+1];
29444  hi[1] = pSRC->sinc.table[ixabsv[1]+1];
29445  hi[2] = pSRC->sinc.table[ixabsv[2]+1];
29446  hi[3] = pSRC->sinc.table[ixabsv[3]+1];
29447 
29448  a = vsubq_f32(xabs, vcvtq_f32_s32(ixabs));
29449  r = ma_mix_f32_fast__neon(vld1q_f32(lo), vld1q_f32(hi), a);
29450 
29451  return r;
29452 }
29453 #endif
29454 
29455 ma_uint64 ma_src_read_deinterleaved__sinc(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
29456 {
29457  float factor;
29458  float inverseFactor;
29459  ma_int32 windowWidth;
29460  ma_int32 windowWidth2;
29461  ma_int32 windowWidthSIMD;
29462  ma_int32 windowWidthSIMD2;
29463  float* ppNextSamplesOut[MA_MAX_CHANNELS];
29464  float _windowSamplesUnaligned[MA_SRC_SINC_MAX_WINDOW_WIDTH*2 + MA_SIMD_ALIGNMENT];
29465  float* windowSamples;
29466  float _iWindowFUnaligned[MA_SRC_SINC_MAX_WINDOW_WIDTH*2 + MA_SIMD_ALIGNMENT];
29467  float* iWindowF;
29468  ma_int32 i;
29469  ma_uint64 totalOutputFramesRead;
29470 
29471  ma_assert(pSRC != NULL);
29472  ma_assert(frameCount > 0);
29473  ma_assert(ppSamplesOut != NULL);
29474 
29475  factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
29476  inverseFactor = 1/factor;
29477 
29478  windowWidth = (ma_int32)pSRC->config.sinc.windowWidth;
29479  windowWidth2 = windowWidth*2;
29480 
29481  /*
29482  There are cases where it's actually more efficient to increase the window width so that it's aligned with the respective
29483  SIMD pipeline being used.
29484  */
29485  windowWidthSIMD = windowWidth;
29486  if (pSRC->useNEON) {
29487  windowWidthSIMD = (windowWidthSIMD + 1) & ~(1);
29488  } else if (pSRC->useAVX512) {
29489  windowWidthSIMD = (windowWidthSIMD + 7) & ~(7);
29490  } else if (pSRC->useAVX2) {
29491  windowWidthSIMD = (windowWidthSIMD + 3) & ~(3);
29492  } else if (pSRC->useSSE2) {
29493  windowWidthSIMD = (windowWidthSIMD + 1) & ~(1);
29494  }
29495 
29496  windowWidthSIMD2 = windowWidthSIMD*2;
29497  (void)windowWidthSIMD2; /* <-- Silence a warning when SIMD is disabled. */
29498 
29499  ma_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pSRC->config.channels);
29500 
29501  windowSamples = (float*)(((ma_uintptr)_windowSamplesUnaligned + MA_SIMD_ALIGNMENT-1) & ~(MA_SIMD_ALIGNMENT-1));
29502  ma_zero_memory(windowSamples, MA_SRC_SINC_MAX_WINDOW_WIDTH*2 * sizeof(float));
29503 
29504  iWindowF = (float*)(((ma_uintptr)_iWindowFUnaligned + MA_SIMD_ALIGNMENT-1) & ~(MA_SIMD_ALIGNMENT-1));
29505  ma_zero_memory(iWindowF, MA_SRC_SINC_MAX_WINDOW_WIDTH*2 * sizeof(float));
29506 
29507  for (i = 0; i < windowWidth2; ++i) {
29508  iWindowF[i] = (float)(i - windowWidth);
29509  }
29510 
29511  totalOutputFramesRead = 0;
29512  while (totalOutputFramesRead < frameCount) {
29513  ma_uint32 maxInputSamplesAvailableInCache;
29514  float timeInBeg;
29515  float timeInEnd;
29516  ma_uint64 maxOutputFramesToRead;
29517  ma_uint64 outputFramesRemaining;
29518  ma_uint64 outputFramesToRead;
29519  ma_uint32 iChannel;
29520  ma_uint32 prevWindowPosInSamples;
29521  ma_uint32 availableOutputFrames;
29522 
29523  /*
29524  The maximum number of frames we can read this iteration depends on how many input samples we have available to us. This is the number
29525  of input samples between the end of the window and the end of the cache.
29526  */
29527  maxInputSamplesAvailableInCache = ma_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth*2) - pSRC->sinc.windowPosInSamples;
29528  if (maxInputSamplesAvailableInCache > pSRC->sinc.inputFrameCount) {
29529  maxInputSamplesAvailableInCache = pSRC->sinc.inputFrameCount;
29530  }
29531 
29532  /* Never consume the tail end of the input data if requested. */
29533  if (pSRC->config.neverConsumeEndOfInput) {
29534  if (maxInputSamplesAvailableInCache >= pSRC->config.sinc.windowWidth) {
29535  maxInputSamplesAvailableInCache -= pSRC->config.sinc.windowWidth;
29536  } else {
29537  maxInputSamplesAvailableInCache = 0;
29538  }
29539  }
29540 
29541  timeInBeg = pSRC->sinc.timeIn;
29542  timeInEnd = (float)(pSRC->sinc.windowPosInSamples + maxInputSamplesAvailableInCache);
29543 
29544  ma_assert(timeInBeg >= 0);
29545  ma_assert(timeInBeg <= timeInEnd);
29546 
29547  maxOutputFramesToRead = (ma_uint64)(((timeInEnd - timeInBeg) * inverseFactor));
29548 
29549  outputFramesRemaining = frameCount - totalOutputFramesRead;
29550  outputFramesToRead = outputFramesRemaining;
29551  if (outputFramesToRead > maxOutputFramesToRead) {
29552  outputFramesToRead = maxOutputFramesToRead;
29553  }
29554 
29555  for (iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
29556  /* Do SRC. */
29557  float timeIn = timeInBeg;
29558  ma_uint32 iSample;
29559  for (iSample = 0; iSample < outputFramesToRead; iSample += 1) {
29560  float sampleOut = 0;
29561  float iTimeInF = ma_floorf(timeIn);
29562  ma_uint32 iTimeIn = (ma_uint32)iTimeInF;
29563  ma_int32 iWindow = 0;
29564  float tScalar;
29565 
29566  /* Pre-load the window samples into an aligned buffer to begin with. Need to put these into an aligned buffer to make SIMD easier. */
29567  windowSamples[0] = 0; /* <-- The first sample is always zero. */
29568  for (i = 1; i < windowWidth2; ++i) {
29569  windowSamples[i] = pSRC->sinc.input[iChannel][iTimeIn + i];
29570  }
29571 
29572 #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX512)
29573  if (pSRC->useAVX2 || pSRC->useAVX512) {
29574  __m256i ixabs[MA_SRC_SINC_MAX_WINDOW_WIDTH*2/8];
29575  __m256 a[MA_SRC_SINC_MAX_WINDOW_WIDTH*2/8];
29576  __m256 resolution256;
29577  __m256 t;
29578  __m256 r;
29579  ma_int32 windowWidth8;
29580  ma_int32 iWindow8;
29581 
29582  resolution256 = _mm256_set1_ps(MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION);
29583 
29584  t = _mm256_set1_ps((timeIn - iTimeInF));
29585  r = _mm256_set1_ps(0);
29586 
29587  windowWidth8 = windowWidthSIMD2 >> 3;
29588  for (iWindow8 = 0; iWindow8 < windowWidth8; iWindow8 += 1) {
29589  __m256 w = *((__m256*)iWindowF + iWindow8);
29590 
29591  __m256 xabs = _mm256_sub_ps(t, w);
29592  xabs = ma_fabsf_avx2(xabs);
29593  xabs = _mm256_mul_ps(xabs, resolution256);
29594 
29595  ixabs[iWindow8] = _mm256_cvttps_epi32(xabs);
29596  a[iWindow8] = _mm256_sub_ps(xabs, _mm256_cvtepi32_ps(ixabs[iWindow8]));
29597  }
29598 
29599  for (iWindow8 = 0; iWindow8 < windowWidth8; iWindow8 += 1) {
29600  int* ixabsv = (int*)&ixabs[iWindow8];
29601 
29602  __m256 lo = _mm256_set_ps(
29603  pSRC->sinc.table[ixabsv[7]],
29604  pSRC->sinc.table[ixabsv[6]],
29605  pSRC->sinc.table[ixabsv[5]],
29606  pSRC->sinc.table[ixabsv[4]],
29607  pSRC->sinc.table[ixabsv[3]],
29608  pSRC->sinc.table[ixabsv[2]],
29609  pSRC->sinc.table[ixabsv[1]],
29610  pSRC->sinc.table[ixabsv[0]]
29611  );
29612 
29613  __m256 hi = _mm256_set_ps(
29614  pSRC->sinc.table[ixabsv[7]+1],
29615  pSRC->sinc.table[ixabsv[6]+1],
29616  pSRC->sinc.table[ixabsv[5]+1],
29617  pSRC->sinc.table[ixabsv[4]+1],
29618  pSRC->sinc.table[ixabsv[3]+1],
29619  pSRC->sinc.table[ixabsv[2]+1],
29620  pSRC->sinc.table[ixabsv[1]+1],
29621  pSRC->sinc.table[ixabsv[0]+1]
29622  );
29623 
29624  __m256 s = *((__m256*)windowSamples + iWindow8);
29625  r = _mm256_add_ps(r, _mm256_mul_ps(s, ma_mix_f32_fast__avx2(lo, hi, a[iWindow8])));
29626  }
29627 
29628  /* Horizontal add. */
29629  __m256 x = _mm256_hadd_ps(r, _mm256_permute2f128_ps(r, r, 1));
29630  x = _mm256_hadd_ps(x, x);
29631  x = _mm256_hadd_ps(x, x);
29632  sampleOut += _mm_cvtss_f32(_mm256_castps256_ps128(x));
29633 
29634  iWindow += windowWidth8 * 8;
29635  }
29636  else
29637 #endif
29638 #if defined(MA_SUPPORT_SSE2)
29639  if (pSRC->useSSE2) {
29640  __m128 t = _mm_set1_ps((timeIn - iTimeInF));
29641  __m128 r = _mm_set1_ps(0);
29642 
29643  ma_int32 windowWidth4 = windowWidthSIMD2 >> 2;
29644  ma_int32 iWindow4;
29645  for (iWindow4 = 0; iWindow4 < windowWidth4; iWindow4 += 1) {
29646  __m128* s = (__m128*)windowSamples + iWindow4;
29647  __m128* w = (__m128*)iWindowF + iWindow4;
29648 
29649  __m128 a = ma_src_sinc__interpolation_factor__sse2(pSRC, _mm_sub_ps(t, *w));
29650  r = _mm_add_ps(r, _mm_mul_ps(*s, a));
29651  }
29652 
29653  sampleOut += ((float*)(&r))[0];
29654  sampleOut += ((float*)(&r))[1];
29655  sampleOut += ((float*)(&r))[2];
29656  sampleOut += ((float*)(&r))[3];
29657 
29658  iWindow += windowWidth4 * 4;
29659  }
29660  else
29661 #endif
29662 #if defined(MA_SUPPORT_NEON)
29663  if (pSRC->useNEON) {
29664  float32x4_t t = vmovq_n_f32((timeIn - iTimeInF));
29665  float32x4_t r = vmovq_n_f32(0);
29666 
29667  ma_int32 windowWidth4 = windowWidthSIMD2 >> 2;
29668  ma_int32 iWindow4;
29669  for (iWindow4 = 0; iWindow4 < windowWidth4; iWindow4 += 1) {
29670  float32x4_t* s = (float32x4_t*)windowSamples + iWindow4;
29671  float32x4_t* w = (float32x4_t*)iWindowF + iWindow4;
29672 
29673  float32x4_t a = ma_src_sinc__interpolation_factor__neon(pSRC, vsubq_f32(t, *w));
29674  r = vaddq_f32(r, vmulq_f32(*s, a));
29675  }
29676 
29677  sampleOut += ((float*)(&r))[0];
29678  sampleOut += ((float*)(&r))[1];
29679  sampleOut += ((float*)(&r))[2];
29680  sampleOut += ((float*)(&r))[3];
29681 
29682  iWindow += windowWidth4 * 4;
29683  }
29684  else
29685 #endif
29686  {
29687  iWindow += 1; /* The first one is a dummy for SIMD alignment purposes. Skip it. */
29688  }
29689 
29690  /* Non-SIMD/Reference implementation. */
29691  tScalar = (timeIn - iTimeIn);
29692  for (; iWindow < windowWidth2; iWindow += 1) {
29693  float s = windowSamples[iWindow];
29694  float w = iWindowF[iWindow];
29695 
29696  float a = ma_src_sinc__interpolation_factor(pSRC, (tScalar - w));
29697  float r = s * a;
29698 
29699  sampleOut += r;
29700  }
29701 
29702  ppNextSamplesOut[iChannel][iSample] = (float)sampleOut;
29703 
29704  timeIn += factor;
29705  }
29706 
29707  ppNextSamplesOut[iChannel] += outputFramesToRead;
29708  }
29709 
29710  totalOutputFramesRead += outputFramesToRead;
29711 
29712  prevWindowPosInSamples = pSRC->sinc.windowPosInSamples;
29713 
29714  pSRC->sinc.timeIn += ((ma_int64)outputFramesToRead * factor); /* Cast to int64 required for VC6. */
29715  pSRC->sinc.windowPosInSamples = (ma_uint32)pSRC->sinc.timeIn;
29716  pSRC->sinc.inputFrameCount -= pSRC->sinc.windowPosInSamples - prevWindowPosInSamples;
29717 
29718  /* If the window has reached a point where we cannot read a whole output sample it needs to be moved back to the start. */
29719  availableOutputFrames = (ma_uint32)((timeInEnd - pSRC->sinc.timeIn) * inverseFactor);
29720 
29721  if (availableOutputFrames == 0) {
29722  size_t samplesToMove = ma_countof(pSRC->sinc.input[0]) - pSRC->sinc.windowPosInSamples;
29723 
29724  pSRC->sinc.timeIn -= ma_floorf(pSRC->sinc.timeIn);
29725  pSRC->sinc.windowPosInSamples = 0;
29726 
29727  /* Move everything from the end of the cache up to the front. */
29728  for (iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
29729  memmove(pSRC->sinc.input[iChannel], pSRC->sinc.input[iChannel] + ma_countof(pSRC->sinc.input[iChannel]) - samplesToMove, samplesToMove * sizeof(*pSRC->sinc.input[iChannel]));
29730  }
29731  }
29732 
29733  /* Read more data from the client if required. */
29734  if (pSRC->isEndOfInputLoaded) {
29735  pSRC->isEndOfInputLoaded = MA_FALSE;
29736  break;
29737  }
29738 
29739  /*
29740  Everything beyond this point is reloading. If we're at the end of the input data we do _not_ want to try reading any more in this function call. If the
29741  caller wants to keep trying, they can reload their internal data sources and call this function again. We should never be
29742  */
29743  ma_assert(pSRC->isEndOfInputLoaded == MA_FALSE);
29744 
29745  if (pSRC->sinc.inputFrameCount <= pSRC->config.sinc.windowWidth || availableOutputFrames == 0) {
29746  float* ppInputDst[MA_MAX_CHANNELS] = {0};
29747  ma_uint32 framesToReadFromClient;
29748  ma_uint32 framesReadFromClient;
29749  ma_uint32 leftoverFrames;
29750 
29751  for (iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
29752  ppInputDst[iChannel] = pSRC->sinc.input[iChannel] + pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount;
29753  }
29754 
29755  /* Now read data from the client. */
29756  framesToReadFromClient = ma_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount);
29757 
29758  framesReadFromClient = 0;
29759  if (framesToReadFromClient > 0) {
29760  framesReadFromClient = pSRC->config.onReadDeinterleaved(pSRC, framesToReadFromClient, (void**)ppInputDst, pUserData);
29761  }
29762 
29763  if (framesReadFromClient != framesToReadFromClient) {
29764  pSRC->isEndOfInputLoaded = MA_TRUE;
29765  } else {
29766  pSRC->isEndOfInputLoaded = MA_FALSE;
29767  }
29768 
29769  if (framesReadFromClient != 0) {
29770  pSRC->sinc.inputFrameCount += framesReadFromClient;
29771  } else {
29772  /* We couldn't get anything more from the client. If no more output samples can be computed from the available input samples we need to return. */
29773  if (pSRC->config.neverConsumeEndOfInput) {
29774  if ((pSRC->sinc.inputFrameCount * inverseFactor) <= pSRC->config.sinc.windowWidth) {
29775  break;
29776  }
29777  } else {
29778  if ((pSRC->sinc.inputFrameCount * inverseFactor) < 1) {
29779  break;
29780  }
29781  }
29782  }
29783 
29784  /* Anything left over in the cache must be set to zero. */
29785  leftoverFrames = ma_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount);
29786  if (leftoverFrames > 0) {
29787  for (iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
29788  ma_zero_memory(pSRC->sinc.input[iChannel] + pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount, leftoverFrames * sizeof(float));
29789  }
29790  }
29791  }
29792  }
29793 
29794  return totalOutputFramesRead;
29795 }
29796 
29797 
29798 
29799 /**************************************************************************************************************************************************************
29800 
29801 Format Conversion
29802 
29803 **************************************************************************************************************************************************************/
29804 void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
29805 {
29806  if (formatOut == formatIn) {
29807  ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
29808  return;
29809  }
29810 
29811  switch (formatIn)
29812  {
29813  case ma_format_u8:
29814  {
29815  switch (formatOut)
29816  {
29817  case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
29818  case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
29819  case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
29820  case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
29821  default: break;
29822  }
29823  } break;
29824 
29825  case ma_format_s16:
29826  {
29827  switch (formatOut)
29828  {
29829  case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
29830  case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
29831  case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
29832  case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
29833  default: break;
29834  }
29835  } break;
29836 
29837  case ma_format_s24:
29838  {
29839  switch (formatOut)
29840  {
29841  case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
29842  case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
29843  case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
29844  case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
29845  default: break;
29846  }
29847  } break;
29848 
29849  case ma_format_s32:
29850  {
29851  switch (formatOut)
29852  {
29853  case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
29854  case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
29855  case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
29856  case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
29857  default: break;
29858  }
29859  } break;
29860 
29861  case ma_format_f32:
29862  {
29863  switch (formatOut)
29864  {
29865  case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
29866  case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
29867  case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
29868  case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
29869  default: break;
29870  }
29871  } break;
29872 
29873  default: break;
29874  }
29875 }
29876 
29877 void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
29878 {
29879  if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
29880  return; /* Invalid args. */
29881  }
29882 
29883  /* For efficiency we do this per format. */
29884  switch (format) {
29885  case ma_format_s16:
29886  {
29887  const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
29888  ma_uint64 iPCMFrame;
29889  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
29890  ma_uint32 iChannel;
29891  for (iChannel = 0; iChannel < channels; ++iChannel) {
29892  ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
29893  pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
29894  }
29895  }
29896  } break;
29897 
29898  case ma_format_f32:
29899  {
29900  const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
29901  ma_uint64 iPCMFrame;
29902  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
29903  ma_uint32 iChannel;
29904  for (iChannel = 0; iChannel < channels; ++iChannel) {
29905  float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
29906  pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
29907  }
29908  }
29909  } break;
29910 
29911  default:
29912  {
29913  ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
29914  ma_uint64 iPCMFrame;
29915  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
29916  ma_uint32 iChannel;
29917  for (iChannel = 0; iChannel < channels; ++iChannel) {
29918  void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
29919  const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
29920  memcpy(pDst, pSrc, sampleSizeInBytes);
29921  }
29922  }
29923  } break;
29924  }
29925 }
29926 
29927 void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
29928 {
29929  switch (format)
29930  {
29931  case ma_format_s16:
29932  {
29933  ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
29934  ma_uint64 iPCMFrame;
29935  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
29936  ma_uint32 iChannel;
29937  for (iChannel = 0; iChannel < channels; ++iChannel) {
29938  const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
29939  pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
29940  }
29941  }
29942  } break;
29943 
29944  case ma_format_f32:
29945  {
29946  float* pDstF32 = (float*)pInterleavedPCMFrames;
29947  ma_uint64 iPCMFrame;
29948  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
29949  ma_uint32 iChannel;
29950  for (iChannel = 0; iChannel < channels; ++iChannel) {
29951  const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
29952  pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
29953  }
29954  }
29955  } break;
29956 
29957  default:
29958  {
29959  ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
29960  ma_uint64 iPCMFrame;
29961  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
29962  ma_uint32 iChannel;
29963  for (iChannel = 0; iChannel < channels; ++iChannel) {
29964  void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
29965  const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
29966  memcpy(pDst, pSrc, sampleSizeInBytes);
29967  }
29968  }
29969  } break;
29970  }
29971 }
29972 
29973 
29974 
29975 typedef struct
29976 {
29977  ma_pcm_converter* pDSP;
29978  void* pUserDataForClient;
29979 } ma_pcm_converter_callback_data;
29980 
29981 ma_uint32 ma_pcm_converter__pre_format_converter_on_read(ma_format_converter* pConverter, ma_uint32 frameCount, void* pFramesOut, void* pUserData)
29982 {
29983  ma_pcm_converter_callback_data* pData;
29984  ma_pcm_converter* pDSP;
29985 
29986  (void)pConverter;
29987 
29988  pData = (ma_pcm_converter_callback_data*)pUserData;
29989  ma_assert(pData != NULL);
29990 
29991  pDSP = pData->pDSP;
29992  ma_assert(pDSP != NULL);
29993 
29994  return pDSP->onRead(pDSP, pFramesOut, frameCount, pData->pUserDataForClient);
29995 }
29996 
29997 ma_uint32 ma_pcm_converter__post_format_converter_on_read(ma_format_converter* pConverter, ma_uint32 frameCount, void* pFramesOut, void* pUserData)
29998 {
29999  ma_pcm_converter_callback_data* pData;
30000  ma_pcm_converter* pDSP;
30001 
30002  (void)pConverter;
30003 
30004  pData = (ma_pcm_converter_callback_data*)pUserData;
30005  ma_assert(pData != NULL);
30006 
30007  pDSP = pData->pDSP;
30008  ma_assert(pDSP != NULL);
30009 
30010  /* When this version of this callback is used it means we're reading directly from the client. */
30011  ma_assert(pDSP->isPreFormatConversionRequired == MA_FALSE);
30012  ma_assert(pDSP->isChannelRoutingRequired == MA_FALSE);
30013  ma_assert(pDSP->isSRCRequired == MA_FALSE);
30014 
30015  return pDSP->onRead(pDSP, pFramesOut, frameCount, pData->pUserDataForClient);
30016 }
30017 
30018 ma_uint32 ma_pcm_converter__post_format_converter_on_read_deinterleaved(ma_format_converter* pConverter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
30019 {
30020  ma_pcm_converter_callback_data* pData;
30021  ma_pcm_converter* pDSP;
30022 
30023  (void)pConverter;
30024 
30025  pData = (ma_pcm_converter_callback_data*)pUserData;
30026  ma_assert(pData != NULL);
30027 
30028  pDSP = pData->pDSP;
30029  ma_assert(pDSP != NULL);
30030 
30031  if (!pDSP->isChannelRoutingAtStart) {
30032  return (ma_uint32)ma_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
30033  } else {
30034  if (pDSP->isSRCRequired) {
30035  return (ma_uint32)ma_src_read_deinterleaved(&pDSP->src, frameCount, ppSamplesOut, pUserData);
30036  } else {
30037  return (ma_uint32)ma_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
30038  }
30039  }
30040 }
30041 
30042 ma_uint32 ma_pcm_converter__src_on_read_deinterleaved(ma_src* pSRC, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
30043 {
30044  ma_pcm_converter_callback_data* pData;
30045  ma_pcm_converter* pDSP;
30046 
30047  (void)pSRC;
30048 
30049  pData = (ma_pcm_converter_callback_data*)pUserData;
30050  ma_assert(pData != NULL);
30051 
30052  pDSP = pData->pDSP;
30053  ma_assert(pDSP != NULL);
30054 
30055  /* If the channel routing stage is at the front we need to read from that. Otherwise we read from the pre format converter. */
30056  if (pDSP->isChannelRoutingAtStart) {
30057  return (ma_uint32)ma_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
30058  } else {
30059  return (ma_uint32)ma_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
30060  }
30061 }
30062 
30063 ma_uint32 ma_pcm_converter__channel_router_on_read_deinterleaved(ma_channel_router* pRouter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
30064 {
30065  ma_pcm_converter_callback_data* pData;
30066  ma_pcm_converter* pDSP;
30067 
30068  (void)pRouter;
30069 
30070  pData = (ma_pcm_converter_callback_data*)pUserData;
30071  ma_assert(pData != NULL);
30072 
30073  pDSP = pData->pDSP;
30074  ma_assert(pDSP != NULL);
30075 
30076  /* If the channel routing stage is at the front of the pipeline we read from the pre format converter. Otherwise we read from the sample rate converter. */
30077  if (pDSP->isChannelRoutingAtStart) {
30078  return (ma_uint32)ma_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
30079  } else {
30080  if (pDSP->isSRCRequired) {
30081  return (ma_uint32)ma_src_read_deinterleaved(&pDSP->src, frameCount, ppSamplesOut, pUserData);
30082  } else {
30083  return (ma_uint32)ma_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
30084  }
30085  }
30086 }
30087 
30089 {
30090  ma_result result;
30091 
30092  if (pDSP == NULL) {
30093  return MA_INVALID_ARGS;
30094  }
30095 
30096  ma_zero_object(pDSP);
30097  pDSP->onRead = pConfig->onRead;
30098  pDSP->pUserData = pConfig->pUserData;
30099  pDSP->isDynamicSampleRateAllowed = pConfig->allowDynamicSampleRate;
30100 
30101  /*
30102  In general, this is the pipeline used for data conversion. Note that this can actually change which is explained later.
30103 
30104  Pre Format Conversion -> Sample Rate Conversion -> Channel Routing -> Post Format Conversion
30105 
30106  Pre Format Conversion
30107  ---------------------
30108  This is where the sample data is converted to a format that's usable by the later stages in the pipeline. Input data
30109  is converted to deinterleaved floating-point.
30110 
30111  Channel Routing
30112  ---------------
30113  Channel routing is where stereo is converted to 5.1, mono is converted to stereo, etc. This stage depends on the
30114  pre format conversion stage.
30115 
30116  Sample Rate Conversion
30117  ----------------------
30118  Sample rate conversion depends on the pre format conversion stage and as the name implies performs sample rate conversion.
30119 
30120  Post Format Conversion
30121  ----------------------
30122  This stage is where our deinterleaved floating-point data from the previous stages are converted to the requested output
30123  format.
30124 
30125 
30126  Optimizations
30127  -------------
30128  Sometimes the conversion pipeline is rearranged for efficiency. The first obvious optimization is to eliminate unnecessary
30129  stages in the pipeline. When no channel routing nor sample rate conversion is necessary, the entire pipeline is optimized
30130  down to just this:
30131 
30132  Post Format Conversion
30133 
30134  When sample rate conversion is not unnecessary:
30135 
30136  Pre Format Conversion -> Channel Routing -> Post Format Conversion
30137 
30138  When channel routing is unnecessary:
30139 
30140  Pre Format Conversion -> Sample Rate Conversion -> Post Format Conversion
30141 
30142  A slightly less obvious optimization is used depending on whether or not we are increasing or decreasing the number of
30143  channels. Because everything in the pipeline works on a per-channel basis, the efficiency of the pipeline is directly
30144  proportionate to the number of channels that need to be processed. Therefore, it's can be more efficient to move the
30145  channel conversion stage to an earlier or later stage. When the channel count is being reduced, we move the channel
30146  conversion stage to the start of the pipeline so that later stages can work on a smaller number of channels at a time.
30147  Otherwise, we move the channel conversion stage to the end of the pipeline. When reducing the channel count, the pipeline
30148  will look like this:
30149 
30150  Pre Format Conversion -> Channel Routing -> Sample Rate Conversion -> Post Format Conversion
30151 
30152  Notice how the Channel Routing and Sample Rate Conversion stages are swapped so that the SRC stage has less data to process.
30153  */
30154 
30155  /* First we need to determine what's required and what's not. */
30156  if (pConfig->sampleRateIn != pConfig->sampleRateOut || pConfig->allowDynamicSampleRate) {
30157  pDSP->isSRCRequired = MA_TRUE;
30158  }
30159  if (pConfig->channelsIn != pConfig->channelsOut || !ma_channel_map_equal(pConfig->channelsIn, pConfig->channelMapIn, pConfig->channelMapOut)) {
30160  pDSP->isChannelRoutingRequired = MA_TRUE;
30161  }
30162 
30163  /* If neither a sample rate conversion nor channel conversion is necessary we can skip the pre format conversion. */
30164  if (!pDSP->isSRCRequired && !pDSP->isChannelRoutingRequired) {
30165  /* We don't need a pre format conversion stage, but we may still need a post format conversion stage. */
30166  if (pConfig->formatIn != pConfig->formatOut) {
30167  pDSP->isPostFormatConversionRequired = MA_TRUE;
30168  }
30169  } else {
30170  pDSP->isPreFormatConversionRequired = MA_TRUE;
30171  pDSP->isPostFormatConversionRequired = MA_TRUE;
30172  }
30173 
30174  /* Use a passthrough if none of the stages are being used. */
30175  if (!pDSP->isPreFormatConversionRequired && !pDSP->isPostFormatConversionRequired && !pDSP->isChannelRoutingRequired && !pDSP->isSRCRequired) {
30176  pDSP->isPassthrough = MA_TRUE;
30177  }
30178 
30179  /* Move the channel conversion stage to the start of the pipeline if we are reducing the channel count. */
30180  if (pConfig->channelsOut < pConfig->channelsIn) {
30181  pDSP->isChannelRoutingAtStart = MA_TRUE;
30182  }
30183 
30184 
30185  /*
30186  We always initialize every stage of the pipeline regardless of whether or not the stage is used because it simplifies
30187  a few things when it comes to dynamically changing properties post-initialization.
30188  */
30189  result = MA_SUCCESS;
30190 
30191  /* Pre format conversion. */
30192  {
30194  pConfig->formatIn,
30195  ma_format_f32,
30196  pConfig->channelsIn,
30197  ma_pcm_converter__pre_format_converter_on_read,
30198  pDSP
30199  );
30200  preFormatConverterConfig.ditherMode = pConfig->ditherMode;
30201  preFormatConverterConfig.noSSE2 = pConfig->noSSE2;
30202  preFormatConverterConfig.noAVX2 = pConfig->noAVX2;
30203  preFormatConverterConfig.noAVX512 = pConfig->noAVX512;
30204  preFormatConverterConfig.noNEON = pConfig->noNEON;
30205 
30206  result = ma_format_converter_init(&preFormatConverterConfig, &pDSP->formatConverterIn);
30207  if (result != MA_SUCCESS) {
30208  return result;
30209  }
30210  }
30211 
30212  /*
30213  Post format conversion. The exact configuration for this depends on whether or not we are reading data directly from the client
30214  or from an earlier stage in the pipeline.
30215  */
30216  {
30218  postFormatConverterConfig.formatIn = pConfig->formatIn;
30219  postFormatConverterConfig.formatOut = pConfig->formatOut;
30220  postFormatConverterConfig.channels = pConfig->channelsOut;
30221  postFormatConverterConfig.ditherMode = pConfig->ditherMode;
30222  postFormatConverterConfig.noSSE2 = pConfig->noSSE2;
30223  postFormatConverterConfig.noAVX2 = pConfig->noAVX2;
30224  postFormatConverterConfig.noAVX512 = pConfig->noAVX512;
30225  postFormatConverterConfig.noNEON = pConfig->noNEON;
30226  if (pDSP->isPreFormatConversionRequired) {
30227  postFormatConverterConfig.onReadDeinterleaved = ma_pcm_converter__post_format_converter_on_read_deinterleaved;
30228  postFormatConverterConfig.formatIn = ma_format_f32;
30229  } else {
30230  postFormatConverterConfig.onRead = ma_pcm_converter__post_format_converter_on_read;
30231  }
30232 
30233  result = ma_format_converter_init(&postFormatConverterConfig, &pDSP->formatConverterOut);
30234  if (result != MA_SUCCESS) {
30235  return result;
30236  }
30237  }
30238 
30239  /* SRC */
30240  {
30241  ma_src_config srcConfig = ma_src_config_init(
30242  pConfig->sampleRateIn,
30243  pConfig->sampleRateOut,
30244  ((pConfig->channelsIn < pConfig->channelsOut) ? pConfig->channelsIn : pConfig->channelsOut),
30245  ma_pcm_converter__src_on_read_deinterleaved,
30246  pDSP
30247  );
30248  srcConfig.algorithm = pConfig->srcAlgorithm;
30249  srcConfig.neverConsumeEndOfInput = pConfig->neverConsumeEndOfInput;
30250  srcConfig.noSSE2 = pConfig->noSSE2;
30251  srcConfig.noAVX2 = pConfig->noAVX2;
30252  srcConfig.noAVX512 = pConfig->noAVX512;
30253  srcConfig.noNEON = pConfig->noNEON;
30254  ma_copy_memory(&srcConfig.sinc, &pConfig->sinc, sizeof(pConfig->sinc));
30255 
30256  result = ma_src_init(&srcConfig, &pDSP->src);
30257  if (result != MA_SUCCESS) {
30258  return result;
30259  }
30260  }
30261 
30262  /* Channel conversion */
30263  {
30265  pConfig->channelsIn,
30266  pConfig->channelMapIn,
30267  pConfig->channelsOut,
30268  pConfig->channelMapOut,
30269  pConfig->channelMixMode,
30270  ma_pcm_converter__channel_router_on_read_deinterleaved,
30271  pDSP);
30272  routerConfig.noSSE2 = pConfig->noSSE2;
30273  routerConfig.noAVX2 = pConfig->noAVX2;
30274  routerConfig.noAVX512 = pConfig->noAVX512;
30275  routerConfig.noNEON = pConfig->noNEON;
30276 
30277  result = ma_channel_router_init(&routerConfig, &pDSP->channelRouter);
30278  if (result != MA_SUCCESS) {
30279  return result;
30280  }
30281  }
30282 
30283  return MA_SUCCESS;
30284 }
30285 
30286 
30287 ma_result ma_pcm_converter_refresh_sample_rate(ma_pcm_converter* pDSP)
30288 {
30289  /* The SRC stage will already have been initialized so we can just set it there. */
30290  ma_src_set_sample_rate(&pDSP->src, pDSP->src.config.sampleRateIn, pDSP->src.config.sampleRateOut);
30291  return MA_SUCCESS;
30292 }
30293 
30295 {
30296  if (pDSP == NULL) {
30297  return MA_INVALID_ARGS;
30298  }
30299 
30300  /* Must have a sample rate of > 0. */
30301  if (sampleRateIn == 0) {
30302  return MA_INVALID_ARGS;
30303  }
30304 
30305  /* Must have been initialized with allowDynamicSampleRate. */
30306  if (!pDSP->isDynamicSampleRateAllowed) {
30307  return MA_INVALID_OPERATION;
30308  }
30309 
30310  ma_atomic_exchange_32(&pDSP->src.config.sampleRateIn, sampleRateIn);
30311  return ma_pcm_converter_refresh_sample_rate(pDSP);
30312 }
30313 
30315 {
30316  if (pDSP == NULL) {
30317  return MA_INVALID_ARGS;
30318  }
30319 
30320  /* Must have a sample rate of > 0. */
30321  if (sampleRateOut == 0) {
30322  return MA_INVALID_ARGS;
30323  }
30324 
30325  /* Must have been initialized with allowDynamicSampleRate. */
30326  if (!pDSP->isDynamicSampleRateAllowed) {
30327  return MA_INVALID_OPERATION;
30328  }
30329 
30330  ma_atomic_exchange_32(&pDSP->src.config.sampleRateOut, sampleRateOut);
30331  return ma_pcm_converter_refresh_sample_rate(pDSP);
30332 }
30333 
30335 {
30336  if (pDSP == NULL) {
30337  return MA_INVALID_ARGS;
30338  }
30339 
30340  /* Must have a sample rate of > 0. */
30341  if (sampleRateIn == 0 || sampleRateOut == 0) {
30342  return MA_INVALID_ARGS;
30343  }
30344 
30345  /* Must have been initialized with allowDynamicSampleRate. */
30346  if (!pDSP->isDynamicSampleRateAllowed) {
30347  return MA_INVALID_OPERATION;
30348  }
30349 
30350  ma_atomic_exchange_32(&pDSP->src.config.sampleRateIn, sampleRateIn);
30351  ma_atomic_exchange_32(&pDSP->src.config.sampleRateOut, sampleRateOut);
30352 
30353  return ma_pcm_converter_refresh_sample_rate(pDSP);
30354 }
30355 
30356 ma_uint64 ma_pcm_converter_read(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint64 frameCount)
30357 {
30358  ma_pcm_converter_callback_data data;
30359 
30360  if (pDSP == NULL || pFramesOut == NULL) {
30361  return 0;
30362  }
30363 
30364  /* Fast path. */
30365  if (pDSP->isPassthrough) {
30366  if (frameCount <= 0xFFFFFFFF) {
30367  return (ma_uint32)pDSP->onRead(pDSP, pFramesOut, (ma_uint32)frameCount, pDSP->pUserData);
30368  } else {
30369  ma_uint8* pNextFramesOut = (ma_uint8*)pFramesOut;
30370 
30371  ma_uint64 totalFramesRead = 0;
30372  while (totalFramesRead < frameCount) {
30373  ma_uint32 framesRead;
30374  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
30375  ma_uint64 framesToReadRightNow = framesRemaining;
30376  if (framesToReadRightNow > 0xFFFFFFFF) {
30377  framesToReadRightNow = 0xFFFFFFFF;
30378  }
30379 
30380  framesRead = pDSP->onRead(pDSP, pNextFramesOut, (ma_uint32)framesToReadRightNow, pDSP->pUserData);
30381  if (framesRead == 0) {
30382  break;
30383  }
30384 
30385  pNextFramesOut += framesRead * pDSP->channelRouter.config.channelsOut * ma_get_bytes_per_sample(pDSP->formatConverterOut.config.formatOut);
30386  totalFramesRead += framesRead;
30387  }
30388 
30389  return totalFramesRead;
30390  }
30391  }
30392 
30393  /* Slower path. The real work is done here. To do this all we need to do is read from the last stage in the pipeline. */
30394  ma_assert(pDSP->isPostFormatConversionRequired == MA_TRUE);
30395 
30396  data.pDSP = pDSP;
30397  data.pUserDataForClient = pDSP->pUserData;
30398  return ma_format_converter_read(&pDSP->formatConverterOut, frameCount, pFramesOut, &data);
30399 }
30400 
30401 
30402 typedef struct
30403 {
30404  const void* pDataIn;
30405  ma_format formatIn;
30406  ma_uint32 channelsIn;
30407  ma_uint64 totalFrameCount;
30408  ma_uint64 iNextFrame;
30409  ma_bool32 isFeedingZeros; /* When set to true, feeds the DSP zero samples. */
30410 } ma_convert_frames__data;
30411 
30412 ma_uint32 ma_convert_frames__on_read(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
30413 {
30414  ma_convert_frames__data* pData;
30415  ma_uint32 framesToRead;
30416  ma_uint64 framesRemaining;
30417  ma_uint32 frameSizeInBytes;
30418 
30419  (void)pDSP;
30420 
30421  pData = (ma_convert_frames__data*)pUserData;
30422  ma_assert(pData != NULL);
30423  ma_assert(pData->totalFrameCount >= pData->iNextFrame);
30424 
30425  framesToRead = frameCount;
30426  framesRemaining = (pData->totalFrameCount - pData->iNextFrame);
30427  if (framesToRead > framesRemaining) {
30428  framesToRead = (ma_uint32)framesRemaining;
30429  }
30430 
30431  frameSizeInBytes = ma_get_bytes_per_frame(pData->formatIn, pData->channelsIn);
30432 
30433  if (!pData->isFeedingZeros) {
30434  ma_copy_memory(pFramesOut, (const ma_uint8*)pData->pDataIn + (frameSizeInBytes * pData->iNextFrame), frameSizeInBytes * framesToRead);
30435  } else {
30436  ma_zero_memory(pFramesOut, frameSizeInBytes * framesToRead);
30437  }
30438 
30439  pData->iNextFrame += framesToRead;
30440  return framesToRead;
30441 }
30442 
30444 {
30445  ma_pcm_converter_config config;
30446  ma_zero_object(&config);
30447 
30448  return config;
30449 }
30450 
30451 ma_pcm_converter_config ma_pcm_converter_config_init(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_pcm_converter_read_proc onRead, void* pUserData)
30452 {
30453  return ma_pcm_converter_config_init_ex(formatIn, channelsIn, sampleRateIn, NULL, formatOut, channelsOut, sampleRateOut, NULL, onRead, pUserData);
30454 }
30455 
30456 ma_pcm_converter_config ma_pcm_converter_config_init_ex(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], ma_pcm_converter_read_proc onRead, void* pUserData)
30457 {
30458  ma_pcm_converter_config config;
30459  ma_zero_object(&config);
30460  config.formatIn = formatIn;
30461  config.channelsIn = channelsIn;
30462  config.sampleRateIn = sampleRateIn;
30463  config.formatOut = formatOut;
30464  config.channelsOut = channelsOut;
30465  config.sampleRateOut = sampleRateOut;
30466  if (channelMapIn != NULL) {
30467  ma_copy_memory(config.channelMapIn, channelMapIn, sizeof(config.channelMapIn));
30468  }
30469  if (channelMapOut != NULL) {
30470  ma_copy_memory(config.channelMapOut, channelMapOut, sizeof(config.channelMapOut));
30471  }
30472  config.onRead = onRead;
30473  config.pUserData = pUserData;
30474 
30475  return config;
30476 }
30477 
30478 
30479 
30480 ma_uint64 ma_convert_frames(void* pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_uint64 frameCount)
30481 {
30482  ma_channel channelMapOut[MA_MAX_CHANNELS];
30483  ma_channel channelMapIn[MA_MAX_CHANNELS];
30484 
30485  ma_get_standard_channel_map(ma_standard_channel_map_default, channelsOut, channelMapOut);
30487 
30488  return ma_convert_frames_ex(pOut, formatOut, channelsOut, sampleRateOut, channelMapOut, pIn, formatIn, channelsIn, sampleRateIn, channelMapIn, frameCount);
30489 }
30490 
30491 ma_uint64 ma_convert_frames_ex(void* pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], const void* pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint64 frameCount)
30492 {
30493  ma_uint64 frameCountOut;
30494  ma_convert_frames__data data;
30495  ma_pcm_converter_config converterConfig;
30496  ma_pcm_converter converter;
30497  ma_uint64 totalFramesRead;
30498 
30499  if (frameCount == 0) {
30500  return 0;
30501  }
30502 
30503  frameCountOut = ma_calculate_frame_count_after_src(sampleRateOut, sampleRateIn, frameCount);
30504  if (pOut == NULL) {
30505  return frameCountOut;
30506  }
30507 
30508  data.pDataIn = pIn;
30509  data.formatIn = formatIn;
30510  data.channelsIn = channelsIn;
30511  data.totalFrameCount = frameCount;
30512  data.iNextFrame = 0;
30513  data.isFeedingZeros = MA_FALSE;
30514 
30515  ma_zero_object(&converterConfig);
30516 
30517  converterConfig.formatIn = formatIn;
30518  converterConfig.channelsIn = channelsIn;
30519  converterConfig.sampleRateIn = sampleRateIn;
30520  if (channelMapIn != NULL) {
30521  ma_channel_map_copy(converterConfig.channelMapIn, channelMapIn, channelsIn);
30522  } else {
30524  }
30525 
30526  converterConfig.formatOut = formatOut;
30527  converterConfig.channelsOut = channelsOut;
30528  converterConfig.sampleRateOut = sampleRateOut;
30529  if (channelMapOut != NULL) {
30530  ma_channel_map_copy(converterConfig.channelMapOut, channelMapOut, channelsOut);
30531  } else {
30533  }
30534 
30535  converterConfig.onRead = ma_convert_frames__on_read;
30536  converterConfig.pUserData = &data;
30537 
30538  if (ma_pcm_converter_init(&converterConfig, &converter) != MA_SUCCESS) {
30539  return 0;
30540  }
30541 
30542  /*
30543  Always output our computed frame count. There is a chance the sample rate conversion routine may not output the last sample
30544  due to precision issues with 32-bit floats, in which case we should feed the DSP zero samples so it can generate that last
30545  frame.
30546  */
30547  totalFramesRead = ma_pcm_converter_read(&converter, pOut, frameCountOut);
30548  if (totalFramesRead < frameCountOut) {
30549  ma_uint32 bpf = ma_get_bytes_per_frame(formatIn, channelsIn);
30550 
30551  data.isFeedingZeros = MA_TRUE;
30552  data.totalFrameCount = ((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF; /* C89 does not support 64-bit constants so need to instead construct it like this. Annoying... */ /*data.totalFrameCount = 0xFFFFFFFFFFFFFFFF;*/
30553  data.pDataIn = NULL;
30554 
30555  while (totalFramesRead < frameCountOut) {
30556  ma_uint64 framesToRead;
30557  ma_uint64 framesJustRead;
30558 
30559  framesToRead = (frameCountOut - totalFramesRead);
30560  ma_assert(framesToRead > 0);
30561 
30562  framesJustRead = ma_pcm_converter_read(&converter, ma_offset_ptr(pOut, totalFramesRead * bpf), framesToRead);
30563  totalFramesRead += framesJustRead;
30564 
30565  if (framesJustRead < framesToRead) {
30566  break;
30567  }
30568  }
30569 
30570  /* At this point we should have output every sample, but just to be super duper sure, just fill the rest with zeros. */
30571  if (totalFramesRead < frameCountOut) {
30572  ma_zero_memory_64(ma_offset_ptr(pOut, totalFramesRead * bpf), ((frameCountOut - totalFramesRead) * bpf));
30573  totalFramesRead = frameCountOut;
30574  }
30575  }
30576 
30577  ma_assert(totalFramesRead == frameCountOut);
30578  return totalFramesRead;
30579 }
30580 
30581 
30582 /**************************************************************************************************************************************************************
30583 
30584 Ring Buffer
30585 
30586 **************************************************************************************************************************************************************/
30587 MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
30588 {
30589  return encodedOffset & 0x7FFFFFFF;
30590 }
30591 
30592 MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
30593 {
30594  return encodedOffset & 0x80000000;
30595 }
30596 
30597 MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
30598 {
30599  ma_assert(pRB != NULL);
30600  return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(pRB->encodedReadOffset));
30601 }
30602 
30603 MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
30604 {
30605  ma_assert(pRB != NULL);
30606  return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(pRB->encodedWriteOffset));
30607 }
30608 
30609 MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
30610 {
30611  return offsetLoopFlag | offsetInBytes;
30612 }
30613 
30614 MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
30615 {
30616  ma_assert(pOffsetInBytes != NULL);
30617  ma_assert(pOffsetLoopFlag != NULL);
30618 
30619  *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
30620  *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
30621 }
30622 
30623 
30624 ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, ma_rb* pRB)
30625 {
30626  const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
30627 
30628  if (pRB == NULL) {
30629  return MA_INVALID_ARGS;
30630  }
30631 
30632  if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
30633  return MA_INVALID_ARGS;
30634  }
30635 
30636  if (subbufferSizeInBytes > maxSubBufferSize) {
30637  return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */
30638  }
30639 
30640 
30641  ma_zero_object(pRB);
30642  pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
30643  pRB->subbufferCount = (ma_uint32)subbufferCount;
30644 
30645  if (pOptionalPreallocatedBuffer != NULL) {
30646  pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
30647  pRB->pBuffer = pOptionalPreallocatedBuffer;
30648  } else {
30649  size_t bufferSizeInBytes;
30650 
30651  /*
30652  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
30653  we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.
30654  */
30656 
30657  bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
30658  pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT);
30659  if (pRB->pBuffer == NULL) {
30660  return MA_OUT_OF_MEMORY;
30661  }
30662 
30663  ma_zero_memory(pRB->pBuffer, bufferSizeInBytes);
30664  pRB->ownsBuffer = MA_TRUE;
30665  }
30666 
30667  return MA_SUCCESS;
30668 }
30669 
30670 ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, ma_rb* pRB)
30671 {
30672  return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pRB);
30673 }
30674 
30675 void ma_rb_uninit(ma_rb* pRB)
30676 {
30677  if (pRB == NULL) {
30678  return;
30679  }
30680 
30681  if (pRB->ownsBuffer) {
30682  ma_aligned_free(pRB->pBuffer);
30683  }
30684 }
30685 
30686 ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
30687 {
30688  ma_uint32 writeOffset;
30689  ma_uint32 writeOffsetInBytes;
30690  ma_uint32 writeOffsetLoopFlag;
30691  ma_uint32 readOffset;
30692  ma_uint32 readOffsetInBytes;
30693  ma_uint32 readOffsetLoopFlag;
30694  size_t bytesAvailable;
30695  size_t bytesRequested;
30696 
30697  if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
30698  return MA_INVALID_ARGS;
30699  }
30700 
30701  /* The returned buffer should never move ahead of the write pointer. */
30702  writeOffset = pRB->encodedWriteOffset;
30703  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
30704 
30705  readOffset = pRB->encodedReadOffset;
30706  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
30707 
30708  /*
30709  The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we
30710  can only read up to the write pointer. If not, we can only read up to the end of the buffer.
30711  */
30712  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
30713  bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
30714  } else {
30715  bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
30716  }
30717 
30718  bytesRequested = *pSizeInBytes;
30719  if (bytesRequested > bytesAvailable) {
30720  bytesRequested = bytesAvailable;
30721  }
30722 
30723  *pSizeInBytes = bytesRequested;
30724  (*ppBufferOut) = ma_rb__get_read_ptr(pRB);
30725 
30726  return MA_SUCCESS;
30727 }
30728 
30729 ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
30730 {
30731  ma_uint32 readOffset;
30732  ma_uint32 readOffsetInBytes;
30733  ma_uint32 readOffsetLoopFlag;
30734  ma_uint32 newReadOffsetInBytes;
30735  ma_uint32 newReadOffsetLoopFlag;
30736 
30737  if (pRB == NULL) {
30738  return MA_INVALID_ARGS;
30739  }
30740 
30741  /* Validate the buffer. */
30742  if (pBufferOut != ma_rb__get_read_ptr(pRB)) {
30743  return MA_INVALID_ARGS;
30744  }
30745 
30746  readOffset = pRB->encodedReadOffset;
30747  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
30748 
30749  /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
30750  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
30751  if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
30752  return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
30753  }
30754 
30755  /* Move the read pointer back to the start if necessary. */
30756  newReadOffsetLoopFlag = readOffsetLoopFlag;
30757  if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
30758  newReadOffsetInBytes = 0;
30759  newReadOffsetLoopFlag ^= 0x80000000;
30760  }
30761 
30762  ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));
30763  return MA_SUCCESS;
30764 }
30765 
30766 ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
30767 {
30768  ma_uint32 readOffset;
30769  ma_uint32 readOffsetInBytes;
30770  ma_uint32 readOffsetLoopFlag;
30771  ma_uint32 writeOffset;
30772  ma_uint32 writeOffsetInBytes;
30773  ma_uint32 writeOffsetLoopFlag;
30774  size_t bytesAvailable;
30775  size_t bytesRequested;
30776 
30777  if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
30778  return MA_INVALID_ARGS;
30779  }
30780 
30781  /* The returned buffer should never overtake the read buffer. */
30782  readOffset = pRB->encodedReadOffset;
30783  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
30784 
30785  writeOffset = pRB->encodedWriteOffset;
30786  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
30787 
30788  /*
30789  In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only
30790  write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should
30791  never overtake the read pointer.
30792  */
30793  if (writeOffsetLoopFlag == readOffsetLoopFlag) {
30794  bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
30795  } else {
30796  bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
30797  }
30798 
30799  bytesRequested = *pSizeInBytes;
30800  if (bytesRequested > bytesAvailable) {
30801  bytesRequested = bytesAvailable;
30802  }
30803 
30804  *pSizeInBytes = bytesRequested;
30805  *ppBufferOut = ma_rb__get_write_ptr(pRB);
30806 
30807  /* Clear the buffer if desired. */
30808  if (pRB->clearOnWriteAcquire) {
30809  ma_zero_memory(*ppBufferOut, *pSizeInBytes);
30810  }
30811 
30812  return MA_SUCCESS;
30813 }
30814 
30815 ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
30816 {
30817  ma_uint32 writeOffset;
30818  ma_uint32 writeOffsetInBytes;
30819  ma_uint32 writeOffsetLoopFlag;
30820  ma_uint32 newWriteOffsetInBytes;
30821  ma_uint32 newWriteOffsetLoopFlag;
30822 
30823  if (pRB == NULL) {
30824  return MA_INVALID_ARGS;
30825  }
30826 
30827  /* Validate the buffer. */
30828  if (pBufferOut != ma_rb__get_write_ptr(pRB)) {
30829  return MA_INVALID_ARGS;
30830  }
30831 
30832  writeOffset = pRB->encodedWriteOffset;
30833  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
30834 
30835  /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
30836  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
30837  if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
30838  return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
30839  }
30840 
30841  /* Move the read pointer back to the start if necessary. */
30842  newWriteOffsetLoopFlag = writeOffsetLoopFlag;
30843  if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
30844  newWriteOffsetInBytes = 0;
30845  newWriteOffsetLoopFlag ^= 0x80000000;
30846  }
30847 
30848  ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));
30849  return MA_SUCCESS;
30850 }
30851 
30852 ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
30853 {
30854  ma_uint32 readOffset;
30855  ma_uint32 readOffsetInBytes;
30856  ma_uint32 readOffsetLoopFlag;
30857  ma_uint32 writeOffset;
30858  ma_uint32 writeOffsetInBytes;
30859  ma_uint32 writeOffsetLoopFlag;
30860  ma_uint32 newReadOffsetInBytes;
30861  ma_uint32 newReadOffsetLoopFlag;
30862 
30863  if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
30864  return MA_INVALID_ARGS;
30865  }
30866 
30867  readOffset = pRB->encodedReadOffset;
30868  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
30869 
30870  writeOffset = pRB->encodedWriteOffset;
30871  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
30872 
30873  newReadOffsetInBytes = readOffsetInBytes;
30874  newReadOffsetLoopFlag = readOffsetLoopFlag;
30875 
30876  /* We cannot go past the write buffer. */
30877  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
30878  if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
30879  newReadOffsetInBytes = writeOffsetInBytes;
30880  } else {
30881  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
30882  }
30883  } else {
30884  /* May end up looping. */
30885  if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
30886  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
30887  newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
30888  } else {
30889  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
30890  }
30891  }
30892 
30893  ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
30894  return MA_SUCCESS;
30895 }
30896 
30897 ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
30898 {
30899  ma_uint32 readOffset;
30900  ma_uint32 readOffsetInBytes;
30901  ma_uint32 readOffsetLoopFlag;
30902  ma_uint32 writeOffset;
30903  ma_uint32 writeOffsetInBytes;
30904  ma_uint32 writeOffsetLoopFlag;
30905  ma_uint32 newWriteOffsetInBytes;
30906  ma_uint32 newWriteOffsetLoopFlag;
30907 
30908  if (pRB == NULL) {
30909  return MA_INVALID_ARGS;
30910  }
30911 
30912  readOffset = pRB->encodedReadOffset;
30913  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
30914 
30915  writeOffset = pRB->encodedWriteOffset;
30916  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
30917 
30918  newWriteOffsetInBytes = writeOffsetInBytes;
30919  newWriteOffsetLoopFlag = writeOffsetLoopFlag;
30920 
30921  /* We cannot go past the write buffer. */
30922  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
30923  /* May end up looping. */
30924  if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
30925  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
30926  newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
30927  } else {
30928  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
30929  }
30930  } else {
30931  if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
30932  newWriteOffsetInBytes = readOffsetInBytes;
30933  } else {
30934  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
30935  }
30936  }
30937 
30938  ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
30939  return MA_SUCCESS;
30940 }
30941 
30943 {
30944  ma_uint32 readOffset;
30945  ma_uint32 readOffsetInBytes;
30946  ma_uint32 readOffsetLoopFlag;
30947  ma_uint32 writeOffset;
30948  ma_uint32 writeOffsetInBytes;
30949  ma_uint32 writeOffsetLoopFlag;
30950 
30951  if (pRB == NULL) {
30952  return 0;
30953  }
30954 
30955  readOffset = pRB->encodedReadOffset;
30956  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
30957 
30958  writeOffset = pRB->encodedWriteOffset;
30959  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
30960 
30961  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
30962  return writeOffsetInBytes - readOffsetInBytes;
30963  } else {
30964  return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
30965  }
30966 }
30967 
30968 size_t ma_rb_get_subbuffer_size(ma_rb* pRB)
30969 {
30970  if (pRB == NULL) {
30971  return 0;
30972  }
30973 
30974  return pRB->subbufferSizeInBytes;
30975 }
30976 
30977 size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)
30978 {
30979  if (pRB == NULL) {
30980  return 0;
30981  }
30982 
30983  if (pRB->subbufferStrideInBytes == 0) {
30984  return (size_t)pRB->subbufferSizeInBytes;
30985  }
30986 
30987  return (size_t)pRB->subbufferStrideInBytes;
30988 }
30989 
30990 size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
30991 {
30992  if (pRB == NULL) {
30993  return 0;
30994  }
30995 
30996  return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
30997 }
30998 
30999 void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
31000 {
31001  if (pRB == NULL) {
31002  return NULL;
31003  }
31004 
31005  return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
31006 }
31007 
31008 
31009 static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
31010 {
31011  ma_assert(pRB != NULL);
31012 
31013  return ma_get_bytes_per_frame(pRB->format, pRB->channels);
31014 }
31015 
31016 ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, ma_pcm_rb* pRB)
31017 {
31018  ma_uint32 bpf;
31019  ma_result result;
31020 
31021  if (pRB == NULL) {
31022  return MA_INVALID_ARGS;
31023  }
31024 
31025  ma_zero_object(pRB);
31026 
31028  if (bpf == 0) {
31029  return MA_INVALID_ARGS;
31030  }
31031 
31032  result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, &pRB->rb);
31033  if (result != MA_SUCCESS) {
31034  return result;
31035  }
31036 
31037  pRB->format = format;
31038  pRB->channels = channels;
31039 
31040  return MA_SUCCESS;
31041 }
31042 
31043 ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, ma_pcm_rb* pRB)
31044 {
31045  return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pRB);
31046 }
31047 
31048 void ma_pcm_rb_uninit(ma_pcm_rb* pRB)
31049 {
31050  if (pRB == NULL) {
31051  return;
31052  }
31053 
31054  ma_rb_uninit(&pRB->rb);
31055 }
31056 
31057 ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
31058 {
31059  size_t sizeInBytes;
31060  ma_result result;
31061 
31062  if (pRB == NULL || pSizeInFrames == NULL) {
31063  return MA_INVALID_ARGS;
31064  }
31065 
31066  sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
31067 
31068  result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
31069  if (result != MA_SUCCESS) {
31070  return result;
31071  }
31072 
31073  *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
31074  return MA_SUCCESS;
31075 }
31076 
31077 ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
31078 {
31079  if (pRB == NULL) {
31080  return MA_INVALID_ARGS;
31081  }
31082 
31083  return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
31084 }
31085 
31086 ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
31087 {
31088  size_t sizeInBytes;
31089  ma_result result;
31090 
31091  if (pRB == NULL) {
31092  return MA_INVALID_ARGS;
31093  }
31094 
31095  sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
31096 
31097  result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
31098  if (result != MA_SUCCESS) {
31099  return result;
31100  }
31101 
31102  *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
31103  return MA_SUCCESS;
31104 }
31105 
31106 ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
31107 {
31108  if (pRB == NULL) {
31109  return MA_INVALID_ARGS;
31110  }
31111 
31112  return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
31113 }
31114 
31115 ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
31116 {
31117  if (pRB == NULL) {
31118  return MA_INVALID_ARGS;
31119  }
31120 
31121  return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
31122 }
31123 
31124 ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
31125 {
31126  if (pRB == NULL) {
31127  return MA_INVALID_ARGS;
31128  }
31129 
31130  return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
31131 }
31132 
31134 {
31135  if (pRB == NULL) {
31136  return MA_INVALID_ARGS;
31137  }
31138 
31139  return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
31140 }
31141 
31143 {
31144  if (pRB == NULL) {
31145  return 0;
31146  }
31147 
31148  return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
31149 }
31150 
31152 {
31153  if (pRB == NULL) {
31154  return 0;
31155  }
31156 
31157  return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
31158 }
31159 
31161 {
31162  if (pRB == NULL) {
31163  return 0;
31164  }
31165 
31166  return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
31167 }
31168 
31169 void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
31170 {
31171  if (pRB == NULL) {
31172  return NULL;
31173  }
31174 
31175  return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
31176 }
31177 
31178 
31179 
31180 /**************************************************************************************************************************************************************
31181 
31182 Miscellaneous Helpers
31183 
31184 **************************************************************************************************************************************************************/
31185 void* ma_malloc(size_t sz)
31186 {
31187  return MA_MALLOC(sz);
31188 }
31189 
31190 void* ma_realloc(void* p, size_t sz)
31191 {
31192  return MA_REALLOC(p, sz);
31193 }
31194 
31195 void ma_free(void* p)
31196 {
31197  MA_FREE(p);
31198 }
31199 
31200 void* ma_aligned_malloc(size_t sz, size_t alignment)
31201 {
31202  size_t extraBytes;
31203  void* pUnaligned;
31204  void* pAligned;
31205 
31206  if (alignment == 0) {
31207  return 0;
31208  }
31209 
31210  extraBytes = alignment-1 + sizeof(void*);
31211 
31212  pUnaligned = ma_malloc(sz + extraBytes);
31213  if (pUnaligned == NULL) {
31214  return NULL;
31215  }
31216 
31217  pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
31218  ((void**)pAligned)[-1] = pUnaligned;
31219 
31220  return pAligned;
31221 }
31222 
31223 void ma_aligned_free(void* p)
31224 {
31225  ma_free(((void**)p)[-1]);
31226 }
31227 
31228 const char* ma_get_format_name(ma_format format)
31229 {
31230  switch (format)
31231  {
31232  case ma_format_unknown: return "Unknown";
31233  case ma_format_u8: return "8-bit Unsigned Integer";
31234  case ma_format_s16: return "16-bit Signed Integer";
31235  case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
31236  case ma_format_s32: return "32-bit Signed Integer";
31237  case ma_format_f32: return "32-bit IEEE Floating Point";
31238  default: return "Invalid";
31239  }
31240 }
31241 
31242 void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
31243 {
31244  ma_uint32 i;
31245  for (i = 0; i < channels; ++i) {
31246  pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
31247  }
31248 }
31249 
31250 
31252 {
31253  ma_uint32 sizes[] = {
31254  0, /* unknown */
31255  1, /* u8 */
31256  2, /* s16 */
31257  3, /* s24 */
31258  4, /* s32 */
31259  4, /* f32 */
31260  };
31261  return sizes[format];
31262 }
31263 
31264 
31265 /**************************************************************************************************************************************************************
31266 
31267 Decoding
31268 
31269 **************************************************************************************************************************************************************/
31270 #ifndef MA_NO_DECODING
31271 
31272 size_t ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
31273 {
31274  size_t bytesRead;
31275 
31276  ma_assert(pDecoder != NULL);
31277  ma_assert(pBufferOut != NULL);
31278 
31279  bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
31280  pDecoder->readPointer += bytesRead;
31281 
31282  return bytesRead;
31283 }
31284 
31285 ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
31286 {
31287  ma_bool32 wasSuccessful;
31288 
31289  ma_assert(pDecoder != NULL);
31290 
31291  wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
31292  if (wasSuccessful) {
31293  if (origin == ma_seek_origin_start) {
31294  pDecoder->readPointer = (ma_uint64)byteOffset;
31295  } else {
31296  pDecoder->readPointer += byteOffset;
31297  }
31298  }
31299 
31300  return wasSuccessful;
31301 }
31302 
31303 ma_bool32 ma_decoder_seek_bytes_64(ma_decoder* pDecoder, ma_uint64 byteOffset, ma_seek_origin origin)
31304 {
31305  ma_assert(pDecoder != NULL);
31306 
31307  if (origin == ma_seek_origin_start) {
31308  ma_uint64 bytesToSeekThisIteration = 0x7FFFFFFF;
31309  if (bytesToSeekThisIteration > byteOffset) {
31310  bytesToSeekThisIteration = byteOffset;
31311  }
31312 
31313  if (!ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_start)) {
31314  return MA_FALSE;
31315  }
31316 
31317  byteOffset -= bytesToSeekThisIteration;
31318  }
31319 
31320  /* Getting here means we need to seek relative to the current position. */
31321  while (byteOffset > 0) {
31322  ma_uint64 bytesToSeekThisIteration = 0x7FFFFFFF;
31323  if (bytesToSeekThisIteration > byteOffset) {
31324  bytesToSeekThisIteration = byteOffset;
31325  }
31326 
31327  if (!ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_current)) {
31328  return MA_FALSE;
31329  }
31330 
31331  byteOffset -= bytesToSeekThisIteration;
31332  }
31333 
31334  return MA_TRUE;
31335 }
31336 
31337 
31338 ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
31339 {
31340  ma_decoder_config config;
31341  ma_zero_object(&config);
31342  config.format = outputFormat;
31343  config.channels = outputChannels;
31344  config.sampleRate = outputSampleRate;
31346 
31347  return config;
31348 }
31349 
31350 ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
31351 {
31352  ma_decoder_config config;
31353  if (pConfig != NULL) {
31354  config = *pConfig;
31355  } else {
31356  ma_zero_object(&config);
31357  }
31358 
31359  return config;
31360 }
31361 
31362 ma_result ma_decoder__init_dsp(ma_decoder* pDecoder, const ma_decoder_config* pConfig, ma_pcm_converter_read_proc onRead)
31363 {
31364  ma_pcm_converter_config dspConfig;
31365 
31366  ma_assert(pDecoder != NULL);
31367 
31368  /* Output format. */
31369  if (pConfig->format == ma_format_unknown) {
31370  pDecoder->outputFormat = pDecoder->internalFormat;
31371  } else {
31372  pDecoder->outputFormat = pConfig->format;
31373  }
31374 
31375  if (pConfig->channels == 0) {
31376  pDecoder->outputChannels = pDecoder->internalChannels;
31377  } else {
31378  pDecoder->outputChannels = pConfig->channels;
31379  }
31380 
31381  if (pConfig->sampleRate == 0) {
31382  pDecoder->outputSampleRate = pDecoder->internalSampleRate;
31383  } else {
31384  pDecoder->outputSampleRate = pConfig->sampleRate;
31385  }
31386 
31387  if (ma_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) {
31389  } else {
31390  ma_copy_memory(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap));
31391  }
31392 
31393 
31394  /* DSP. */
31395  dspConfig = ma_pcm_converter_config_init_ex(
31396  pDecoder->internalFormat, pDecoder->internalChannels, pDecoder->internalSampleRate, pDecoder->internalChannelMap,
31397  pDecoder->outputFormat, pDecoder->outputChannels, pDecoder->outputSampleRate, pDecoder->outputChannelMap,
31398  onRead, pDecoder);
31399  dspConfig.channelMixMode = pConfig->channelMixMode;
31400  dspConfig.ditherMode = pConfig->ditherMode;
31401  dspConfig.srcAlgorithm = pConfig->srcAlgorithm;
31402  dspConfig.sinc = pConfig->src.sinc;
31403 
31404  return ma_pcm_converter_init(&dspConfig, &pDecoder->dsp);
31405 }
31406 
31407 /* WAV */
31408 #ifdef dr_wav_h
31409 #define MA_HAS_WAV
31410 
31411 size_t ma_decoder_internal_on_read__wav(void* pUserData, void* pBufferOut, size_t bytesToRead)
31412 {
31413  ma_decoder* pDecoder = (ma_decoder*)pUserData;
31414  ma_assert(pDecoder != NULL);
31415 
31416  return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
31417 }
31418 
31419 drwav_bool32 ma_decoder_internal_on_seek__wav(void* pUserData, int offset, drwav_seek_origin origin)
31420 {
31421  ma_decoder* pDecoder = (ma_decoder*)pUserData;
31422  ma_assert(pDecoder != NULL);
31423 
31424  return ma_decoder_seek_bytes(pDecoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
31425 }
31426 
31427 ma_uint32 ma_decoder_internal_on_read_pcm_frames__wav(ma_pcm_converter* pDSP, void* pSamplesOut, ma_uint32 frameCount, void* pUserData)
31428 {
31429  ma_decoder* pDecoder;
31430  drwav* pWav;
31431 
31432  (void)pDSP;
31433 
31434  pDecoder = (ma_decoder*)pUserData;
31435  ma_assert(pDecoder != NULL);
31436 
31437  pWav = (drwav*)pDecoder->pInternalDecoder;
31438  ma_assert(pWav != NULL);
31439 
31440  switch (pDecoder->internalFormat) {
31441  case ma_format_s16: return (ma_uint32)drwav_read_pcm_frames_s16(pWav, frameCount, (drwav_int16*)pSamplesOut);
31442  case ma_format_s32: return (ma_uint32)drwav_read_pcm_frames_s32(pWav, frameCount, (drwav_int32*)pSamplesOut);
31443  case ma_format_f32: return (ma_uint32)drwav_read_pcm_frames_f32(pWav, frameCount, (float*)pSamplesOut);
31444  default: break;
31445  }
31446 
31447  /* Should never get here. If we do, it means the internal format was not set correctly at initialization time. */
31448  ma_assert(MA_FALSE);
31449  return 0;
31450 }
31451 
31452 ma_result ma_decoder_internal_on_seek_to_pcm_frame__wav(ma_decoder* pDecoder, ma_uint64 frameIndex)
31453 {
31454  drwav* pWav;
31455  drwav_bool32 result;
31456 
31457  pWav = (drwav*)pDecoder->pInternalDecoder;
31458  ma_assert(pWav != NULL);
31459 
31460  result = drwav_seek_to_pcm_frame(pWav, frameIndex);
31461  if (result) {
31462  return MA_SUCCESS;
31463  } else {
31464  return MA_ERROR;
31465  }
31466 }
31467 
31468 ma_result ma_decoder_internal_on_uninit__wav(ma_decoder* pDecoder)
31469 {
31470  drwav_close((drwav*)pDecoder->pInternalDecoder);
31471  return MA_SUCCESS;
31472 }
31473 
31474 ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__wav(ma_decoder* pDecoder)
31475 {
31476  return ((drwav*)pDecoder->pInternalDecoder)->totalPCMFrameCount;
31477 }
31478 
31479 ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
31480 {
31481  drwav* pWav;
31482  ma_result result;
31483 
31484  ma_assert(pConfig != NULL);
31485  ma_assert(pDecoder != NULL);
31486 
31487  /* Try opening the decoder first. */
31488  pWav = drwav_open(ma_decoder_internal_on_read__wav, ma_decoder_internal_on_seek__wav, pDecoder);
31489  if (pWav == NULL) {
31490  return MA_ERROR;
31491  }
31492 
31493  /* If we get here it means we successfully initialized the WAV decoder. We can now initialize the rest of the ma_decoder. */
31494  pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__wav;
31495  pDecoder->onUninit = ma_decoder_internal_on_uninit__wav;
31496  pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__wav;
31497  pDecoder->pInternalDecoder = pWav;
31498 
31499  /* Try to be as optimal as possible for the internal format. If miniaudio does not support a format we will fall back to f32. */
31500  pDecoder->internalFormat = ma_format_unknown;
31501  switch (pWav->translatedFormatTag) {
31502  case DR_WAVE_FORMAT_PCM:
31503  {
31504  if (pWav->bitsPerSample == 8) {
31505  pDecoder->internalFormat = ma_format_s16;
31506  } else if (pWav->bitsPerSample == 16) {
31507  pDecoder->internalFormat = ma_format_s16;
31508  } else if (pWav->bitsPerSample == 32) {
31509  pDecoder->internalFormat = ma_format_s32;
31510  }
31511  } break;
31512 
31514  {
31515  if (pWav->bitsPerSample == 32) {
31516  pDecoder->internalFormat = ma_format_f32;
31517  }
31518  } break;
31519 
31520  case DR_WAVE_FORMAT_ALAW:
31521  case DR_WAVE_FORMAT_MULAW:
31522  case DR_WAVE_FORMAT_ADPCM:
31524  {
31525  pDecoder->internalFormat = ma_format_s16;
31526  } break;
31527  }
31528 
31529  if (pDecoder->internalFormat == ma_format_unknown) {
31530  pDecoder->internalFormat = ma_format_f32;
31531  }
31532 
31533  pDecoder->internalChannels = pWav->channels;
31534  pDecoder->internalSampleRate = pWav->sampleRate;
31536 
31537  result = ma_decoder__init_dsp(pDecoder, pConfig, ma_decoder_internal_on_read_pcm_frames__wav);
31538  if (result != MA_SUCCESS) {
31539  drwav_close(pWav);
31540  return result;
31541  }
31542 
31543  return MA_SUCCESS;
31544 }
31545 #endif
31546 
31547 /* FLAC */
31548 #ifdef dr_flac_h
31549 #define MA_HAS_FLAC
31550 
31551 size_t ma_decoder_internal_on_read__flac(void* pUserData, void* pBufferOut, size_t bytesToRead)
31552 {
31553  ma_decoder* pDecoder = (ma_decoder*)pUserData;
31554  ma_assert(pDecoder != NULL);
31555 
31556  return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
31557 }
31558 
31559 drflac_bool32 ma_decoder_internal_on_seek__flac(void* pUserData, int offset, drflac_seek_origin origin)
31560 {
31561  ma_decoder* pDecoder = (ma_decoder*)pUserData;
31562  ma_assert(pDecoder != NULL);
31563 
31564  return ma_decoder_seek_bytes(pDecoder, offset, (origin == drflac_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
31565 }
31566 
31567 ma_uint32 ma_decoder_internal_on_read_pcm_frames__flac(ma_pcm_converter* pDSP, void* pSamplesOut, ma_uint32 frameCount, void* pUserData)
31568 {
31569  ma_decoder* pDecoder;
31570  drflac* pFlac;
31571 
31572  (void)pDSP;
31573 
31574  pDecoder = (ma_decoder*)pUserData;
31575  ma_assert(pDecoder != NULL);
31576 
31577  pFlac = (drflac*)pDecoder->pInternalDecoder;
31578  ma_assert(pFlac != NULL);
31579 
31580  switch (pDecoder->internalFormat) {
31581  case ma_format_s16: return (ma_uint32)drflac_read_pcm_frames_s16(pFlac, frameCount, (drflac_int16*)pSamplesOut);
31582  case ma_format_s32: return (ma_uint32)drflac_read_pcm_frames_s32(pFlac, frameCount, (drflac_int32*)pSamplesOut);
31583  case ma_format_f32: return (ma_uint32)drflac_read_pcm_frames_f32(pFlac, frameCount, (float*)pSamplesOut);
31584  default: break;
31585  }
31586 
31587  /* Should never get here. If we do, it means the internal format was not set correctly at initialization time. */
31588  ma_assert(MA_FALSE);
31589  return 0;
31590 }
31591 
31592 ma_result ma_decoder_internal_on_seek_to_pcm_frame__flac(ma_decoder* pDecoder, ma_uint64 frameIndex)
31593 {
31594  drflac* pFlac;
31595  drflac_bool32 result;
31596 
31597  pFlac = (drflac*)pDecoder->pInternalDecoder;
31598  ma_assert(pFlac != NULL);
31599 
31600  result = drflac_seek_to_pcm_frame(pFlac, frameIndex);
31601  if (result) {
31602  return MA_SUCCESS;
31603  } else {
31604  return MA_ERROR;
31605  }
31606 }
31607 
31608 ma_result ma_decoder_internal_on_uninit__flac(ma_decoder* pDecoder)
31609 {
31610  drflac_close((drflac*)pDecoder->pInternalDecoder);
31611  return MA_SUCCESS;
31612 }
31613 
31614 ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__flac(ma_decoder* pDecoder)
31615 {
31616  return ((drflac*)pDecoder->pInternalDecoder)->totalPCMFrameCount;
31617 }
31618 
31619 ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
31620 {
31621  drflac* pFlac;
31622  ma_result result;
31623 
31624  ma_assert(pConfig != NULL);
31625  ma_assert(pDecoder != NULL);
31626 
31627  /* Try opening the decoder first. */
31628  pFlac = drflac_open(ma_decoder_internal_on_read__flac, ma_decoder_internal_on_seek__flac, pDecoder);
31629  if (pFlac == NULL) {
31630  return MA_ERROR;
31631  }
31632 
31633  /* If we get here it means we successfully initialized the FLAC decoder. We can now initialize the rest of the ma_decoder. */
31634  pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__flac;
31635  pDecoder->onUninit = ma_decoder_internal_on_uninit__flac;
31636  pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__flac;
31637  pDecoder->pInternalDecoder = pFlac;
31638 
31639  /*
31640  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
31641  since it's the only one that's truly lossless.
31642  */
31643  pDecoder->internalFormat = ma_format_s32;
31644  if (pConfig->format == ma_format_s16) {
31645  pDecoder->internalFormat = ma_format_s16;
31646  } else if (pConfig->format == ma_format_f32) {
31647  pDecoder->internalFormat = ma_format_f32;
31648  }
31649 
31650  pDecoder->internalChannels = pFlac->channels;
31651  pDecoder->internalSampleRate = pFlac->sampleRate;
31653 
31654  result = ma_decoder__init_dsp(pDecoder, pConfig, ma_decoder_internal_on_read_pcm_frames__flac);
31655  if (result != MA_SUCCESS) {
31656  drflac_close(pFlac);
31657  return result;
31658  }
31659 
31660  return MA_SUCCESS;
31661 }
31662 #endif
31663 
31664 /* Vorbis */
31665 #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
31666 #define MA_HAS_VORBIS
31667 
31668 /* The size in bytes of each chunk of data to read from the Vorbis stream. */
31669 #define MA_VORBIS_DATA_CHUNK_SIZE 4096
31670 
31671 typedef struct
31672 {
31673  stb_vorbis* pInternalVorbis;
31674  ma_uint8* pData;
31675  size_t dataSize;
31676  size_t dataCapacity;
31677  ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
31678  ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
31679  float** ppPacketData;
31680 } ma_vorbis_decoder;
31681 
31682 ma_uint32 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, void* pSamplesOut, ma_uint32 frameCount)
31683 {
31684  float* pSamplesOutF;
31685  ma_uint32 totalFramesRead;
31686 
31687  ma_assert(pVorbis != NULL);
31688  ma_assert(pDecoder != NULL);
31689 
31690  pSamplesOutF = (float*)pSamplesOut;
31691 
31692  totalFramesRead = 0;
31693  while (frameCount > 0) {
31694  /* Read from the in-memory buffer first. */
31695  while (pVorbis->framesRemaining > 0 && frameCount > 0) {
31696  ma_uint32 iChannel;
31697  for (iChannel = 0; iChannel < pDecoder->internalChannels; ++iChannel) {
31698  pSamplesOutF[0] = pVorbis->ppPacketData[iChannel][pVorbis->framesConsumed];
31699  pSamplesOutF += 1;
31700  }
31701 
31702  pVorbis->framesConsumed += 1;
31703  pVorbis->framesRemaining -= 1;
31704  frameCount -= 1;
31705  totalFramesRead += 1;
31706  }
31707 
31708  if (frameCount == 0) {
31709  break;
31710  }
31711 
31712  ma_assert(pVorbis->framesRemaining == 0);
31713 
31714  /* We've run out of cached frames, so decode the next packet and continue iteration. */
31715  do
31716  {
31717  int samplesRead;
31718  int consumedDataSize;
31719 
31720  if (pVorbis->dataSize > INT_MAX) {
31721  break; /* Too big. */
31722  }
31723 
31724  samplesRead = 0;
31725  consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->pInternalVorbis, pVorbis->pData, (int)pVorbis->dataSize, NULL, (float***)&pVorbis->ppPacketData, &samplesRead);
31726  if (consumedDataSize != 0) {
31727  size_t leftoverDataSize = (pVorbis->dataSize - (size_t)consumedDataSize);
31728  size_t i;
31729  for (i = 0; i < leftoverDataSize; ++i) {
31730  pVorbis->pData[i] = pVorbis->pData[i + consumedDataSize];
31731  }
31732 
31733  pVorbis->dataSize = leftoverDataSize;
31734  pVorbis->framesConsumed = 0;
31735  pVorbis->framesRemaining = samplesRead;
31736  break;
31737  } else {
31738  /* Need more data. If there's any room in the existing buffer allocation fill that first. Otherwise expand. */
31739  size_t bytesRead;
31740  if (pVorbis->dataCapacity == pVorbis->dataSize) {
31741  /* No room. Expand. */
31742  size_t newCap = pVorbis->dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
31743  ma_uint8* pNewData;
31744 
31745  pNewData = (ma_uint8*)ma_realloc(pVorbis->pData, newCap);
31746  if (pNewData == NULL) {
31747  return totalFramesRead; /* Out of memory. */
31748  }
31749 
31750  pVorbis->pData = pNewData;
31751  pVorbis->dataCapacity = newCap;
31752  }
31753 
31754  /* Fill in a chunk. */
31755  bytesRead = ma_decoder_read_bytes(pDecoder, pVorbis->pData + pVorbis->dataSize, (pVorbis->dataCapacity - pVorbis->dataSize));
31756  if (bytesRead == 0) {
31757  return totalFramesRead; /* Error reading more data. */
31758  }
31759 
31760  pVorbis->dataSize += bytesRead;
31761  }
31762  } while (MA_TRUE);
31763  }
31764 
31765  return totalFramesRead;
31766 }
31767 
31768 ma_result ma_vorbis_decoder_seek_to_pcm_frame(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, ma_uint64 frameIndex)
31769 {
31770  float buffer[4096];
31771 
31772  ma_assert(pVorbis != NULL);
31773  ma_assert(pDecoder != NULL);
31774 
31775  /*
31776  This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs
31777  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
31778  find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis.
31779  */
31780  if (!ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start)) {
31781  return MA_ERROR;
31782  }
31783 
31784  stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis);
31785  pVorbis->framesConsumed = 0;
31786  pVorbis->framesRemaining = 0;
31787  pVorbis->dataSize = 0;
31788 
31789  while (frameIndex > 0) {
31790  ma_uint32 framesRead;
31791  ma_uint32 framesToRead = ma_countof(buffer)/pDecoder->internalChannels;
31792  if (framesToRead > frameIndex) {
31793  framesToRead = (ma_uint32)frameIndex;
31794  }
31795 
31796  framesRead = ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, buffer, framesToRead);
31797  if (framesRead == 0) {
31798  return MA_ERROR;
31799  }
31800 
31801  frameIndex -= framesRead;
31802  }
31803 
31804  return MA_SUCCESS;
31805 }
31806 
31807 
31808 ma_result ma_decoder_internal_on_seek_to_pcm_frame__vorbis(ma_decoder* pDecoder, ma_uint64 frameIndex)
31809 {
31810  ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
31811  ma_assert(pVorbis != NULL);
31812 
31813  return ma_vorbis_decoder_seek_to_pcm_frame(pVorbis, pDecoder, frameIndex);
31814 }
31815 
31816 ma_result ma_decoder_internal_on_uninit__vorbis(ma_decoder* pDecoder)
31817 {
31818  ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
31819  ma_assert(pVorbis != NULL);
31820 
31821  stb_vorbis_close(pVorbis->pInternalVorbis);
31822  ma_free(pVorbis->pData);
31823  ma_free(pVorbis);
31824 
31825  return MA_SUCCESS;
31826 }
31827 
31828 ma_uint32 ma_decoder_internal_on_read_pcm_frames__vorbis(ma_pcm_converter* pDSP, void* pSamplesOut, ma_uint32 frameCount, void* pUserData)
31829 {
31830  ma_decoder* pDecoder;
31831  ma_vorbis_decoder* pVorbis;
31832 
31833  (void)pDSP;
31834 
31835  pDecoder = (ma_decoder*)pUserData;
31836  ma_assert(pDecoder != NULL);
31837  ma_assert(pDecoder->internalFormat == ma_format_f32);
31838 
31839  pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
31840  ma_assert(pVorbis != NULL);
31841 
31842  return ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, pSamplesOut, frameCount);
31843 }
31844 
31845 ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__vorbis(ma_decoder* pDecoder)
31846 {
31847  /* No good way to do this with Vorbis. */
31848  (void)pDecoder;
31849  return 0;
31850 }
31851 
31852 ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
31853 {
31854  ma_result result;
31855  stb_vorbis* pInternalVorbis = NULL;
31856  size_t dataSize = 0;
31857  size_t dataCapacity = 0;
31858  ma_uint8* pData = NULL;
31859  stb_vorbis_info vorbisInfo;
31860  size_t vorbisDataSize;
31861  ma_vorbis_decoder* pVorbis;
31862 
31863  ma_assert(pConfig != NULL);
31864  ma_assert(pDecoder != NULL);
31865 
31866  /* We grow the buffer in chunks. */
31867  do
31868  {
31869  /* Allocate memory for a new chunk. */
31870  ma_uint8* pNewData;
31871  size_t bytesRead;
31872  int vorbisError = 0;
31873  int consumedDataSize = 0;
31874 
31875  dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
31876  pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity);
31877  if (pNewData == NULL) {
31878  ma_free(pData);
31879  return MA_OUT_OF_MEMORY;
31880  }
31881 
31882  pData = pNewData;
31883 
31884  /* Fill in a chunk. */
31885  bytesRead = ma_decoder_read_bytes(pDecoder, pData + dataSize, (dataCapacity - dataSize));
31886  if (bytesRead == 0) {
31887  return MA_ERROR;
31888  }
31889 
31890  dataSize += bytesRead;
31891  if (dataSize > INT_MAX) {
31892  return MA_ERROR; /* Too big. */
31893  }
31894 
31895  pInternalVorbis = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
31896  if (pInternalVorbis != NULL) {
31897  /*
31898  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
31899  we need to move those bytes down to the front of the buffer since they'll be needed for future decoding.
31900  */
31901  size_t leftoverDataSize = (dataSize - (size_t)consumedDataSize);
31902  size_t i;
31903  for (i = 0; i < leftoverDataSize; ++i) {
31904  pData[i] = pData[i + consumedDataSize];
31905  }
31906 
31907  dataSize = leftoverDataSize;
31908  break; /* Success. */
31909  } else {
31910  if (vorbisError == VORBIS_need_more_data) {
31911  continue;
31912  } else {
31913  return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
31914  }
31915  }
31916  } while (MA_TRUE);
31917 
31918 
31919  /* If we get here it means we successfully opened the Vorbis decoder. */
31920  vorbisInfo = stb_vorbis_get_info(pInternalVorbis);
31921 
31922  /* Don't allow more than MA_MAX_CHANNELS channels. */
31923  if (vorbisInfo.channels > MA_MAX_CHANNELS) {
31924  stb_vorbis_close(pInternalVorbis);
31925  ma_free(pData);
31926  return MA_ERROR; /* Too many channels. */
31927  }
31928 
31929  vorbisDataSize = sizeof(ma_vorbis_decoder) + sizeof(float)*vorbisInfo.max_frame_size;
31930  pVorbis = (ma_vorbis_decoder*)ma_malloc(vorbisDataSize);
31931  if (pVorbis == NULL) {
31932  stb_vorbis_close(pInternalVorbis);
31933  ma_free(pData);
31934  return MA_OUT_OF_MEMORY;
31935  }
31936 
31937  ma_zero_memory(pVorbis, vorbisDataSize);
31938  pVorbis->pInternalVorbis = pInternalVorbis;
31939  pVorbis->pData = pData;
31940  pVorbis->dataSize = dataSize;
31941  pVorbis->dataCapacity = dataCapacity;
31942 
31943  pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__vorbis;
31944  pDecoder->onUninit = ma_decoder_internal_on_uninit__vorbis;
31945  pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__vorbis;
31946  pDecoder->pInternalDecoder = pVorbis;
31947 
31948  /* The internal format is always f32. */
31949  pDecoder->internalFormat = ma_format_f32;
31950  pDecoder->internalChannels = vorbisInfo.channels;
31951  pDecoder->internalSampleRate = vorbisInfo.sample_rate;
31953 
31954  result = ma_decoder__init_dsp(pDecoder, pConfig, ma_decoder_internal_on_read_pcm_frames__vorbis);
31955  if (result != MA_SUCCESS) {
31956  stb_vorbis_close(pVorbis->pInternalVorbis);
31957  ma_free(pVorbis->pData);
31958  ma_free(pVorbis);
31959  return result;
31960  }
31961 
31962  return MA_SUCCESS;
31963 }
31964 #endif
31965 
31966 /* MP3 */
31967 #ifdef dr_mp3_h
31968 #define MA_HAS_MP3
31969 
31970 size_t ma_decoder_internal_on_read__mp3(void* pUserData, void* pBufferOut, size_t bytesToRead)
31971 {
31972  ma_decoder* pDecoder = (ma_decoder*)pUserData;
31973  ma_assert(pDecoder != NULL);
31974 
31975  return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
31976 }
31977 
31978 drmp3_bool32 ma_decoder_internal_on_seek__mp3(void* pUserData, int offset, drmp3_seek_origin origin)
31979 {
31980  ma_decoder* pDecoder = (ma_decoder*)pUserData;
31981  ma_assert(pDecoder != NULL);
31982 
31983  return ma_decoder_seek_bytes(pDecoder, offset, (origin == drmp3_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
31984 }
31985 
31986 ma_uint32 ma_decoder_internal_on_read_pcm_frames__mp3(ma_pcm_converter* pDSP, void* pSamplesOut, ma_uint32 frameCount, void* pUserData)
31987 {
31988  ma_decoder* pDecoder;
31989  drmp3* pMP3;
31990 
31991  (void)pDSP;
31992 
31993  pDecoder = (ma_decoder*)pUserData;
31994  ma_assert(pDecoder != NULL);
31995  ma_assert(pDecoder->internalFormat == ma_format_f32);
31996 
31997  pMP3 = (drmp3*)pDecoder->pInternalDecoder;
31998  ma_assert(pMP3 != NULL);
31999 
32000  return (ma_uint32)drmp3_read_pcm_frames_f32(pMP3, frameCount, (float*)pSamplesOut);
32001 }
32002 
32003 ma_result ma_decoder_internal_on_seek_to_pcm_frame__mp3(ma_decoder* pDecoder, ma_uint64 frameIndex)
32004 {
32005  drmp3* pMP3;
32006  drmp3_bool32 result;
32007 
32008  pMP3 = (drmp3*)pDecoder->pInternalDecoder;
32009  ma_assert(pMP3 != NULL);
32010 
32011  result = drmp3_seek_to_pcm_frame(pMP3, frameIndex);
32012  if (result) {
32013  return MA_SUCCESS;
32014  } else {
32015  return MA_ERROR;
32016  }
32017 }
32018 
32019 ma_result ma_decoder_internal_on_uninit__mp3(ma_decoder* pDecoder)
32020 {
32021  drmp3_uninit((drmp3*)pDecoder->pInternalDecoder);
32022  ma_free(pDecoder->pInternalDecoder);
32023  return MA_SUCCESS;
32024 }
32025 
32026 ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__mp3(ma_decoder* pDecoder)
32027 {
32028  return drmp3_get_pcm_frame_count((drmp3*)pDecoder->pInternalDecoder);
32029 }
32030 
32031 ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32032 {
32033  drmp3* pMP3;
32034  drmp3_config mp3Config;
32035  ma_result result;
32036 
32037  ma_assert(pConfig != NULL);
32038  ma_assert(pDecoder != NULL);
32039 
32040  pMP3 = (drmp3*)ma_malloc(sizeof(*pMP3));
32041  if (pMP3 == NULL) {
32042  return MA_OUT_OF_MEMORY;
32043  }
32044 
32045  /*
32046  Try opening the decoder first. MP3 can have variable sample rates (it's per frame/packet). We therefore need
32047  to use some smarts to determine the most appropriate internal sample rate. These are the rules we're going
32048  to use:
32049 
32050  Sample Rates
32051  1) If an output sample rate is specified in pConfig we just use that. Otherwise;
32052  2) Fall back to 44100.
32053 
32054  The internal channel count is always stereo, and the internal format is always f32.
32055  */
32056  ma_zero_object(&mp3Config);
32057  mp3Config.outputChannels = 2;
32058  mp3Config.outputSampleRate = (pConfig->sampleRate != 0) ? pConfig->sampleRate : 44100;
32059  if (!drmp3_init(pMP3, ma_decoder_internal_on_read__mp3, ma_decoder_internal_on_seek__mp3, pDecoder, &mp3Config)) {
32060  return MA_ERROR;
32061  }
32062 
32063  /* If we get here it means we successfully initialized the MP3 decoder. We can now initialize the rest of the ma_decoder. */
32064  pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__mp3;
32065  pDecoder->onUninit = ma_decoder_internal_on_uninit__mp3;
32066  pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__mp3;
32067  pDecoder->pInternalDecoder = pMP3;
32068 
32069  /* Internal format. */
32070  pDecoder->internalFormat = ma_format_f32;
32071  pDecoder->internalChannels = pMP3->channels;
32072  pDecoder->internalSampleRate = pMP3->sampleRate;
32074 
32075  result = ma_decoder__init_dsp(pDecoder, pConfig, ma_decoder_internal_on_read_pcm_frames__mp3);
32076  if (result != MA_SUCCESS) {
32077  ma_free(pMP3);
32078  return result;
32079  }
32080 
32081  return MA_SUCCESS;
32082 }
32083 #endif
32084 
32085 /* Raw */
32086 ma_uint32 ma_decoder_internal_on_read_pcm_frames__raw(ma_pcm_converter* pDSP, void* pSamplesOut, ma_uint32 frameCount, void* pUserData)
32087 {
32088  ma_decoder* pDecoder;
32089  ma_uint32 bpf;
32090 
32091  (void)pDSP;
32092 
32093  pDecoder = (ma_decoder*)pUserData;
32094  ma_assert(pDecoder != NULL);
32095 
32096  /* For raw decoding we just read directly from the decoder's callbacks. */
32097  bpf = ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
32098  return (ma_uint32)ma_decoder_read_bytes(pDecoder, pSamplesOut, frameCount * bpf) / bpf;
32099 }
32100 
32101 ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDecoder, ma_uint64 frameIndex)
32102 {
32103  ma_bool32 result = MA_FALSE;
32104  ma_uint64 totalBytesToSeek;
32105 
32106  ma_assert(pDecoder != NULL);
32107 
32108  if (pDecoder->onSeek == NULL) {
32109  return MA_ERROR;
32110  }
32111 
32112  /* 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. */
32113  totalBytesToSeek = frameIndex * ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
32114  if (totalBytesToSeek < 0x7FFFFFFF) {
32115  /* Simple case. */
32116  result = ma_decoder_seek_bytes(pDecoder, (int)(frameIndex * ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels)), ma_seek_origin_start);
32117  } else {
32118  /* Complex case. Start by doing a seek relative to the start. Then keep looping using offset seeking. */
32119  result = ma_decoder_seek_bytes(pDecoder, 0x7FFFFFFF, ma_seek_origin_start);
32120  if (result == MA_TRUE) {
32121  totalBytesToSeek -= 0x7FFFFFFF;
32122 
32123  while (totalBytesToSeek > 0) {
32124  ma_uint64 bytesToSeekThisIteration = totalBytesToSeek;
32125  if (bytesToSeekThisIteration > 0x7FFFFFFF) {
32126  bytesToSeekThisIteration = 0x7FFFFFFF;
32127  }
32128 
32129  result = ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_current);
32130  if (result != MA_TRUE) {
32131  break;
32132  }
32133 
32134  totalBytesToSeek -= bytesToSeekThisIteration;
32135  }
32136  }
32137  }
32138 
32139  if (result) {
32140  return MA_SUCCESS;
32141  } else {
32142  return MA_ERROR;
32143  }
32144 }
32145 
32146 ma_result ma_decoder_internal_on_uninit__raw(ma_decoder* pDecoder)
32147 {
32148  (void)pDecoder;
32149  return MA_SUCCESS;
32150 }
32151 
32152 ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__raw(ma_decoder* pDecoder)
32153 {
32154  (void)pDecoder;
32155  return 0;
32156 }
32157 
32158 ma_result ma_decoder_init_raw__internal(const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
32159 {
32160  ma_result result;
32161 
32162  ma_assert(pConfigIn != NULL);
32163  ma_assert(pConfigOut != NULL);
32164  ma_assert(pDecoder != NULL);
32165 
32166  pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__raw;
32167  pDecoder->onUninit = ma_decoder_internal_on_uninit__raw;
32168  pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__raw;
32169 
32170  /* Internal format. */
32171  pDecoder->internalFormat = pConfigIn->format;
32172  pDecoder->internalChannels = pConfigIn->channels;
32173  pDecoder->internalSampleRate = pConfigIn->sampleRate;
32174  ma_channel_map_copy(pDecoder->internalChannelMap, pConfigIn->channelMap, pConfigIn->channels);
32175 
32176  result = ma_decoder__init_dsp(pDecoder, pConfigOut, ma_decoder_internal_on_read_pcm_frames__raw);
32177  if (result != MA_SUCCESS) {
32178  return result;
32179  }
32180 
32181  return MA_SUCCESS;
32182 }
32183 
32184 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)
32185 {
32186  ma_assert(pConfig != NULL);
32187 
32188  if (pDecoder == NULL) {
32189  return MA_INVALID_ARGS;
32190  }
32191 
32192  ma_zero_object(pDecoder);
32193 
32194  if (onRead == NULL || onSeek == NULL) {
32195  return MA_INVALID_ARGS;
32196  }
32197 
32198  pDecoder->onRead = onRead;
32199  pDecoder->onSeek = onSeek;
32200  pDecoder->pUserData = pUserData;
32201 
32202  (void)pConfig;
32203  return MA_SUCCESS;
32204 }
32205 
32206 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)
32207 {
32208  ma_decoder_config config;
32209  ma_result result;
32210 
32211  config = ma_decoder_config_init_copy(pConfig);
32212 
32213  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
32214  if (result != MA_SUCCESS) {
32215  return result;
32216  }
32217 
32218 #ifdef MA_HAS_WAV
32219  return ma_decoder_init_wav__internal(&config, pDecoder);
32220 #else
32221  return MA_NO_BACKEND;
32222 #endif
32223 }
32224 
32225 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)
32226 {
32227  ma_decoder_config config;
32228  ma_result result;
32229 
32230  config = ma_decoder_config_init_copy(pConfig);
32231 
32232  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
32233  if (result != MA_SUCCESS) {
32234  return result;
32235  }
32236 
32237 #ifdef MA_HAS_FLAC
32238  return ma_decoder_init_flac__internal(&config, pDecoder);
32239 #else
32240  return MA_NO_BACKEND;
32241 #endif
32242 }
32243 
32244 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)
32245 {
32246  ma_decoder_config config;
32247  ma_result result;
32248 
32249  config = ma_decoder_config_init_copy(pConfig);
32250 
32251  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
32252  if (result != MA_SUCCESS) {
32253  return result;
32254  }
32255 
32256 #ifdef MA_HAS_VORBIS
32257  return ma_decoder_init_vorbis__internal(&config, pDecoder);
32258 #else
32259  return MA_NO_BACKEND;
32260 #endif
32261 }
32262 
32263 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)
32264 {
32265  ma_decoder_config config;
32266  ma_result result;
32267 
32268  config = ma_decoder_config_init_copy(pConfig);
32269 
32270  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
32271  if (result != MA_SUCCESS) {
32272  return result;
32273  }
32274 
32275 #ifdef MA_HAS_MP3
32276  return ma_decoder_init_mp3__internal(&config, pDecoder);
32277 #else
32278  return MA_NO_BACKEND;
32279 #endif
32280 }
32281 
32282 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)
32283 {
32284  ma_decoder_config config;
32285  ma_result result;
32286 
32287  config = ma_decoder_config_init_copy(pConfigOut);
32288 
32289  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
32290  if (result != MA_SUCCESS) {
32291  return result;
32292  }
32293 
32294  return ma_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
32295 }
32296 
32297 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)
32298 {
32299  ma_result result = MA_NO_BACKEND;
32300 
32301  ma_assert(pConfig != NULL);
32302  ma_assert(pDecoder != NULL);
32303 
32304  /* Silence some warnings in the case that we don't have any decoder backends enabled. */
32305  (void)onRead;
32306  (void)onSeek;
32307  (void)pUserData;
32308  (void)pConfig;
32309  (void)pDecoder;
32310 
32311  /* We use trial and error to open a decoder. */
32312 
32313 #ifdef MA_HAS_WAV
32314  if (result != MA_SUCCESS) {
32315  result = ma_decoder_init_wav__internal(pConfig, pDecoder);
32316  if (result != MA_SUCCESS) {
32317  onSeek(pDecoder, 0, ma_seek_origin_start);
32318  }
32319  }
32320 #endif
32321 #ifdef MA_HAS_FLAC
32322  if (result != MA_SUCCESS) {
32323  result = ma_decoder_init_flac__internal(pConfig, pDecoder);
32324  if (result != MA_SUCCESS) {
32325  onSeek(pDecoder, 0, ma_seek_origin_start);
32326  }
32327  }
32328 #endif
32329 #ifdef MA_HAS_VORBIS
32330  if (result != MA_SUCCESS) {
32331  result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
32332  if (result != MA_SUCCESS) {
32333  onSeek(pDecoder, 0, ma_seek_origin_start);
32334  }
32335  }
32336 #endif
32337 #ifdef MA_HAS_MP3
32338  if (result != MA_SUCCESS) {
32339  result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
32340  if (result != MA_SUCCESS) {
32341  onSeek(pDecoder, 0, ma_seek_origin_start);
32342  }
32343  }
32344 #endif
32345 
32346  if (result != MA_SUCCESS) {
32347  return result;
32348  }
32349 
32350  return result;
32351 }
32352 
32353 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)
32354 {
32355  ma_decoder_config config;
32356  ma_result result;
32357 
32358  config = ma_decoder_config_init_copy(pConfig);
32359 
32360  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
32361  if (result != MA_SUCCESS) {
32362  return result;
32363  }
32364 
32365  return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
32366 }
32367 
32368 
32369 size_t ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
32370 {
32371  size_t bytesRemaining;
32372 
32373  ma_assert(pDecoder->memory.dataSize >= pDecoder->memory.currentReadPos);
32374 
32375  bytesRemaining = pDecoder->memory.dataSize - pDecoder->memory.currentReadPos;
32376  if (bytesToRead > bytesRemaining) {
32377  bytesToRead = bytesRemaining;
32378  }
32379 
32380  if (bytesToRead > 0) {
32381  ma_copy_memory(pBufferOut, pDecoder->memory.pData + pDecoder->memory.currentReadPos, bytesToRead);
32382  pDecoder->memory.currentReadPos += bytesToRead;
32383  }
32384 
32385  return bytesToRead;
32386 }
32387 
32388 ma_bool32 ma_decoder__on_seek_memory(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
32389 {
32390  if (origin == ma_seek_origin_current) {
32391  if (byteOffset > 0) {
32392  if (pDecoder->memory.currentReadPos + byteOffset > pDecoder->memory.dataSize) {
32393  byteOffset = (int)(pDecoder->memory.dataSize - pDecoder->memory.currentReadPos); /* Trying to seek too far forward. */
32394  }
32395  } else {
32396  if (pDecoder->memory.currentReadPos < (size_t)-byteOffset) {
32397  byteOffset = -(int)pDecoder->memory.currentReadPos; /* Trying to seek too far backwards. */
32398  }
32399  }
32400 
32401  /* This will never underflow thanks to the clamps above. */
32402  pDecoder->memory.currentReadPos += byteOffset;
32403  } else {
32404  if ((ma_uint32)byteOffset <= pDecoder->memory.dataSize) {
32405  pDecoder->memory.currentReadPos = byteOffset;
32406  } else {
32407  pDecoder->memory.currentReadPos = pDecoder->memory.dataSize; /* Trying to seek too far forward. */
32408  }
32409  }
32410 
32411  return MA_TRUE;
32412 }
32413 
32414 ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32415 {
32416  ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, pConfig, pDecoder);
32417  if (result != MA_SUCCESS) {
32418  return result;
32419  }
32420 
32421  if (pData == NULL || dataSize == 0) {
32422  return MA_INVALID_ARGS;
32423  }
32424 
32425  pDecoder->memory.pData = (const ma_uint8*)pData;
32426  pDecoder->memory.dataSize = dataSize;
32427  pDecoder->memory.currentReadPos = 0;
32428 
32429  (void)pConfig;
32430  return MA_SUCCESS;
32431 }
32432 
32433 ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32434 {
32435  ma_decoder_config config;
32436  ma_result result;
32437 
32438  config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
32439 
32440  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
32441  if (result != MA_SUCCESS) {
32442  return result;
32443  }
32444 
32445  return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
32446 }
32447 
32448 ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32449 {
32450  ma_decoder_config config;
32451  ma_result result;
32452 
32453  config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
32454 
32455  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
32456  if (result != MA_SUCCESS) {
32457  return result;
32458  }
32459 
32460 #ifdef MA_HAS_WAV
32461  return ma_decoder_init_wav__internal(&config, pDecoder);
32462 #else
32463  return MA_NO_BACKEND;
32464 #endif
32465 }
32466 
32467 ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32468 {
32469  ma_decoder_config config;
32470  ma_result result;
32471 
32472  config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
32473 
32474  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
32475  if (result != MA_SUCCESS) {
32476  return result;
32477  }
32478 
32479 #ifdef MA_HAS_FLAC
32480  return ma_decoder_init_flac__internal(&config, pDecoder);
32481 #else
32482  return MA_NO_BACKEND;
32483 #endif
32484 }
32485 
32486 ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32487 {
32488  ma_decoder_config config;
32489  ma_result result;
32490 
32491  config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
32492 
32493  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
32494  if (result != MA_SUCCESS) {
32495  return result;
32496  }
32497 
32498 #ifdef MA_HAS_VORBIS
32499  return ma_decoder_init_vorbis__internal(&config, pDecoder);
32500 #else
32501  return MA_NO_BACKEND;
32502 #endif
32503 }
32504 
32505 ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32506 {
32507  ma_decoder_config config;
32508  ma_result result;
32509 
32510  config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
32511 
32512  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
32513  if (result != MA_SUCCESS) {
32514  return result;
32515  }
32516 
32517 #ifdef MA_HAS_MP3
32518  return ma_decoder_init_mp3__internal(&config, pDecoder);
32519 #else
32520  return MA_NO_BACKEND;
32521 #endif
32522 }
32523 
32524 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)
32525 {
32526  ma_decoder_config config;
32527  ma_result result;
32528 
32529  config = ma_decoder_config_init_copy(pConfigOut); /* Make sure the config is not NULL. */
32530 
32531  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
32532  if (result != MA_SUCCESS) {
32533  return result;
32534  }
32535 
32536  return ma_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
32537 }
32538 
32539 #ifndef MA_NO_STDIO
32540 #include <stdio.h>
32541 #if !defined(_MSC_VER) && !defined(__DMC__)
32542 #include <strings.h> /* For strcasecmp(). */
32543 #endif
32544 
32545 const char* ma_path_file_name(const char* path)
32546 {
32547  const char* fileName;
32548 
32549  if (path == NULL) {
32550  return NULL;
32551  }
32552 
32553  fileName = path;
32554 
32555  /* We just loop through the path until we find the last slash. */
32556  while (path[0] != '\0') {
32557  if (path[0] == '/' || path[0] == '\\') {
32558  fileName = path;
32559  }
32560 
32561  path += 1;
32562  }
32563 
32564  /* At this point the file name is sitting on a slash, so just move forward. */
32565  while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
32566  fileName += 1;
32567  }
32568 
32569  return fileName;
32570 }
32571 
32572 const char* ma_path_extension(const char* path)
32573 {
32574  const char* extension;
32575  const char* lastOccurance;
32576 
32577  if (path == NULL) {
32578  path = "";
32579  }
32580 
32581  extension = ma_path_file_name(path);
32582  lastOccurance = NULL;
32583 
32584  /* Just find the last '.' and return. */
32585  while (extension[0] != '\0') {
32586  if (extension[0] == '.') {
32587  extension += 1;
32588  lastOccurance = extension;
32589  }
32590 
32591  extension += 1;
32592  }
32593 
32594  return (lastOccurance != NULL) ? lastOccurance : extension;
32595 }
32596 
32597 ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
32598 {
32599  const char* ext1;
32600  const char* ext2;
32601 
32602  if (path == NULL || extension == NULL) {
32603  return MA_FALSE;
32604  }
32605 
32606  ext1 = extension;
32607  ext2 = ma_path_extension(path);
32608 
32609 #if defined(_MSC_VER) || defined(__DMC__)
32610  return _stricmp(ext1, ext2) == 0;
32611 #else
32612  return strcasecmp(ext1, ext2) == 0;
32613 #endif
32614 }
32615 
32616 size_t ma_decoder__on_read_stdio(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
32617 {
32618  return fread(pBufferOut, 1, bytesToRead, (FILE*)pDecoder->pUserData);
32619 }
32620 
32621 ma_bool32 ma_decoder__on_seek_stdio(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
32622 {
32623  return fseek((FILE*)pDecoder->pUserData, byteOffset, (origin == ma_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
32624 }
32625 
32626 ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32627 {
32628  FILE* pFile;
32629 
32630  if (pDecoder == NULL) {
32631  return MA_INVALID_ARGS;
32632  }
32633 
32634  ma_zero_object(pDecoder);
32635 
32636  if (pFilePath == NULL || pFilePath[0] == '\0') {
32637  return MA_INVALID_ARGS;
32638  }
32639 
32640 #if defined(_MSC_VER) && _MSC_VER >= 1400
32641  if (fopen_s(&pFile, pFilePath, "rb") != 0) {
32642  return MA_ERROR;
32643  }
32644 #else
32645  pFile = fopen(pFilePath, "rb");
32646  if (pFile == NULL) {
32647  return MA_ERROR;
32648  }
32649 #endif
32650 
32651  /* We need to manually set the user data so the calls to ma_decoder__on_seek_stdio() succeed. */
32652  pDecoder->pUserData = pFile;
32653 
32654  (void)pConfig;
32655  return MA_SUCCESS;
32656 }
32657 
32658 ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32659 {
32660  ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder); /* This sets pDecoder->pUserData to a FILE*. */
32661  if (result != MA_SUCCESS) {
32662  return result;
32663  }
32664 
32665  /* WAV */
32666  if (ma_path_extension_equal(pFilePath, "wav")) {
32667  result = ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
32668  if (result == MA_SUCCESS) {
32669  return MA_SUCCESS;
32670  }
32671 
32672  ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
32673  }
32674 
32675  /* FLAC */
32676  if (ma_path_extension_equal(pFilePath, "flac")) {
32677  result = ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
32678  if (result == MA_SUCCESS) {
32679  return MA_SUCCESS;
32680  }
32681 
32682  ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
32683  }
32684 
32685  /* MP3 */
32686  if (ma_path_extension_equal(pFilePath, "mp3")) {
32687  result = ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
32688  if (result == MA_SUCCESS) {
32689  return MA_SUCCESS;
32690  }
32691 
32692  ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
32693  }
32694 
32695  /* Trial and error. */
32696  return ma_decoder_init(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
32697 }
32698 
32699 ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32700 {
32701  ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
32702  if (result != MA_SUCCESS) {
32703  return result;
32704  }
32705 
32706  return ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
32707 }
32708 
32709 ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32710 {
32711  ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
32712  if (result != MA_SUCCESS) {
32713  return result;
32714  }
32715 
32716  return ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
32717 }
32718 
32719 ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32720 {
32721  ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
32722  if (result != MA_SUCCESS) {
32723  return result;
32724  }
32725 
32726  return ma_decoder_init_vorbis(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
32727 }
32728 
32729 ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
32730 {
32731  ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
32732  if (result != MA_SUCCESS) {
32733  return result;
32734  }
32735 
32736  return ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
32737 }
32738 #endif
32739 
32741 {
32742  if (pDecoder == NULL) {
32743  return MA_INVALID_ARGS;
32744  }
32745 
32746  if (pDecoder->onUninit) {
32747  pDecoder->onUninit(pDecoder);
32748  }
32749 
32750 #ifndef MA_NO_STDIO
32751  /* If we have a file handle, close it. */
32752  if (pDecoder->onRead == ma_decoder__on_read_stdio) {
32753  fclose((FILE*)pDecoder->pUserData);
32754  }
32755 #endif
32756 
32757  return MA_SUCCESS;
32758 }
32759 
32761 {
32762  if (pDecoder == NULL) {
32763  return 0;
32764  }
32765 
32766  if (pDecoder->onGetLengthInPCMFrames) {
32767  return pDecoder->onGetLengthInPCMFrames(pDecoder);
32768  }
32769 
32770  return 0;
32771 }
32772 
32773 ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
32774 {
32775  if (pDecoder == NULL) {
32776  return 0;
32777  }
32778 
32779  return ma_pcm_converter_read(&pDecoder->dsp, pFramesOut, frameCount);
32780 }
32781 
32783 {
32784  if (pDecoder == NULL) {
32785  return 0;
32786  }
32787 
32788  if (pDecoder->onSeekToPCMFrame) {
32789  return pDecoder->onSeekToPCMFrame(pDecoder, frameIndex);
32790  }
32791 
32792  /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
32793  return MA_INVALID_ARGS;
32794 }
32795 
32796 
32797 ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
32798 {
32799  ma_uint64 totalFrameCount;
32800  ma_uint64 bpf;
32801  ma_uint64 dataCapInFrames;
32802  void* pPCMFramesOut;
32803 
32804  ma_assert(pDecoder != NULL);
32805 
32806  totalFrameCount = 0;
32807  bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
32808 
32809  /* The frame count is unknown until we try reading. Thus, we just run in a loop. */
32810  dataCapInFrames = 0;
32811  pPCMFramesOut = NULL;
32812  for (;;) {
32813  ma_uint64 frameCountToTryReading;
32814  ma_uint64 framesJustRead;
32815 
32816  /* Make room if there's not enough. */
32817  if (totalFrameCount == dataCapInFrames) {
32818  void* pNewPCMFramesOut;
32819  ma_uint64 newDataCapInFrames = dataCapInFrames*2;
32820  if (newDataCapInFrames == 0) {
32821  newDataCapInFrames = 4096;
32822  }
32823 
32824  if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
32825  ma_free(pPCMFramesOut);
32826  return MA_TOO_LARGE;
32827  }
32828 
32829 
32830  pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf));
32831  if (pNewPCMFramesOut == NULL) {
32832  ma_free(pPCMFramesOut);
32833  return MA_OUT_OF_MEMORY;
32834  }
32835 
32836  dataCapInFrames = newDataCapInFrames;
32837  pPCMFramesOut = pNewPCMFramesOut;
32838  }
32839 
32840  frameCountToTryReading = dataCapInFrames - totalFrameCount;
32841  ma_assert(frameCountToTryReading > 0);
32842 
32843  framesJustRead = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading);
32844  totalFrameCount += framesJustRead;
32845 
32846  if (framesJustRead < frameCountToTryReading) {
32847  break;
32848  }
32849  }
32850 
32851 
32852  if (pConfigOut != NULL) {
32853  pConfigOut->format = pDecoder->outputFormat;
32854  pConfigOut->channels = pDecoder->outputChannels;
32855  pConfigOut->sampleRate = pDecoder->outputSampleRate;
32856  ma_channel_map_copy(pConfigOut->channelMap, pDecoder->outputChannelMap, pDecoder->outputChannels);
32857  }
32858 
32859  if (ppPCMFramesOut != NULL) {
32860  *ppPCMFramesOut = pPCMFramesOut;
32861  } else {
32862  ma_free(pPCMFramesOut);
32863  }
32864 
32865  if (pFrameCountOut != NULL) {
32866  *pFrameCountOut = totalFrameCount;
32867  }
32868 
32869  ma_decoder_uninit(pDecoder);
32870  return MA_SUCCESS;
32871 }
32872 
32873 #ifndef MA_NO_STDIO
32874 ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
32875 {
32876  ma_decoder_config config;
32878  ma_result result;
32879 
32880  if (pFrameCountOut != NULL) {
32881  *pFrameCountOut = 0;
32882  }
32883  if (ppPCMFramesOut != NULL) {
32884  *ppPCMFramesOut = NULL;
32885  }
32886 
32887  if (pFilePath == NULL) {
32888  return MA_INVALID_ARGS;
32889  }
32890 
32891  config = ma_decoder_config_init_copy(pConfig);
32892 
32893  result = ma_decoder_init_file(pFilePath, &config, &decoder);
32894  if (result != MA_SUCCESS) {
32895  return result;
32896  }
32897 
32898  return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
32899 }
32900 #endif
32901 
32902 ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
32903 {
32904  ma_decoder_config config;
32906  ma_result result;
32907 
32908  if (pFrameCountOut != NULL) {
32909  *pFrameCountOut = 0;
32910  }
32911  if (ppPCMFramesOut != NULL) {
32912  *ppPCMFramesOut = NULL;
32913  }
32914 
32915  if (pData == NULL || dataSize == 0) {
32916  return MA_INVALID_ARGS;
32917  }
32918 
32919  config = ma_decoder_config_init_copy(pConfig);
32920 
32921  result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
32922  if (result != MA_SUCCESS) {
32923  return result;
32924  }
32925 
32926  return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
32927 }
32928 
32929 #endif /* MA_NO_DECODING */
32930 
32931 
32932 
32933 
32934 /**************************************************************************************************************************************************************
32935 
32936 Generation
32937 
32938 **************************************************************************************************************************************************************/
32939 ma_result ma_sine_wave_init(double amplitude, double periodsPerSecond, ma_uint32 sampleRate, ma_sine_wave* pSineWave)
32940 {
32941  if (pSineWave == NULL) {
32942  return MA_INVALID_ARGS;
32943  }
32944  ma_zero_object(pSineWave);
32945 
32946  if (amplitude == 0 || periodsPerSecond == 0) {
32947  return MA_INVALID_ARGS;
32948  }
32949 
32950  if (amplitude > 1) {
32951  amplitude = 1;
32952  }
32953  if (amplitude < -1) {
32954  amplitude = -1;
32955  }
32956 
32957  pSineWave->amplitude = amplitude;
32958  pSineWave->periodsPerSecond = periodsPerSecond;
32959  pSineWave->delta = MA_TAU_D / sampleRate;
32960  pSineWave->time = 0;
32961 
32962  return MA_SUCCESS;
32963 }
32964 
32965 ma_uint64 ma_sine_wave_read_f32(ma_sine_wave* pSineWave, ma_uint64 count, float* pSamples)
32966 {
32967  return ma_sine_wave_read_f32_ex(pSineWave, count, 1, ma_stream_layout_interleaved, &pSamples);
32968 }
32969 
32970 ma_uint64 ma_sine_wave_read_f32_ex(ma_sine_wave* pSineWave, ma_uint64 frameCount, ma_uint32 channels, ma_stream_layout layout, float** ppFrames)
32971 {
32972  if (pSineWave == NULL) {
32973  return 0;
32974  }
32975 
32976  if (ppFrames != NULL) {
32977  ma_uint64 iFrame;
32978  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
32979  ma_uint32 iChannel;
32980 
32981  float s = (float)(sin(pSineWave->time * pSineWave->periodsPerSecond) * pSineWave->amplitude);
32982  pSineWave->time += pSineWave->delta;
32983 
32984  if (layout == ma_stream_layout_interleaved) {
32985  for (iChannel = 0; iChannel < channels; iChannel += 1) {
32986  ppFrames[0][iFrame*channels + iChannel] = s;
32987  }
32988  } else {
32989  for (iChannel = 0; iChannel < channels; iChannel += 1) {
32990  ppFrames[iChannel][iFrame] = s;
32991  }
32992  }
32993  }
32994  } else {
32995  pSineWave->time += pSineWave->delta * (ma_int64)frameCount; /* Cast to int64 required for VC6. */
32996  }
32997 
32998  return frameCount;
32999 }
33000 
33001 #if defined(_MSC_VER)
33002  #pragma warning(pop)
33003 #endif
33004 
33005 #endif /* MINIAUDIO_IMPLEMENTATION */
33006 
33007 /*
33008 BACKEND IMPLEMENTATION GUIDLINES
33009 ================================
33010 Context
33011 -------
33012 - Run-time linking if possible.
33013 - Set whether or not it's an asynchronous backend
33014 
33015 Device
33016 ------
33017 - If a full-duplex device is requested and the backend does not support full duplex devices, have ma_device_init__[backend]()
33018  return MA_DEVICE_TYPE_NOT_SUPPORTED.
33019 - If exclusive mode is requested, but the backend does not support it, return MA_SHARE_MODE_NOT_SUPPORTED. If practical, try
33020  not to fall back to a different share mode - give the client exactly what they asked for. Some backends, such as ALSA, may
33021  not have a practical way to distinguish between the two.
33022 - If pDevice->usingDefault* is set, prefer the device's native value if the backend supports it. Otherwise use the relevant
33023  value from the config.
33024 - If the configs buffer size in frames is 0, set it based on the buffer size in milliseconds, keeping in mind to handle the
33025  case when the default sample rate is being used where practical.
33026 - Backends must set the following members of pDevice before returning successfully from ma_device_init__[backend]():
33027  - internalFormat
33028  - internalChannels
33029  - internalSampleRate
33030  - internalChannelMap
33031  - bufferSizeInFrames
33032  - periods
33033 */
33034 
33035 /*
33036 REVISION HISTORY
33037 ================
33038 v0.9.5 - 2019-05-21
33039  - Add logging to ma_dlopen() and ma_dlsym().
33040  - Add ma_decoder_get_length_in_pcm_frames().
33041  - Fix a bug with capture on the OpenSL|ES backend.
33042  - Fix a bug with the ALSA backend where a device would not restart after being stopped.
33043 
33044 v0.9.4 - 2019-05-06
33045  - Add support for C89. With this change, miniaudio should compile clean with GCC/Clang with "-std=c89 -ansi -pedantic" and
33046  Microsoft copmilers back to VC6. Other compilers should also work, but have not been tested.
33047 
33048 v0.9.3 - 2019-04-19
33049  - Fix compiler errors on GCC when compiling with -std=c99.
33050 
33051 v0.9.2 - 2019-04-08
33052  - Add support for per-context user data.
33053  - Fix a potential bug with context configs.
33054  - Fix some bugs with PulseAudio.
33055 
33056 v0.9.1 - 2019-03-17
33057  - Fix a bug where the output buffer is not getting zeroed out before calling the data callback. This happens when
33058  the device is running in passthrough mode (not doing any data conversion).
33059  - Fix an issue where the data callback is getting called too frequently on the WASAPI and DirectSound backends.
33060  - Fix error on the UWP build.
33061  - Fix a build error on Apple platforms.
33062 
33063 v0.9 - 2019-03-06
33064  - Rebranded to "miniaudio". All namespaces have been renamed from "mal" to "ma".
33065  - API CHANGE: ma_device_init() and ma_device_config_init() have changed significantly:
33066  - The device type, device ID and user data pointer have moved from ma_device_init() to the config.
33067  - All variations of ma_device_config_init_*() have been removed in favor of just ma_device_config_init().
33068  - ma_device_config_init() now takes only one parameter which is the device type. All other properties need
33069  to be set on the returned object directly.
33070  - The onDataCallback and onStopCallback members of ma_device_config have been renamed to "dataCallback"
33071  and "stopCallback".
33072  - The ID of the physical device is now split into two: one for the playback device and the other for the
33073  capture device. This is required for full-duplex. These are named "pPlaybackDeviceID" and "pCaptureDeviceID".
33074  - API CHANGE: The data callback has changed. It now uses a unified callback for all device types rather than
33075  being separate for each. It now takes two pointers - one containing input data and the other output data. This
33076  design in required for full-duplex. The return value is now void instead of the number of frames written. The
33077  new callback looks like the following:
33078  void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
33079  - API CHANGE: Remove the log callback parameter from ma_context_config_init(). With this change,
33080  ma_context_config_init() now takes no parameters and the log callback is set via the structure directly. The
33081  new policy for config initialization is that only mandatory settings are passed in to *_config_init(). The
33082  "onLog" member of ma_context_config has been renamed to "logCallback".
33083  - API CHANGE: Remove ma_device_get_buffer_size_in_bytes().
33084  - API CHANGE: Rename decoding APIs to "pcm_frames" convention.
33085  - mal_decoder_read() -> ma_decoder_read_pcm_frames()
33086  - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
33087  - API CHANGE: Rename sine wave reading APIs to f32 convention.
33088  - mal_sine_wave_read() -> ma_sine_wave_read_f32()
33089  - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
33090  - API CHANGE: Remove some deprecated APIs
33091  - mal_device_set_recv_callback()
33092  - mal_device_set_send_callback()
33093  - mal_src_set_input_sample_rate()
33094  - mal_src_set_output_sample_rate()
33095  - API CHANGE: Add log level to the log callback. New signature:
33096  - void on_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
33097  - API CHANGE: Changes to result codes. Constants have changed and unused codes have been removed. If you're
33098  a binding mainainer you will need to update your result code constants.
33099  - API CHANGE: Change the order of the ma_backend enums to priority order. If you are a binding maintainer, you
33100  will need to update.
33101  - API CHANGE: Rename mal_dsp to ma_pcm_converter. All functions have been renamed from mal_dsp_*() to
33102  ma_pcm_converter_*(). All structures have been renamed from mal_dsp* to ma_pcm_converter*.
33103  - API CHANGE: Reorder parameters of ma_decoder_read_pcm_frames() to be consistent with the new parameter order scheme.
33104  - The resampling algorithm has been changed from sinc to linear. The rationale for this is that the sinc implementation
33105  is too inefficient right now. This will hopefully be improved at a later date.
33106  - Device initialization will no longer fall back to shared mode if exclusive mode is requested but is unusable.
33107  With this change, if you request an device in exclusive mode, but exclusive mode is not supported, it will not
33108  automatically fall back to shared mode. The client will need to reinitialize the device in shared mode if that's
33109  what they want.
33110  - Add ring buffer API. This is ma_rb and ma_pcm_rb, the difference being that ma_rb operates on bytes and
33111  ma_pcm_rb operates on PCM frames.
33112  - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously
33113  used for web support, will be removed in a future version.
33114  - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts
33115  with Android 8. OpenSL|ES is used as a fallback for older versions of Android.
33116  - Remove OpenAL and SDL backends.
33117  - Fix a possible deadlock when rapidly stopping the device after it has started.
33118  - Update documentation.
33119  - Change licensing to a choice of public domain _or_ MIT-0 (No Attribution).
33120 
33121 v0.8.14 - 2018-12-16
33122  - Core Audio: Fix a bug where the device state is not set correctly after stopping.
33123  - Add support for custom weights to the channel router.
33124  - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav.
33125 
33126 v0.8.13 - 2018-12-04
33127  - Core Audio: Fix a bug with channel mapping.
33128  - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight.
33129 
33130 v0.8.12 - 2018-11-27
33131  - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2".
33132  - Fix a linking error with ALSA.
33133  - Fix a bug on iOS where the device name is not set correctly.
33134 
33135 v0.8.11 - 2018-11-21
33136  - iOS bug fixes.
33137  - Minor tweaks to PulseAudio.
33138 
33139 v0.8.10 - 2018-10-21
33140  - Core Audio: Fix a hang when uninitializing a device.
33141  - Fix a bug where an incorrect value is returned from ma_device_stop().
33142 
33143 v0.8.9 - 2018-09-28
33144  - Fix a bug with the SDL backend where device initialization fails.
33145 
33146 v0.8.8 - 2018-09-14
33147  - Fix Linux build with the ALSA backend.
33148  - Minor documentation fix.
33149 
33150 v0.8.7 - 2018-09-12
33151  - Fix a bug with UWP detection.
33152 
33153 v0.8.6 - 2018-08-26
33154  - Automatically switch the internal device when the default device is unplugged. Note that this is still in the
33155  early stages and not all backends handle this the same way. As of this version, this will not detect a default
33156  device switch when changed from the operating system's audio preferences (unless the backend itself handles
33157  this automatically). This is not supported in exclusive mode.
33158  - WASAPI and Core Audio: Add support for stream routing. When the application is using a default device and the
33159  user switches the default device via the operating system's audio preferences, miniaudio will automatically switch
33160  the internal device to the new default. This is not supported in exclusive mode.
33161  - WASAPI: Add support for hardware offloading via IAudioClient2. Only supported on Windows 8 and newer.
33162  - WASAPI: Add support for low-latency shared mode via IAudioClient3. Only supported on Windows 10 and newer.
33163  - Add support for compiling the UWP build as C.
33164  - mal_device_set_recv_callback() and mal_device_set_send_callback() have been deprecated. You must now set this
33165  when the device is initialized with mal_device_init*(). These will be removed in version 0.9.0.
33166 
33167 v0.8.5 - 2018-08-12
33168  - Add support for specifying the size of a device's buffer in milliseconds. You can still set the buffer size in
33169  frames if that suits you. When bufferSizeInFrames is 0, bufferSizeInMilliseconds will be used. If both are non-0
33170  then bufferSizeInFrames will take priority. If both are set to 0 the default buffer size is used.
33171  - Add support for the audio(4) backend to OpenBSD.
33172  - Fix a bug with the ALSA backend that was causing problems on Raspberry Pi. This significantly improves the
33173  Raspberry Pi experience.
33174  - Fix a bug where an incorrect number of samples is returned from sinc resampling.
33175  - Add support for setting the value to be passed to internal calls to CoInitializeEx().
33176  - WASAPI and WinMM: Stop the device when it is unplugged.
33177 
33178 v0.8.4 - 2018-08-06
33179  - Add sndio backend for OpenBSD.
33180  - Add audio(4) backend for NetBSD.
33181  - Drop support for the OSS backend on everything except FreeBSD and DragonFly BSD.
33182  - Formats are now native-endian (were previously little-endian).
33183  - Mark some APIs as deprecated:
33184  - mal_src_set_input_sample_rate() and mal_src_set_output_sample_rate() are replaced with mal_src_set_sample_rate().
33185  - mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() are replaced with mal_dsp_set_sample_rate().
33186  - Fix a bug when capturing using the WASAPI backend.
33187  - Fix some aliasing issues with resampling, specifically when increasing the sample rate.
33188  - Fix warnings.
33189 
33190 v0.8.3 - 2018-07-15
33191  - Fix a crackling bug when resampling in capture mode.
33192  - Core Audio: Fix a bug where capture does not work.
33193  - ALSA: Fix a bug where the worker thread can get stuck in an infinite loop.
33194  - PulseAudio: Fix a bug where mal_context_init() succeeds when PulseAudio is unusable.
33195  - JACK: Fix a bug where mal_context_init() succeeds when JACK is unusable.
33196 
33197 v0.8.2 - 2018-07-07
33198  - Fix a bug on macOS with Core Audio where the internal callback is not called.
33199 
33200 v0.8.1 - 2018-07-06
33201  - Fix compilation errors and warnings.
33202 
33203 v0.8 - 2018-07-05
33204  - Changed MA_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old
33205  way is still supported for now, but you should update as it may be removed in the future.
33206  - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with
33207  mal_context_get_devices(). An additional low-level device enumration API has been introduced called
33208  mal_context_enumerate_devices() which uses a callback to report devices.
33209  - API CHANGE: Rename mal_get_sample_size_in_bytes() to mal_get_bytes_per_sample() and add
33210  mal_get_bytes_per_frame().
33211  - API CHANGE: Replace mal_device_config.preferExclusiveMode with ma_device_config.shareMode.
33212  - This new config can be set to mal_share_mode_shared (default) or ma_share_mode_exclusive.
33213  - API CHANGE: Remove excludeNullDevice from mal_context_config.alsa.
33214  - API CHANGE: Rename MA_MAX_SAMPLE_SIZE_IN_BYTES to MA_MAX_PCM_SAMPLE_SIZE_IN_BYTES.
33215  - API CHANGE: Change the default channel mapping to the standard Microsoft mapping.
33216  - API CHANGE: Remove backend-specific result codes.
33217  - API CHANGE: Changes to the format conversion APIs (mal_pcm_f32_to_s16(), etc.)
33218  - Add support for Core Audio (Apple).
33219  - Add support for PulseAudio.
33220  - This is the highest priority backend on Linux (higher priority than ALSA) since it is commonly
33221  installed by default on many of the popular distros and offer's more seamless integration on
33222  platforms where PulseAudio is used. In addition, if PulseAudio is installed and running (which
33223  is extremely common), it's better to just use PulseAudio directly rather than going through the
33224  "pulse" ALSA plugin (which is what the "default" ALSA device is likely set to).
33225  - Add support for JACK.
33226  - Remove dependency on asound.h for the ALSA backend. This means the ALSA development packages are no
33227  longer required to build miniaudio.
33228  - Remove dependency on dsound.h for the DirectSound backend. This fixes build issues with some
33229  distributions of MinGW.
33230  - Remove dependency on audioclient.h for the WASAPI backend. This fixes build issues with some
33231  distributions of MinGW.
33232  - Add support for dithering to format conversion.
33233  - Add support for configuring the priority of the worker thread.
33234  - Add a sine wave generator.
33235  - Improve efficiency of sample rate conversion.
33236  - Introduce the notion of standard channel maps. Use mal_get_standard_channel_map().
33237  - Introduce the notion of default device configurations. A default config uses the same configuration
33238  as the backend's internal device, and as such results in a pass-through data transmission pipeline.
33239  - Add support for passing in NULL for the device config in mal_device_init(), which uses a default
33240  config. This requires manually calling mal_device_set_send/recv_callback().
33241  - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.)
33242  - Make mal_device_init_ex() more robust.
33243  - Make some APIs more const-correct.
33244  - Fix errors with SDL detection on Apple platforms.
33245  - Fix errors with OpenAL detection.
33246  - Fix some memory leaks.
33247  - Fix a bug with opening decoders from memory.
33248  - Early work on SSE2, AVX2 and NEON optimizations.
33249  - Miscellaneous bug fixes.
33250  - Documentation updates.
33251 
33252 v0.7 - 2018-02-25
33253  - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts.
33254  - Add decoder APIs for loading WAV, FLAC, Vorbis and MP3 files.
33255  - Allow opening of devices without a context.
33256  - In this case the context is created and managed internally by the device.
33257  - Change the default channel mapping to the same as that used by FLAC.
33258  - Fix build errors with macOS.
33259 
33260 v0.6c - 2018-02-12
33261  - Fix build errors with BSD/OSS.
33262 
33263 v0.6b - 2018-02-03
33264  - Fix some warnings when compiling with Visual C++.
33265 
33266 v0.6a - 2018-01-26
33267  - Fix errors with channel mixing when increasing the channel count.
33268  - Improvements to the build system for the OpenAL backend.
33269  - Documentation fixes.
33270 
33271 v0.6 - 2017-12-08
33272  - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll
33273  need to update.
33274  - API CHANGE: SRC and DSP callbacks now take a pointer to a mal_src and ma_dsp object respectively.
33275  - API CHANGE: Improvements to event and thread APIs. These changes make these APIs more consistent.
33276  - Add support for SDL and Emscripten.
33277  - Simplify the build system further for when development packages for various backends are not installed.
33278  With this change, when the compiler supports __has_include, backends without the relevant development
33279  packages installed will be ignored. This fixes the build for old versions of MinGW.
33280  - Fixes to the Android build.
33281  - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of
33282  audio data to a different format.
33283  - Improvements to f32 -> u8/s16/s24/s32 conversion routines.
33284  - Fix a bug where the wrong value is returned from mal_device_start() for the OpenSL backend.
33285  - Fixes and improvements for Raspberry Pi.
33286  - Warning fixes.
33287 
33288 v0.5 - 2017-11-11
33289  - API CHANGE: The mal_context_init() function now takes a pointer to a ma_context_config object for
33290  configuring the context. The works in the same kind of way as the device config. The rationale for this
33291  change is to give applications better control over context-level properties, add support for backend-
33292  specific configurations, and support extensibility without breaking the API.
33293  - API CHANGE: The alsa.preferPlugHW device config variable has been removed since it's not really useful for
33294  anything anymore.
33295  - ALSA: By default, device enumeration will now only enumerate over unique card/device pairs. Applications
33296  can enable verbose device enumeration by setting the alsa.useVerboseDeviceEnumeration context config
33297  variable.
33298  - ALSA: When opening a device in shared mode (the default), the dmix/dsnoop plugin will be prioritized. If
33299  this fails it will fall back to the hw plugin. With this change the preferExclusiveMode config is now
33300  honored. Note that this does not happen when alsa.useVerboseDeviceEnumeration is set to true (see above)
33301  which is by design.
33302  - ALSA: Add support for excluding the "null" device using the alsa.excludeNullDevice context config variable.
33303  - ALSA: Fix a bug with channel mapping which causes an assertion to fail.
33304  - Fix errors with enumeration when pInfo is set to NULL.
33305  - OSS: Fix a bug when starting a device when the client sends 0 samples for the initial buffer fill.
33306 
33307 v0.4 - 2017-11-05
33308  - API CHANGE: The log callback is now per-context rather than per-device and as is thus now passed to
33309  mal_context_init(). The rationale for this change is that it allows applications to capture diagnostic
33310  messages at the context level. Previously this was only available at the device level.
33311  - API CHANGE: The device config passed to mal_device_init() is now const.
33312  - Added support for OSS which enables support on BSD platforms.
33313  - Added support for WinMM (waveOut/waveIn).
33314  - Added support for UWP (Universal Windows Platform) applications. Currently C++ only.
33315  - Added support for exclusive mode for selected backends. Currently supported on WASAPI.
33316  - POSIX builds no longer require explicit linking to libpthread (-lpthread).
33317  - ALSA: Explicit linking to libasound (-lasound) is no longer required.
33318  - ALSA: Latency improvements.
33319  - ALSA: Use MMAP mode where available. This can be disabled with the alsa.noMMap config.
33320  - ALSA: Use "hw" devices instead of "plughw" devices by default. This can be disabled with the
33321  alsa.preferPlugHW config.
33322  - WASAPI is now the highest priority backend on Windows platforms.
33323  - Fixed an error with sample rate conversion which was causing crackling when capturing.
33324  - Improved error handling.
33325  - Improved compiler support.
33326  - Miscellaneous bug fixes.
33327 
33328 v0.3 - 2017-06-19
33329  - API CHANGE: Introduced the notion of a context. The context is the highest level object and is required for
33330  enumerating and creating devices. Now, applications must first create a context, and then use that to
33331  enumerate and create devices. The reason for this change is to ensure device enumeration and creation is
33332  tied to the same backend. In addition, some backends are better suited to this design.
33333  - API CHANGE: Removed the rewinding APIs because they're too inconsistent across the different backends, hard
33334  to test and maintain, and just generally unreliable.
33335  - Added helper APIs for initializing mal_device_config objects.
33336  - Null Backend: Fixed a crash when recording.
33337  - Fixed build for UWP.
33338  - Added support for f32 formats to the OpenSL|ES backend.
33339  - Added initial implementation of the WASAPI backend.
33340  - Added initial implementation of the OpenAL backend.
33341  - Added support for low quality linear sample rate conversion.
33342  - Added early support for basic channel mapping.
33343 
33344 v0.2 - 2016-10-28
33345  - API CHANGE: Add user data pointer as the last parameter to mal_device_init(). The rationale for this
33346  change is to ensure the logging callback has access to the user data during initialization.
33347  - API CHANGE: Have device configuration properties be passed to mal_device_init() via a structure. Rationale:
33348  1) The number of parameters is just getting too much.
33349  2) It makes it a bit easier to add new configuration properties in the future. In particular, there's a
33350  chance there will be support added for backend-specific properties.
33351  - Dropped support for f64, A-law and Mu-law formats since they just aren't common enough to justify the
33352  added maintenance cost.
33353  - DirectSound: Increased the default buffer size for capture devices.
33354  - Added initial implementation of the OpenSL|ES backend.
33355 
33356 v0.1 - 2016-10-21
33357  - Initial versioned release.
33358 */
33359 
33360 
33361 /*
33362 This software is available as a choice of the following licenses. Choose
33363 whichever you prefer.
33364 
33365 ===============================================================================
33366 ALTERNATIVE 1 - Public Domain (www.unlicense.org)
33367 ===============================================================================
33368 This is free and unencumbered software released into the public domain.
33369 
33370 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
33371 software, either in source code form or as a compiled binary, for any purpose,
33372 commercial or non-commercial, and by any means.
33373 
33374 In jurisdictions that recognize copyright laws, the author or authors of this
33375 software dedicate any and all copyright interest in the software to the public
33376 domain. We make this dedication for the benefit of the public at large and to
33377 the detriment of our heirs and successors. We intend this dedication to be an
33378 overt act of relinquishment in perpetuity of all present and future rights to
33379 this software under copyright law.
33380 
33381 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33382 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33383 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33384 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
33385 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33386 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33387 
33388 For more information, please refer to <http://unlicense.org/>
33389 
33390 ===============================================================================
33391 ALTERNATIVE 2 - MIT No Attribution
33392 ===============================================================================
33393 Copyright 2019 David Reid
33394 
33395 Permission is hereby granted, free of charge, to any person obtaining a copy of
33396 this software and associated documentation files (the "Software"), to deal in
33397 the Software without restriction, including without limitation the rights to
33398 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
33399 of the Software, and to permit persons to whom the Software is furnished to do
33400 so.
33401 
33402 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33403 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33404 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33405 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33406 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33407 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33408 SOFTWARE.
33409 */
ma_log_proc
void(* ma_log_proc)(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message)
Definition: miniaudio.h:1802
ma_pcm_converter_config::srcAlgorithm
ma_src_algorithm srcAlgorithm
Definition: miniaudio.h:961
MA_LOG_LEVEL
#define MA_LOG_LEVEL
Definition: miniaudio.h:608
ma_device_config::bufferSizeInMilliseconds
ma_uint32 bufferSizeInMilliseconds
Definition: miniaudio.h:1896
ma_src_sinc_window_function
ma_src_sinc_window_function
Definition: miniaudio.h:885
ma_format_converter_config::noNEON
ma_bool32 noNEON
Definition: miniaudio.h:821
MA_CHANNEL_AUX_30
#define MA_CHANNEL_AUX_30
Definition: miniaudio.h:665
ma_device_config::pDeviceID
ma_device_id * pDeviceID
Definition: miniaudio.h:1904
MA_CHANNEL_AUX_24
#define MA_CHANNEL_AUX_24
Definition: miniaudio.h:659
ma_context::hAdvapi32DLL
ma_handle hAdvapi32DLL
Definition: miniaudio.h:2296
ma_pcm_rb_get_subbuffer_size
ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb *pRB)
ma_seek_origin_current
@ ma_seek_origin_current
Definition: miniaudio.h:2997
ma_thread::hThread
ma_handle hThread
Definition: miniaudio.h:1703
MA_SAMPLE_RATE_88200
#define MA_SAMPLE_RATE_88200
Definition: miniaudio.h:725
ma_pcm_s16_to_f32
void ma_pcm_s16_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_device_config::shareMode
ma_share_mode shareMode
Definition: miniaudio.h:1908
ma_thread_priority_default
@ ma_thread_priority_default
Definition: miniaudio.h:1691
ma_channel_router_config::channelMapIn
ma_channel channelMapIn[MA_MAX_CHANNELS]
Definition: miniaudio.h:848
drwav::bitsPerSample
drwav_uint16 bitsPerSample
Definition: dr_wav.h:413
L
#define L
Definition: stb_vorbis.c:5002
drmp3::channels
drmp3_uint32 channels
Definition: dr_mp3.h:242
ma_context::playbackDeviceInfoCount
ma_uint32 playbackDeviceInfoCount
Definition: miniaudio.h:1964
ma_src_algorithm_linear
@ ma_src_algorithm_linear
Definition: miniaudio.h:879
ma_context::waveOutUnprepareHeader
ma_proc waveOutUnprepareHeader
Definition: miniaudio.h:2008
MA_FALSE
#define MA_FALSE
Definition: miniaudio.h:548
ma_pcm_rb_acquire_write
ma_result ma_pcm_rb_acquire_write(ma_pcm_rb *pRB, ma_uint32 *pSizeInFrames, void **ppBufferOut)
drflac_seek_origin
drflac_seek_origin
Definition: dr_flac.h:225
drwav_seek_origin
drwav_seek_origin
Definition: dr_wav.h:204
ma_standard_channel_map_alsa
@ ma_standard_channel_map_alsa
Definition: miniaudio.h:789
ma_pcm_rb_commit_write
ma_result ma_pcm_rb_commit_write(ma_pcm_rb *pRB, ma_uint32 sizeInFrames, void *pBufferOut)
ma_context::jack_get_ports
ma_proc jack_get_ports
Definition: miniaudio.h:2152
ma_src_config::sampleRateOut
ma_uint32 sampleRateOut
Definition: miniaudio.h:901
MA_FAILED_TO_STOP_BACKEND_DEVICE
#define MA_FAILED_TO_STOP_BACKEND_DEVICE
Definition: miniaudio.h:709
value
GLfloat value
Definition: qgl_win.c:63
ma_rb::encodedReadOffset
volatile ma_uint32 encodedReadOffset
Definition: miniaudio.h:1464
ma_decoder_init_flac
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)
ma_pcm_rb_seek_write
ma_result ma_pcm_rb_seek_write(ma_pcm_rb *pRB, ma_uint32 offsetInFrames)
ma_thread_priority
ma_thread_priority
Definition: miniaudio.h:1682
DR_WAVE_FORMAT_IEEE_FLOAT
#define DR_WAVE_FORMAT_IEEE_FLOAT
Definition: dr_wav.h:190
ma_context::pClientName
char * pClientName
Definition: miniaudio.h:2161
ma_thread
Definition: miniaudio.h:1694
MA_CHANNEL_AUX_1
#define MA_CHANNEL_AUX_1
Definition: miniaudio.h:636
drflac_open
drflac * drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void *pUserData)
MA_SAMPLE_RATE_8000
#define MA_SAMPLE_RATE_8000
Definition: miniaudio.h:717
i1
GLint i1
Definition: qgl_win.c:147
ma_rb::subbufferStrideInBytes
ma_uint32 subbufferStrideInBytes
Definition: miniaudio.h:1463
ma_pcm_convert
void ma_pcm_convert(void *pOut, ma_format formatOut, const void *pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
ma_context::waveInUnprepareHeader
ma_proc waveInUnprepareHeader
Definition: miniaudio.h:2016
TRUE
#define TRUE
Definition: stb_vorbis.c:617
ma_decoder::dsp
ma_pcm_converter dsp
Definition: miniaudio.h:3035
ma_context::waveOutClose
ma_proc waveOutClose
Definition: miniaudio.h:2006
ma_rb::subbufferCount
ma_uint32 subbufferCount
Definition: miniaudio.h:1462
ma_decoder_config::sinc
ma_src_config_sinc sinc
Definition: miniaudio.h:3017
decoder
static ma_decoder decoder
Definition: snd_miniaudio.c:46
ma_dither_mode
ma_dither_mode
Definition: miniaudio.h:755
ma_context::DirectSoundCaptureEnumerateA
ma_proc DirectSoundCaptureEnumerateA
Definition: miniaudio.h:1996
MA_FAILED_TO_UNMAP_DEVICE_BUFFER
#define MA_FAILED_TO_UNMAP_DEVICE_BUFFER
Definition: miniaudio.h:701
ma_standard_channel_map_vorbis
@ ma_standard_channel_map_vorbis
Definition: miniaudio.h:792
ma_decoder_seek_to_pcm_frame_proc
ma_result(* ma_decoder_seek_to_pcm_frame_proc)(ma_decoder *pDecoder, ma_uint64 frameIndex)
Definition: miniaudio.h:3002
int
CONST PIXELFORMATDESCRIPTOR int
Definition: qgl_win.c:35
MA_CHANNEL_AUX_28
#define MA_CHANNEL_AUX_28
Definition: miniaudio.h:663
ma_backend_dsound
@ ma_backend_dsound
Definition: miniaudio.h:1666
ma_pcm_converter_config::channelMapOut
ma_channel channelMapOut[MA_MAX_CHANNELS]
Definition: miniaudio.h:958
ma_decoder_init_memory_mp3
ma_result ma_decoder_init_memory_mp3(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_timer::counter
ma_int64 counter
Definition: miniaudio.h:1887
ma_context::waveOutPrepareHeader
ma_proc waveOutPrepareHeader
Definition: miniaudio.h:2007
ma_context::pUserData
void * pUserData
Definition: miniaudio.h:1960
ma_channel_router_config::weights
float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]
Definition: miniaudio.h:851
ma_rb::clearOnWriteAcquire
ma_bool32 clearOnWriteAcquire
Definition: miniaudio.h:1467
ma_calculate_buffer_size_in_frames_from_milliseconds
ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
ma_format_converter_read_deinterleaved
ma_uint64 ma_format_converter_read_deinterleaved(ma_format_converter *pConverter, ma_uint64 frameCount, void **ppSamplesOut, void *pUserData)
drmp3_init
drmp3_bool32 drmp3_init(drmp3 *pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void *pUserData, const drmp3_config *pConfig)
ma_decoder_config::sampleRate
ma_uint32 sampleRate
Definition: miniaudio.h:3010
MA_CHANNEL_AUX_6
#define MA_CHANNEL_AUX_6
Definition: miniaudio.h:641
MA_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES
#define MA_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES
Definition: miniaudio.h:742
ma_rb_commit_write
ma_result ma_rb_commit_write(ma_rb *pRB, size_t sizeInBytes, void *pBufferOut)
ma_context::waveOutGetDevCapsA
ma_proc waveOutGetDevCapsA
Definition: miniaudio.h:2004
ma_channel_map_valid
ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS])
lpData
HPSTR lpData
Definition: snd_win.c:57
ma_context::DirectSoundCaptureCreate
ma_proc DirectSoundCaptureCreate
Definition: miniaudio.h:1995
DR_WAVE_FORMAT_PCM
#define DR_WAVE_FORMAT_PCM
Definition: dr_wav.h:188
ma_format_converter_config::streamFormatIn
ma_stream_format streamFormatIn
Definition: miniaudio.h:815
ma_uintptr
uintptr_t ma_uintptr
Definition: miniaudio.h:526
first
GLint first
Definition: qgl_win.c:128
ma_src_algorithm_none
@ ma_src_algorithm_none
Definition: miniaudio.h:881
ma_format_converter_config_init_new
ma_format_converter_config ma_format_converter_config_init_new(void)
drwav_int32
int32_t drwav_int32
Definition: dr_wav.h:173
ma_context_config::jack
struct ma_context_config::@34 jack
MA_ACCESS_DENIED
#define MA_ACCESS_DENIED
Definition: miniaudio.h:680
MA_SAMPLE_RATE_24000
#define MA_SAMPLE_RATE_24000
Definition: miniaudio.h:721
MA_CHANNEL_BACK_CENTER
#define MA_CHANNEL_BACK_CENTER
Definition: miniaudio.h:625
ma_pcm_s32_to_s16
void ma_pcm_s32_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
drflac
Definition: dr_flac.h:487
VORBIS_need_more_data
@ VORBIS_need_more_data
Definition: stb_vorbis.c:346
ma_context::jack_client_open
ma_proc jack_client_open
Definition: miniaudio.h:2144
ma_context::deviceInfoCapacity
ma_uint32 deviceInfoCapacity
Definition: miniaudio.h:1963
ma_pcm_converter_config::sampleRateOut
ma_uint32 sampleRateOut
Definition: miniaudio.h:957
drflac::channels
drflac_uint8 channels
Definition: dr_flac.h:503
ma_pcm_s16_to_s32
void ma_pcm_s16_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_decoder_config
Definition: miniaudio.h:3006
ma_pcm_rb::rb
ma_rb rb
Definition: miniaudio.h:1435
ma_device_config::deviceType
ma_device_type deviceType
Definition: miniaudio.h:1893
MA_SAMPLE_RATE_44100
#define MA_SAMPLE_RATE_44100
Definition: miniaudio.h:723
ma_thread_priority_high
@ ma_thread_priority_high
Definition: miniaudio.h:1688
ma_context::waveInGetNumDevs
ma_proc waveInGetNumDevs
Definition: miniaudio.h:2011
MA_CHANNEL_TOP_FRONT_LEFT
#define MA_CHANNEL_TOP_FRONT_LEFT
Definition: miniaudio.h:629
drmp3_config
Definition: dr_mp3.h:232
ma_device_callback_proc
void(* ma_device_callback_proc)(ma_device *pDevice, void *pOutput, const void *pInput, ma_uint32 frameCount)
Definition: miniaudio.h:1778
ma_src_config::neverConsumeEndOfInput
ma_bool32 neverConsumeEndOfInput
Definition: miniaudio.h:904
drflac_seek_to_pcm_frame
drflac_bool32 drflac_seek_to_pcm_frame(drflac *pFlac, drflac_uint64 pcmFrameIndex)
NULL
#define NULL
Definition: miniaudio.h:560
MA_SAMPLE_RATE_22050
#define MA_SAMPLE_RATE_22050
Definition: miniaudio.h:720
MA_CHANNEL_AUX_5
#define MA_CHANNEL_AUX_5
Definition: miniaudio.h:640
drwav_close
void drwav_close(drwav *pWav)
ma_backend_opensl
@ ma_backend_opensl
Definition: miniaudio.h:1676
ma_context::jackSO
ma_handle jackSO
Definition: miniaudio.h:2143
ma_decoder_get_length_in_pcm_frames
ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder *pDecoder)
ma_src_config
Definition: miniaudio.h:898
MA_CHANNEL_AUX_16
#define MA_CHANNEL_AUX_16
Definition: miniaudio.h:651
st
spawn_temp_t st
Definition: g_main.c:27
ma_standard_channel_map_microsoft
@ ma_standard_channel_map_microsoft
Definition: miniaudio.h:788
ma_decoder::onGetLengthInPCMFrames
ma_decoder_get_length_in_pcm_frames_proc onGetLengthInPCMFrames
Definition: miniaudio.h:3038
ma_format_converter_config::noSSE2
ma_bool32 noSSE2
Definition: miniaudio.h:818
ma_rb_uninit
void ma_rb_uninit(ma_rb *pRB)
drwav_bool32
drwav_uint32 drwav_bool32
Definition: dr_wav.h:179
ma_context::waveOutReset
ma_proc waveOutReset
Definition: miniaudio.h:2010
MA_FAILED_TO_CREATE_THREAD
#define MA_FAILED_TO_CREATE_THREAD
Definition: miniaudio.h:713
MA_SAMPLE_RATE_32000
#define MA_SAMPLE_RATE_32000
Definition: miniaudio.h:722
ma_decoder::onUninit
ma_decoder_uninit_proc onUninit
Definition: miniaudio.h:3037
ma_pcm_converter
struct ma_pcm_converter ma_pcm_converter
Definition: miniaudio.h:946
ma_context::jack_get_sample_rate
ma_proc jack_get_sample_rate
Definition: miniaudio.h:2150
ma_src_sinc_window_function_hann
@ ma_src_sinc_window_function_hann
Definition: miniaudio.h:887
stb_vorbis_decode_frame_pushdata
int stb_vorbis_decode_frame_pushdata(stb_vorbis *f, const unsigned char *datablock, int datablock_length_in_bytes, int *channels, float ***output, int *samples)
Definition: stb_vorbis.c:4330
ma_seek_origin_start
@ ma_seek_origin_start
Definition: miniaudio.h:2996
MA_CHANNEL_TOP_BACK_LEFT
#define MA_CHANNEL_TOP_BACK_LEFT
Definition: miniaudio.h:632
ma_decoder::onRead
ma_decoder_read_proc onRead
Definition: miniaudio.h:3023
ma_backend_sndio
@ ma_backend_sndio
Definition: miniaudio.h:1669
ma_src_config::noAVX512
ma_bool32 noAVX512
Definition: miniaudio.h:907
ma_pcm_converter_config_init
ma_pcm_converter_config ma_pcm_converter_config_init(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_pcm_converter_read_proc onRead, void *pUserData)
MA_CHANNEL_FRONT_CENTER
#define MA_CHANNEL_FRONT_CENTER
Definition: miniaudio.h:619
ma_rb::pBuffer
void * pBuffer
Definition: miniaudio.h:1460
ma_decode_memory
ma_result ma_decode_memory(const void *pData, size_t dataSize, ma_decoder_config *pConfig, ma_uint64 *pFrameCountOut, void **ppDataOut)
ma_channel_router_config::channelsOut
ma_uint32 channelsOut
Definition: miniaudio.h:847
ma_pcm_converter_init
ma_result ma_pcm_converter_init(const ma_pcm_converter_config *pConfig, ma_pcm_converter *pDSP)
stb_vorbis_info
Definition: stb_vorbis.c:114
ma_channel
ma_uint8 ma_channel
Definition: miniaudio.h:614
ma_context::jack_set_buffer_size_callback
ma_proc jack_set_buffer_size_callback
Definition: miniaudio.h:2148
ma_format_count
@ ma_format_count
Definition: miniaudio.h:774
ma_context::waveInPrepareHeader
ma_proc waveInPrepareHeader
Definition: miniaudio.h:2015
x2
GLdouble GLdouble x2
Definition: qgl_win.c:301
ma_rb::subbufferSizeInBytes
ma_uint32 subbufferSizeInBytes
Definition: miniaudio.h:1461
ma_get_backend_name
const char * ma_get_backend_name(ma_backend backend)
ma_pcm_converter_config::channelsOut
ma_uint32 channelsOut
Definition: miniaudio.h:956
MA_LOG_LEVEL_WARNING
#define MA_LOG_LEVEL_WARNING
Definition: miniaudio.h:604
x
GLint GLenum GLint x
Definition: qgl_win.c:116
ma_device_info::name
char name[256]
Definition: miniaudio.h:1867
MA_SAMPLE_RATE_192000
#define MA_SAMPLE_RATE_192000
Definition: miniaudio.h:728
ma_pcm_f32_to_s24
void ma_pcm_f32_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_pcm_converter_config::noSSE2
ma_bool32 noSSE2
Definition: miniaudio.h:964
z
GLdouble GLdouble z
Definition: qgl_win.c:283
ma_channel_router_config_init
ma_channel_router_config ma_channel_router_config_init(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, ma_channel_router_read_deinterleaved_proc onRead, void *pUserData)
ma_format_converter_read_deinterleaved_proc
ma_uint32(* ma_format_converter_read_deinterleaved_proc)(ma_format_converter *pConverter, ma_uint32 frameCount, void **ppSamplesOut, void *pUserData)
Definition: miniaudio.h:808
drmp3_config::outputChannels
drmp3_uint32 outputChannels
Definition: dr_mp3.h:234
i
int i
Definition: q_shared.c:305
ma_device_info::minChannels
ma_uint32 minChannels
Definition: miniaudio.h:1879
ma_pcm_s24_to_s32
void ma_pcm_s24_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_device_stop
ma_result ma_device_stop(ma_device *pDevice)
device
static ma_device device
Definition: snd_miniaudio.c:47
ma_device_id::nullbackend
int nullbackend
Definition: miniaudio.h:1859
ma_format_converter::useAVX2
ma_bool32 useAVX2
Definition: miniaudio.h:831
ma_scale_buffer_size
ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale)
ma_pcm_converter_config::formatIn
ma_format formatIn
Definition: miniaudio.h:951
drflac_seek_origin_start
@ drflac_seek_origin_start
Definition: dr_flac.h:227
ma_decoder_init_memory_raw
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)
ma_device_config::stopCallback
ma_stop_proc stopCallback
Definition: miniaudio.h:1900
drflac_int16
int16_t drflac_int16
Definition: dr_flac.h:135
ma_event::win32
struct ma_event::@25::@27 win32
ma_convert_frames
ma_uint64 ma_convert_frames(void *pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void *pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_uint64 frameCount)
ma_context::jack_port_register
ma_proc jack_port_register
Definition: miniaudio.h:2156
ma_rb_init
ma_result ma_rb_init(size_t bufferSizeInBytes, void *pOptionalPreallocatedBuffer, ma_rb *pRB)
ma_decoder::readPointer
ma_uint64 readPointer
Definition: miniaudio.h:3026
ma_src_config_init_new
ma_src_config ma_src_config_init_new(void)
ma_thread_priority_idle
@ ma_thread_priority_idle
Definition: miniaudio.h:1684
ma_pcm_converter_config::neverConsumeEndOfInput
ma_bool32 neverConsumeEndOfInput
Definition: miniaudio.h:963
ma_channel_router::useSSE2
ma_bool32 useSSE2
Definition: miniaudio.h:865
ma_channel_router_read_deinterleaved
ma_uint64 ma_channel_router_read_deinterleaved(ma_channel_router *pRouter, ma_uint64 frameCount, void **ppSamplesOut, void *pUserData)
ma_decoder_init_file_wav
ma_result ma_decoder_init_file_wav(const char *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_context::onDeviceMainLoop
ma_result(* onDeviceMainLoop)(ma_device *pDevice)
Definition: miniaudio.h:1979
ma_context_config::threadPriority
ma_thread_priority threadPriority
Definition: miniaudio.h:1933
ma_thread::_unused
int _unused
Definition: miniaudio.h:1712
buffer
GLenum GLfloat * buffer
Definition: qgl_win.c:151
ma_decoder_seek_proc
ma_bool32(* ma_decoder_seek_proc)(ma_decoder *pDecoder, int byteOffset, ma_seek_origin origin)
Definition: miniaudio.h:3001
ma_device_config_init
ma_device_config ma_device_config_init(ma_device_type deviceType)
MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE
#define MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE
Definition: miniaudio.h:710
drflac_close
void drflac_close(drflac *pFlac)
ma_device_config::pUserData
void * pUserData
Definition: miniaudio.h:1901
ma_context_config::pUserData
void * pUserData
Definition: miniaudio.h:1934
ma_src_config_sinc::windowFunction
ma_src_sinc_window_function windowFunction
Definition: miniaudio.h:894
ma_context_config::useVerboseDeviceEnumeration
ma_bool32 useVerboseDeviceEnumeration
Definition: miniaudio.h:1938
ma_uint8
uint8_t ma_uint8
Definition: miniaudio.h:516
ma_performance_profile
ma_performance_profile
Definition: miniaudio.h:799
ma_channel_router::isSimpleShuffle
ma_bool32 isSimpleShuffle
Definition: miniaudio.h:864
ma_device_start
ma_result ma_device_start(ma_device *pDevice)
ma_thread::pContext
ma_context * pContext
Definition: miniaudio.h:1696
ma_context::tryStartServer
ma_bool32 tryStartServer
Definition: miniaudio.h:2162
ma_dither_mode_triangle
@ ma_dither_mode_triangle
Definition: miniaudio.h:759
ma_rb
Definition: miniaudio.h:1405
ma_deinterleave_pcm_frames
void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void *pInterleavedPCMFrames, void **ppDeinterleavedPCMFrames)
ma_context::backend
ma_backend backend
Definition: miniaudio.h:1957
ma_pcm_rb_get_subbuffer_stride
ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb *pRB)
ma_context::jack_client_close
ma_proc jack_client_close
Definition: miniaudio.h:2145
MA_SIZE_MAX
#define MA_SIZE_MAX
Definition: miniaudio.h:566
ma_src_config::channels
ma_uint32 channels
Definition: miniaudio.h:902
MA_OUT_OF_MEMORY
#define MA_OUT_OF_MEMORY
Definition: miniaudio.h:679
ma_standard_channel_map_sound4
@ ma_standard_channel_map_sound4
Definition: miniaudio.h:793
ma_pcm_s24_to_s16
void ma_pcm_s24_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_decoder_config_init
ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
ma_device_id::dsound
ma_uint8 dsound[16]
Definition: miniaudio.h:1823
ma_device_info::minSampleRate
ma_uint32 minSampleRate
Definition: miniaudio.h:1881
MA_FAILED_TO_START_BACKEND_DEVICE
#define MA_FAILED_TO_START_BACKEND_DEVICE
Definition: miniaudio.h:708
tv
float * tv(float x, float y, float z)
Definition: g_utils.c:266
MA_CHANNEL_AUX_27
#define MA_CHANNEL_AUX_27
Definition: miniaudio.h:662
ma_stream_format
ma_stream_format
Definition: miniaudio.h:744
MA_CHANNEL_AUX_26
#define MA_CHANNEL_AUX_26
Definition: miniaudio.h:661
MA_ALIGNED_STRUCT
#define MA_ALIGNED_STRUCT(alignment)
Definition: miniaudio.h:594
ma_src_config_sinc
Definition: miniaudio.h:892
ma_device_config::noMMap
ma_bool32 noMMap
Definition: miniaudio.h:1921
ma_device_config::dataCallback
ma_device_callback_proc dataCallback
Definition: miniaudio.h:1899
ma_pcm_f32_to_u8
void ma_pcm_f32_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_sine_wave::time
double time
Definition: miniaudio.h:3114
drmp3
Definition: dr_mp3.h:238
ma_context::hWinMM
ma_handle hWinMM
Definition: miniaudio.h:2002
ma_sine_wave::periodsPerSecond
double periodsPerSecond
Definition: miniaudio.h:3112
type
GLenum type
Definition: qgl_win.c:72
ma_decoder::pData
const ma_uint8 * pData
Definition: miniaudio.h:3042
ma_context::hOle32DLL
ma_handle hOle32DLL
Definition: miniaudio.h:2284
ma_src_config_init
ma_src_config ma_src_config_init(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint32 channels, ma_src_read_deinterleaved_proc onReadDeinterleaved, void *pUserData)
ma_src_config::onReadDeinterleaved
ma_src_read_deinterleaved_proc onReadDeinterleaved
Definition: miniaudio.h:909
ma_src_algorithm
ma_src_algorithm
Definition: miniaudio.h:877
ma_backend_winmm
@ ma_backend_winmm
Definition: miniaudio.h:1667
ma_device_init_ex
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)
MA_MAX_CHANNELS
#define MA_MAX_CHANNELS
Definition: miniaudio.h:735
ma_channel_router_config::channelMapOut
ma_channel channelMapOut[MA_MAX_CHANNELS]
Definition: miniaudio.h:849
ma_decoder_init_mp3
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)
ma_context::dsound
struct ma_context::@35::@40 dsound
drwav::sampleRate
drwav_uint32 sampleRate
Definition: dr_wav.h:407
ma_src_algorithm_sinc
@ ma_src_algorithm_sinc
Definition: miniaudio.h:880
ma_context::jack_port_get_buffer
ma_proc jack_port_get_buffer
Definition: miniaudio.h:2158
ma_src_config_sinc::windowWidth
ma_uint32 windowWidth
Definition: miniaudio.h:895
ma_decoder::outputFormat
ma_format outputFormat
Definition: miniaudio.h:3031
ma_decoder::onSeekToPCMFrame
ma_decoder_seek_to_pcm_frame_proc onSeekToPCMFrame
Definition: miniaudio.h:3036
DR_WAVE_FORMAT_DVI_ADPCM
#define DR_WAVE_FORMAT_DVI_ADPCM
Definition: dr_wav.h:193
ma_decoder_config::channelMixMode
ma_channel_mix_mode channelMixMode
Definition: miniaudio.h:3012
ma_pcm_converter_config
Definition: miniaudio.h:949
stb_vorbis_close
void stb_vorbis_close(stb_vorbis *f)
Definition: stb_vorbis.c:4166
ma_context_config::tryStartServer
ma_bool32 tryStartServer
Definition: miniaudio.h:1949
ma_performance_profile_conservative
@ ma_performance_profile_conservative
Definition: miniaudio.h:802
ma_channel_router::useAVX2
ma_bool32 useAVX2
Definition: miniaudio.h:866
MA_CHANNEL_AUX_7
#define MA_CHANNEL_AUX_7
Definition: miniaudio.h:642
ma_decoder_init_memory_wav
ma_result ma_decoder_init_memory_wav(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_rb_seek_write
ma_result ma_rb_seek_write(ma_rb *pRB, size_t offsetInBytes)
ma_rb_acquire_write
ma_result ma_rb_acquire_write(ma_rb *pRB, size_t *pSizeInBytes, void **ppBufferOut)
MA_CHANNEL_AUX_2
#define MA_CHANNEL_AUX_2
Definition: miniaudio.h:637
ma_format_converter_config_init_deinterleaved
ma_format_converter_config ma_format_converter_config_init_deinterleaved(ma_format formatIn, ma_format formatOut, ma_uint32 channels, ma_format_converter_read_deinterleaved_proc onReadDeinterleaved, void *pUserData)
ma_int16
int16_t ma_int16
Definition: miniaudio.h:517
ma_channel_router_config::mixingMode
ma_channel_mix_mode mixingMode
Definition: miniaudio.h:850
ma_decoder_init_memory
ma_result ma_decoder_init_memory(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_pcm_rb::format
ma_format format
Definition: miniaudio.h:1436
ma_pcm_converter_set_sample_rate
ma_result ma_pcm_converter_set_sample_rate(ma_pcm_converter *pDSP, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
ma_decoder::onSeek
ma_decoder_seek_proc onSeek
Definition: miniaudio.h:3024
MA_CHANNEL_AUX_22
#define MA_CHANNEL_AUX_22
Definition: miniaudio.h:657
drmp3_uninit
void drmp3_uninit(drmp3 *pMP3)
j
GLint j
Definition: qgl_win.c:150
MA_CHANNEL_BACK_LEFT
#define MA_CHANNEL_BACK_LEFT
Definition: miniaudio.h:621
ma_bool8
ma_uint8 ma_bool8
Definition: miniaudio.h:545
ma_context::waveOutGetNumDevs
ma_proc waveOutGetNumDevs
Definition: miniaudio.h:2003
ma_rb_get_subbuffer_stride
size_t ma_rb_get_subbuffer_stride(ma_rb *pRB)
ma_pcm_converter_config::ditherMode
ma_dither_mode ditherMode
Definition: miniaudio.h:960
ma_decoder_read_pcm_frames
ma_uint64 ma_decoder_read_pcm_frames(ma_decoder *pDecoder, void *pFramesOut, ma_uint64 frameCount)
DR_WAVE_FORMAT_ALAW
#define DR_WAVE_FORMAT_ALAW
Definition: dr_wav.h:191
MA_CHANNEL_TOP_FRONT_RIGHT
#define MA_CHANNEL_TOP_FRONT_RIGHT
Definition: miniaudio.h:631
ma_decoder_init_raw
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)
ma_mutex::win32
struct ma_mutex::@22::@24 win32
ma_IMMNotificationClient::counter
ma_uint32 counter
Definition: miniaudio.h:1657
ma_event::_unused
int _unused
Definition: miniaudio.h:1758
ma_get_default_buffer_size_in_frames
ma_uint32 ma_get_default_buffer_size_in_frames(ma_performance_profile performanceProfile, ma_uint32 sampleRate)
ma_thread_priority_lowest
@ ma_thread_priority_lowest
Definition: miniaudio.h:1685
ma_format_f32
@ ma_format_f32
Definition: miniaudio.h:773
ma_pcm_converter_config::allowDynamicSampleRate
ma_bool32 allowDynamicSampleRate
Definition: miniaudio.h:962
ma_pcm_u8_to_f32
void ma_pcm_u8_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_CHANNEL_FRONT_LEFT
#define MA_CHANNEL_FRONT_LEFT
Definition: miniaudio.h:617
ma_format_converter
Definition: miniaudio.h:827
ma_standard_channel_map
ma_standard_channel_map
Definition: miniaudio.h:786
MA_CHANNEL_AUX_31
#define MA_CHANNEL_AUX_31
Definition: miniaudio.h:666
ma_format_converter::onInterleavePCM
void(* onInterleavePCM)(void *dst, const void **src, ma_uint64 frameCount, ma_uint32 channels)
Definition: miniaudio.h:835
MA_CHANNEL_POSITION_COUNT
#define MA_CHANNEL_POSITION_COUNT
Definition: miniaudio.h:669
MA_CHANNEL_AUX_14
#define MA_CHANNEL_AUX_14
Definition: miniaudio.h:649
MA_CHANNEL_AUX_18
#define MA_CHANNEL_AUX_18
Definition: miniaudio.h:653
ma_stream_layout_deinterleaved
@ ma_stream_layout_deinterleaved
Definition: miniaudio.h:752
ma_thread_priority_realtime
@ ma_thread_priority_realtime
Definition: miniaudio.h:1690
ma_channel_mix_mode
ma_channel_mix_mode
Definition: miniaudio.h:777
ma_backend
ma_backend
Definition: miniaudio.h:1663
ma_pcm_converter_set_input_sample_rate
ma_result ma_pcm_converter_set_input_sample_rate(ma_pcm_converter *pDSP, ma_uint32 sampleRateOut)
ma_convert_frames_ex
ma_uint64 ma_convert_frames_ex(void *pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], const void *pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint64 frameCount)
ma_standard_channel_map_sndio
@ ma_standard_channel_map_sndio
Definition: miniaudio.h:794
ma_share_mode_shared
@ ma_share_mode_shared
Definition: miniaudio.h:1813
MA_SAMPLE_RATE_176400
#define MA_SAMPLE_RATE_176400
Definition: miniaudio.h:727
ma_backend_aaudio
@ ma_backend_aaudio
Definition: miniaudio.h:1675
ma_blend_f32
void ma_blend_f32(float *pOut, float *pInA, float *pInB, float factor, ma_uint32 channels)
ma_device_config::performanceProfile
ma_performance_profile performanceProfile
Definition: miniaudio.h:1898
ma_standard_channel_map_flac
@ ma_standard_channel_map_flac
Definition: miniaudio.h:791
drmp3::sampleRate
drmp3_uint32 sampleRate
Definition: dr_mp3.h:243
ma_decoder::outputChannelMap
ma_channel outputChannelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:3034
ma_pcm_rb_seek_read
ma_result ma_pcm_rb_seek_read(ma_pcm_rb *pRB, ma_uint32 offsetInFrames)
ma_mutex_unlock
void ma_mutex_unlock(ma_mutex *pMutex)
ma_standard_channel_map_webaudio
@ ma_standard_channel_map_webaudio
Definition: miniaudio.h:795
MA_CHANNEL_LEFT
#define MA_CHANNEL_LEFT
Definition: miniaudio.h:667
ma_device_uninit
void ma_device_uninit(ma_device *pDevice)
ma_src_config::sinc
ma_src_config_sinc sinc
Definition: miniaudio.h:913
ma_format_unknown
@ ma_format_unknown
Definition: miniaudio.h:768
ma_stream_format_pcm
@ ma_stream_format_pcm
Definition: miniaudio.h:746
ma_pcm_converter_config::pUserData
void * pUserData
Definition: miniaudio.h:969
ma_backend_pulseaudio
@ ma_backend_pulseaudio
Definition: miniaudio.h:1672
ma_decoder::dataSize
size_t dataSize
Definition: miniaudio.h:3043
ma_get_bytes_per_frame
static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels)
Definition: miniaudio.h:1506
ma_context_get_devices
ma_result ma_context_get_devices(ma_context *pContext, ma_device_info **ppPlaybackDeviceInfos, ma_uint32 *pPlaybackDeviceCount, ma_device_info **ppCaptureDeviceInfos, ma_uint32 *pCaptureDeviceCount)
ma_sine_wave_read_f32
ma_uint64 ma_sine_wave_read_f32(ma_sine_wave *pSineWave, ma_uint64 count, float *pSamples)
r
GLdouble GLdouble r
Definition: qgl_win.c:336
ma_decoder::memory
struct ma_decoder::@46 memory
ma_sine_wave_init
ma_result ma_sine_wave_init(double amplitude, double period, ma_uint32 sampleRate, ma_sine_wave *pSineWave)
ma_device_info::maxChannels
ma_uint32 maxChannels
Definition: miniaudio.h:1880
ma_context::waveOutWrite
ma_proc waveOutWrite
Definition: miniaudio.h:2009
ma_pcm_rb_pointer_disance
ma_int32 ma_pcm_rb_pointer_disance(ma_pcm_rb *pRB)
ma_device_set_stop_callback
void ma_device_set_stop_callback(ma_device *pDevice, ma_stop_proc proc)
ma_pcm_converter_config_init_ex
ma_pcm_converter_config ma_pcm_converter_config_init_ex(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], ma_pcm_converter_read_proc onRead, void *pUserData)
ma_stream_layout
ma_stream_layout
Definition: miniaudio.h:749
internalFormat
GLint GLenum internalFormat
Definition: qgl_win.c:116
ma_device_info::id
ma_device_id id
Definition: miniaudio.h:1866
MA_MAX_PCM_SAMPLE_SIZE_IN_BYTES
#define MA_MAX_PCM_SAMPLE_SIZE_IN_BYTES
Definition: miniaudio.h:733
ma_rb_acquire_read
ma_result ma_rb_acquire_read(ma_rb *pRB, size_t *pSizeInBytes, void **ppBufferOut)
ma_format_converter::useSSE2
ma_bool32 useSSE2
Definition: miniaudio.h:830
MA_CHANNEL_SIDE_RIGHT
#define MA_CHANNEL_SIDE_RIGHT
Definition: miniaudio.h:627
ma_proc
void(* ma_proc)(void)
Definition: miniaudio.h:552
MA_CHANNEL_AUX_12
#define MA_CHANNEL_AUX_12
Definition: miniaudio.h:647
ma_format_converter::useAVX512
ma_bool32 useAVX512
Definition: miniaudio.h:832
ma_context::onDeviceStop
ma_result(* onDeviceStop)(ma_device *pDevice)
Definition: miniaudio.h:1976
ma_context_config::pulse
struct ma_context_config::@33 pulse
ma_context::GetDesktopWindow
ma_proc GetDesktopWindow
Definition: miniaudio.h:2294
MA_CHANNEL_AUX_4
#define MA_CHANNEL_AUX_4
Definition: miniaudio.h:639
ma_mutex::pContext
ma_context * pContext
Definition: miniaudio.h:1718
ma_context::onDeviceIDEqual
ma_bool32(* onDeviceIDEqual)(ma_context *pContext, const ma_device_id *pID0, const ma_device_id *pID1)
Definition: miniaudio.h:1970
ma_int64
int64_t ma_int64
Definition: miniaudio.h:521
ma_device_info::formatCount
ma_uint32 formatCount
Definition: miniaudio.h:1877
ma_context::wasapi
struct ma_context::@35::@39 wasapi
ma_channel_router_config::noAVX2
ma_bool32 noAVX2
Definition: miniaudio.h:853
ma_get_bytes_per_sample
ma_uint32 ma_get_bytes_per_sample(ma_format format)
BOOL
CONST COLORREF COLORREF BOOL
Definition: qgl_win.c:60
params
const GLfloat * params
Definition: qgl_win.c:155
param
GLfloat param
Definition: qgl_win.c:154
drflac_read_pcm_frames_f32
drflac_uint64 drflac_read_pcm_frames_f32(drflac *pFlac, drflac_uint64 framesToRead, float *pBufferOut)
ma_backend_audio4
@ ma_backend_audio4
Definition: miniaudio.h:1670
ma_src_config::noAVX2
ma_bool32 noAVX2
Definition: miniaudio.h:906
ma_backend_webaudio
@ ma_backend_webaudio
Definition: miniaudio.h:1677
ma_pcm_converter_config::onRead
ma_pcm_converter_read_proc onRead
Definition: miniaudio.h:968
UINT
CONST PIXELFORMATDESCRIPTOR UINT
Definition: qgl_win.c:35
ma_context::CoTaskMemFree
ma_proc CoTaskMemFree
Definition: miniaudio.h:2288
ma_decoder_init_vorbis
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)
ma_device_config::pulse
struct ma_device_config::@31 pulse
ma_handle
void * ma_handle
Definition: miniaudio.h:550
ma_IMMNotificationClient::pDevice
ma_device * pDevice
Definition: miniaudio.h:1658
ma_context::RegQueryValueExA
ma_proc RegQueryValueExA
Definition: miniaudio.h:2299
t
GLdouble t
Definition: qgl_win.c:328
ma_channel_mix_mode_planar_blend
@ ma_channel_mix_mode_planar_blend
Definition: miniaudio.h:782
ma_context::pDeviceInfos
ma_device_info * pDeviceInfos
Definition: miniaudio.h:1966
ma_format_converter::config
ma_format_converter_config config
Definition: miniaudio.h:829
ma_format_s24
@ ma_format_s24
Definition: miniaudio.h:771
ma_get_format_name
const char * ma_get_format_name(ma_format format)
ma_context_config::pApplicationName
const char * pApplicationName
Definition: miniaudio.h:1942
ma_channel_router_config::noAVX512
ma_bool32 noAVX512
Definition: miniaudio.h:854
ma_dither_mode_rectangle
@ ma_dither_mode_rectangle
Definition: miniaudio.h:758
ma_decoder_init
ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void *pUserData, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
MA_CHANNEL_AUX_25
#define MA_CHANNEL_AUX_25
Definition: miniaudio.h:660
ma_context::jack
struct ma_context::@35::@42 jack
ma_ptr
void * ma_ptr
Definition: miniaudio.h:551
ma_format_converter_config::pUserData
void * pUserData
Definition: miniaudio.h:824
ma_pcm_s32_to_s24
void ma_pcm_s32_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_pcm_converter_config::formatOut
ma_format formatOut
Definition: miniaudio.h:955
ma_sine_wave::delta
double delta
Definition: miniaudio.h:3113
drflac_bool32
drflac_uint32 drflac_bool32
Definition: dr_flac.h:143
ma_decoder_config::channelMap
ma_channel channelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:3011
ma_device_info::maxSampleRate
ma_uint32 maxSampleRate
Definition: miniaudio.h:1882
ma_context::threadPriority
ma_thread_priority threadPriority
Definition: miniaudio.h:1959
ma_bool32
ma_uint32 ma_bool32
Definition: miniaudio.h:546
ma_free
void ma_free(void *p)
ma_context::jack_port_name
ma_proc jack_port_name
Definition: miniaudio.h:2157
ma_src_config::algorithm
ma_src_algorithm algorithm
Definition: miniaudio.h:903
ma_decoder_config::ditherMode
ma_dither_mode ditherMode
Definition: miniaudio.h:3013
drwav_seek_origin_start
@ drwav_seek_origin_start
Definition: dr_wav.h:206
ma_thread_priority_highest
@ ma_thread_priority_highest
Definition: miniaudio.h:1689
ma_sine_wave
Definition: miniaudio.h:3105
ma_context::waveInOpen
ma_proc waveInOpen
Definition: miniaudio.h:2013
ma_rb_init_ex
ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void *pOptionalPreallocatedBuffer, ma_rb *pRB)
ma_channel_router::isPassthrough
ma_bool32 isPassthrough
Definition: miniaudio.h:863
ma_device_config::pStreamNamePlayback
const char * pStreamNamePlayback
Definition: miniaudio.h:1925
drmp3_seek_to_pcm_frame
drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3 *pMP3, drmp3_uint64 frameIndex)
ma_context::null_backend
struct ma_context::@35::@43 null_backend
ma_context::onGetDeviceInfo
ma_result(* onGetDeviceInfo)(ma_context *pContext, ma_device_type deviceType, const ma_device_id *pDeviceID, ma_share_mode shareMode, ma_device_info *pDeviceInfo)
Definition: miniaudio.h:1972
ma_pcm_converter_config::channelMixMode
ma_channel_mix_mode channelMixMode
Definition: miniaudio.h:959
DR_WAVE_FORMAT_MULAW
#define DR_WAVE_FORMAT_MULAW
Definition: dr_wav.h:192
ma_device_init
ma_result ma_device_init(ma_context *pContext, const ma_device_config *pConfig, ma_device *pDevice)
ma_pcm_s16_to_u8
void ma_pcm_s16_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_DEVICE_NOT_INITIALIZED
#define MA_DEVICE_NOT_INITIALIZED
Definition: miniaudio.h:695
MA_CHANNEL_FRONT_RIGHT
#define MA_CHANNEL_FRONT_RIGHT
Definition: miniaudio.h:618
drwav_read_pcm_frames_s16
drwav_uint64 drwav_read_pcm_frames_s16(drwav *pWav, drwav_uint64 framesToRead, drwav_int16 *pBufferOut)
ma_calculate_buffer_size_in_milliseconds_from_frames
ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
ma_context::RegCloseKey
ma_proc RegCloseKey
Definition: miniaudio.h:2298
ma_format_converter_read_proc
ma_uint32(* ma_format_converter_read_proc)(ma_format_converter *pConverter, ma_uint32 frameCount, void *pFramesOut, void *pUserData)
Definition: miniaudio.h:807
ma_stop_proc
void(* ma_stop_proc)(ma_device *pDevice)
Definition: miniaudio.h:1788
ma_format_converter_config::channels
ma_uint32 channels
Definition: miniaudio.h:814
ma_decoder::internalFormat
ma_format internalFormat
Definition: miniaudio.h:3027
ma_format_converter_init
ma_result ma_format_converter_init(const ma_format_converter_config *pConfig, ma_format_converter *pConverter)
ma_stream_layout_interleaved
@ ma_stream_layout_interleaved
Definition: miniaudio.h:751
ma_format_converter_config::ditherMode
ma_dither_mode ditherMode
Definition: miniaudio.h:817
MA_ALIGN
#define MA_ALIGN(alignment)
Definition: miniaudio.h:585
ma_malloc
void * ma_malloc(size_t sz)
ma_src_config::pUserData
void * pUserData
Definition: miniaudio.h:910
ma_channel_map_copy
void ma_channel_map_copy(ma_channel *pOut, const ma_channel *pIn, ma_uint32 channels)
error
static int error(vorb *f, enum STBVorbisError e)
Definition: stb_vorbis.c:865
ma_decode_file
ma_result ma_decode_file(const char *pFilePath, ma_decoder_config *pConfig, ma_uint64 *pFrameCountOut, void **ppDataOut)
MA_ERROR
#define MA_ERROR
Definition: miniaudio.h:676
ma_context_config_init
ma_context_config ma_context_config_init(void)
MA_SAMPLE_RATE_11025
#define MA_SAMPLE_RATE_11025
Definition: miniaudio.h:718
ma_pcm_s32_to_f32
void ma_pcm_s32_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_src_sinc_window_function_default
@ ma_src_sinc_window_function_default
Definition: miniaudio.h:889
ma_format_converter::onConvertPCM
void(* onConvertPCM)(void *dst, const void *src, ma_uint64 count, ma_dither_mode ditherMode)
Definition: miniaudio.h:834
ma_int32
int32_t ma_int32
Definition: miniaudio.h:519
ma_IMMNotificationClient
Definition: miniaudio.h:1654
ma_device_info
Definition: miniaudio.h:1863
MA_NO_DEVICE
#define MA_NO_DEVICE
Definition: miniaudio.h:689
ma_context::hUser32DLL
ma_handle hUser32DLL
Definition: miniaudio.h:2292
MA_SAMPLE_RATE_96000
#define MA_SAMPLE_RATE_96000
Definition: miniaudio.h:726
ma_format_converter_config::streamFormatOut
ma_stream_format streamFormatOut
Definition: miniaudio.h:816
MA_TOO_LARGE
#define MA_TOO_LARGE
Definition: miniaudio.h:681
ma_thread::win32
struct ma_thread::@19::@21 win32
ma_standard_channel_map_rfc3551
@ ma_standard_channel_map_rfc3551
Definition: miniaudio.h:790
ma_decoder_config::src
union ma_decoder_config::@45 src
ma_device_type
ma_device_type
Definition: miniaudio.h:1804
MA_FAILED_TO_READ_DATA_FROM_DEVICE
#define MA_FAILED_TO_READ_DATA_FROM_DEVICE
Definition: miniaudio.h:704
ma_context::waveInReset
ma_proc waveInReset
Definition: miniaudio.h:2019
ma_context::jack_deactivate
ma_proc jack_deactivate
Definition: miniaudio.h:2154
ma_context::hDSoundDLL
ma_handle hDSoundDLL
Definition: miniaudio.h:1992
ma_device_config::capture
struct ma_device_config::@29 capture
MA_CHANNEL_TOP_BACK_CENTER
#define MA_CHANNEL_TOP_BACK_CENTER
Definition: miniaudio.h:633
ma_device_id
Definition: miniaudio.h:1817
MA_DEVICE_BUSY
#define MA_DEVICE_BUSY
Definition: miniaudio.h:694
stb_vorbis_get_info
stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f)
Definition: stb_vorbis.c:4200
ma_pcm_converter_set_output_sample_rate
ma_result ma_pcm_converter_set_output_sample_rate(ma_pcm_converter *pDSP, ma_uint32 sampleRateOut)
ma_decoder_init_memory_flac
ma_result ma_decoder_init_memory_flac(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_pcm_rb_init_ex
ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void *pOptionalPreallocatedBuffer, ma_pcm_rb *pRB)
ma_sine_wave::amplitude
double amplitude
Definition: miniaudio.h:3111
ma_pcm_s24_to_u8
void ma_pcm_s24_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_CHANNEL_AUX_20
#define MA_CHANNEL_AUX_20
Definition: miniaudio.h:655
MA_CHANNEL_AUX_8
#define MA_CHANNEL_AUX_8
Definition: miniaudio.h:643
channels
channel_t channels[MAX_CHANNELS]
Definition: snd_dma.c:42
ma_pcm_converter_config::noAVX2
ma_bool32 noAVX2
Definition: miniaudio.h:965
ma_standard_channel_map_default
@ ma_standard_channel_map_default
Definition: miniaudio.h:796
ma_channel_mix_mode_rectangular
@ ma_channel_mix_mode_rectangular
Definition: miniaudio.h:779
ma_channel_mix_mode_default
@ ma_channel_mix_mode_default
Definition: miniaudio.h:783
ma_mutex_lock
void ma_mutex_lock(ma_mutex *pMutex)
MA_FAILED_TO_CREATE_MUTEX
#define MA_FAILED_TO_CREATE_MUTEX
Definition: miniaudio.h:711
ma_dither_mode_none
@ ma_dither_mode_none
Definition: miniaudio.h:757
ma_format_converter_read
ma_uint64 ma_format_converter_read(ma_format_converter *pConverter, ma_uint64 frameCount, void *pFramesOut, void *pUserData)
drwav_read_pcm_frames_s32
drwav_uint64 drwav_read_pcm_frames_s32(drwav *pWav, drwav_uint64 framesToRead, drwav_int32 *pBufferOut)
ma_decoder
Definition: miniaudio.h:3021
ma_backend_wasapi
@ ma_backend_wasapi
Definition: miniaudio.h:1665
ma_context::waveInGetDevCapsA
ma_proc waveInGetDevCapsA
Definition: miniaudio.h:2012
ma_format_s16
@ ma_format_s16
Definition: miniaudio.h:770
MA_INVALID_OPERATION
#define MA_INVALID_OPERATION
Definition: miniaudio.h:678
ma_src_config::noSSE2
ma_bool32 noSSE2
Definition: miniaudio.h:905
alpha
GLfloat GLfloat GLfloat alpha
Definition: qgl_win.c:74
ma_decoder_config::format
ma_format format
Definition: miniaudio.h:3008
ma_context::CoUninitialize
ma_proc CoUninitialize
Definition: miniaudio.h:2286
MA_SUCCESS
#define MA_SUCCESS
Definition: miniaudio.h:673
ma_decoder_init_file
ma_result ma_decoder_init_file(const char *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
stb_vorbis
Definition: stb_vorbis.c:744
ma_channel_router::shuffleTable
ma_uint8 shuffleTable[MA_MAX_CHANNELS]
Definition: miniaudio.h:869
ma_context::CoCreateInstance
ma_proc CoCreateInstance
Definition: miniaudio.h:2287
ma_log_level_to_string
const char * ma_log_level_to_string(ma_uint32 logLevel)
ma_device_info::formats
ma_format formats[ma_format_count]
Definition: miniaudio.h:1878
ma_device_type_playback
@ ma_device_type_playback
Definition: miniaudio.h:1806
ma_pcm_f32_to_s32
void ma_pcm_f32_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_device_config::bufferSizeInFrames
ma_uint32 bufferSizeInFrames
Definition: miniaudio.h:1895
ma_context::captureDeviceInfoCount
ma_uint32 captureDeviceInfoCount
Definition: miniaudio.h:1965
ma_pcm_converter_config::noNEON
ma_bool32 noNEON
Definition: miniaudio.h:967
ma_decoder::internalChannels
ma_uint32 internalChannels
Definition: miniaudio.h:3028
ma_format
ma_format
Definition: miniaudio.h:762
MA_LOG_LEVEL_ERROR
#define MA_LOG_LEVEL_ERROR
Definition: miniaudio.h:605
ma_channel_router::useAVX512
ma_bool32 useAVX512
Definition: miniaudio.h:867
ma_context::deviceEnumLock
ma_mutex deviceEnumLock
Definition: miniaudio.h:1961
ma_rb_pointer_distance
ma_int32 ma_rb_pointer_distance(ma_rb *pRB)
MA_CHANNEL_AUX_13
#define MA_CHANNEL_AUX_13
Definition: miniaudio.h:648
ma_pcm_u8_to_s32
void ma_pcm_u8_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_device_config::alsa
struct ma_device_config::@30 alsa
name
cvar_t * name
Definition: cl_main.c:79
ma_src_config::noNEON
ma_bool32 noNEON
Definition: miniaudio.h:908
ma_context::onDeviceInit
ma_result(* onDeviceInit)(ma_context *pContext, const ma_device_config *pConfig, ma_device *pDevice)
Definition: miniaudio.h:1973
ma_src_read_deinterleaved
ma_uint64 ma_src_read_deinterleaved(ma_src *pSRC, ma_uint64 frameCount, void **ppSamplesOut, void *pUserData)
ma_event
Definition: miniaudio.h:1738
ma_context::jack_activate
ma_proc jack_activate
Definition: miniaudio.h:2153
ma_rb::ownsBuffer
ma_bool32 ownsBuffer
Definition: miniaudio.h:1466
ma_timer
Definition: miniaudio.h:1885
MA_INVALID_DEVICE_CONFIG
#define MA_INVALID_DEVICE_CONFIG
Definition: miniaudio.h:691
drflac_read_pcm_frames_s32
drflac_uint64 drflac_read_pcm_frames_s32(drflac *pFlac, drflac_uint64 framesToRead, drflac_int32 *pBufferOut)
ma_uint32
uint32_t ma_uint32
Definition: miniaudio.h:520
ma_context::jack_connect
ma_proc jack_connect
Definition: miniaudio.h:2155
ma_context::logCallback
ma_log_proc logCallback
Definition: miniaudio.h:1958
ma_channel_router::config
ma_channel_router_config config
Definition: miniaudio.h:862
ma_rb_get_subbuffer_offset
size_t ma_rb_get_subbuffer_offset(ma_rb *pRB, size_t subbufferIndex)
s
static fixed16_t s
Definition: r_scan.c:30
MA_FAILED_TO_SEND_DATA_TO_DEVICE
#define MA_FAILED_TO_SEND_DATA_TO_DEVICE
Definition: miniaudio.h:706
ma_format_s32
@ ma_format_s32
Definition: miniaudio.h:772
MA_SIMD_ALIGNMENT
#define MA_SIMD_ALIGNMENT
Definition: miniaudio.h:598
y
GLint y
Definition: qgl_win.c:115
ma_channel_router_init
ma_result ma_channel_router_init(const ma_channel_router_config *pConfig, ma_channel_router *pRouter)
ma_channel_router_read_deinterleaved_proc
ma_uint32(* ma_channel_router_read_deinterleaved_proc)(ma_channel_router *pRouter, ma_uint32 frameCount, void **ppSamplesOut, void *pUserData)
Definition: miniaudio.h:842
MA_SAMPLE_RATE_384000
#define MA_SAMPLE_RATE_384000
Definition: miniaudio.h:730
drmp3_config::outputSampleRate
drmp3_uint32 outputSampleRate
Definition: dr_mp3.h:235
MA_CHANNEL_TOP_BACK_RIGHT
#define MA_CHANNEL_TOP_BACK_RIGHT
Definition: miniaudio.h:634
ma_pcm_converter_config::channelsIn
ma_uint32 channelsIn
Definition: miniaudio.h:952
ma_interleave_pcm_frames
void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void **ppDeinterleavedPCMFrames, void *pInterleavedPCMFrames)
drwav::channels
drwav_uint16 channels
Definition: dr_wav.h:410
MA_MAX_SAMPLE_RATE
#define MA_MAX_SAMPLE_RATE
Definition: miniaudio.h:737
ma_context::onDeviceWrite
ma_result(* onDeviceWrite)(ma_device *pDevice, const void *pPCMFrames, ma_uint32 frameCount)
Definition: miniaudio.h:1977
drwav_open
drwav * drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void *pUserData)
ma_context::jack_set_process_callback
ma_proc jack_set_process_callback
Definition: miniaudio.h:2147
ma_backend_oss
@ ma_backend_oss
Definition: miniaudio.h:1671
MA_CHANNEL_AUX_11
#define MA_CHANNEL_AUX_11
Definition: miniaudio.h:646
MA_SRC_SINC_DEFAULT_WINDOW_WIDTH
#define MA_SRC_SINC_DEFAULT_WINDOW_WIDTH
Definition: miniaudio.h:740
ma_pcm_u8_to_s24
void ma_pcm_u8_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
stb_vorbis_info::sample_rate
unsigned int sample_rate
Definition: stb_vorbis.c:116
ma_decoder::currentReadPos
size_t currentReadPos
Definition: miniaudio.h:3044
MA_NO_BACKEND
#define MA_NO_BACKEND
Definition: miniaudio.h:688
ma_zero_pcm_frames
void ma_zero_pcm_frames(void *p, ma_uint32 frameCount, ma_format format, ma_uint32 channels)
ma_context_config::logCallback
ma_log_proc logCallback
Definition: miniaudio.h:1932
ma_context_config::pServerName
const char * pServerName
Definition: miniaudio.h:1943
ma_context::onDeviceStart
ma_result(* onDeviceStart)(ma_device *pDevice)
Definition: miniaudio.h:1975
ma_share_mode_exclusive
@ ma_share_mode_exclusive
Definition: miniaudio.h:1814
rate
cvar_t * rate
Definition: cl_main.c:81
ma_rb_get_subbuffer_size
size_t ma_rb_get_subbuffer_size(ma_rb *pRB)
MA_CHANNEL_AUX_3
#define MA_CHANNEL_AUX_3
Definition: miniaudio.h:638
MA_CHANNEL_AUX_29
#define MA_CHANNEL_AUX_29
Definition: miniaudio.h:664
MA_SAMPLE_RATE_16000
#define MA_SAMPLE_RATE_16000
Definition: miniaudio.h:719
ma_aligned_malloc
void * ma_aligned_malloc(size_t sz, size_t alignment)
ma_context::waveOutOpen
ma_proc waveOutOpen
Definition: miniaudio.h:2005
ma_device_id::winmm
ma_uint32 winmm
Definition: miniaudio.h:1826
ma_format_converter_config::formatOut
ma_format formatOut
Definition: miniaudio.h:813
ma_device_is_started
ma_bool32 ma_device_is_started(ma_device *pDevice)
ma_device_config::format
ma_format format
Definition: miniaudio.h:1905
ma_pcm_rb
Definition: miniaudio.h:1433
MA_SAMPLE_RATE_48000
#define MA_SAMPLE_RATE_48000
Definition: miniaudio.h:724
ma_timer::counterD
double counterD
Definition: miniaudio.h:1888
ma_context_config::pClientName
const char * pClientName
Definition: miniaudio.h:1948
ma_decoder::internalSampleRate
ma_uint32 internalSampleRate
Definition: miniaudio.h:3029
ma_pcm_s24_to_f32
void ma_pcm_s24_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_int8
int8_t ma_int8
Definition: miniaudio.h:515
ma_format_converter_config::noAVX512
ma_bool32 noAVX512
Definition: miniaudio.h:820
ma_mutex_init
ma_result ma_mutex_init(ma_context *pContext, ma_mutex *pMutex)
ma_context::waveInAddBuffer
ma_proc waveInAddBuffer
Definition: miniaudio.h:2017
ma_context::_unused
int _unused
Definition: miniaudio.h:1986
MA_MIN_CHANNELS
#define MA_MIN_CHANNELS
Definition: miniaudio.h:734
ma_context::onDeviceRead
ma_result(* onDeviceRead)(ma_device *pDevice, void *pPCMFrames, ma_uint32 frameCount)
Definition: miniaudio.h:1978
ma_src_set_sample_rate
ma_result ma_src_set_sample_rate(ma_src *pSRC, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
ma_device_config::periods
ma_uint32 periods
Definition: miniaudio.h:1897
ma_pcm_converter_read_proc
ma_uint32(* ma_pcm_converter_read_proc)(ma_pcm_converter *pDSP, void *pSamplesOut, ma_uint32 frameCount, void *pUserData)
Definition: miniaudio.h:947
drmp3_read_pcm_frames_f32
drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3 *pMP3, drmp3_uint64 framesToRead, float *pBufferOut)
drwav
Definition: dr_wav.h:384
stb_vorbis_info::channels
int channels
Definition: stb_vorbis.c:117
MA_LOG_LEVEL_INFO
#define MA_LOG_LEVEL_INFO
Definition: miniaudio.h:603
ma_uint64
uint64_t ma_uint64
Definition: miniaudio.h:522
drmp3_bool32
drmp3_uint32 drmp3_bool32
Definition: dr_mp3.h:90
ma_aligned_free
void ma_aligned_free(void *p)
ma_rb_seek_read
ma_result ma_rb_seek_read(ma_rb *pRB, size_t offsetInBytes)
ma_mutex
Definition: miniaudio.h:1716
MA_CHANNEL_AUX_10
#define MA_CHANNEL_AUX_10
Definition: miniaudio.h:645
MA_INLINE
#define MA_INLINE
Definition: miniaudio.h:576
ma_channel_router::useNEON
ma_bool32 useNEON
Definition: miniaudio.h:868
ma_decoder_uninit_proc
ma_result(* ma_decoder_uninit_proc)(ma_decoder *pDecoder)
Definition: miniaudio.h:3003
ma_src_read_deinterleaved_proc
ma_uint32(* ma_src_read_deinterleaved_proc)(ma_src *pSRC, ma_uint32 frameCount, void **ppSamplesOut, void *pUserData)
Definition: miniaudio.h:875
MA_CHANNEL_AUX_19
#define MA_CHANNEL_AUX_19
Definition: miniaudio.h:654
MA_SAMPLE_RATE_352800
#define MA_SAMPLE_RATE_352800
Definition: miniaudio.h:729
ma_pcm_s32_to_u8
void ma_pcm_s32_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_context_config::alsa
struct ma_context_config::@32 alsa
ma_pcm_rb_get_subbuffer_offset
ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb *pRB, ma_uint32 subbufferIndex)
ma_uint16
uint16_t ma_uint16
Definition: miniaudio.h:518
ma_context::DirectSoundEnumerateA
ma_proc DirectSoundEnumerateA
Definition: miniaudio.h:1994
MA_DEVICE_UNAVAILABLE
#define MA_DEVICE_UNAVAILABLE
Definition: miniaudio.h:697
ma_decoder_uninit
ma_result ma_decoder_uninit(ma_decoder *pDecoder)
ma_decoder_init_wav
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)
ma_device_config::channels
ma_uint32 channels
Definition: miniaudio.h:1906
ma_decoder::outputChannels
ma_uint32 outputChannels
Definition: miniaudio.h:3032
MA_FORMAT_NOT_SUPPORTED
#define MA_FORMAT_NOT_SUPPORTED
Definition: miniaudio.h:685
ma_format_converter_config::noAVX2
ma_bool32 noAVX2
Definition: miniaudio.h:819
sqrt
double sqrt(double x)
bitmap
GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte * bitmap
Definition: qgl_win.c:69
ma_context::StringFromGUID2
ma_proc StringFromGUID2
Definition: miniaudio.h:2290
ma_device_id::jack
int jack
Definition: miniaudio.h:1835
ma_format_u8
@ ma_format_u8
Definition: miniaudio.h:769
MA_LOG_LEVEL_VERBOSE
#define MA_LOG_LEVEL_VERBOSE
Definition: miniaudio.h:602
ma_pcm_converter_config::sampleRateIn
ma_uint32 sampleRateIn
Definition: miniaudio.h:953
ma_decoder_config::channels
ma_uint32 channels
Definition: miniaudio.h:3009
ma_channel_router_config::noSSE2
ma_bool32 noSSE2
Definition: miniaudio.h:852
MA_CHANNEL_AUX_0
#define MA_CHANNEL_AUX_0
Definition: miniaudio.h:635
drmp3_seek_origin_start
@ drmp3_seek_origin_start
Definition: dr_mp3.h:192
MA_FAILED_TO_CREATE_EVENT
#define MA_FAILED_TO_CREATE_EVENT
Definition: miniaudio.h:712
ma_rb::encodedWriteOffset
volatile ma_uint32 encodedWriteOffset
Definition: miniaudio.h:1465
ma_pcm_rb_uninit
void ma_pcm_rb_uninit(ma_pcm_rb *pRB)
MA_TRUE
#define MA_TRUE
Definition: miniaudio.h:547
MA_CHANNEL_AUX_15
#define MA_CHANNEL_AUX_15
Definition: miniaudio.h:650
drwav_int16
int16_t drwav_int16
Definition: dr_wav.h:171
ma_context::CoInitializeEx
ma_proc CoInitializeEx
Definition: miniaudio.h:2285
ma_share_mode
ma_share_mode
Definition: miniaudio.h:1811
ma_channel_router_config
Definition: miniaudio.h:844
ma_context::onEnumDevices
ma_result(* onEnumDevices)(ma_context *pContext, ma_enum_devices_callback_proc callback, void *pUserData)
Definition: miniaudio.h:1971
ma_device
struct ma_device ma_device
Definition: miniaudio.h:612
ma_decoder::internalChannelMap
ma_channel internalChannelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:3030
drflac_int32
int32_t drflac_int32
Definition: dr_flac.h:137
ma_thread_priority_normal
@ ma_thread_priority_normal
Definition: miniaudio.h:1687
MA_SRC_SINC_MAX_WINDOW_WIDTH
#define MA_SRC_SINC_MAX_WINDOW_WIDTH
Definition: miniaudio.h:739
MA_FAILED_TO_INIT_BACKEND
#define MA_FAILED_TO_INIT_BACKEND
Definition: miniaudio.h:702
ma_context
Definition: miniaudio.h:1955
ma_channel_router_config::onReadDeinterleaved
ma_channel_router_read_deinterleaved_proc onReadDeinterleaved
Definition: miniaudio.h:856
ma_context_init
ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config *pConfig, ma_context *pContext)
ma_channel_map_blank
ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS])
format
GLsizei GLenum format
Definition: qgl_win.c:131
ma_context::DirectSoundCreate
ma_proc DirectSoundCreate
Definition: miniaudio.h:1993
ma_enum_devices_callback_proc
ma_bool32(* ma_enum_devices_callback_proc)(ma_context *pContext, ma_device_type deviceType, const ma_device_info *pInfo, void *pUserData)
Definition: miniaudio.h:1953
ma_realloc
void * ma_realloc(void *p, size_t sz)
MA_CHANNEL_AUX_21
#define MA_CHANNEL_AUX_21
Definition: miniaudio.h:656
ma_pcm_converter_config::sinc
ma_src_config_sinc sinc
Definition: miniaudio.h:972
ma_format_converter_config::onReadDeinterleaved
ma_format_converter_read_deinterleaved_proc onReadDeinterleaved
Definition: miniaudio.h:823
ma_pcm_rb_init
ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void *pOptionalPreallocatedBuffer, ma_pcm_rb *pRB)
ma_device_config
Definition: miniaudio.h:1891
ma_channel_router_config::channelsIn
ma_uint32 channelsIn
Definition: miniaudio.h:846
ma_device_config::sampleRate
ma_uint32 sampleRate
Definition: miniaudio.h:1894
MA_DEVICE_NOT_STARTED
#define MA_DEVICE_NOT_STARTED
Definition: miniaudio.h:696
drwav_read_pcm_frames_f32
drwav_uint64 drwav_read_pcm_frames_f32(drwav *pWav, drwav_uint64 framesToRead, float *pBufferOut)
ma_pcm_rb_get_subbuffer_ptr
void * ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb *pRB, ma_uint32 subbufferIndex, void *pBuffer)
ma_format_converter_config
Definition: miniaudio.h:810
drflac::sampleRate
drflac_uint32 sampleRate
Definition: dr_flac.h:497
DWORD
DWORD
Definition: qgl_win.c:49
MA_CHANNEL_NONE
#define MA_CHANNEL_NONE
Definition: miniaudio.h:615
ma_decoder_read_proc
size_t(* ma_decoder_read_proc)(ma_decoder *pDecoder, void *pBufferOut, size_t bytesToRead)
Definition: miniaudio.h:3000
MA_SHARE_MODE_NOT_SUPPORTED
#define MA_SHARE_MODE_NOT_SUPPORTED
Definition: miniaudio.h:687
ma_pcm_rb_commit_read
ma_result ma_pcm_rb_commit_read(ma_pcm_rb *pRB, ma_uint32 sizeInFrames, void *pBufferOut)
ma_channel_map_equal
ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel channelMapA[MA_MAX_CHANNELS], const ma_channel channelMapB[MA_MAX_CHANNELS])
sample
Definition: jar_mod.h:111
ma_format_converter::onDeinterleavePCM
void(* onDeinterleavePCM)(void **dst, const void *src, ma_uint64 frameCount, ma_uint32 channels)
Definition: miniaudio.h:836
w
GLdouble GLdouble GLdouble w
Definition: qgl_win.c:291
FALSE
#define FALSE
Definition: stb_vorbis.c:618
ma_context_uninit
ma_result ma_context_uninit(ma_context *pContext)
ma_channel_mix_mode_custom_weights
@ ma_channel_mix_mode_custom_weights
Definition: miniaudio.h:781
values
GLfloat * values
Definition: qgl_win.c:175
ma_context::PropVariantClear
ma_proc PropVariantClear
Definition: miniaudio.h:2289
ma_context::GetForegroundWindow
ma_proc GetForegroundWindow
Definition: miniaudio.h:2293
ma_format_converter_config_init
ma_format_converter_config ma_format_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channels, ma_format_converter_read_proc onRead, void *pUserData)
ma_device_config::pStreamNameCapture
const char * pStreamNameCapture
Definition: miniaudio.h:1926
ma_channel_router
Definition: miniaudio.h:860
ma_channel_mix_mode_simple
@ ma_channel_mix_mode_simple
Definition: miniaudio.h:780
ma_channel_map_contains_channel_position
ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS], ma_channel channelPosition)
ma_context::onDeviceUninit
void(* onDeviceUninit)(ma_device *pDevice)
Definition: miniaudio.h:1974
drmp3_seek_origin
drmp3_seek_origin
Definition: dr_mp3.h:190
ma_decoder_config::srcAlgorithm
ma_src_algorithm srcAlgorithm
Definition: miniaudio.h:3014
ma_device_type_duplex
@ ma_device_type_duplex
Definition: miniaudio.h:1808
MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION
#define MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION
Definition: miniaudio.h:741
ma_sine_wave_read_f32_ex
ma_uint64 ma_sine_wave_read_f32_ex(ma_sine_wave *pSineWave, ma_uint64 frameCount, ma_uint32 channels, ma_stream_layout layout, float **ppFrames)
ma_pcm_s16_to_s24
void ma_pcm_s16_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_CHANNEL_SIDE_LEFT
#define MA_CHANNEL_SIDE_LEFT
Definition: miniaudio.h:626
ma_mutex::hMutex
ma_handle hMutex
Definition: miniaudio.h:1725
MA_CHANNEL_TOP_CENTER
#define MA_CHANNEL_TOP_CENTER
Definition: miniaudio.h:628
mode
GLenum mode
Definition: qgl_win.c:113
MA_CHANNEL_AUX_17
#define MA_CHANNEL_AUX_17
Definition: miniaudio.h:652
ma_context::win32
struct ma_context::@37::@44 win32
ma_result
int ma_result
Definition: miniaudio.h:672
ma_pcm_converter_config::channelMapIn
ma_channel channelMapIn[MA_MAX_CHANNELS]
Definition: miniaudio.h:954
ma_event::pContext
ma_context * pContext
Definition: miniaudio.h:1740
ma_pcm_converter_config::noAVX512
ma_bool32 noAVX512
Definition: miniaudio.h:966
ma_pcm_rb::channels
ma_uint32 channels
Definition: miniaudio.h:1437
ma_pcm_u8_to_s16
void ma_pcm_u8_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_context::jack_free
ma_proc jack_free
Definition: miniaudio.h:2159
stb_vorbis_open_pushdata
stb_vorbis * stb_vorbis_open_pushdata(const unsigned char *datablock, int datablock_length_in_bytes, int *datablock_memory_consumed_in_bytes, int *error, const stb_vorbis_alloc *alloc_buffer)
Definition: stb_vorbis.c:4400
mask
GLint GLuint mask
Definition: qgl_win.c:317
ma_mutex_uninit
void ma_mutex_uninit(ma_mutex *pMutex)
ma_context::isBackendAsynchronous
ma_bool32 isBackendAsynchronous
Definition: miniaudio.h:1967
ma_device_id::wasapi
wchar_t wasapi[64]
Definition: miniaudio.h:1820
MA_CHANNEL_LFE
#define MA_CHANNEL_LFE
Definition: miniaudio.h:620
ma_src_init
ma_result ma_src_init(const ma_src_config *pConfig, ma_src *pSRC)
drflac_read_pcm_frames_s16
drflac_uint64 drflac_read_pcm_frames_s16(drflac *pFlac, drflac_uint64 framesToRead, drflac_int16 *pBufferOut)
ma_decoder::pInternalDecoder
void * pInternalDecoder
Definition: miniaudio.h:3039
ma_backend_jack
@ ma_backend_jack
Definition: miniaudio.h:1674
ma_event::hEvent
ma_handle hEvent
Definition: miniaudio.h:1747
ma_context::jack_get_buffer_size
ma_proc jack_get_buffer_size
Definition: miniaudio.h:2151
ma_context::onUninit
ma_result(* onUninit)(ma_context *pContext)
Definition: miniaudio.h:1969
MA_CHANNEL_BACK_RIGHT
#define MA_CHANNEL_BACK_RIGHT
Definition: miniaudio.h:622
ma_decoder_init_memory_vorbis
ma_result ma_decoder_init_memory_vorbis(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
DR_WAVE_FORMAT_ADPCM
#define DR_WAVE_FORMAT_ADPCM
Definition: dr_wav.h:189
stb_vorbis_flush_pushdata
void stb_vorbis_flush_pushdata(stb_vorbis *f)
Definition: stb_vorbis.c:4227
channel
Definition: jar_mod.h:142
ma_rb_commit_read
ma_result ma_rb_commit_read(ma_rb *pRB, size_t sizeInBytes, void *pBufferOut)
MA_FAILED_TO_MAP_DEVICE_BUFFER
#define MA_FAILED_TO_MAP_DEVICE_BUFFER
Definition: miniaudio.h:700
ma_backend_null
@ ma_backend_null
Definition: miniaudio.h:1678
ma_performance_profile_low_latency
@ ma_performance_profile_low_latency
Definition: miniaudio.h:801
ma_get_default_buffer_size_in_milliseconds
ma_uint32 ma_get_default_buffer_size_in_milliseconds(ma_performance_profile performanceProfile)
ma_format_converter_config::formatIn
ma_format formatIn
Definition: miniaudio.h:812
ma_context_enumerate_devices
ma_result ma_context_enumerate_devices(ma_context *pContext, ma_enum_devices_callback_proc callback, void *pUserData)
ma_device_type_capture
@ ma_device_type_capture
Definition: miniaudio.h:1807
MA_API_NOT_FOUND
#define MA_API_NOT_FOUND
Definition: miniaudio.h:690
MA_CHANNEL_FRONT_RIGHT_CENTER
#define MA_CHANNEL_FRONT_RIGHT_CENTER
Definition: miniaudio.h:624
ma_backend_coreaudio
@ ma_backend_coreaudio
Definition: miniaudio.h:1668
ma_src_config::sampleRateIn
ma_uint32 sampleRateIn
Definition: miniaudio.h:900
ma_thread_priority_low
@ ma_thread_priority_low
Definition: miniaudio.h:1686
ma_context::jack_client_name_size
ma_proc jack_client_name_size
Definition: miniaudio.h:2146
ma_pcm_converter_config_init_new
ma_pcm_converter_config ma_pcm_converter_config_init_new(void)
ma_backend_alsa
@ ma_backend_alsa
Definition: miniaudio.h:1673
ma_context::jack_on_shutdown
ma_proc jack_on_shutdown
Definition: miniaudio.h:2149
drwav::translatedFormatTag
drwav_uint16 translatedFormatTag
Definition: dr_wav.h:416
ma_device_config::playback
struct ma_device_config::@28 playback
ma_context::deviceInfoLock
ma_mutex deviceInfoLock
Definition: miniaudio.h:1962
HRESULT
HRESULT(WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID
MA_CHANNEL_TOP_FRONT_CENTER
#define MA_CHANNEL_TOP_FRONT_CENTER
Definition: miniaudio.h:630
ma_src_algorithm_default
@ ma_src_algorithm_default
Definition: miniaudio.h:882
ma_context::winmm
struct ma_context::@35::@41 winmm
ma_src
struct ma_src ma_src
Definition: miniaudio.h:874
pUnkOuter
LPDIRECTSOUND FAR IUnknown FAR * pUnkOuter
Definition: snd_win.c:28
ma_decoder::pUserData
void * pUserData
Definition: miniaudio.h:3025
MA_SRC_SINC_MIN_WINDOW_WIDTH
#define MA_SRC_SINC_MIN_WINDOW_WIDTH
Definition: miniaudio.h:738
ma_IMMNotificationClient::lpVtbl
void * lpVtbl
Definition: miniaudio.h:1656
ma_context_config::tryAutoSpawn
ma_bool32 tryAutoSpawn
Definition: miniaudio.h:1944
ma_pcm_f32_to_s16
void ma_pcm_f32_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_decoder_get_length_in_pcm_frames_proc
ma_uint64(* ma_decoder_get_length_in_pcm_frames_proc)(ma_decoder *pDecoder)
Definition: miniaudio.h:3004
ma_context::RegOpenKeyExA
ma_proc RegOpenKeyExA
Definition: miniaudio.h:2297
drwav_seek_to_pcm_frame
drwav_bool32 drwav_seek_to_pcm_frame(drwav *pWav, drwav_uint64 targetFrameIndex)
ma_device_config::channelMap
ma_channel channelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:1907
ma_pcm_converter_read
ma_uint64 ma_pcm_converter_read(ma_pcm_converter *pDSP, void *pFramesOut, ma_uint64 frameCount)
ma_pcm_rb_acquire_read
ma_result ma_pcm_rb_acquire_read(ma_pcm_rb *pRB, ma_uint32 *pSizeInFrames, void **ppBufferOut)
void
void(APIENTRY *qglAccum)(GLenum op
MA_CHANNEL_MONO
#define MA_CHANNEL_MONO
Definition: miniaudio.h:616
count
GLint GLsizei count
Definition: qgl_win.c:128
ma_channel_router_config::pUserData
void * pUserData
Definition: miniaudio.h:857
MA_MIN_SAMPLE_RATE
#define MA_MIN_SAMPLE_RATE
Definition: miniaudio.h:736
ma_context::waveInClose
ma_proc waveInClose
Definition: miniaudio.h:2014
MA_CHANNEL_AUX_23
#define MA_CHANNEL_AUX_23
Definition: miniaudio.h:658
ma_context::waveInStart
ma_proc waveInStart
Definition: miniaudio.h:2018
ma_mutex::_unused
int _unused
Definition: miniaudio.h:1734
MA_INVALID_ARGS
#define MA_INVALID_ARGS
Definition: miniaudio.h:677
ma_decoder_seek_to_pcm_frame
ma_result ma_decoder_seek_to_pcm_frame(ma_decoder *pDecoder, ma_uint64 frameIndex)
stb_vorbis_info::max_frame_size
int max_frame_size
Definition: stb_vorbis.c:123
ma_channel_router_config::noNEON
ma_bool32 noNEON
Definition: miniaudio.h:855
MA_CHANNEL_FRONT_LEFT_CENTER
#define MA_CHANNEL_FRONT_LEFT_CENTER
Definition: miniaudio.h:623
MA_CHANNEL_AUX_9
#define MA_CHANNEL_AUX_9
Definition: miniaudio.h:644
ma_format_converter::useNEON
ma_bool32 useNEON
Definition: miniaudio.h:833
MA_CHANNEL_RIGHT
#define MA_CHANNEL_RIGHT
Definition: miniaudio.h:668
ma_decoder::outputSampleRate
ma_uint32 outputSampleRate
Definition: miniaudio.h:3033
ma_context_get_device_info
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)
ma_rb_get_subbuffer_ptr
void * ma_rb_get_subbuffer_ptr(ma_rb *pRB, size_t subbufferIndex, void *pBuffer)
ma_seek_origin
ma_seek_origin
Definition: miniaudio.h:2994
ma_src_sinc_window_function_rectangular
@ ma_src_sinc_window_function_rectangular
Definition: miniaudio.h:888
MA_FAILED_TO_OPEN_BACKEND_DEVICE
#define MA_FAILED_TO_OPEN_BACKEND_DEVICE
Definition: miniaudio.h:707
ma_context_config
Definition: miniaudio.h:1930
ma_get_standard_channel_map
void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
drmp3_get_pcm_frame_count
drmp3_uint64 drmp3_get_pcm_frame_count(drmp3 *pMP3)
ma_format_converter_config::onRead
ma_format_converter_read_proc onRead
Definition: miniaudio.h:822