view release on metacpan or search on metacpan
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_device_uninit(&device); // This will stop the device so no need to do that manually.
return 0;
}
```
In the example above, `data_callback()` is where audio data is written and read from the device.
The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data
to the output buffer (`pOutput` in the example). In capture mode you read data from the input
buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you
how many frames can be written to the output buffer and read from the input buffer. A "frame" is
one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2
samples: one for the left, one for the right. The channel count is defined by the device config.
The size in bytes of an individual sample is defined by the sample format which is also specified
in the device config. Multi-channel audio data is always interleaved, which means the samples for
each frame are stored next to each other in memory. For example, in a stereo stream the first pair
of samples will be the left and right samples for the first frame, the second pair of samples will
be the left and right samples for the second frame, etc.
The configuration of the device is defined by the `ma_device_config` structure. The config object
is always initialized with `ma_device_config_init()`. It's important to always initialize the
config with this function as it initializes it with logical defaults and ensures your program
doesn't break when new members are added to the `ma_device_config` structure. The example above
uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes
a single parameter, which is whether or not the device is a playback, capture, duplex or loopback
device (loopback devices are not supported on all backends). The `config.playback.format` member
sets the sample format which can be one of the following (all formats are native-endian):
+---------------+----------------------------------------+---------------------------+
| Symbol | Description | Range |
+---------------+----------------------------------------+---------------------------+
| ma_format_f32 | 32-bit floating point | [-1, 1] |
| ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
| ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
| ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
| ma_format_u8 | 8-bit unsigned integer | [0, 255] |
+---------------+----------------------------------------+---------------------------+
The `config.playback.channels` member sets the number of channels to use with the device. The
channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate
(which must be the same for both playback and capture in full-duplex configurations). This is
usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between
8000 and 384000, however.
Note that leaving the format, channel count and/or sample rate at their default values will result
in the internal device's native configuration being used which is useful if you want to avoid the
overhead of miniaudio's automatic data conversion.
In addition to the sample format, channel count and sample rate, the data callback and user data
pointer are also set via the config. The user data pointer is not passed into the callback as a
parameter, but is instead set to the `pUserData` member of `ma_device` which you can access
directly since all miniaudio structures are transparent.
Initializing the device is done with `ma_device_init()`. This will return a result code telling you
what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is
complete the device will be in a stopped state. To start it, use `ma_device_start()`.
Uninitializing the device will stop it, which is what the example above does, but you can also stop
the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again.
Note that it's important to never stop or start the device from inside the callback. This will
result in a deadlock. Instead you set a variable or signal an event indicating that the device
needs to stop and handle it in a different thread. The following APIs must never be called inside
the callback:
```c
ma_device_init()
ma_device_init_ex()
ma_device_uninit()
ma_device_start()
ma_device_stop()
```
You must never try uninitializing and reinitializing a device inside the callback. You must also
never try to stop and start it from inside the callback. There are a few other things you shouldn't
do in the callback depending on your requirements, however this isn't so much a thread-safety
thing, but rather a real-time processing thing which is beyond the scope of this introduction.
The example above demonstrates the initialization of a playback device, but it works exactly the
same for capture. All you need to do is change the device type from `ma_device_type_playback` to
`ma_device_type_capture` when setting up the config, like so:
```c
ma_device_config config = ma_device_config_init(ma_device_type_capture);
config.capture.format = MY_FORMAT;
config.capture.channels = MY_CHANNEL_COUNT;
```
In the data callback you just read from the input buffer (`pInput` in the example above) and leave
the output buffer alone (it will be set to NULL when the device type is set to
`ma_device_type_capture`).
These are the available device types and how you should handle the buffers in the callback:
+-------------------------+--------------------------------------------------------+
| Device Type | Callback Behavior |
+-------------------------+--------------------------------------------------------+
| ma_device_type_playback | Write to output buffer, leave input buffer untouched. |
| ma_device_type_capture | Read from input buffer, leave output buffer untouched. |
| ma_device_type_duplex | Read from input buffer, write to output buffer. |
| ma_device_type_loopback | Read from input buffer, leave output buffer untouched. |
+-------------------------+--------------------------------------------------------+
You will notice in the example above that the sample format and channel count is specified
separately for playback and capture. This is to support different data formats between the playback
and capture devices in a full-duplex system. An example may be that you want to capture audio data
as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you
use different formats between playback and capture in a full-duplex configuration you will need to
convert the data yourself. There are functions available to help you do this which will be
explained later.
The example above did not specify a physical device to connect to which means it will use the
operating system's default device. If you have multiple physical devices connected and you want to
use a specific one you will need to specify the device ID in the configuration, like so:
```c
config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device.
config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device.
```
To retrieve the device ID you will need to perform device enumeration, however this requires the
use of a new concept called the "context". Conceptually speaking the context sits above the device.
There is one context to many devices. The purpose of the context is to represent the backend at a
more global level and to perform operations outside the scope of an individual device. Mainly it is
used for performing run-time linking against backend libraries, initializing backends and
enumerating devices. The example below shows how to enumerate devices.
```c
ma_context context;
if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {
// Error.
}
ma_device_info* pPlaybackInfos;
ma_uint32 playbackCount;
ma_device_info* pCaptureInfos;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
initialized. The easiest but least flexible way of playing a sound is like so:
```c
ma_engine_play_sound(&engine, "my_sound.wav", NULL);
```
This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the
internal sound up for recycling. The last parameter is used to specify which sound group the sound
should be associated with which will be explained later. This particular way of playing a sound is
simple, but lacks flexibility and features. A more flexible way of playing a sound is to first
initialize a sound:
```c
ma_result result;
ma_sound sound;
result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound);
if (result != MA_SUCCESS) {
return result;
}
ma_sound_start(&sound);
```
This returns a `ma_sound` object which represents a single instance of the specified sound file. If
you want to play the same file multiple times simultaneously, you need to create one sound for each
instance.
Sounds should be uninitialized with `ma_sound_uninit()`.
Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with
`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use
`ma_sound_seek_to_pcm_frames(&sound, 0)` to seek back to the start of a sound. By default, starting
and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound
the be started and/or stopped at a specific time. This can be done with the following functions:
```c
ma_sound_set_start_time_in_pcm_frames()
ma_sound_set_start_time_in_milliseconds()
ma_sound_set_stop_time_in_pcm_frames()
ma_sound_set_stop_time_in_milliseconds()
```
The start/stop time needs to be specified based on the absolute timer which is controlled by the
engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`.
The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if
required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()`
before anything will play:
```c
ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2);
ma_sound_start(&sound);
```
The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be
loaded and a few options on which features should be enabled for that sound. By default, the sound
is synchronously loaded fully into memory straight from the file system without any kind of
decoding. If you want to decode the sound before storing it in memory, you need to specify the
`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier
stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing
time which might be too expensive on the audio thread.
If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This
will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing
until the sound has had some audio decoded.
The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise
sounds into groups which have their own effect processing and volume control. An example is a game
which might have separate groups for sfx, voice and music. Each of these groups have their own
independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize
a sound group.
Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node`
API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex
effect chains.
A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume
control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear.
Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know
a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect,
you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization.
By default, sounds and sound groups have spatialization enabled. If you don't ever want to
spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The
spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and
environmental occlusion are not currently supported, but planned for the future. The supported
features include:
* Sound and listener positioning and orientation with cones
* Attenuation models: none, inverse, linear and exponential
* Doppler effect
Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`.
To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound
is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with
`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping.
2. Building
===========
miniaudio should work cleanly out of the box without the need to download or install any
dependencies. See below for platform-specific details.
2.1. Windows
------------
The Windows build should compile cleanly on all popular compilers without the need to configure any
include paths nor link to any libraries.
The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external
symbol for `ActivateAudioInterfaceAsync()`.
2.2. macOS and iOS
------------------
The macOS build should compile cleanly without the need to download any dependencies nor link to
any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to
link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling
through the command line requires linking to `-lpthread` and `-lm`.
Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's
notarization process. To fix this there are two options. The first is to use the
`MA_NO_RUNTIME_LINKING` option, like so:
```c
#ifdef __APPLE__
#define MA_NO_RUNTIME_LINKING
#endif
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
```
This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`.
Alternatively, if you would rather keep using runtime linking you can add the following to your
entitlements.xcent file:
```
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
```
See this discussion for more info: https://github.com/mackron/miniaudio/issues/203.
2.3. Linux
----------
The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any
development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM.
2.4. BSD
--------
The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses
sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit
ARM.
2.5. Android
------------
AAudio is the highest priority backend on Android. This should work out of the box without needing
any kind of compiler configuration. Support for AAudio starts with Android 8 which means older
versions will fall back to OpenSL|ES which requires API level 16+.
There have been reports that the OpenSL|ES backend fails to initialize on some Android based
devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform
you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES.
2.6. Emscripten
---------------
The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box.
You cannot use `-std=c*` compiler flags, nor `-ansi`.
2.7. Build Options
------------------
`#define` these options before including miniaudio.h.
+----------------------------------+--------------------------------------------------------------------+
| Option | Description |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_WASAPI | Disables the WASAPI backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_DSOUND | Disables the DirectSound backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_WINMM | Disables the WinMM backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_ALSA | Disables the ALSA backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_PULSEAUDIO | Disables the PulseAudio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_JACK | Disables the JACK backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_COREAUDIO | Disables the Core Audio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_SNDIO | Disables the sndio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_AUDIO4 | Disables the audio(4) backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_OSS | Disables the OSS backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_AAUDIO | Disables the AAudio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_OPENSL | Disables the OpenSL|ES backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_WEBAUDIO | Disables the Web Audio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_NULL | Disables the null backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to |
| | enable specific backends. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the WASAPI backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the DirectSound backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the WinMM backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the ALSA backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the PulseAudio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the JACK backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the Core Audio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the sndio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the audio(4) backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the OSS backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the AAudio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the OpenSL|ES backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the Web Audio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the null backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_DECODING | Disables decoding APIs. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_ENCODING | Disables encoding APIs. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_WAV | Disables the built-in WAV decoder and encoder. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_FLAC | Disables the built-in FLAC decoder. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_MP3 | Disables the built-in MP3 decoder. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` |
| | and `ma_device` APIs. This is useful if you only want to use |
| | miniaudio's data conversion and/or decoding APIs. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and |
| | `ma_event` APIs. This option is useful if you only need to use |
| | miniaudio for data conversion, decoding and/or encoding. Some |
| | families of APIsrequire threading which means the following |
| | options must also be set: |
| | |
| | ``` |
| | MA_NO_DEVICE_IO |
| | ``` |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_SSE2 | Disables SSE2 optimizations. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_AVX2 | Disables AVX2 optimizations. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_NEON | Disables NEON optimizations. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's |
| | notarization process. When enabling this, you may need to avoid |
| | using `-std=c89` or `-std=c99` on Linux builds or else you may end |
| | up with compilation errors due to conflicts with `timespec` and |
| | `timeval` data types. |
| | |
| | You may need to enable this if your target platform does not allow |
| | runtime linking via `dlopen()`. |
+----------------------------------+--------------------------------------------------------------------+
| MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). |
+----------------------------------+--------------------------------------------------------------------+
| MA_COINIT_VALUE | Windows only. The value to pass to internal calls to |
| | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. |
+----------------------------------+--------------------------------------------------------------------+
| MA_API | Controls how public APIs should be decorated. Default is `extern`. |
+----------------------------------+--------------------------------------------------------------------+
3. Definitions
==============
This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity
in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio
uses each term.
3.1. Sample
-----------
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.
3.2. Frame / PCM Frame
----------------------
A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2
samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame"
and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame.
If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always
clarify what it's referring to with something like "FLAC frame".
3.3. Channel
------------
A stream of monaural audio that is emitted from an individual speaker in a speaker system, or
received from an individual microphone in a microphone system. A stereo stream has two channels (a
left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio
systems refer to a channel as a complex audio stream that's mixed with other channels to produce
the final mix - this is completely different to miniaudio's use of the term "channel" and should
not be confused.
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
There may be cases where you want to implement something like a sound bank where you only want to
read data within a certain range of the underlying data. To do this you can use a range:
```c
result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames);
if (result != MA_SUCCESS) {
return result; // Failed to set the range.
}
```
This is useful if you have a sound bank where many sounds are stored in the same file and you want
the data source to only play one of those sub-sounds.
Custom loop points can also be used with data sources. By default, data sources will loop after
they reach the end of the data source, but if you need to loop at a specific location, you can do
the following:
```c
result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames);
if (result != MA_SUCCESS) {
return result; // Failed to set the loop point.
}
```
The loop point is relative to the current range.
It's sometimes useful to chain data sources together so that a seamless transition can be achieved.
To do this, you can use chaining:
```c
ma_decoder decoder1;
ma_decoder decoder2;
// ... initialize decoders with ma_decoder_init_*() ...
result = ma_data_source_set_next(&decoder1, &decoder2);
if (result != MA_SUCCESS) {
return result; // Failed to set the next data source.
}
result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead, MA_FALSE);
if (result != MA_SUCCESS) {
return result; // Failed to read from the decoder.
}
```
In the example above we're using decoders. When reading from a chain, you always want to read from
the top level data source in the chain. In the example above, `decoder1` is the top level data
source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any
gaps.
Note that the `loop` parameter is set to false in the example above. When this is set to true, only
the current data source will be looped. You can loop the entire chain by linking in a loop like so:
```c
ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2
ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start).
```
Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically
changing links while the audio thread is in the middle of reading.
Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple
instances of the same sound simultaneously. Instead, initialize multiple data sources for each
instance. This can be extremely inefficient depending on the data source and can result in
glitching due to subtle changes to the state of internal filters.
4.1. Custom Data Sources
------------------------
You can implement a custom data source by implementing the functions in `ma_data_source_vtable`.
Your custom object must have `ma_data_source_base` as it's first member:
```c
struct my_data_source
{
ma_data_source_base base;
...
};
```
In your initialization routine, you need to call `ma_data_source_init()` in order to set up the
base object (`ma_data_source_base`):
```c
static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
// Read data here. Output in the same format returned by my_data_source_get_data_format().
}
static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
{
// Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported.
}
static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
// Return the format of the data here.
}
static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
{
// Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor.
}
static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
{
// Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown.
}
static g_my_data_source_vtable =
{
my_data_source_read,
my_data_source_seek,
my_data_source_get_data_format,
my_data_source_get_cursor,
my_data_source_get_length
};
ma_result my_data_source_init(my_data_source* pMyDataSource)
{
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility
you'll want to initialize a sound object:
```c
ma_sound sound;
result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound);
if (result != MA_SUCCESS) {
return result; // Failed to load sound.
}
```
Sounds need to be uninitialized with `ma_sound_uninit()`.
The example above loads a sound from a file. If the resource manager has been disabled you will not
be able to use this function and instead you'll need to initialize a sound directly from a data
source:
```c
ma_sound sound;
result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound);
if (result != MA_SUCCESS) {
return result;
}
```
Each `ma_sound` object represents a single instance of the sound. If you want to play the same
sound multiple times at the same time, you need to initialize a separate `ma_sound` object.
For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's
standard config/init pattern:
```c
ma_sound sound;
ma_sound_config soundConfig;
soundConfig = ma_sound_config_init();
soundConfig.pFilePath = NULL; // Set this to load from a file path.
soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source.
soundConfig.pInitialAttachment = &someNodeInTheNodeGraph;
soundConfig.initialAttachmentInputBusIndex = 0;
soundConfig.channelsIn = 1;
soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count.
result = ma_sound_init_ex(&soundConfig, &sound);
if (result != MA_SUCCESS) {
return result;
}
```
In the example above, the sound is being initialized without a file nor a data source. This is
valid, in which case the sound acts as a node in the middle of the node graph. This means you can
connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly
what a `ma_sound_group` is.
When loading a sound, you specify a set of flags that control how the sound is loaded and what
features are enabled for that sound. When no flags are set, the sound will be fully loaded into
memory in exactly the same format as how it's stored on the file system. The resource manager will
allocate a block of memory and then load the file directly into it. When reading audio data, it
will be decoded dynamically on the fly. In order to save processing time on the audio thread, it
might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag:
```c
ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound);
```
By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until
the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously
by specificying the `MA_SOUND_FLAG_ASYNC` flag:
```c
ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound);
```
This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully
loaded. When you start the sound, it won't output anything until some sound is available. The sound
will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE`
is specified.
If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A
fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal
counter hit's zero. You can specify a fence like so:
```c
ma_result result;
ma_fence fence;
ma_sound sounds[4];
result = ma_fence_init(&fence);
if (result != MA_SUCCES) {
return result;
}
// Load some sounds asynchronously.
for (int iSound = 0; iSound < 4; iSound += 1) {
ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]);
}
// ... do some other stuff here in the mean time ...
// Wait for all sounds to finish loading.
ma_fence_wait(&fence);
```
If loading the entire sound into memory is prohibitive, you can also configure the engine to stream
the audio data:
```c
ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound);
```
When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work
fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music
tracks in games.
When you initialize a sound, if you specify a sound group the sound will be attached to that group
automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint.
If you would instead rather leave the sound unattached by default, you can can specify the
`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node
graph.
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
the data to be loaded asynchronously.
The example below is how you can initialize a resource manager using it's default configuration:
```c
ma_resource_manager_config config;
ma_resource_manager resourceManager;
config = ma_resource_manager_config_init();
result = ma_resource_manager_init(&config, &resourceManager);
if (result != MA_SUCCESS) {
ma_device_uninit(&device);
printf("Failed to initialize the resource manager.");
return -1;
}
```
You can configure the format, channels and sample rate of the decoded audio data. By default it
will use the file's native data format, but you can configure it to use a consistent format. This
is useful for offloading the cost of data conversion to load time rather than dynamically
converting at mixing time. To do this, you configure the decoded format, channels and sample rate
like the code below:
```c
config = ma_resource_manager_config_init();
config.decodedFormat = device.playback.format;
config.decodedChannels = device.playback.channels;
config.decodedSampleRate = device.sampleRate;
```
In the code above, the resource manager will be configured so that any decoded audio data will be
pre-converted at load time to the device's native data format. If instead you used defaults and
the data format of the file did not match the device's data format, you would need to convert the
data at mixing time which may be prohibitive in high-performance and large scale scenarios like
games.
Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it
only supports decoders that are built into miniaudio. It's possible to support additional encoding
formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable`
vtables into the resource manager config:
```c
ma_decoding_backend_vtable* pCustomBackendVTables[] =
{
&g_ma_decoding_backend_vtable_libvorbis,
&g_ma_decoding_backend_vtable_libopus
};
...
resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables;
resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
resourceManagerConfig.pCustomDecodingBackendUserData = NULL;
```
This system can allow you to support any kind of file format. See the "Decoding" section for
details on how to implement custom decoders. The miniaudio repository includes examples for Opus
via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile.
Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the
decoding of a page, a job will be posted to a queue which will then be processed by a job thread.
By default there will be only one job thread running, but this can be configured, like so:
```c
config = ma_resource_manager_config_init();
config.jobThreadCount = MY_JOB_THREAD_COUNT;
```
By default job threads are managed internally by the resource manager, however you can also self
manage your job threads if, for example, you want to integrate the job processing into your
existing job infrastructure, or if you simply don't like the way the resource manager does it. To
do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first
need to retrieve a job using `ma_resource_manager_next_job()` and then process it using
`ma_job_process()`:
```c
config = ma_resource_manager_config_init();
config.jobThreadCount = 0; // Don't manage any job threads internally.
config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking.
// ... Initialize your custom job threads ...
void my_custom_job_thread(...)
{
for (;;) {
ma_job job;
ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job);
if (result != MA_SUCCESS) {
if (result == MA_NOT_DATA_AVAILABLE) {
// No jobs are available. Keep going. Will only get this if the resource manager was initialized
// with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.
continue;
} else if (result == MA_CANCELLED) {
// MA_JOB_TYPE_QUIT was posted. Exit.
break;
} else {
// Some other error occurred.
break;
}
}
ma_job_process(&job);
}
}
```
In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination
indicator, but you can use whatever you would like to terminate the thread. The call to
`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking
by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration
flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This
is to give every thread the opportunity to catch the event and terminate naturally.
When loading a file, it's sometimes convenient to be able to customize how files are opened and
read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by
default. This can be done by setting `pVFS` member of the resource manager's config:
```c
// Initialize your custom VFS object. See documentation for VFS for information on how to do this.
my_custom_vfs vfs = my_custom_vfs_init();
config = ma_resource_manager_config_init();
config.pVFS = &vfs;
```
This is particularly useful in programs like games where you want to read straight from an archive
rather than the normal file system. If you do not specify a custom VFS, the resource manager will
use the operating system's normal file operations. This is default.
To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When
loading a sound you need to specify the file path and options for how the sounds should be loaded.
By default a sound will be loaded synchronously. The returned data source is owned by the caller
which means the caller is responsible for the allocation and freeing of the data source. Below is
an example for initializing a data source:
```c
ma_resource_manager_data_source dataSource;
ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource);
if (result != MA_SUCCESS) {
// Error.
}
// ...
// A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call
// the `ma_data_source_read_pcm_frames()` like you would with any normal data source.
result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead);
if (result != MA_SUCCESS) {
// Failed to read PCM frames.
}
// ...
ma_resource_manager_data_source_uninit(pResourceManager, &dataSource);
```
The `flags` parameter specifies how you want to perform loading of the sound file. It can be a
combination of the following flags:
```
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT
```
When no flags are specified (set to 0), the sound will be fully loaded into memory, but not
decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when
`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in
memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will
be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after
the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You
can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag.
This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be
returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is
available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by
`ma_data_source_read_pcm_frames()`.
For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you
can instead stream audio data which you can do by specifying the
`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1
second pages. When a new page needs to be decoded, a job will be posted to the job queue and then
subsequently processed in a job thread.
For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means
multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in
the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be
matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful
for a program to register self-managed raw audio data and associate it with a file path. Use the
`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this.
`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed
decoded audio data in the specified data format with the specified name. Likewise,
`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed
encoded audio data (the raw file data) with the specified name. Note that these names need not be
actual file paths. When `ma_resource_manager_data_source_init()` is called (without the
`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these
explicitly registered data buffers and, if found, will use it as the backing data for the data
source. Note that the resource manager does *not* make a copy of this data so it is up to the
caller to ensure the pointer stays valid for it's lifetime. Use
`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use
`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and
unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM`
flag with a self-managed data pointer.
6.1. Asynchronous Loading and Synchronization
---------------------------------------------
When loading asynchronously, it can be useful to poll whether or not loading has finished. Use
`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will
return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded,
`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed
to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been
decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY`
will be returned. Otherwise, some other error code will be returned if the sound failed to load.
In addition to polling, you can also use a simple synchronization object called a "fence" to wait
for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a
fence is that it can be used to wait for a group of sounds to finish loading rather than waiting
for sounds on an individual basis. There are two stages to loading a sound:
* Initialization of the internal decoder; and
* Completion of decoding of the file (the file is fully decoded)
You can specify separate fences for each of the different stages. Waiting for the initialization
of the internal decoder is important for when you need to know the sample format, channels and
sample rate of the file.
The example below shows how you could use a fence when loading a number of sounds:
```c
// This fence will be released when all sounds are finished loading entirely.
ma_fence fence;
ma_fence_init(&fence);
// This will be passed into the initialization routine for each sound.
ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
notifications.done.pFence = &fence;
// Now load a bunch of sounds:
for (iSound = 0; iSound < soundCount; iSound += 1) {
ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]);
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
In the example above we used a fence for waiting until the entire file has been fully decoded. If
you only need to wait for the initialization of the internal decoder to complete, you can use the
`init` member of the `ma_resource_manager_pipeline_notifications` object:
```c
notifications.init.pFence = &fence;
```
If a fence is not appropriate for your situation, you can instead use a callback that is fired on
an individual sound basis. This is done in a very similar way to fences:
```c
typedef struct
{
ma_async_notification_callbacks cb;
void* pMyData;
} my_notification;
void my_notification_callback(ma_async_notification* pNotification)
{
my_notification* pMyNotification = (my_notification*)pNotification;
// Do something in response to the sound finishing loading.
}
...
my_notification myCallback;
myCallback.cb.onSignal = my_notification_callback;
myCallback.pMyData = pMyData;
ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
notifications.done.pNotification = &myCallback;
ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound);
```
In the example above we just extend the `ma_async_notification_callbacks` object and pass an
instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with
the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same
time and they should both work as expected. If using the `pNotification` system, you need to ensure
your `ma_async_notification_callbacks` object stays valid.
6.2. Resource Manager Implementation Details
--------------------------------------------
Resources are managed in two main ways:
* By storing the entire sound inside an in-memory buffer (referred to as a data buffer)
* By streaming audio data on the fly (referred to as a data stream)
A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or
data stream, depending on whether or not the data source was initialized with the
`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a
`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer`
object. Both of these objects are data sources which means they can be used with any
`ma_data_source_*()` API.
Another major feature of the resource manager is the ability to asynchronously decode audio files.
This relieves the audio thread of time-consuming decoding which can negatively affect scalability
due to the audio thread needing to complete it's work extremely quickly to avoid glitching.
Asynchronous decoding is achieved through a job system. There is a central multi-producer,
multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is
posted to the queue which is then read by a job thread. The number of job threads can be
configured for improved scalability, and job threads can all run in parallel without needing to
worry about the order of execution (how this is achieved is explained below).
When a sound is being loaded asynchronously, playback can begin before the sound has been fully
decoded. This enables the application to start playback of the sound quickly, while at the same
time allowing to resource manager to keep loading in the background. Since there may be less
threads than the number of sounds being loaded at a given time, a simple scheduling system is used
to keep decoding time balanced and fair. The resource manager solves this by splitting decoding
into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a
new job will be posted to start decoding the next page. By dividing up decoding into pages, an
individual sound shouldn't ever delay every other sound from having their first page decoded. Of
course, when loading many sounds at the same time, there will always be an amount of time required
to process jobs in the queue so in heavy load situations there will still be some delay. To
determine if a data source is ready to have some frames read, use
`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames
available starting from the current position.
6.2.1. Job Queue
----------------
The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity.
This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety.
Only a fixed number of jobs can be allocated and inserted into the queue which is done through a
lock-free data structure for allocating an index into a fixed sized array, with reference counting
for mitigation of the ABA problem. The reference count is 32-bit.
For many types of jobs it's important that they execute in a specific order. In these cases, jobs
are executed serially. For the resource manager, serial execution of jobs is only required on a
per-object basis (per data buffer or per data stream). Each of these objects stores an execution
counter. When a job is posted it is associated with an execution counter. When the job is
processed, it checks if the execution counter of the job equals the execution counter of the
owning object and if so, processes the job. If the counters are not equal, the job will be posted
back onto the job queue for later processing. When the job finishes processing the execution order
of the main object is incremented. This system means the no matter how many job threads are
executing, decoding of an individual sound will always get processed serially. The advantage to
having multiple threads comes into play when loading multiple sounds at the same time.
The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve
thread-safety for a very small section of code. This is only relevant when the resource manager
uses more than one job thread. If only using a single job thread, which is the default, the
lock should never actually wait in practice. The amount of time spent locking should be quite
short, but it's something to be aware of for those who have pedantic lock-free requirements and
need to use more than one job thread. There are plans to remove this lock in a future version.
In addition, posting a job will release a semaphore, which on Win32 is implemented with
`ReleaseSemaphore` and on POSIX platforms via a condition variable:
```c
pthread_mutex_lock(&pSemaphore->lock);
{
pSemaphore->value += 1;
pthread_cond_signal(&pSemaphore->cond);
}
pthread_mutex_unlock(&pSemaphore->lock);
```
Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid
this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING`
flag) and implement your own job processing routine (see the "Resource Manager" section above for
details on how to do this).
6.2.2. Data Buffers
-------------------
When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the
resource manager will try to load the data into an in-memory data buffer. Before doing so, however,
it will first check if the specified file is already loaded. If so, it will increment a reference
counter and just use the already loaded data. This saves both time and memory. When the data buffer
is uninitialized, the reference counter will be decremented. If the counter hits zero, the file
will be unloaded. This is a detail to keep in mind because it could result in excessive loading and
unloading of a sound. For example, the following sequence will result in a file be loaded twice,
once after the other:
```c
ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load.
ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded.
ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it.
ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded.
```
A binary search tree (BST) is used for storing data buffers as it has good balance between
efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed
into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves
memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST
due to the random nature of the hash. The disadvantage is that file names are case-sensitive. If
this is an issue, you should normalize your file names to upper- or lower-case before initializing
your data sources.
When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC`
flag is excluded, the file will be decoded synchronously by the calling thread. There are two
options for controlling how the audio is stored in the data buffer - encoded or decoded. When the
`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored
in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is
a very simple and standard process of simply adding an item to the BST, allocating a block of
memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified).
When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer
is done asynchronously. In this case, a job is posted to the queue to start loading and then the
function immediately returns, setting an internal result code to `MA_BUSY`. This result code is
returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully
completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed.
When loading asynchronously, a single job is posted to the queue of the type
`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and
associating it with job. When the job is processed by the job thread, it will first load the file
using the VFS associated with the resource manager. When using a custom VFS, it's important that it
be completely thread-safe because it will be used from one or more job threads at the same time.
Individual files should only ever be accessed by one thread at a time, however. After opening the
file via the VFS, the job will determine whether or not the file is being decoded. If not, it
simply allocates a block of memory and loads the raw file contents into it and returns. On the
other hand, when the file is being decoded, it will first allocate a decoder on the heap and
initialize it. Then it will check if the length of the file is known. If so it will allocate a
block of memory to store the decoded output and initialize it to silence. If the size is unknown,
it will allocate room for one page. After memory has been allocated, the first page will be
decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the
completion event will be signalled and loading is now complete. If, however, there is more to
decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job
will decode the next page and perform the same process if it reaches the end. If there is more to
decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will
keep on happening until the sound has been fully decoded. For sounds of an unknown length, each
page will be linked together as a linked list. Internally this is implemented via the
`ma_paged_audio_buffer` object.
6.2.3. Data Streams
-------------------
Data streams only ever store two pages worth of data for each instance. They are most useful for
large sounds like music tracks in games that would consume too much memory if fully decoded in
memory. After every frame from a page has been read, a job will be posted to load the next page
which is done from the VFS.
For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or
not initialization of the data source waits until the two pages have been decoded. When unset,
`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise
it will return immediately.
When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`,
`MA_BUSY` will be returned if there are no frames available. If there are some frames available,
but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames
read will be less than the number requested. Due to the asynchronous nature of data streams,
seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be
returned when trying to read frames.
When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed
a job is posted to load the next page. This will be posted from the same thread that called
`ma_resource_manager_data_source_read_pcm_frames()`.
Data streams are uninitialized by posting a job to the queue, but the function won't return until
that job has been processed. The reason for this is that the caller owns the data stream object and
therefore miniaudio needs to ensure everything completes before handing back control to the caller.
Also, if the data stream is uninitialized while pages are in the middle of decoding, they must
complete before destroying any underlying object and the job system handles this cleanly.
Note that when a new page needs to be loaded, a job will be posted to the resource manager's job
thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue"
section above regarding locking when posting an event if you require a strictly lock-free audio
thread.
7. Node Graph
=============
miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a
node whose outputs are attached to inputs of another node, thereby creating a graph. There are
different types of nodes, with each node in the graph processing input data to produce output,
which is then fed through the chain. Each node in the graph can apply their own custom effects. At
the start of the graph will usually be one or more data source nodes which have no inputs, but
instead pull their data from a data source. At the end of the graph is an endpoint which represents
the end of the chain and is where the final output is ultimately extracted from.
Each node has a number of input buses and a number of output buses. An output bus from a node is
attached to an input bus of another. Multiple nodes can connect their output buses to another
node's input bus, in which case their outputs will be mixed before processing by the node. Below is
a diagram that illustrates a hypothetical node graph setup:
```
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+---------------+ +-----------------+
| Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+
+---------------+ | | =----+ +-----------------+ | +----------+
+----= Splitter | +----= ENDPOINT |
+---------------+ | | =----+ +-----------------+ | +----------+
| Data Source 2 =----+ +----------+ +----= Echo / Delay =----+
+---------------+ +-----------------+
```
In the above graph, it starts with two data sources whose outputs are attached to the input of a
splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter
performs it's processing routine and produces two outputs which is simply a duplication of the
input stream. One output is attached to a low pass filter, whereas the other output is attached to
a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and
since they're both connected to the same input but, they'll be mixed.
Each input bus must be configured to accept the same number of channels, but the number of channels
used by input buses can be different to the number of channels for output buses in which case
miniaudio will automatically convert the input data to the output channel count before processing.
The number of channels of an output bus of one node must match the channel count of the input bus
it's attached to. The channel counts cannot be changed after the node has been initialized. If you
attempt to attach an output bus to an input bus with a different channel count, attachment will
fail.
To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a
container around the entire graph. The `ma_node_graph` object is required for some thread-safety
issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's
standard config/init system:
```c
ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount);
result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks.
if (result != MA_SUCCESS) {
// Failed to initialize node graph.
}
```
When you initialize the node graph, you're specifying the channel count of the endpoint. The
endpoint is a special node which has one input bus and one output bus, both of which have the
same channel count, which is specified in the config. Any nodes that connect directly to the
endpoint must be configured such that their output buses have the same channel count. When you read
audio data from the node graph, it'll have the channel count you specified in the config. To read
data from the graph:
```c
ma_uint32 framesRead;
result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead);
if (result != MA_SUCCESS) {
// Failed to read data from the node graph.
}
```
When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in
data from it's input attachments, which in turn recusively pull in data from their inputs, and so
on. At the start of the graph there will be some kind of data source node which will have zero
inputs and will instead read directly from a data source. The base nodes don't literally need to
read from a `ma_data_source` object, but they will always have some kind of underlying object that
sources some kind of audio. The `ma_data_source_node` node can be used to read from a
`ma_data_source`. Data is always in floating-point format and in the number of channels you
specified when the graph was initialized. The sample rate is defined by the underlying data sources.
It's up to you to ensure they use a consistent and appropraite sample rate.
The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but
miniaudio includes a few stock nodes for common functionality. This is how you would initialize a
node which reads directly from a data source (`ma_data_source_node`) which is an example of one
of the stock nodes that comes with miniaudio:
```c
ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource);
ma_data_source_node dataSourceNode;
result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode);
if (result != MA_SUCCESS) {
// Failed to create data source node.
}
```
The data source node will use the output channel count to determine the channel count of the output
bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data
source). The data source must output to floating-point (`ma_format_f32`) or else an error will be
returned from `ma_data_source_node_init()`.
By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`:
```c
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint.
ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node.
```
The volume of an output bus can be configured on a per-bus basis:
```c
ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f);
ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f);
```
In the code above we're using the splitter node from before and changing the volume of each of the
copied streams.
You can start and stop a node with the following:
```c
ma_node_set_state(&splitterNode, ma_node_state_started); // The default state.
ma_node_set_state(&splitterNode, ma_node_state_stopped);
```
By default the node is in a started state, but since it won't be connected to anything won't
actually be invoked by the node graph until it's connected. When you stop a node, data will not be
read from any of it's input connections. You can use this property to stop a group of sounds
atomically.
You can configure the initial state of a node in it's config:
```c
nodeConfig.initialState = ma_node_state_stopped;
```
Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member
which is the config to use with the base node. This is where the initial state can be configured
for specialized nodes:
```c
dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped;
```
When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not
modify the `vtable` member of the `nodeConfig` object.
7.1. Timing
-----------
The node graph supports starting and stopping nodes at scheduled times. This is especially useful
for data source nodes where you want to get the node set up, but only start playback at a specific
time. There are two clocks: local and global.
A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can
only be done based on the global clock because the local clock will not be running while the node
is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the
other hand, the local clock only advances when the node's processing callback is fired, and is
advanced based on the output frame count.
To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with
`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline.
Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time,
and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the
audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these
outside of the node processing callbacks which are always run on the audio thread.
There is basic support for scheduling the starting and stopping of nodes. You can only schedule one
start and one stop at a time. This is mainly intended for putting nodes into a started or stopped
state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited
to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks
of several milliseconds. The following APIs can be used for scheduling node states:
```c
ma_node_set_state_time()
ma_node_get_state_time()
```
The time is absolute and must be based on the global clock. An example is below:
```c
ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second.
ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds.
```
An example for changing the state using a relative time.
```c
ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph));
ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph));
```
Note that due to the nature of multi-threading the times may not be 100% exact. If this is an
issue, consider scheduling state changes from within a processing callback. An idea might be to
have some kind of passthrough trigger node that is used specifically for tracking time and handling
events.
7.2. Thread Safety and Locking
------------------------------
When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's
expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so
without the use of any locks. This section discusses the implementation used by miniaudio and goes
over some of the compromises employed by miniaudio to achieve this goal. Note that the current
implementation may not be ideal - feedback and critiques are most welcome.
The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected
to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the
implementation, but are crafted in a way such that such locking is not required when reading audio
data from the graph. Locking in these areas are achieved by means of spinlocks.
The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact
that a node can be uninitialized, and it's memory potentially freed, while in the middle of being
processed on the audio thread. There are times when the audio thread will be referencing a node,
which means the uninitialization process of a node needs to make sure it delays returning until the
audio thread is finished so that control is not handed back to the caller thereby giving them a
chance to free the node's memory.
When the audio thread is processing a node, it does so by reading from each of the output buses of
the node. In order for a node to process data for one of it's output buses, it needs to read from
each of it's input buses, and so on an so forth. It follows that once all output buses of a node
are detached, the node as a whole will be disconnected and no further processing will occur unless
it's output buses are reattached, which won't be happening when the node is being uninitialized.
By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can
simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By
doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output
nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean
up.
With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as
it takes to process the output bus being detached. This will happen if it's called at just the
wrong moment where the audio thread has just iterated it and has just started processing. The
caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which
includes the cost of recursively processing it's inputs. This is the biggest compromise made with
the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes
earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching
higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass
detachments, detach starting from the lowest level nodes and work your way towards the final
endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not
running, detachment will be fast and detachment in any order will be the same. The reason nodes
need to wait for their input attachments to complete is due to the potential for desyncs between
data sources. If the node was to terminate processing mid way through processing it's inputs,
there's a chance that some of the underlying data sources will have been read, but then others not.
That will then result in a potential desynchronization when detaching and reattaching higher-level
nodes. A possible solution to this is to have an option when detaching to terminate processing
before processing all input attachments which should be fairly simple.
Another compromise, albeit less significant, is locking when attaching and detaching nodes. This
locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present
for each input bus and output bus. When an output bus is connected to an input bus, both the output
bus and input bus is locked. This locking is specifically for attaching and detaching across
different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and
unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when
considering that iterating over attachments must not break as a result of attaching or detaching a
node while iteration is occuring.
Attaching and detaching are both quite simple. When an output bus of a node is attached to an input
bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where
each item in the list is and output bus. We have some intentional (and convenient) restrictions on
what can done with the linked list in order to simplify the implementation. First of all, whenever
something needs to iterate over the list, it must do so in a forward direction. Backwards iteration
is not supported. Also, items can only be added to the start of the list.
The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer
to the next item in the list, and another to the previous item. A pointer to the previous item is
only required for fast detachment of the node - it is never used in iteration. This is an
important property because it means from the perspective of iteration, attaching and detaching of
an item can be done with a single atomic assignment. This is exploited by both the attachment and
detachment process. When attaching the node, the first thing that is done is the setting of the
local "next" and "previous" pointers of the node. After that, the item is "attached" to the list
by simply performing an atomic exchange with the head pointer. After that, the node is "attached"
to the list from the perspective of iteration. Even though the "previous" pointer of the next item
hasn't yet been set, from the perspective of iteration it's been attached because iteration will
only be happening in a forward direction which means the "previous" pointer won't actually ever get
used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and
`ma_node_detach_output_bus()` for the implementation of this mechanism.
8. Decoding
===========
The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from
devices and can be used independently. The following formats are supported:
+---------+------------------+----------+
| Format | Decoding Backend | Built-In |
+---------+------------------+----------+
| WAV | dr_wav | Yes |
| MP3 | dr_mp3 | Yes |
| FLAC | dr_flac | Yes |
| Vorbis | stb_vorbis | No |
+---------+------------------+----------+
Vorbis is supported via stb_vorbis which can be enabled by including the header section before the
implementation of miniaudio, like the following:
```c
#define STB_VORBIS_HEADER_ONLY
#include "extras/stb_vorbis.c" // Enables Vorbis decoding.
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
// The stb_vorbis implementation must come after the implementation of miniaudio.
#undef STB_VORBIS_HEADER_ONLY
#include "extras/stb_vorbis.c"
```
A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio).
Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the
built-in decoders by specifying one or more of the following options before the miniaudio
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is
that it does not handle looping for you. You can determine if the buffer is at the end for the
purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of
`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END`
as an error when returned by `ma_audio_buffer_unmap()`.
14. Ring Buffers
================
miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via
the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb`
operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around
`ma_rb`.
Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved
streams. The caller can also allocate their own backing memory for the ring buffer to use
internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for
you.
The examples below use the PCM frame variant of the ring buffer since that's most likely the one
you will want to use. To initialize a ring buffer, do something like the following:
```c
ma_pcm_rb rb;
ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb);
if (result != MA_SUCCESS) {
// Error
}
```
The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because
it's the PCM varient of the ring buffer API. For the regular ring buffer that operates on bytes you
would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes
instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter
is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines.
Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used.
Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is
offset from each other based on the stride. To manage your sub-buffers you can use
`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and
`ma_pcm_rb_get_subbuffer_ptr()`.
Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section
of the ring buffer. You specify the number of frames you need, and on output it will set to what
was actually acquired. If the read or write pointer is positioned such that the number of frames
requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number
of frames you're given may be less than the number you requested.
After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the
buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is
where the read/write pointers are updated. When you commit you need to pass in the buffer that was
returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is
only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and
`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was
originally requested.
If you want to correct for drift between the write pointer and the read pointer you can use a
combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and
`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only
move the read pointer forward via the consumer thread, and the write pointer forward by the
producer thread. If there is too much space between the pointers, move the read pointer forward. If
there is too little space between the pointers, move the write pointer forward.
You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb`
API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and
instead of frame counts you will pass around byte counts.
The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most
significant bit being used to encode a loop flag and the internally managed buffers always being
aligned to `MA_SIMD_ALIGNMENT`.
Note that the ring buffer is only thread safe when used by a single consumer thread and single
producer thread.
15. Backends
============
The following backends are supported by miniaudio.
+-------------+-----------------------+--------------------------------------------------------+
| Name | Enum Name | Supported Operating Systems |
+-------------+-----------------------+--------------------------------------------------------+
| WASAPI | ma_backend_wasapi | Windows Vista+ |
| DirectSound | ma_backend_dsound | Windows XP+ |
| WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
| Core Audio | ma_backend_coreaudio | macOS, iOS |
| ALSA | ma_backend_alsa | Linux |
| PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
| JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
| sndio | ma_backend_sndio | OpenBSD |
| audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
| OSS | ma_backend_oss | FreeBSD |
| AAudio | ma_backend_aaudio | Android 8+ |
| OpenSL ES | ma_backend_opensl | Android (API level 16+) |
| Web Audio | ma_backend_webaudio | Web (via Emscripten) |
| Custom | ma_backend_custom | Cross Platform |
| Null | ma_backend_null | Cross Platform (not used on Web) |
+-------------+-----------------------+--------------------------------------------------------+
Some backends have some nuance details you may want to be aware of.
15.1. WASAPI
------------
- Low-latency shared mode will be disabled when using an application-defined sample rate which is
different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC`
to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing
when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC
will result in miniaudio's internal resampler being used instead which will in turn enable the
use of low-latency shared mode.
15.2. PulseAudio
----------------
- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling.
Alternatively, consider using a different backend such as ALSA.
15.3. Android
-------------
- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
`<uses-permission android:name="android.permission.RECORD_AUDIO" />`
- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a
limitation with OpenSL|ES.
- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration
API (devices are enumerated through Java). You can however perform your own device enumeration
through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it
to ma_device_init().
- The backend API will perform resampling where possible. The reason for this as opposed to using
miniaudio's built-in resampler is to take advantage of any potential device-specific
optimizations the driver may implement.
15.4. UWP
---------
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlong-long"
#if defined(__clang__)
#pragma GCC diagnostic ignored "-Wc++11-long-long"
#endif
#endif
typedef signed long long ma_int64;
typedef unsigned long long ma_uint64;
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic pop
#endif
#endif
#endif /* MA_USE_STDINT */
#if MA_SIZEOF_PTR == 8
typedef ma_uint64 ma_uintptr;
#else
typedef ma_uint32 ma_uintptr;
#endif
typedef ma_uint8 ma_bool8;
typedef ma_uint32 ma_bool32;
#define MA_TRUE 1
#define MA_FALSE 0
typedef void* ma_handle;
typedef void* ma_ptr;
typedef void (* ma_proc)(void);
#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
typedef ma_uint16 wchar_t;
#endif
/* Define NULL for some compilers. */
#ifndef NULL
#define NULL 0
#endif
#if defined(SIZE_MAX)
#define MA_SIZE_MAX SIZE_MAX
#else
#define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
#endif
/* Platform/backend detection. */
#ifdef _WIN32
#define MA_WIN32
#if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))
#define MA_WIN32_UWP
#elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
#define MA_WIN32_GDK
#else
#define MA_WIN32_DESKTOP
#endif
#else
#define MA_POSIX
/*
Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented.
You can use this to avoid including pthread.h in the header section. The downside is that it
results in some fixed sized structures being declared for the various types that are used in
miniaudio. The risk here is that these types might be too small for a given platform. This
risk is yours to take and no support will be offered if you enable this option.
*/
#ifndef MA_NO_PTHREAD_IN_HEADER
#include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
typedef pthread_t ma_pthread_t;
typedef pthread_mutex_t ma_pthread_mutex_t;
typedef pthread_cond_t ma_pthread_cond_t;
#else
typedef ma_uintptr ma_pthread_t;
typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t;
typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t;
#endif
#ifdef __unix__
#define MA_UNIX
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define MA_BSD
#endif
#endif
#ifdef __linux__
#define MA_LINUX
#endif
#ifdef __APPLE__
#define MA_APPLE
#endif
#ifdef __ANDROID__
#define MA_ANDROID
#endif
#ifdef __EMSCRIPTEN__
#define MA_EMSCRIPTEN
#endif
#endif
#ifdef _MSC_VER
#define MA_INLINE __forceinline
#elif defined(__GNUC__)
/*
I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
I am using "__inline__" only when we're compiling in strict ANSI mode.
*/
#if defined(__STRICT_ANSI__)
#define MA_GNUC_INLINE_HINT __inline__
#else
#define MA_GNUC_INLINE_HINT inline
#endif
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
#define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline))
#else
#define MA_INLINE MA_GNUC_INLINE_HINT
#endif
#elif defined(__WATCOMC__)
#define MA_INLINE __inline
#else
#define MA_INLINE
#endif
#if !defined(MA_API)
#if defined(MA_DLL)
#if defined(_WIN32)
#define MA_DLL_IMPORT __declspec(dllimport)
#define MA_DLL_EXPORT __declspec(dllexport)
#define MA_DLL_PRIVATE static
#else
#if defined(__GNUC__) && __GNUC__ >= 4
#define MA_DLL_IMPORT __attribute__((visibility("default")))
#define MA_DLL_EXPORT __attribute__((visibility("default")))
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_standard_sample_rate_16000 = 16000, /* Extreme lows */
ma_standard_sample_rate_11025 = 11250,
ma_standard_sample_rate_8000 = 8000,
ma_standard_sample_rate_352800 = 352800, /* Extreme highs */
ma_standard_sample_rate_384000 = 384000,
ma_standard_sample_rate_min = ma_standard_sample_rate_8000,
ma_standard_sample_rate_max = ma_standard_sample_rate_384000,
ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */
} ma_standard_sample_rate;
typedef enum
{
ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */
ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */
ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */
ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular
} ma_channel_mix_mode;
typedef enum
{
ma_standard_channel_map_microsoft,
ma_standard_channel_map_alsa,
ma_standard_channel_map_rfc3551, /* Based off AIFF. */
ma_standard_channel_map_flac,
ma_standard_channel_map_vorbis,
ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */
ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */
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. */
ma_standard_channel_map_default = ma_standard_channel_map_microsoft
} ma_standard_channel_map;
typedef enum
{
ma_performance_profile_low_latency = 0,
ma_performance_profile_conservative
} ma_performance_profile;
typedef struct
{
void* pUserData;
void* (* onMalloc)(size_t sz, void* pUserData);
void* (* onRealloc)(void* p, size_t sz, void* pUserData);
void (* onFree)(void* p, void* pUserData);
} ma_allocation_callbacks;
typedef struct
{
ma_int32 state;
} ma_lcg;
/* Spinlocks are 32-bit for compatibility reasons. */
typedef ma_uint32 ma_spinlock;
#ifndef MA_NO_THREADING
/* Thread priorities should be ordered such that the default priority of the worker thread is 0. */
typedef enum
{
ma_thread_priority_idle = -5,
ma_thread_priority_lowest = -4,
ma_thread_priority_low = -3,
ma_thread_priority_normal = -2,
ma_thread_priority_high = -1,
ma_thread_priority_highest = 0,
ma_thread_priority_realtime = 1,
ma_thread_priority_default = 0
} ma_thread_priority;
#if defined(MA_WIN32)
typedef ma_handle ma_thread;
#endif
#if defined(MA_POSIX)
typedef ma_pthread_t ma_thread;
#endif
#if defined(MA_WIN32)
typedef ma_handle ma_mutex;
#endif
#if defined(MA_POSIX)
typedef ma_pthread_mutex_t ma_mutex;
#endif
#if defined(MA_WIN32)
typedef ma_handle ma_event;
#endif
#if defined(MA_POSIX)
typedef struct
{
ma_uint32 value;
ma_pthread_mutex_t lock;
ma_pthread_cond_t cond;
} ma_event;
#endif /* MA_POSIX */
#if defined(MA_WIN32)
typedef ma_handle ma_semaphore;
#endif
#if defined(MA_POSIX)
typedef struct
{
int value;
ma_pthread_mutex_t lock;
ma_pthread_cond_t cond;
} ma_semaphore;
#endif /* MA_POSIX */
#else
/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */
#ifndef MA_NO_DEVICE_IO
#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
#endif
#endif /* MA_NO_THREADING */
/*
Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required.
*/
MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
/*
Retrieves the version of miniaudio as a string which can be useful for logging purposes.
*/
MA_API const char* ma_version_string(void);
/**************************************************************************************************************************************************************
Logging
**************************************************************************************************************************************************************/
#include <stdarg.h> /* For va_list. */
#if defined(__has_attribute)
#if __has_attribute(format)
#define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va)))
#endif
#endif
#ifndef MA_ATTRIBUTE_FORMAT
#define MA_ATTRIBUTE_FORMAT(fmt,va)
#endif
#ifndef MA_MAX_LOG_CALLBACKS
#define MA_MAX_LOG_CALLBACKS 4
#endif
/*
The callback for handling log messages.
Parameters
----------
pUserData (in)
The user data pointer that was passed into ma_log_register_callback().
logLevel (in)
The log level. This can be one of the following:
+----------------------+
| Log Level |
+----------------------+
| MA_LOG_LEVEL_DEBUG |
| MA_LOG_LEVEL_INFO |
| MA_LOG_LEVEL_WARNING |
| MA_LOG_LEVEL_ERROR |
+----------------------+
pMessage (in)
The log message.
Remarks
-------
Do not modify the state of the device from inside the callback.
*/
typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage);
typedef struct
{
ma_log_callback_proc onLog;
void* pUserData;
} ma_log_callback;
MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData);
typedef struct
{
ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS];
ma_uint32 callbackCount;
ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */
#ifndef MA_NO_THREADING
ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */
#endif
} ma_log;
MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog);
MA_API void ma_log_uninit(ma_log* pLog);
MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback);
MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback);
MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage);
MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args);
MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4);
/**************************************************************************************************************************************************************
Biquad Filtering
**************************************************************************************************************************************************************/
typedef union
{
float f32;
ma_int32 s32;
} ma_biquad_coefficient;
typedef struct
{
ma_format format;
ma_uint32 channels;
double b0;
double b1;
double b2;
double a0;
double a1;
double a2;
} ma_biquad_config;
MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2);
typedef struct
{
ma_format format;
ma_uint32 channels;
ma_biquad_coefficient b0;
ma_biquad_coefficient b1;
ma_biquad_coefficient b2;
ma_biquad_coefficient a1;
ma_biquad_coefficient a2;
ma_biquad_coefficient* pR1;
ma_biquad_coefficient* pR2;
/* Memory management. */
void* _pHeap;
ma_bool32 _ownsHeap;
} ma_biquad;
MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes);
MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ);
MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ);
MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);
MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
*/
MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock);
/*
Unlocks a spinlock.
*/
MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock);
#ifndef MA_NO_THREADING
/*
Creates a mutex.
A mutex must be created from a valid context. A mutex is initially unlocked.
*/
MA_API ma_result ma_mutex_init(ma_mutex* pMutex);
/*
Deletes a mutex.
*/
MA_API void ma_mutex_uninit(ma_mutex* pMutex);
/*
Locks a mutex with an infinite timeout.
*/
MA_API void ma_mutex_lock(ma_mutex* pMutex);
/*
Unlocks a mutex.
*/
MA_API void ma_mutex_unlock(ma_mutex* pMutex);
/*
Initializes an auto-reset event.
*/
MA_API ma_result ma_event_init(ma_event* pEvent);
/*
Uninitializes an auto-reset event.
*/
MA_API void ma_event_uninit(ma_event* pEvent);
/*
Waits for the specified auto-reset event to become signalled.
*/
MA_API ma_result ma_event_wait(ma_event* pEvent);
/*
Signals the specified auto-reset event.
*/
MA_API ma_result ma_event_signal(ma_event* pEvent);
#endif /* MA_NO_THREADING */
/*
Fence
=====
This locks while the counter is larger than 0. Counter can be incremented and decremented by any
thread, but care needs to be taken when waiting. It is possible for one thread to acquire the
fence just as another thread returns from ma_fence_wait().
The idea behind a fence is to allow you to wait for a group of operations to complete. When an
operation starts, the counter is incremented which locks the fence. When the operation completes,
the fence will be released which decrements the counter. ma_fence_wait() will block until the
counter hits zero.
If threading is disabled, ma_fence_wait() will spin on the counter.
*/
typedef struct
{
#ifndef MA_NO_THREADING
ma_event e;
#endif
ma_uint32 counter;
} ma_fence;
MA_API ma_result ma_fence_init(ma_fence* pFence);
MA_API void ma_fence_uninit(ma_fence* pFence);
MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */
MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */
MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */
/*
Notification callback for asynchronous operations.
*/
typedef void ma_async_notification;
typedef struct
{
void (* onSignal)(ma_async_notification* pNotification);
} ma_async_notification_callbacks;
MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification);
/*
Simple polling notification.
This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled()
*/
typedef struct
{
ma_async_notification_callbacks cb;
ma_bool32 signalled;
} ma_async_notification_poll;
MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll);
MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll);
/*
Event Notification
This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail.
*/
typedef struct
{
ma_async_notification_callbacks cb;
#ifndef MA_NO_THREADING
ma_event e;
#endif
} ma_async_notification_event;
MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent);
MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent);
MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent);
MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent);
/************************************************************************************************************************************************************
Job Queue
************************************************************************************************************************************************************/
/*
Slot Allocator
--------------
The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used
as the insertion point for an object.
Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs.
The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits:
+-----------------+-----------------+
| 32 Bits | 32 Bits |
+-----------------+-----------------+
| Reference Count | Slot Index |
+-----------------+-----------------+
*/
typedef struct
{
ma_uint32 capacity; /* The number of slots to make available. */
} ma_slot_allocator_config;
MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity);
typedef struct
{
MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */
} ma_slot_allocator_group;
typedef struct
{
ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */
ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */
ma_uint32 count; /* Allocation count. */
ma_uint32 capacity;
/* Memory management. */
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
{
ma_job_proc proc;
ma_uintptr data0;
ma_uintptr data1;
} custom;
/* Resource Manager */
union
{
struct
{
/*ma_resource_manager**/ void* pResourceManager;
/*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
char* pFilePath;
wchar_t* pFilePathW;
ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */
ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */
ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */
ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */
} loadDataBufferNode;
struct
{
/*ma_resource_manager**/ void* pResourceManager;
/*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
ma_async_notification* pDoneNotification;
ma_fence* pDoneFence;
} freeDataBufferNode;
struct
{
/*ma_resource_manager**/ void* pResourceManager;
/*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
/*ma_decoder**/ void* pDecoder;
ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */
} pageDataBufferNode;
struct
{
/*ma_resource_manager_data_buffer**/ void* pDataBuffer;
ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */
ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */
ma_uint64 rangeBegInPCMFrames;
ma_uint64 rangeEndInPCMFrames;
ma_uint64 loopPointBegInPCMFrames;
ma_uint64 loopPointEndInPCMFrames;
ma_uint32 isLooping;
} loadDataBuffer;
struct
{
/*ma_resource_manager_data_buffer**/ void* pDataBuffer;
ma_async_notification* pDoneNotification;
ma_fence* pDoneFence;
} freeDataBuffer;
struct
{
/*ma_resource_manager_data_stream**/ void* pDataStream;
char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */
wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */
ma_uint64 initialSeekPoint;
ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */
ma_fence* pInitFence;
} loadDataStream;
struct
{
/*ma_resource_manager_data_stream**/ void* pDataStream;
ma_async_notification* pDoneNotification;
ma_fence* pDoneFence;
} freeDataStream;
struct
{
/*ma_resource_manager_data_stream**/ void* pDataStream;
ma_uint32 pageIndex; /* The index of the page to decode into. */
} pageDataStream;
struct
{
/*ma_resource_manager_data_stream**/ void* pDataStream;
ma_uint64 frameIndex;
} seekDataStream;
} resourceManager;
/* Device. */
union
{
union
{
struct
{
/*ma_device**/ void* pDevice;
/*ma_device_type*/ ma_uint32 deviceType;
} reroute;
} aaudio;
} device;
} data;
};
MA_API ma_job ma_job_init(ma_uint16 code);
MA_API ma_result ma_job_process(ma_job* pJob);
/*
When set, ma_job_queue_next() will not wait and no semaphore will be signaled in
ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available.
This flag should always be used for platforms that do not support multithreading.
*/
typedef enum
{
MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001
} ma_job_queue_flags;
typedef struct
{
ma_uint32 flags;
ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */
} ma_job_queue_config;
MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity);
typedef struct
{
ma_uint32 flags; /* Flags passed in at initialization time. */
ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */
MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */
MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */
#ifndef MA_NO_THREADING
ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */
#endif
ma_slot_allocator allocator;
ma_job* pJobs;
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
ma_spinlock lock;
#endif
/* Memory management. */
void* _pHeap;
ma_bool32 _ownsHeap;
} ma_job_queue;
MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes);
MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue);
MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue);
MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob);
MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */
/************************************************************************************************************************************************************
*************************************************************************************************************************************************************
DEVICE I/O
==========
This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.
*************************************************************************************************************************************************************
************************************************************************************************************************************************************/
#ifndef MA_NO_DEVICE_IO
/* Some backends are only supported on certain platforms. */
#if defined(MA_WIN32)
#define MA_SUPPORT_WASAPI
#if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
#define MA_SUPPORT_DSOUND
#define MA_SUPPORT_WINMM
#define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
#endif
#endif
#if defined(MA_UNIX)
#if defined(MA_LINUX)
#if !defined(MA_ANDROID) /* ALSA is not supported on Android. */
#define MA_SUPPORT_ALSA
#endif
#endif
#if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
#define MA_SUPPORT_PULSEAUDIO
#define MA_SUPPORT_JACK
#endif
#if defined(MA_ANDROID)
#define MA_SUPPORT_AAUDIO
#define MA_SUPPORT_OPENSL
#endif
#if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
#define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__)
#define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
#define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */
#endif
#endif
#if defined(MA_APPLE)
#define MA_SUPPORT_COREAUDIO
#endif
#if defined(MA_EMSCRIPTEN)
#define MA_SUPPORT_WEBAUDIO
#endif
/* All platforms should support custom backends. */
#define MA_SUPPORT_CUSTOM
/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
#if !defined(MA_EMSCRIPTEN)
#define MA_SUPPORT_NULL
#endif
#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI))
#define MA_HAS_WASAPI
#endif
#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND))
#define MA_HAS_DSOUND
#endif
#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM))
#define MA_HAS_WINMM
#endif
#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA))
#define MA_HAS_ALSA
#endif
#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO))
#define MA_HAS_PULSEAUDIO
#endif
#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK))
#define MA_HAS_JACK
#endif
#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO))
#define MA_HAS_COREAUDIO
#endif
#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO))
#define MA_HAS_SNDIO
#endif
#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4))
#define MA_HAS_AUDIO4
#endif
#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS))
#define MA_HAS_OSS
#endif
#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO))
#define MA_HAS_AAUDIO
#endif
#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL))
#define MA_HAS_OPENSL
#endif
#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
#define MA_HAS_WEBAUDIO
#endif
#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
#define MA_HAS_CUSTOM
#endif
#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))
#define MA_HAS_NULL
#endif
typedef enum
{
ma_device_state_uninitialized = 0,
ma_device_state_stopped = 1, /* The device's default state after initialization. */
ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */
ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */
ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */
} ma_device_state;
#ifdef MA_SUPPORT_WASAPI
/* We need a IMMNotificationClient object for WASAPI. */
typedef struct
{
void* lpVtbl;
ma_uint32 counter;
ma_device* pDevice;
} ma_IMMNotificationClient;
#endif
/* Backend enums must be in priority order. */
typedef enum
{
ma_backend_wasapi,
ma_backend_dsound,
ma_backend_winmm,
ma_backend_coreaudio,
ma_backend_sndio,
ma_backend_audio4,
ma_backend_oss,
ma_backend_pulseaudio,
ma_backend_alsa,
ma_backend_jack,
ma_backend_aaudio,
ma_backend_opensl,
ma_backend_webaudio,
ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */
ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
} ma_backend;
#define MA_BACKEND_COUNT (ma_backend_null+1)
/*
Device job thread. This is used by backends that require asynchronous processing of certain
operations. It is not used by all backends.
The device job thread is made up of a thread and a job queue. You can post a job to the thread with
ma_device_job_thread_post(). The thread will do the processing of the job.
*/
typedef struct
{
ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */
ma_uint32 jobQueueCapacity;
ma_uint32 jobQueueFlags;
} ma_device_job_thread_config;
MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void);
typedef struct
{
ma_thread thread;
ma_job_queue jobQueue;
ma_bool32 _hasThread;
} ma_device_job_thread;
MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread);
MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob);
MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob);
/* Device notification types. */
typedef enum
{
ma_device_notification_type_started,
ma_device_notification_type_stopped,
ma_device_notification_type_rerouted,
ma_device_notification_type_interruption_began,
ma_device_notification_type_interruption_ended
} ma_device_notification_type;
typedef struct
{
ma_device* pDevice;
ma_device_notification_type type;
union
{
struct
{
int _unused;
} started;
struct
{
int _unused;
} stopped;
struct
{
int _unused;
} rerouted;
struct
{
int _unused;
} interruption;
} data;
} ma_device_notification;
/*
The notification callback for when the application should be notified of a change to the device.
This callback is used for notifying the application of changes such as when the device has started,
stopped, rerouted or an interruption has occurred. Note that not all backends will post all
notification types. For example, some backends will perform automatic stream routing without any
kind of notification to the host program which means miniaudio will never know about it and will
never be able to fire the rerouted notification. You should keep this in mind when designing your
program.
The stopped notification will *not* get fired when a device is rerouted.
Parameters
----------
pNotification (in)
A pointer to a structure containing information about the event. Use the `pDevice` member of
this object to retrieve the relevant device. The `type` member can be used to discriminate
against each of the notification types.
Remarks
-------
Do not restart or uninitialize the device from the callback.
Not all notifications will be triggered by all backends, however the started and stopped events
should be reliable for all backends. Some backends do not have a good way to detect device
stoppages due to unplugging the device which may result in the stopped callback not getting
fired. This has been observed with at least one BSD variant.
The rerouted notification is fired *after* the reroute has occurred. The stopped notification will
*not* get fired when a device is rerouted. The following backends are known to do automatic stream
rerouting, but do not have a way to be notified of the change:
* DirectSound
The interruption notifications are used on mobile platforms for detecting when audio is interrupted
due to things like an incoming phone call. Currently this is only implemented on iOS. None of the
Android backends will report this notification.
*/
typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification);
/*
The callback for processing audio data from the device.
The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data
available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the
callback will be fired with a consistent frame count.
Parameters
----------
pDevice (in)
A pointer to the relevant device.
pOutput (out)
A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or
full-duplex device and null for a capture and loopback device.
pInput (in)
A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a
playback device.
frameCount (in)
The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The
`periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must
not assume this will always be the same value each time the callback is fired.
Remarks
-------
You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the
callback. The following APIs cannot be called from inside the callback:
ma_device_init()
ma_device_init_ex()
ma_device_uninit()
ma_device_start()
ma_device_stop()
The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread.
*/
typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
/*
DEPRECATED. Use ma_device_notification_proc instead.
The callback for when the device has been stopped.
This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces
such as being unplugged or an internal error occuring.
Parameters
----------
pDevice (in)
A pointer to the device that has just stopped.
Remarks
-------
Do not restart or uninitialize the device from the callback.
*/
typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */
typedef enum
{
ma_device_type_playback = 1,
ma_device_type_capture = 2,
ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */
ma_device_type_loopback = 4
} ma_device_type;
typedef enum
{
ma_share_mode_shared = 0,
ma_share_mode_exclusive
} ma_share_mode;
/* iOS/tvOS/watchOS session categories. */
typedef enum
{
ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord with AVAudioSessionCategoryOptionDefaultToSpeaker. */
ma_ios_session_category_none, /* Leave the session category unchanged. */
ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */
ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */
ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */
ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */
ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */
ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */
} ma_ios_session_category;
/* iOS/tvOS/watchOS session category options */
typedef enum
{
ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */
ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */
ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_channel channelMap[MA_MAX_CHANNELS];
ma_uint32 periodSizeInFrames;
ma_uint32 periodSizeInMilliseconds;
ma_uint32 periodCount;
} ma_device_descriptor;
/*
These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context
to many devices. A device is created from a context.
The general flow goes like this:
1) A context is created with `onContextInit()`
1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required.
1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required.
2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was
selected from device enumeration via `onContextEnumerateDevices()`.
3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()`
4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call
to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by
miniaudio internally.
Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the
callbacks defined in this structure.
Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which
physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the
given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration
needs to stop and the `onContextEnumerateDevices()` function returns with a success code.
Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID,
and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the
case when the device ID is NULL, in which case information about the default device needs to be retrieved.
Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created.
This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a
device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input,
the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to
the requested format. The conversion between the format requested by the application and the device's native format will be handled
internally by miniaudio.
On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's
supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for
sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to
`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should
inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period
size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the
sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_data_format`
object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set).
Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses
asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented.
The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit
easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and
`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the
backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback.
This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback.
If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback
which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been
encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback.
The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this
callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated
which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback,
look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to
wake up the audio thread.
If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the
`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient.
*/
struct ma_backend_callbacks
{
ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks);
ma_result (* onContextUninit)(ma_context* pContext);
ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);
ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture);
ma_result (* onDeviceUninit)(ma_device* pDevice);
ma_result (* onDeviceStart)(ma_device* pDevice);
ma_result (* onDeviceStop)(ma_device* pDevice);
ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead);
ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten);
ma_result (* onDeviceDataLoop)(ma_device* pDevice);
ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice);
ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo);
};
struct ma_context_config
{
ma_log* pLog;
ma_thread_priority threadPriority;
size_t threadStackSize;
void* pUserData;
ma_allocation_callbacks allocationCallbacks;
struct
{
ma_bool32 useVerboseDeviceEnumeration;
} alsa;
struct
{
const char* pApplicationName;
const char* pServerName;
ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */
} pulse;
struct
{
ma_ios_session_category sessionCategory;
ma_uint32 sessionCategoryOptions;
ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */
ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */
} coreaudio;
struct
{
const char* pClientName;
ma_bool32 tryStartServer;
} jack;
ma_backend_callbacks custom;
};
/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */
typedef struct
{
int code;
ma_event* pEvent; /* This will be signalled when the event is complete. */
union
{
struct
{
int _unused;
} quit;
struct
{
ma_device_type deviceType;
void* pAudioClient;
void** ppAudioClientService;
ma_result* pResult; /* The result from creating the audio client service. */
} createAudioClient;
struct
{
ma_device* pDevice;
ma_device_type deviceType;
} releaseAudioClient;
} data;
} ma_context_command__wasapi;
struct ma_context
{
ma_backend_callbacks callbacks;
ma_backend backend; /* DirectSound, ALSA, etc. */
ma_log* pLog;
ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */
ma_thread_priority threadPriority;
size_t threadStackSize;
void* pUserData;
ma_allocation_callbacks allocationCallbacks;
ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */
ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */
ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */
ma_uint32 playbackDeviceInfoCount;
ma_uint32 captureDeviceInfoCount;
ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */
union
{
#ifdef MA_SUPPORT_WASAPI
struct
{
ma_thread commandThread;
ma_mutex commandLock;
ma_semaphore commandSem;
ma_uint32 commandIndex;
ma_uint32 commandCount;
ma_context_command__wasapi commands[4];
} wasapi;
#endif
#ifdef MA_SUPPORT_DSOUND
struct
{
ma_handle hDSoundDLL;
ma_proc DirectSoundCreate;
ma_proc DirectSoundEnumerateA;
ma_proc DirectSoundCaptureCreate;
ma_proc DirectSoundCaptureEnumerateA;
} dsound;
#endif
#ifdef MA_SUPPORT_WINMM
struct
{
ma_handle hWinMM;
ma_proc waveOutGetNumDevs;
ma_proc waveOutGetDevCapsA;
ma_proc waveOutOpen;
ma_proc waveOutClose;
ma_proc waveOutPrepareHeader;
ma_proc waveOutUnprepareHeader;
ma_proc waveOutWrite;
ma_proc waveOutReset;
ma_proc waveInGetNumDevs;
ma_proc waveInGetDevCapsA;
ma_proc waveInOpen;
ma_proc waveInClose;
ma_proc waveInPrepareHeader;
ma_proc waveInUnprepareHeader;
ma_proc waveInAddBuffer;
ma_proc waveInStart;
ma_proc waveInReset;
} winmm;
#endif
#ifdef MA_SUPPORT_ALSA
struct
{
ma_handle asoundSO;
ma_proc snd_pcm_open;
ma_proc snd_pcm_close;
ma_proc snd_pcm_hw_params_sizeof;
ma_proc snd_pcm_hw_params_any;
ma_proc snd_pcm_hw_params_set_format;
ma_proc snd_pcm_hw_params_set_format_first;
ma_proc snd_pcm_hw_params_get_format_mask;
ma_proc snd_pcm_hw_params_set_channels;
ma_proc snd_pcm_hw_params_set_channels_near;
ma_proc snd_pcm_hw_params_set_channels_minmax;
ma_proc snd_pcm_hw_params_set_rate_resample;
ma_proc snd_pcm_hw_params_set_rate;
ma_proc snd_pcm_hw_params_set_rate_near;
ma_proc snd_pcm_hw_params_set_buffer_size_near;
ma_proc snd_pcm_hw_params_set_periods_near;
ma_proc snd_pcm_hw_params_set_access;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_proc snd_pcm_hw_params_get_rate_min;
ma_proc snd_pcm_hw_params_get_rate_max;
ma_proc snd_pcm_hw_params_get_buffer_size;
ma_proc snd_pcm_hw_params_get_periods;
ma_proc snd_pcm_hw_params_get_access;
ma_proc snd_pcm_hw_params_test_format;
ma_proc snd_pcm_hw_params_test_channels;
ma_proc snd_pcm_hw_params_test_rate;
ma_proc snd_pcm_hw_params;
ma_proc snd_pcm_sw_params_sizeof;
ma_proc snd_pcm_sw_params_current;
ma_proc snd_pcm_sw_params_get_boundary;
ma_proc snd_pcm_sw_params_set_avail_min;
ma_proc snd_pcm_sw_params_set_start_threshold;
ma_proc snd_pcm_sw_params_set_stop_threshold;
ma_proc snd_pcm_sw_params;
ma_proc snd_pcm_format_mask_sizeof;
ma_proc snd_pcm_format_mask_test;
ma_proc snd_pcm_get_chmap;
ma_proc snd_pcm_state;
ma_proc snd_pcm_prepare;
ma_proc snd_pcm_start;
ma_proc snd_pcm_drop;
ma_proc snd_pcm_drain;
ma_proc snd_pcm_reset;
ma_proc snd_device_name_hint;
ma_proc snd_device_name_get_hint;
ma_proc snd_card_get_index;
ma_proc snd_device_name_free_hint;
ma_proc snd_pcm_mmap_begin;
ma_proc snd_pcm_mmap_commit;
ma_proc snd_pcm_recover;
ma_proc snd_pcm_readi;
ma_proc snd_pcm_writei;
ma_proc snd_pcm_avail;
ma_proc snd_pcm_avail_update;
ma_proc snd_pcm_wait;
ma_proc snd_pcm_nonblock;
ma_proc snd_pcm_info;
ma_proc snd_pcm_info_sizeof;
ma_proc snd_pcm_info_get_name;
ma_proc snd_pcm_poll_descriptors;
ma_proc snd_pcm_poll_descriptors_count;
ma_proc snd_pcm_poll_descriptors_revents;
ma_proc snd_config_update_free_global;
ma_mutex internalDeviceEnumLock;
ma_bool32 useVerboseDeviceEnumeration;
} alsa;
#endif
#ifdef MA_SUPPORT_PULSEAUDIO
struct
{
ma_handle pulseSO;
ma_proc pa_mainloop_new;
ma_proc pa_mainloop_free;
ma_proc pa_mainloop_quit;
ma_proc pa_mainloop_get_api;
ma_proc pa_mainloop_iterate;
ma_proc pa_mainloop_wakeup;
ma_proc pa_threaded_mainloop_new;
ma_proc pa_threaded_mainloop_free;
ma_proc pa_threaded_mainloop_start;
ma_proc pa_threaded_mainloop_stop;
ma_proc pa_threaded_mainloop_lock;
ma_proc pa_threaded_mainloop_unlock;
ma_proc pa_threaded_mainloop_wait;
ma_proc pa_threaded_mainloop_signal;
ma_proc pa_threaded_mainloop_accept;
ma_proc pa_threaded_mainloop_get_retval;
ma_proc pa_threaded_mainloop_get_api;
ma_proc pa_threaded_mainloop_in_thread;
ma_proc pa_threaded_mainloop_set_name;
ma_proc pa_context_new;
ma_proc pa_context_unref;
ma_proc pa_context_connect;
ma_proc pa_context_disconnect;
ma_proc pa_context_set_state_callback;
ma_proc pa_context_get_state;
ma_proc pa_context_get_sink_info_list;
ma_proc pa_context_get_source_info_list;
ma_proc pa_context_get_sink_info_by_name;
ma_proc pa_context_get_source_info_by_name;
ma_proc pa_operation_unref;
ma_proc pa_operation_get_state;
ma_proc pa_channel_map_init_extend;
ma_proc pa_channel_map_valid;
ma_proc pa_channel_map_compatible;
ma_proc pa_stream_new;
ma_proc pa_stream_unref;
ma_proc pa_stream_connect_playback;
ma_proc pa_stream_connect_record;
ma_proc pa_stream_disconnect;
ma_proc pa_stream_get_state;
ma_proc pa_stream_get_sample_spec;
ma_proc pa_stream_get_channel_map;
ma_proc pa_stream_get_buffer_attr;
ma_proc pa_stream_set_buffer_attr;
ma_proc pa_stream_get_device_name;
ma_proc pa_stream_set_write_callback;
ma_proc pa_stream_set_read_callback;
ma_proc pa_stream_set_suspended_callback;
ma_proc pa_stream_set_moved_callback;
ma_proc pa_stream_is_suspended;
ma_proc pa_stream_flush;
ma_proc pa_stream_drain;
ma_proc pa_stream_is_corked;
ma_proc pa_stream_cork;
ma_proc pa_stream_trigger;
ma_proc pa_stream_begin_write;
ma_proc pa_stream_write;
ma_proc pa_stream_peek;
ma_proc pa_stream_drop;
ma_proc pa_stream_writable_size;
ma_proc pa_stream_readable_size;
/*pa_mainloop**/ ma_ptr pMainLoop;
/*pa_context**/ ma_ptr pPulseContext;
char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */
char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */
} pulse;
#endif
#ifdef MA_SUPPORT_JACK
struct
{
ma_handle jackSO;
ma_proc jack_client_open;
ma_proc jack_client_close;
ma_proc jack_client_name_size;
ma_proc jack_set_process_callback;
ma_proc jack_set_buffer_size_callback;
ma_proc jack_on_shutdown;
ma_proc jack_get_sample_rate;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_proc sio_getcap;
ma_proc sio_start;
ma_proc sio_stop;
ma_proc sio_read;
ma_proc sio_write;
ma_proc sio_onmove;
ma_proc sio_nfds;
ma_proc sio_pollfd;
ma_proc sio_revents;
ma_proc sio_eof;
ma_proc sio_setvol;
ma_proc sio_onvol;
ma_proc sio_initpar;
} sndio;
#endif
#ifdef MA_SUPPORT_AUDIO4
struct
{
int _unused;
} audio4;
#endif
#ifdef MA_SUPPORT_OSS
struct
{
int versionMajor;
int versionMinor;
} oss;
#endif
#ifdef MA_SUPPORT_AAUDIO
struct
{
ma_handle hAAudio; /* libaaudio.so */
ma_proc AAudio_createStreamBuilder;
ma_proc AAudioStreamBuilder_delete;
ma_proc AAudioStreamBuilder_setDeviceId;
ma_proc AAudioStreamBuilder_setDirection;
ma_proc AAudioStreamBuilder_setSharingMode;
ma_proc AAudioStreamBuilder_setFormat;
ma_proc AAudioStreamBuilder_setChannelCount;
ma_proc AAudioStreamBuilder_setSampleRate;
ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
ma_proc AAudioStreamBuilder_setDataCallback;
ma_proc AAudioStreamBuilder_setErrorCallback;
ma_proc AAudioStreamBuilder_setPerformanceMode;
ma_proc AAudioStreamBuilder_setUsage;
ma_proc AAudioStreamBuilder_setContentType;
ma_proc AAudioStreamBuilder_setInputPreset;
ma_proc AAudioStreamBuilder_openStream;
ma_proc AAudioStream_close;
ma_proc AAudioStream_getState;
ma_proc AAudioStream_waitForStateChange;
ma_proc AAudioStream_getFormat;
ma_proc AAudioStream_getChannelCount;
ma_proc AAudioStream_getSampleRate;
ma_proc AAudioStream_getBufferCapacityInFrames;
ma_proc AAudioStream_getFramesPerDataCallback;
ma_proc AAudioStream_getFramesPerBurst;
ma_proc AAudioStream_requestStart;
ma_proc AAudioStream_requestStop;
ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */
} aaudio;
#endif
#ifdef MA_SUPPORT_OPENSL
struct
{
ma_handle libOpenSLES;
ma_handle SL_IID_ENGINE;
ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;
ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
ma_handle SL_IID_RECORD;
ma_handle SL_IID_PLAY;
ma_handle SL_IID_OUTPUTMIX;
ma_handle SL_IID_ANDROIDCONFIGURATION;
ma_proc slCreateEngine;
} opensl;
#endif
#ifdef MA_SUPPORT_WEBAUDIO
struct
{
int _unused;
} webaudio;
#endif
#ifdef MA_SUPPORT_NULL
struct
{
int _unused;
} null_backend;
#endif
};
union
{
#ifdef MA_WIN32
struct
{
/*HMODULE*/ ma_handle hOle32DLL;
ma_proc CoInitializeEx;
ma_proc CoUninitialize;
ma_proc CoCreateInstance;
ma_proc CoTaskMemFree;
ma_proc PropVariantClear;
ma_proc StringFromGUID2;
/*HMODULE*/ ma_handle hUser32DLL;
ma_proc GetForegroundWindow;
ma_proc GetDesktopWindow;
/*HMODULE*/ ma_handle hAdvapi32DLL;
ma_proc RegOpenKeyExA;
ma_proc RegCloseKey;
ma_proc RegQueryValueExA;
} win32;
#endif
#ifdef MA_POSIX
struct
{
ma_handle pthreadSO;
ma_proc pthread_create;
ma_proc pthread_join;
ma_proc pthread_mutex_init;
ma_proc pthread_mutex_destroy;
ma_proc pthread_mutex_lock;
ma_proc pthread_mutex_unlock;
ma_proc pthread_cond_init;
ma_proc pthread_cond_destroy;
ma_proc pthread_cond_wait;
ma_proc pthread_cond_signal;
ma_proc pthread_attr_init;
ma_proc pthread_attr_destroy;
ma_proc pthread_attr_setschedpolicy;
ma_proc pthread_attr_getschedparam;
ma_proc pthread_attr_setschedparam;
} posix;
#endif
int _unused;
};
};
struct ma_device
{
ma_context* pContext;
ma_device_type type;
ma_uint32 sampleRate;
MA_ATOMIC(4, ma_device_state) state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */
ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */
ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */
ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */
void* pUserData; /* Application defined data. */
ma_mutex startStopLock;
ma_event wakeupEvent;
ma_event startEvent;
ma_event stopEvent;
ma_thread thread;
ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */
ma_bool8 noPreSilencedOutputBuffer;
ma_bool8 noClip;
ma_bool8 noDisableDenormals;
ma_bool8 noFixedSizedCallback;
MA_ATOMIC(4, float) masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */
ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */
struct
{
ma_resample_algorithm algorithm;
ma_resampling_backend_vtable* pBackendVTable;
void* pBackendUserData;
struct
{
ma_uint32 lpfOrder;
} linear;
} resampling;
struct
{
ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */
ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */
char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */
ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
ma_format format;
ma_uint32 channels;
ma_channel channelMap[MA_MAX_CHANNELS];
ma_format internalFormat;
ma_uint32 internalChannels;
ma_uint32 internalSampleRate;
ma_channel internalChannelMap[MA_MAX_CHANNELS];
ma_uint32 internalPeriodSizeInFrames;
ma_uint32 internalPeriods;
ma_channel_mix_mode channelMixMode;
ma_data_converter converter;
void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */
ma_uint32 intermediaryBufferCap;
ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */
void* pInputCache; /* In external format. Can be null. */
ma_uint64 inputCacheCap;
ma_uint64 inputCacheConsumed;
ma_uint64 inputCacheRemaining;
} playback;
struct
{
ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */
ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */
char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */
ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
ma_format format;
ma_uint32 channels;
ma_channel channelMap[MA_MAX_CHANNELS];
ma_format internalFormat;
ma_uint32 internalChannels;
ma_uint32 internalSampleRate;
ma_channel internalChannelMap[MA_MAX_CHANNELS];
ma_uint32 internalPeriodSizeInFrames;
ma_uint32 internalPeriods;
ma_channel_mix_mode channelMixMode;
ma_data_converter converter;
void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */
ma_uint32 intermediaryBufferCap;
ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */
} capture;
union
{
#ifdef MA_SUPPORT_WASAPI
struct
{
/*IAudioClient**/ ma_ptr pAudioClientPlayback;
/*IAudioClient**/ ma_ptr pAudioClientCapture;
/*IAudioRenderClient**/ ma_ptr pRenderClient;
/*IAudioCaptureClient**/ ma_ptr pCaptureClient;
/*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
ma_IMMNotificationClient notificationClient;
/*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
/*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works...
ma_uint32 actualBufferSizeInFramesCapture;
ma_uint32 originalPeriodSizeInFrames;
ma_uint32 originalPeriodSizeInMilliseconds;
ma_uint32 originalPeriods;
ma_performance_profile originalPerformanceProfile;
ma_uint32 periodSizeInFramesPlayback;
ma_uint32 periodSizeInFramesCapture;
void* pMappedBufferCapture;
ma_uint32 mappedBufferCaptureCap;
ma_uint32 mappedBufferCaptureLen;
void* pMappedBufferPlayback;
ma_uint32 mappedBufferPlaybackCap;
ma_uint32 mappedBufferPlaybackLen;
MA_ATOMIC(4, ma_bool32) isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
MA_ATOMIC(4, ma_bool32) isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
ma_bool8 noHardwareOffloading;
ma_bool8 allowCaptureAutoStreamRouting;
ma_bool8 allowPlaybackAutoStreamRouting;
ma_bool8 isDetachedPlayback;
ma_bool8 isDetachedCapture;
} wasapi;
#endif
#ifdef MA_SUPPORT_DSOUND
struct
{
/*LPDIRECTSOUND*/ ma_ptr pPlayback;
/*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
/*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
/*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
/*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
} dsound;
#endif
#ifdef MA_SUPPORT_WINMM
struct
{
/*HWAVEOUT*/ ma_handle hDevicePlayback;
/*HWAVEIN*/ ma_handle hDeviceCapture;
/*HANDLE*/ ma_handle hEventPlayback;
/*HANDLE*/ ma_handle hEventCapture;
ma_uint32 fragmentSizeInFrames;
ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
ma_uint32 headerFramesConsumedCapture; /* ^^^ */
/*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
/*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
ma_uint8* pIntermediaryBufferPlayback;
ma_uint8* pIntermediaryBufferCapture;
ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
} winmm;
#endif
#ifdef MA_SUPPORT_ALSA
struct
{
/*snd_pcm_t**/ ma_ptr pPCMPlayback;
/*snd_pcm_t**/ ma_ptr pPCMCapture;
/*struct pollfd**/ void* pPollDescriptorsPlayback;
/*struct pollfd**/ void* pPollDescriptorsCapture;
int pollDescriptorCountPlayback;
int pollDescriptorCountCapture;
int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */
int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */
ma_bool8 isUsingMMapPlayback;
ma_bool8 isUsingMMapCapture;
} alsa;
#endif
#ifdef MA_SUPPORT_PULSEAUDIO
struct
{
/*pa_mainloop**/ ma_ptr pMainLoop;
/*pa_context**/ ma_ptr pPulseContext;
/*pa_stream**/ ma_ptr pStreamPlayback;
/*pa_stream**/ ma_ptr pStreamCapture;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_ptr handlePlayback;
ma_ptr handleCapture;
ma_bool32 isStartedPlayback;
ma_bool32 isStartedCapture;
} sndio;
#endif
#ifdef MA_SUPPORT_AUDIO4
struct
{
int fdPlayback;
int fdCapture;
} audio4;
#endif
#ifdef MA_SUPPORT_OSS
struct
{
int fdPlayback;
int fdCapture;
} oss;
#endif
#ifdef MA_SUPPORT_AAUDIO
struct
{
/*AAudioStream**/ ma_ptr pStreamPlayback;
/*AAudioStream**/ ma_ptr pStreamCapture;
ma_aaudio_usage usage;
ma_aaudio_content_type contentType;
ma_aaudio_input_preset inputPreset;
ma_bool32 noAutoStartAfterReroute;
} aaudio;
#endif
#ifdef MA_SUPPORT_OPENSL
struct
{
/*SLObjectItf*/ ma_ptr pOutputMixObj;
/*SLOutputMixItf*/ ma_ptr pOutputMix;
/*SLObjectItf*/ ma_ptr pAudioPlayerObj;
/*SLPlayItf*/ ma_ptr pAudioPlayer;
/*SLObjectItf*/ ma_ptr pAudioRecorderObj;
/*SLRecordItf*/ ma_ptr pAudioRecorder;
/*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;
/*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;
ma_bool32 isDrainingCapture;
ma_bool32 isDrainingPlayback;
ma_uint32 currentBufferIndexPlayback;
ma_uint32 currentBufferIndexCapture;
ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
ma_uint8* pBufferCapture;
} opensl;
#endif
#ifdef MA_SUPPORT_WEBAUDIO
struct
{
int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
int indexCapture;
} webaudio;
#endif
#ifdef MA_SUPPORT_NULL
struct
{
ma_thread deviceThread;
ma_event operationEvent;
ma_event operationCompletionEvent;
ma_semaphore operationSemaphore;
ma_uint32 operation;
ma_result operationResult;
ma_timer timer;
double priorRunTime;
ma_uint32 currentPeriodFramesRemainingPlayback;
ma_uint32 currentPeriodFramesRemainingCapture;
ma_uint64 lastProcessedFramePlayback;
ma_uint64 lastProcessedFrameCapture;
MA_ATOMIC(4, ma_bool32) isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */
} null_device;
#endif
};
};
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(pop)
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
#pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
#endif
/*
Initializes a `ma_context_config` object.
Return Value
------------
A `ma_context_config` initialized to defaults.
Remarks
-------
You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio
is updated and new members are added to `ma_context_config`. It also sets logical defaults.
You can override members of the returned object by changing it's members directly.
See Also
--------
ma_context_init()
*/
MA_API ma_context_config ma_context_config_init(void);
/*
Initializes a context.
The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual
device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices.
Parameters
----------
backends (in, optional)
A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
backendCount (in, optional)
The number of items in `backend`. Ignored if `backend` is NULL.
pConfig (in, optional)
The context configuration.
pContext (in)
A pointer to the context object being initialized.
Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
Remarks
-------
When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order:
|-------------|-----------------------|--------------------------------------------------------|
| Name | Enum Name | Supported Operating Systems |
|-------------|-----------------------|--------------------------------------------------------|
| WASAPI | ma_backend_wasapi | Windows Vista+ |
| DirectSound | ma_backend_dsound | Windows XP+ |
| WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
| Core Audio | ma_backend_coreaudio | macOS, iOS |
| ALSA | ma_backend_alsa | Linux |
| PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
| JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
| sndio | ma_backend_sndio | OpenBSD |
| audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
| OSS | ma_backend_oss | FreeBSD |
| AAudio | ma_backend_aaudio | Android 8+ |
| OpenSL|ES | ma_backend_opensl | Android (API level 16+) |
| Web Audio | ma_backend_webaudio | Web (via Emscripten) |
| Null | ma_backend_null | Cross Platform (not used on Web) |
|-------------|-----------------------|--------------------------------------------------------|
The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings
can then be set directly on the structure. Below are the members of the `ma_context_config` object.
pLog
A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not
require logging. See the `ma_log` API for details on how to use the logging system.
threadPriority
The desired priority to use for the audio thread. Allowable values include the following:
|--------------------------------------|
| Thread Priority |
|--------------------------------------|
| ma_thread_priority_idle |
| ma_thread_priority_lowest |
| ma_thread_priority_low |
| ma_thread_priority_normal |
| ma_thread_priority_high |
| ma_thread_priority_highest (default) |
| ma_thread_priority_realtime |
| ma_thread_priority_default |
|--------------------------------------|
threadStackSize
The desired size of the stack for the audio thread. Defaults to the operating system's default.
pUserData
A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`.
allocationCallbacks
Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation
callbacks will be used for anything tied to the context, including devices.
alsa.useVerboseDeviceEnumeration
ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique
card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes
it so the ALSA backend includes all devices. Defaults to false.
pulse.pApplicationName
PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`.
pulse.pServerName
PulseAudio only. The name of the server to connect to with `pa_context_connect()`.
pulse.tryAutoSpawn
PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that
miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be
intrusive for the end user.
coreaudio.sessionCategory
iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
|-----------------------------------------|-------------------------------------|
| miniaudio Token | Core Audio Token |
|-----------------------------------------|-------------------------------------|
| ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient |
| ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient |
| ma_ios_session_category_playback | AVAudioSessionCategoryPlayback |
| ma_ios_session_category_record | AVAudioSessionCategoryRecord |
| ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord |
| ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute |
| ma_ios_session_category_none | AVAudioSessionCategoryAmbient |
| ma_ios_session_category_default | AVAudioSessionCategoryAmbient |
|-----------------------------------------|-------------------------------------|
coreaudio.sessionCategoryOptions
iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
|---------------------------------------------------------------------------|------------------------------------------------------------------|
| miniaudio Token | Core Audio Token |
|---------------------------------------------------------------------------|------------------------------------------------------------------|
| ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers |
| ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers |
| ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth |
| ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker |
| ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers |
| ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP |
| ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay |
|---------------------------------------------------------------------------|------------------------------------------------------------------|
coreaudio.noAudioSessionActivate
iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization.
coreaudio.noAudioSessionDeactivate
iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization.
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
// Error.
}
```
Example 2 - Custom Configuration
--------------------------------
The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program
wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also
want an error to be returned if no valid backend is available which they achieve by excluding the Null backend.
For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface.
```c
ma_backend backends[] = {
ma_backend_alsa,
ma_backend_pulseaudio,
ma_backend_wasapi,
ma_backend_dsound
};
ma_log log;
ma_log_init(&log);
ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData));
ma_context_config config = ma_context_config_init();
config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured.
ma_context context;
ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context);
if (result != MA_SUCCESS) {
// Error.
if (result == MA_NO_BACKEND) {
// Couldn't find an appropriate backend.
}
}
// You could also attach a log callback post-initialization:
ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData));
```
See Also
--------
ma_context_config_init()
ma_context_uninit()
*/
MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
/*
Uninitializes a context.
Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
Remarks
-------
Results are undefined if you call this while any device created by this context is still active.
See Also
--------
ma_context_init()
*/
MA_API ma_result ma_context_uninit(ma_context* pContext);
/*
Retrieves the size of the ma_context object.
This is mainly for the purpose of bindings to know how much memory to allocate.
*/
MA_API size_t ma_context_sizeof(void);
/*
Retrieves a pointer to the log object associated with this context.
Remarks
-------
Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log
message.
You can attach your own logging callback to the log with `ma_log_register_callback()`
Return Value
------------
A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs,
NULL will be returned.
*/
MA_API ma_log* ma_context_get_log(ma_context* pContext);
/*
Enumerates over every device (both playback and capture).
This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur
an internal heap allocation, or it simply suits your code better.
Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require
opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this,
but don't call it from within the enumeration callback.
Returning false from the callback will stop enumeration. Returning true will continue enumeration.
Parameters
----------
pContext (in)
A pointer to the context performing the enumeration.
callback (in)
The callback to fire for each enumerated device.
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also,
do not try to call `ma_context_get_device_info()` from within the callback.
Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.
Example 1 - Simple Enumeration
------------------------------
ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
{
printf("Device Name: %s\n", pInfo->name);
return MA_TRUE;
}
ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);
if (result != MA_SUCCESS) {
// Error.
}
See Also
--------
ma_context_get_devices()
*/
MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
/*
Retrieves basic information about every active playback and/or capture device.
This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.
Parameters
----------
pContext (in)
A pointer to the context performing the enumeration.
ppPlaybackDeviceInfos (out)
A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.
pPlaybackDeviceCount (out)
A pointer to an unsigned integer that will receive the number of playback devices.
ppCaptureDeviceInfos (out)
A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.
pCaptureDeviceCount (out)
A pointer to an unsigned integer that will receive the number of capture devices.
Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple
threads. Instead, you need to make a copy of the returned data with your own higher level synchronization.
Remarks
-------
It is _not_ safe to assume the first device in the list is the default device.
You can pass in NULL for the playback or capture lists in which case they'll be ignored.
The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers.
See Also
--------
ma_context_get_devices()
*/
MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
/*
Retrieves information about a device of the given type, with the specified ID and share mode.
Parameters
----------
pContext (in)
A pointer to the context performing the query.
deviceType (in)
The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.
pDeviceID (in)
The ID of the device being queried.
pDeviceInfo (out)
A pointer to the `ma_device_info` structure that will receive the device information.
Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Safe. This is guarded using a simple mutex lock.
Remarks
-------
Do _not_ call this from within the `ma_context_enumerate_devices()` callback.
It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in
shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify
which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if
the requested share mode is unsupported.
This leaves pDeviceInfo unmodified in the result of an error.
*/
MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);
/*
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
alsa.noAutoResample
ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false.
pulse.pStreamNamePlayback
PulseAudio only. Sets the stream name for playback.
pulse.pStreamNameCapture
PulseAudio only. Sets the stream name for capture.
coreaudio.allowNominalSampleRateChange
Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This
is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate
that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will
find the closest match between the sample rate requested in the device config and the sample rates natively supported by the
hardware. When set to false, the sample rate currently set by the operating system will always be used.
opensl.streamType
OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the
stream type will be left unset. Think of this as the type of audio you're playing.
opensl.recordingPreset
OpenSL only. Explicitly sets the type of recording your program will be doing. When left
unset, the recording preset will be left unchanged.
aaudio.usage
AAudio only. Explicitly sets the nature of the audio the program will be consuming. When
left unset, the usage will be left unchanged.
aaudio.contentType
AAudio only. Sets the content type. When left unset, the content type will be left unchanged.
aaudio.inputPreset
AAudio only. Explicitly sets the type of recording your program will be doing. When left
unset, the input preset will be left unchanged.
aaudio.noAutoStartAfterReroute
AAudio only. Controls whether or not the device should be automatically restarted after a
stream reroute. When set to false (default) the device will be restarted automatically;
otherwise the device will be stopped.
Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device.
After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`.
If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or
`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or
`ma_performance_profile_conservative`.
If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device
in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the
config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA,
for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user.
Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary.
When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config
and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run
on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`,
`playback/capture.channels` and `sampleRate` members of the device object.
When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message
asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information.
ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture.
If these fail it will try falling back to the "hw" device.
Example 1 - Simple Initialization
---------------------------------
This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default
playback device this is usually all you need.
```c
ma_device_config config = ma_device_config_init(ma_device_type_playback);
config.playback.format = ma_format_f32;
config.playback.channels = 2;
config.sampleRate = 48000;
config.dataCallback = ma_data_callback;
config.pMyUserData = pMyUserData;
ma_device device;
ma_result result = ma_device_init(NULL, &config, &device);
if (result != MA_SUCCESS) {
// Error
}
```
Example 2 - Advanced Initialization
-----------------------------------
This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size
and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device
enumeration.
```c
ma_context context;
ma_result result = ma_context_init(NULL, 0, NULL, &context);
if (result != MA_SUCCESS) {
// Error
}
ma_device_info* pPlaybackDeviceInfos;
ma_uint32 playbackDeviceCount;
result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);
if (result != MA_SUCCESS) {
// Error
}
// ... choose a device from pPlaybackDeviceInfos ...
ma_device_config config = ma_device_config_init(ma_device_type_playback);
config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices().
config.playback.format = ma_format_f32;
config.playback.channels = 2;
config.sampleRate = 48000;
config.dataCallback = ma_data_callback;
config.pUserData = pMyUserData;
config.periodSizeInMilliseconds = 10;
config.periods = 3;
ma_device device;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
pName (out)
A pointer to the buffer that will receive the name.
nameCap (in)
The capacity of the output buffer, including space for the null terminator.
pLengthNotIncludingNullTerminator (out, optional)
A pointer to the variable that will receive the length of the name, not including the null
terminator.
Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Unsafe. This should be considered unsafe because it may be calling into the backend which may or
may not be safe.
Callback Safety
---------------
Unsafe. You should avoid calling this in the data callback because it may call into the backend
which may or may not be safe.
Remarks
-------
If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to
`pName` if you want to first get the length of the name for the purpose of memory allocation of the
output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for
most cases and will avoid the need for the inefficiency of calling this function twice.
This is implemented in terms of `ma_device_get_info()`.
*/
MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator);
/*
Starts the device. For playback devices this begins playback. For capture devices it begins recording.
Use `ma_device_stop()` to stop the device.
Parameters
----------
pDevice (in)
A pointer to the device to start.
Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Safe. It's safe to call this from any thread with the exception of the callback thread.
Callback Safety
---------------
Unsafe. It is not safe to call this inside any callback.
Remarks
-------
For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid
audio data in the buffer, which needs to be done before the device begins playback.
This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety.
Do not call this in any callback.
See Also
--------
ma_device_stop()
*/
MA_API ma_result ma_device_start(ma_device* pDevice);
/*
Stops the device. For playback devices this stops playback. For capture devices it stops recording.
Use `ma_device_start()` to start the device again.
Parameters
----------
pDevice (in)
A pointer to the device to stop.
Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Safe. It's safe to call this from any thread with the exception of the callback thread.
Callback Safety
---------------
Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
Remarks
-------
This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some
backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size
that was specified at initialization time).
Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and
the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the
speakers or received from the microphone which can in turn result in de-syncs.
Do not call this in any callback.
This will be called implicitly by `ma_device_uninit()`.
See Also
--------
ma_device_start()
*/
MA_API ma_result ma_device_stop(ma_device* pDevice);
/*
Determines whether or not the device is started.
Parameters
----------
pDevice (in)
A pointer to the device whose start state is being retrieved.
Return Value
------------
True if the device is started, false otherwise.
Thread Safety
-------------
Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return
value will be out of sync.
Callback Safety
---------------
Safe. This is implemented as a simple accessor.
See Also
--------
ma_device_start()
ma_device_stop()
*/
MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice);
/*
Retrieves the state of the device.
Parameters
----------
pDevice (in)
A pointer to the device whose state is being retrieved.
Return Value
------------
The current state of the device. The return value will be one of the following:
+-------------------------------+------------------------------------------------------------------------------+
| ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. |
+-------------------------------+------------------------------------------------------------------------------+
| ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. |
+-------------------------------+------------------------------------------------------------------------------+
| ma_device_state_started | The device started and requesting and/or delivering audio data. |
+-------------------------------+------------------------------------------------------------------------------+
| ma_device_state_starting | The device is in the process of starting. |
+-------------------------------+------------------------------------------------------------------------------+
| ma_device_state_stopping | The device is in the process of stopping. |
+-------------------------------+------------------------------------------------------------------------------+
Thread Safety
-------------
Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called,
there's a possibility the return value could be out of sync. See remarks.
Callback Safety
---------------
Safe. This is implemented as a simple accessor.
Remarks
-------
The general flow of a devices state goes like this:
```
ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped
ma_device_start() -> ma_device_state_starting -> ma_device_state_started
ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped
```
When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the
value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own
synchronization.
*/
MA_API ma_device_state ma_device_get_state(const ma_device* pDevice);
/*
Performs post backend initialization routines for setting up internal data conversion.
This should be called whenever the backend is initialized. The only time this should be called from
outside of miniaudio is if you're implementing a custom backend, and you would only do it if you
are reinitializing the backend due to rerouting or reinitializing for some reason.
Parameters
----------
pDevice [in]
A pointer to the device.
deviceType [in]
The type of the device that was just reinitialized.
pPlaybackDescriptor [in]
The descriptor of the playback device containing the internal data format and buffer sizes.
pPlaybackDescriptor [in]
The descriptor of the capture device containing the internal data format and buffer sizes.
Return Value
------------
MA_SUCCESS if successful; any other error otherwise.
Thread Safety
-------------
Unsafe. This will be reinitializing internal data converters which may be in use by another thread.
Callback Safety
---------------
Unsafe. This will be reinitializing internal data converters which may be in use by the callback.
Remarks
-------
For a duplex device, you can call this for only one side of the system. This is why the deviceType
is specified as a parameter rather than deriving it from the device.
You do not need to call this manually unless you are doing a custom backend, in which case you need
only do it if you're manually performing rerouting or reinitialization.
*/
MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor);
/*
Sets the master volume factor for the device.
The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and
values less than 0 decreases the volume.
Parameters
----------
pDevice (in)
A pointer to the device whose volume is being set.
volume (in)
The new volume factor. Must be >= 0.
Return Value
------------
MA_SUCCESS if the volume was set successfully.
MA_INVALID_ARGS if pDevice is NULL.
MA_INVALID_ARGS if volume is negative.
Thread Safety
-------------
Safe. This just sets a local member of the device object.
Callback Safety
---------------
Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
Remarks
-------
This applies the volume factor across all channels.
This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
See Also
--------
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
Thread Safety
-------------
This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a
playback and capture device in duplex setups.
Callback Safety
---------------
Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend.
Remarks
-------
If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in
which case `pInput` will be processed first, followed by `pOutput`.
If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that
callback.
*/
MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
/*
Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile.
This function is used by backends for helping determine an appropriately sized buffer to use with
the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the
`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a
best guess at the device's native sample rate is also required which is where `nativeSampleRate`
comes in. In addition, the performance profile is also needed for cases where both the period size
in frames and milliseconds are both zero.
Parameters
----------
pDescriptor (in)
A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members
will be used for the calculation of the buffer size.
nativeSampleRate (in)
The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of
`pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which
case a sample rate is required to convert to a size in frames.
performanceProfile (in)
When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are
zero, miniaudio will fall back to a buffer size based on the performance profile. The profile
to use for this calculation is determine by this parameter.
Return Value
------------
The calculated buffer size in frames.
Thread Safety
-------------
This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function
should only ever be called from within the backend's device initialization routine and therefore
shouldn't have any multithreading concerns.
Callback Safety
---------------
This is safe to call within the data callback, but there is no reason to ever do this.
Remarks
-------
If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that
is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead.
*/
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile);
/*
Retrieves a friendly name for a backend.
*/
MA_API const char* ma_get_backend_name(ma_backend backend);
/*
Determines whether or not the given backend is available by the compilation environment.
*/
MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend);
/*
Retrieves compile-time enabled backends.
Parameters
----------
pBackends (out, optional)
A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting
the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends.
backendCap (in)
The capacity of the `pBackends` buffer.
pBackendCount (out)
A pointer to the variable that will receive the enabled backend count.
Return Value
------------
MA_SUCCESS if successful.
MA_INVALID_ARGS if `pBackendCount` is NULL.
MA_NO_SPACE if the capacity of `pBackends` is not large enough.
If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values.
Thread Safety
-------------
Safe.
Callback Safety
---------------
Safe.
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_uint64 sizeInFrames;
const void* pData;
} ma_audio_buffer_ref;
MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef);
MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef);
MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames);
MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex);
MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount);
MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef);
MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor);
MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength);
MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames);
typedef struct
{
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRate;
ma_uint64 sizeInFrames;
const void* pData; /* If set to NULL, will allocate a block of memory for you. */
ma_allocation_callbacks allocationCallbacks;
} ma_audio_buffer_config;
MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks);
typedef struct
{
ma_audio_buffer_ref ref;
ma_allocation_callbacks allocationCallbacks;
ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */
ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */
} ma_audio_buffer;
MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);
MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);
MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */
MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer);
MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer);
MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex);
MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);
MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer);
MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor);
MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength);
MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames);
/*
Paged Audio Buffer
==================
A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It
can be used for cases where audio data is streamed in asynchronously while allowing data to be read
at the same time.
This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across
simultaneously across different threads, however only one thread at a time can append, and only one
thread at a time can read and seek.
*/
typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page;
struct ma_paged_audio_buffer_page
{
MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext;
ma_uint64 sizeInFrames;
ma_uint8 pAudioData[1];
};
typedef struct
{
ma_format format;
ma_uint32 channels;
ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */
MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */
} ma_paged_audio_buffer_data;
MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData);
MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData);
MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData);
MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength);
MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage);
MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage);
MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks);
typedef struct
{
ma_paged_audio_buffer_data* pData; /* Must not be null. */
} ma_paged_audio_buffer_config;
MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData);
typedef struct
{
ma_data_source_base ds;
ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */
ma_paged_audio_buffer_page* pCurrent;
ma_uint64 relativeCursor; /* Relative to the current page. */
ma_uint64 absoluteCursor;
} ma_paged_audio_buffer;
MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer);
MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer);
MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */
MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex);
MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor);
MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength);
/************************************************************************************************************************************************************
VFS
===
The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
typedef struct
{
ma_uint64 sizeInBytes;
} ma_file_info;
typedef struct
{
ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file);
ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
} ma_vfs_callbacks;
MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file);
MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks);
typedef struct
{
ma_vfs_callbacks cb;
ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */
} ma_default_vfs;
MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks);
typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead);
typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin);
typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor);
#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
typedef enum
{
ma_encoding_format_unknown = 0,
ma_encoding_format_wav,
ma_encoding_format_flac,
ma_encoding_format_mp3,
ma_encoding_format_vorbis
} ma_encoding_format;
#endif
/************************************************************************************************************************************************************
Decoding
========
Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless
you do your own synchronization.
************************************************************************************************************************************************************/
#ifndef MA_NO_DECODING
typedef struct ma_decoder ma_decoder;
typedef struct
{
ma_format preferredFormat;
ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */
} ma_decoding_backend_config;
MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount);
typedef struct
{
ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source...
ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */
ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */
ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */
void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
} ma_decoding_backend_vtable;
typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */
typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin);
typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor);
typedef struct
{
ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */
ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */
ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */
ma_channel* pChannelMap;
ma_channel_mix_mode channelMixMode;
ma_dither_mode ditherMode;
ma_resampler_config resampling;
ma_allocation_callbacks allocationCallbacks;
ma_encoding_format encodingFormat;
ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */
ma_decoding_backend_vtable** ppCustomBackendVTables;
ma_uint32 customBackendCount;
void* pCustomBackendUserData;
} ma_decoder_config;
struct ma_decoder
{
ma_data_source_base ds;
ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */
const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */
void* pBackendUserData;
ma_decoder_read_proc onRead;
ma_decoder_seek_proc onSeek;
ma_decoder_tell_proc onTell;
void* pUserData;
ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */
ma_format outputFormat;
ma_uint32 outputChannels;
ma_uint32 outputSampleRate;
ma_data_converter converter; /* Data conversion is achieved by running frames through this. */
void* pInputCache; /* In input format. Can be null if it's not needed. */
ma_uint64 inputCacheCap; /* The capacity of the input cache. */
ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */
ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */
ma_allocation_callbacks allocationCallbacks;
union
{
struct
{
ma_vfs* pVFS;
ma_vfs_file file;
} vfs;
struct
{
const ma_uint8* pData;
size_t dataSize;
size_t currentReadPos;
} memory; /* Only used for decoders that were opened against a block of memory. */
} data;
};
MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
MA_API ma_decoder_config ma_decoder_config_init_default(void);
MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
/*
Uninitializes a decoder.
*/
MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder);
/*
Reads PCM frames from the given decoder.
This is not thread safe without your own synchronization.
*/
MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
/*
Seeks to a PCM frame based on it's absolute index.
This is not thread safe without your own synchronization.
*/
MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);
/*
Retrieves the decoder's output data format.
*/
MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
/*
Retrieves the current position of the read cursor in PCM frames.
*/
MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor);
/*
Retrieves the length of the decoder in PCM frames.
Do not call this on streams of an undefined length, such as internet radio.
If the length is unknown or an error occurs, 0 will be returned.
This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio
uses internally.
For MP3's, this will decode the entire file. Do not call this in time critical scenarios.
This function is not thread safe without your own synchronization.
*/
MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength);
/*
Retrieves the number of frames that can be read before reaching the end.
This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in
particular ensuring you do not call it on streams of an undefined length, such as internet radio.
If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be
returned.
*/
MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames);
/*
Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
pConfig should be set to what you want. On output it will be set to what you got.
*/
MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
#endif /* MA_NO_DECODING */
/************************************************************************************************************************************************************
Encoding
========
Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned.
************************************************************************************************************************************************************/
#ifndef MA_NO_ENCODING
typedef struct ma_encoder ma_encoder;
typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten);
typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin);
typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder);
typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder);
typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);
typedef struct
{
ma_encoding_format encodingFormat;
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRate;
ma_allocation_callbacks allocationCallbacks;
} ma_encoder_config;
MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
struct ma_encoder
{
ma_encoder_config config;
ma_encoder_write_proc onWrite;
ma_encoder_seek_proc onSeek;
ma_encoder_init_proc onInit;
ma_encoder_uninit_proc onUninit;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
} ma_noise_config;
MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude);
typedef struct
{
ma_data_source_vtable ds;
ma_noise_config config;
ma_lcg lcg;
union
{
struct
{
double** bin;
double* accumulation;
ma_uint32* counter;
} pink;
struct
{
double* accumulation;
} brownian;
} state;
/* Memory management. */
void* _pHeap;
ma_bool32 _ownsHeap;
} ma_noise;
MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes);
MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise);
MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise);
MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude);
MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed);
MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type);
#endif /* MA_NO_GENERATION */
/************************************************************************************************************************************************************
Resource Manager
************************************************************************************************************************************************************/
/* The resource manager cannot be enabled if there is no decoder. */
#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING)
#define MA_NO_RESOURCE_MANAGER
#endif
#ifndef MA_NO_RESOURCE_MANAGER
typedef struct ma_resource_manager ma_resource_manager;
typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node;
typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer;
typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream;
typedef struct ma_resource_manager_data_source ma_resource_manager_data_source;
typedef enum
{
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage...
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010 /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */
} ma_resource_manager_data_source_flags;
/*
Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional.
*/
typedef struct
{
ma_async_notification* pNotification;
ma_fence* pFence;
} ma_resource_manager_pipeline_stage_notification;
typedef struct
{
ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */
ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */
} ma_resource_manager_pipeline_notifications;
MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void);
/* BEGIN BACKWARDS COMPATIBILITY */
/* TODO: Remove this block in version 0.12. */
#if 1
#define ma_resource_manager_job ma_job
#define ma_resource_manager_job_init ma_job_init
#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING
#define ma_resource_manager_job_queue_config ma_job_queue_config
#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init
#define ma_resource_manager_job_queue ma_job_queue
#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size
#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated
#define ma_resource_manager_job_queue_init ma_job_queue_init
#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit
#define ma_resource_manager_job_queue_post ma_job_queue_post
#define ma_resource_manager_job_queue_next ma_job_queue_next
#endif
/* END BACKWARDS COMPATIBILITY */
/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */
#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT
#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64
#endif
typedef enum
{
/* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */
MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001,
/* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */
MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002
} ma_resource_manager_flags;
typedef struct
{
const char* pFilePath;
const wchar_t* pFilePathW;
const ma_resource_manager_pipeline_notifications* pNotifications;
ma_uint64 initialSeekPointInPCMFrames;
ma_uint64 rangeBegInPCMFrames;
ma_uint64 rangeEndInPCMFrames;
ma_uint64 loopPointBegInPCMFrames;
ma_uint64 loopPointEndInPCMFrames;
ma_bool32 isLooping;
ma_uint32 flags;
} ma_resource_manager_data_source_config;
MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void);
typedef enum
{
ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */
ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */
ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */
ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */
} ma_resource_manager_data_supply_type;
typedef struct
{
MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */
union
{
struct
{
const void* pData;
size_t sizeInBytes;
} encoded;
struct
{
const void* pData;
ma_uint64 totalFrameCount;
ma_uint64 decodedFrameCount;
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRate;
} decoded;
struct
{
ma_paged_audio_buffer_data data;
ma_uint64 decodedFrameCount;
ma_uint32 sampleRate;
} decodedPaged;
} backend;
} ma_resource_manager_data_supply;
struct ma_resource_manager_data_buffer_node
{
ma_uint32 hashedName32; /* The hashed name. This is the key. */
ma_uint32 refCount;
MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */
MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */
ma_resource_manager_data_supply data;
ma_resource_manager_data_buffer_node* pParent;
ma_resource_manager_data_buffer_node* pChildLo;
ma_resource_manager_data_buffer_node* pChildHi;
};
struct ma_resource_manager_data_buffer
{
ma_data_source_base ds; /* Base data source. A data buffer is a data source. */
ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */
ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */
ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */
MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */
ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */
MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */
MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */
ma_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */
union
{
ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */
ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */
ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */
} connector; /* Connects this object to the node's data supply. */
};
struct ma_resource_manager_data_stream
{
ma_data_source_base ds; /* Base data source. A data stream is a data source. */
ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */
ma_uint32 flags; /* The flags that were passed used to initialize the stream. */
ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */
ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */
ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */
ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */
MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */
ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */
MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
/* Written by the public API, read by the job thread. */
MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */
/* Written by the job thread, read by the public API. */
void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */
MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */
/* Written and read by both the public API and the job thread. These must be atomic. */
MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */
MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */
MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */
MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */
};
struct ma_resource_manager_data_source
{
union
{
ma_resource_manager_data_buffer buffer;
ma_resource_manager_data_stream stream;
} backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */
ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */
MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
};
typedef struct
{
ma_allocation_callbacks allocationCallbacks;
ma_log* pLog;
ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */
ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */
ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */
ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */
ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */
ma_uint32 flags;
ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */
ma_decoding_backend_vtable** ppCustomDecodingBackendVTables;
ma_uint32 customDecodingBackendCount;
void* pCustomDecodingBackendUserData;
} ma_resource_manager_config;
MA_API ma_resource_manager_config ma_resource_manager_config_init(void);
struct ma_resource_manager
{
ma_resource_manager_config config;
ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */
#ifndef MA_NO_THREADING
ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */
ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */
#endif
ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */
ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */
ma_log log; /* Only used if no log was specified in the config. */
};
/* Init. */
MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager);
MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager);
MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager);
/* Registration. */
MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags);
MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags);
MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments t...
MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS....
MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes);
MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath);
MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath);
MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName);
MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName);
/* Data Buffers. */
MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex);
MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor);
MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength);
MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping);
MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames);
/* Data Streams. */
MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex);
MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor);
MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength);
MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping);
MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames);
/* Data Sources. */
MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
`pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`.
On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set
`pFrameCountIn` to the number of input frames that were consumed.
*/
void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
/*
A callback for retrieving the number of a input frames that are required to output the
specified number of output frames. You would only want to implement this when the node performs
resampling. This is optional, even for nodes that perform resampling, but it does offer a
small reduction in latency as it allows miniaudio to calculate the exact number of input frames
to read at a time instead of having to estimate.
*/
ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount);
/*
The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn`
parameters of the callbacks above.
*/
ma_uint8 inputBusCount;
/*
The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut`
parameters of the callbacks above.
*/
ma_uint8 outputBusCount;
/*
Flags describing characteristics of the node. This is currently just a placeholder for some
ideas for later on.
*/
ma_uint32 flags;
} ma_node_vtable;
typedef struct
{
const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */
ma_node_state initialState; /* Defaults to ma_node_state_started. */
ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */
ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */
const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */
const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */
} ma_node_config;
MA_API ma_node_config ma_node_config_init(void);
/*
A node has multiple output buses. An output bus is attached to an input bus as an item in a linked
list. Think of the input bus as a linked list, with the output bus being an item in that list.
*/
typedef struct ma_node_output_bus ma_node_output_bus;
struct ma_node_output_bus
{
/* Immutable. */
ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */
ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */
ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
/* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */
MA_ATOMIC(1, ma_uint8) inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. */
MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */
MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */
MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */
MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
MA_ATOMIC(4, float) volume; /* Linear. */
MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */
MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */
MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */
};
/*
A node has multiple input buses. The output buses of a node are connecting to the input busses of
another. An input bus is essentially just a linked list of output buses.
*/
typedef struct ma_node_input_bus ma_node_input_bus;
struct ma_node_input_bus
{
/* Mutable via multiple threads. */
ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */
MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */
MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
/* Set once at startup. */
ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
};
typedef struct ma_node_base ma_node_base;
struct ma_node_base
{
/* These variables are set once at startup. */
ma_node_graph* pNodeGraph; /* The graph this node belongs to. */
const ma_node_vtable* vtable;
float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */
ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */
/* These variables are read and written only from the audio thread. */
ma_uint16 cachedFrameCountOut;
ma_uint16 cachedFrameCountIn;
ma_uint16 consumedFrameCountIn;
/* These variables are read and written between different threads. */
MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */
MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */
ma_uint32 inputBusCount;
ma_uint32 outputBusCount;
ma_node_input_bus* pInputBuses;
ma_node_output_bus* pOutputBuses;
/* Memory management. */
ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];
ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];
void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */
ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */
};
MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes);
MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode);
MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode);
MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode);
MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode);
MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode);
MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex);
MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex);
MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex);
MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex);
MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode);
MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume);
MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex);
MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state);
MA_API ma_node_state ma_node_get_state(const ma_node* pNode);
MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime);
MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state);
MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime);
MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd);
MA_API ma_uint64 ma_node_get_time(const ma_node* pNode);
MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime);
typedef struct
{
ma_uint32 channels;
ma_uint16 nodeCacheCapInFrames;
} ma_node_graph_config;
MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels);
struct ma_node_graph
{
/* Immutable. */
ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */
ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */
ma_uint16 nodeCacheCapInFrames;
/* Read and written by multiple threads. */
MA_ATOMIC(4, ma_bool32) isReading;
};
MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph);
MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph);
MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph);
MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph);
MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime);
/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */
typedef struct
{
ma_node_config nodeConfig;
ma_data_source* pDataSource;
} ma_data_source_node_config;
MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource);
typedef struct
{
ma_node_base base;
ma_data_source* pDataSource;
} ma_data_source_node;
MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode);
MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping);
MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode);
/* Splitter Node. 1 input, 2 outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */
typedef struct
{
ma_node_config nodeConfig;
ma_uint32 channels;
} ma_splitter_node_config;
MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels);
typedef struct
{
ma_node_base base;
} ma_splitter_node;
MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode);
MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks);
/*
Biquad Node
*/
typedef struct
{
ma_node_config nodeConfig;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/************************************************************************************************************************************************************
Engine
************************************************************************************************************************************************************/
#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)
typedef struct ma_engine ma_engine;
typedef struct ma_sound ma_sound;
/* Sound flags. */
typedef enum
{
MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */
MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */
MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */
MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */
MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */
MA_SOUND_FLAG_NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */
MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00000040 /* Disable spatialization. */
} ma_sound_flags;
#ifndef MA_ENGINE_MAX_LISTENERS
#define MA_ENGINE_MAX_LISTENERS 4
#endif
#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1)
typedef enum
{
ma_engine_node_type_sound,
ma_engine_node_type_group
} ma_engine_node_type;
typedef struct
{
ma_engine* pEngine;
ma_engine_node_type type;
ma_uint32 channelsIn;
ma_uint32 channelsOut;
ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */
ma_bool8 isPitchDisabled; /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */
ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */
ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */
} ma_engine_node_config;
MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags);
/* Base node object for both ma_sound and ma_sound_group. */
typedef struct
{
ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */
ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */
ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */
ma_fader fader;
ma_linear_resampler resampler; /* For pitch shift. */
ma_spatializer spatializer;
ma_panner panner;
MA_ATOMIC(4, float) pitch;
float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */
float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */
MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */
MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */
MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */
/* Memory management. */
ma_bool8 _ownsHeap;
void* _pHeap;
} ma_engine_node;
MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes);
MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode);
MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode);
MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks);
#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF
typedef struct
{
const char* pFilePath; /* Set this to load from the resource manager. */
const wchar_t* pFilePathW; /* Set this to load from the resource manager. */
ma_data_source* pDataSource; /* Set this to load from an existing data source. */
ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHM...
ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */
ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */
ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */
ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */
ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */
ma_uint64 rangeBegInPCMFrames;
ma_uint64 rangeEndInPCMFrames;
ma_uint64 loopPointBegInPCMFrames;
ma_uint64 loopPointEndInPCMFrames;
ma_bool32 isLooping;
ma_fence* pDoneFence; /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */
} ma_sound_config;
MA_API ma_sound_config ma_sound_config_init(void);
struct ma_sound
{
ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */
ma_data_source* pDataSource;
MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */
MA_ATOMIC(4, ma_bool32) atEnd;
ma_bool8 ownsDataSource;
/*
We're declaring a resource manager data source object here to save us a malloc when loading a
sound via the resource manager, which I *think* will be the most common scenario.
*/
#ifndef MA_NO_RESOURCE_MANAGER
ma_resource_manager_data_source* pResourceManagerDataSource;
#endif
};
/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */
typedef struct ma_sound_inlined ma_sound_inlined;
struct ma_sound_inlined
{
ma_sound sound;
ma_sound_inlined* pNext;
ma_sound_inlined* pPrev;
};
/* A sound group is just a sound. */
typedef ma_sound_config ma_sound_group_config;
typedef ma_sound ma_sound_group;
MA_API ma_sound_group_config ma_sound_group_config_init(void);
typedef struct
{
#if !defined(MA_NO_RESOURCE_MANAGER)
ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */
#endif
#if !defined(MA_NO_DEVICE_IO)
ma_context* pContext;
ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */
ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */
#endif
ma_log* pLog; /* When set to NULL, will use the context's log. */
ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */
ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */
ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */
ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/
ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */
ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */
ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */
ma_allocation_callbacks allocationCallbacks;
ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */
ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */
ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */
ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */
} ma_engine_config;
MA_API ma_engine_config ma_engine_config_init(void);
struct ma_engine
{
ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */
#if !defined(MA_NO_RESOURCE_MANAGER)
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor);
MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup);
MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor);
MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup);
MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup);
MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames);
MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);
MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames);
MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);
MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup);
MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup);
#endif /* MA_NO_ENGINE */
#ifdef __cplusplus
}
#endif
#endif /* miniaudio_h */
/*
This is for preventing greying out of the implementation section.
*/
#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__)
#define MINIAUDIO_IMPLEMENTATION
#endif
/************************************************************************************************************************************************************
*************************************************************************************************************************************************************
IMPLEMENTATION
*************************************************************************************************************************************************************
************************************************************************************************************************************************************/
#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
#ifndef miniaudio_c
#define miniaudio_c
#include <assert.h>
#include <limits.h> /* For INT_MAX */
#include <math.h> /* sin(), etc. */
#include <stdarg.h>
#include <stdio.h>
#if !defined(_MSC_VER) && !defined(__DMC__)
#include <strings.h> /* For strcasecmp(). */
#include <wchar.h> /* For wcslen(), wcsrtombs() */
#endif
#ifdef _MSC_VER
#include <float.h> /* For _controlfp_s constants */
#endif
#ifdef MA_WIN32
#include <windows.h>
#else
#include <stdlib.h> /* For malloc(), free(), wcstombs(). */
#include <string.h> /* For memset() */
#include <sched.h>
#include <sys/time.h> /* select() (used for ma_sleep()). */
#include <pthread.h>
#endif
#include <sys/stat.h> /* For fstat(), etc. */
#ifdef MA_EMSCRIPTEN
#include <emscripten/emscripten.h>
#endif
#if !defined(MA_64BIT) && !defined(MA_32BIT)
#ifdef _WIN32
#ifdef _WIN64
#define MA_64BIT
#else
#define MA_32BIT
#endif
#endif
#endif
#if !defined(MA_64BIT) && !defined(MA_32BIT)
#ifdef __GNUC__
#ifdef __LP64__
#define MA_64BIT
#else
#define MA_32BIT
#endif
#endif
#endif
#if !defined(MA_64BIT) && !defined(MA_32BIT)
#include <stdint.h>
#if INTPTR_MAX == INT64_MAX
#define MA_64BIT
#else
#define MA_32BIT
#endif
#endif
/* Architecture Detection */
#if defined(__x86_64__) || defined(_M_X64)
#define MA_X64
#elif defined(__i386) || defined(_M_IX86)
#define MA_X86
#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
#define MA_ARM
#endif
/* Intrinsics Support */
#if defined(MA_X64) || defined(MA_X86)
#if defined(_MSC_VER) && !defined(__clang__)
/* MSVC. */
#if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */
#define MA_SUPPORT_SSE2
#endif
/*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */
/* #define MA_SUPPORT_AVX*/
/*#endif*/
#if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */
#define MA_SUPPORT_AVX2
#endif
#else
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (result == MA_SUCCESS) {
result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
}
return result;
}
#endif
return MA_SUCCESS;
}
/*
_wfopen() isn't always available in all compilation environments.
* Windows only.
* MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
* MinGW-64 (both 32- and 64-bit) seems to support it.
* MinGW wraps it in !defined(__STRICT_ANSI__).
* OpenWatcom wraps it in !defined(_NO_EXT_KEYS).
This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
*/
#if defined(_WIN32)
#if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
#define MA_HAS_WFOPEN
#endif
#endif
MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks)
{
if (ppFile != NULL) {
*ppFile = NULL; /* Safety. */
}
if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
return MA_INVALID_ARGS;
}
#if defined(MA_HAS_WFOPEN)
{
/* Use _wfopen() on Windows. */
#if defined(_MSC_VER) && _MSC_VER >= 1400
errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
if (err != 0) {
return ma_result_from_errno(err);
}
#else
*ppFile = _wfopen(pFilePath, pOpenMode);
if (*ppFile == NULL) {
return ma_result_from_errno(errno);
}
#endif
(void)pAllocationCallbacks;
}
#else
/*
Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.
*/
{
mbstate_t mbs;
size_t lenMB;
const wchar_t* pFilePathTemp = pFilePath;
char* pFilePathMB = NULL;
char pOpenModeMB[32] = {0};
/* Get the length first. */
MA_ZERO_OBJECT(&mbs);
lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
if (lenMB == (size_t)-1) {
return ma_result_from_errno(errno);
}
pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks);
if (pFilePathMB == NULL) {
return MA_OUT_OF_MEMORY;
}
pFilePathTemp = pFilePath;
MA_ZERO_OBJECT(&mbs);
wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
/* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
{
size_t i = 0;
for (;;) {
if (pOpenMode[i] == 0) {
pOpenModeMB[i] = '\0';
break;
}
pOpenModeMB[i] = (char)pOpenMode[i];
i += 1;
}
}
*ppFile = fopen(pFilePathMB, pOpenModeMB);
ma_free(pFilePathMB, pAllocationCallbacks);
}
if (*ppFile == NULL) {
return MA_ERROR;
}
#endif
return MA_SUCCESS;
}
static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
{
#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes);
#else
while (sizeInBytes > 0) {
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
**************************************************************************************************************************************************************/
MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
{
switch (logLevel)
{
case MA_LOG_LEVEL_DEBUG: return "DEBUG";
case MA_LOG_LEVEL_INFO: return "INFO";
case MA_LOG_LEVEL_WARNING: return "WARNING";
case MA_LOG_LEVEL_ERROR: return "ERROR";
default: return "ERROR";
}
}
#if defined(MA_DEBUG_OUTPUT)
/* Customize this to use a specific tag in __android_log_print() for debug output messages. */
#ifndef MA_ANDROID_LOG_TAG
#define MA_ANDROID_LOG_TAG "miniaudio"
#endif
void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage)
{
(void)pUserData;
/* Special handling for some platforms. */
#if defined(MA_ANDROID)
{
/* Android. */
__android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage);
}
#else
{
/* Everything else. */
printf("%s: %s", ma_log_level_to_string(level), pMessage);
}
#endif
}
#endif
MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData)
{
ma_log_callback callback;
MA_ZERO_OBJECT(&callback);
callback.onLog = onLog;
callback.pUserData = pUserData;
return callback;
}
MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog)
{
if (pLog == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pLog);
ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks);
/* We need a mutex for thread safety. */
#ifndef MA_NO_THREADING
{
ma_result result = ma_mutex_init(&pLog->lock);
if (result != MA_SUCCESS) {
return result;
}
}
#endif
/* If we're using debug output, enable it. */
#if defined(MA_DEBUG_OUTPUT)
{
ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */
}
#endif
return MA_SUCCESS;
}
MA_API void ma_log_uninit(ma_log* pLog)
{
if (pLog == NULL) {
return;
}
#ifndef MA_NO_THREADING
ma_mutex_uninit(&pLog->lock);
#endif
}
static void ma_log_lock(ma_log* pLog)
{
#ifndef MA_NO_THREADING
ma_mutex_lock(&pLog->lock);
#else
(void)pLog;
#endif
}
static void ma_log_unlock(ma_log* pLog)
{
#ifndef MA_NO_THREADING
ma_mutex_unlock(&pLog->lock);
#else
(void)pLog;
#endif
}
MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback)
{
ma_result result = MA_SUCCESS;
if (pLog == NULL || callback.onLog == NULL) {
return MA_INVALID_ARGS;
}
ma_log_lock(pLog);
{
if (pLog->callbackCount == ma_countof(pLog->callbacks)) {
result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
static MA_INLINE double ma_mix_f64(double x, double y, double a)
{
return x*(1-a) + y*a;
}
static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
{
return x + (y - x)*a;
}
static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
{
return lo + x*(hi-lo);
}
/*
Greatest common factor using Euclid's algorithm iteratively.
*/
static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b)
{
for (;;) {
if (b == 0) {
break;
} else {
ma_uint32 t = a;
a = b;
b = t % a;
}
}
return a;
}
static ma_uint32 ma_ffs_32(ma_uint32 x)
{
ma_uint32 i;
/* Just a naive implementation just to get things working for now. Will optimize this later. */
for (i = 0; i < 32; i += 1) {
if ((x & (1 << i)) != 0) {
return i;
}
}
return i;
}
static MA_INLINE ma_int16 ma_float_to_fixed_16(float x)
{
return (ma_int16)(x * (1 << 8));
}
/*
Random Number Generation
miniaudio uses the LCG random number generation algorithm. This is good enough for audio.
Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across
multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for
miniaudio's purposes.
*/
#ifndef MA_DEFAULT_LCG_SEED
#define MA_DEFAULT_LCG_SEED 4321
#endif
#define MA_LCG_M 2147483647
#define MA_LCG_A 48271
#define MA_LCG_C 0
static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */
static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed)
{
MA_ASSERT(pLCG != NULL);
pLCG->state = seed;
}
static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG)
{
pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M;
return pLCG->state;
}
static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG)
{
return (ma_uint32)ma_lcg_rand_s32(pLCG);
}
static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG)
{
return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF);
}
static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG)
{
return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF;
}
static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG)
{
return (float)ma_lcg_rand_f64(pLCG);
}
static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi)
{
return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi);
}
static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi)
{
if (lo == hi) {
return lo;
}
return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1);
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
#endif
#if defined(C89ATOMIC_HAS_32)
static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
{
c89atomic_uint32 result = 0;
(void)order;
__asm {
mov ecx, dst
mov eax, src
lock xadd [ecx], eax
mov result, eax
}
return result;
}
#endif
#else
#if defined(C89ATOMIC_HAS_8)
static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
{
(void)order;
return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src);
}
#endif
#if defined(C89ATOMIC_HAS_16)
static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
{
(void)order;
return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src);
}
#endif
#if defined(C89ATOMIC_HAS_32)
static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
{
(void)order;
return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src);
}
#endif
#if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT)
static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
{
(void)order;
return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src);
}
#else
#endif
#endif
#if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT)
static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
{
c89atomic_uint64 oldValue;
c89atomic_uint64 newValue;
do {
oldValue = *dst;
newValue = oldValue + src;
} while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
(void)order;
return oldValue;
}
#endif
#if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order)
{
(void)order;
__asm {
lock add [esp], 0
}
}
#else
#if defined(C89ATOMIC_X64)
#define c89atomic_thread_fence(order) __faststorefence(), (void)order
#else
static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order)
{
volatile c89atomic_uint32 barrier = 0;
c89atomic_fetch_add_explicit_32(&barrier, 0, order);
}
#endif
#endif
#define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst)
#define c89atomic_signal_fence(order) c89atomic_thread_fence(order)
#if defined(C89ATOMIC_HAS_8)
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order)
{
(void)order;
return c89atomic_compare_and_swap_8((volatile c89atomic_uint8*)ptr, 0, 0);
}
#endif
#if defined(C89ATOMIC_HAS_16)
static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order)
{
(void)order;
return c89atomic_compare_and_swap_16((volatile c89atomic_uint16*)ptr, 0, 0);
}
#endif
#if defined(C89ATOMIC_HAS_32)
static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order)
{
(void)order;
return c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)ptr, 0, 0);
}
#endif
#if defined(C89ATOMIC_HAS_64)
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order)
{
(void)order;
return c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)ptr, 0, 0);
}
#endif
#if defined(C89ATOMIC_HAS_8)
#define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order)
#endif
#if defined(C89ATOMIC_HAS_16)
#define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order)
#endif
#if defined(C89ATOMIC_HAS_32)
#define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order)
#endif
#if defined(C89ATOMIC_HAS_64)
#define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order)
#endif
#if defined(C89ATOMIC_HAS_8)
static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
{
c89atomic_uint8 oldValue;
c89atomic_uint8 newValue;
do {
oldValue = *dst;
newValue = (c89atomic_uint8)(oldValue - src);
} while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
(void)order;
return oldValue;
}
#endif
#if defined(C89ATOMIC_HAS_16)
static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
{
c89atomic_uint16 oldValue;
c89atomic_uint16 newValue;
do {
oldValue = *dst;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
#endif
#if defined(C89ATOMIC_HAS_64)
static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
{
c89atomic_uint64 oldValue;
c89atomic_uint64 newValue;
do {
oldValue = *dst;
newValue = oldValue | src;
} while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
(void)order;
return oldValue;
}
#endif
#if defined(C89ATOMIC_HAS_8)
#define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order)
#endif
#if defined(C89ATOMIC_HAS_16)
#define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order)
#endif
#if defined(C89ATOMIC_HAS_32)
#define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order)
#endif
#if defined(C89ATOMIC_HAS_64)
#define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order)
#endif
#if defined(C89ATOMIC_HAS_8)
#define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order)
#endif
#if defined(C89ATOMIC_HAS_16)
#define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order)
#endif
#if defined(C89ATOMIC_HAS_32)
#define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order)
#endif
#if defined(C89ATOMIC_HAS_64)
#define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order)
#endif
#if defined(C89ATOMIC_HAS_8)
typedef c89atomic_uint8 c89atomic_flag;
#define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order)
#define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order)
#define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
#else
typedef c89atomic_uint32 c89atomic_flag;
#define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_32(ptr, order)
#define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_32(ptr, order)
#define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_32(ptr, order)
#endif
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
#define C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE
#define C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE
#define c89atomic_memory_order_relaxed __ATOMIC_RELAXED
#define c89atomic_memory_order_consume __ATOMIC_CONSUME
#define c89atomic_memory_order_acquire __ATOMIC_ACQUIRE
#define c89atomic_memory_order_release __ATOMIC_RELEASE
#define c89atomic_memory_order_acq_rel __ATOMIC_ACQ_REL
#define c89atomic_memory_order_seq_cst __ATOMIC_SEQ_CST
#define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory")
#define c89atomic_thread_fence(order) __atomic_thread_fence(order)
#define c89atomic_signal_fence(order) __atomic_signal_fence(order)
#define c89atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr)
#define c89atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr)
#define c89atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr)
#define c89atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr)
#define c89atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order)
#define c89atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order)
#define c89atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order)
#define c89atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order)
#define c89atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order)
#define c89atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order)
#define c89atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order)
#define c89atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order)
#define c89atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order)
#define c89atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order)
#define c89atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order)
#define c89atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order)
#define c89atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order)
#define c89atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order)
#define c89atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order)
#define c89atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order)
#define c89atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order)
#define c89atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order)
#define c89atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order)
#define c89atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order)
#define c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
#define c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
#define c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
#define c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
#define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
#define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
#define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
#define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
#define c89atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order)
#define c89atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order)
#define c89atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order)
#define c89atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order)
#define c89atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order)
#define c89atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order)
#define c89atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order)
#define c89atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order)
#define c89atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order)
#define c89atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order)
#define c89atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order)
#define c89atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order)
#define c89atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order)
#define c89atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order)
#define c89atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order)
#define c89atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order)
#define c89atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order)
#define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order)
#define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order)
#define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order)
#define c89atomic_compare_and_swap_8 (dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
#define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
#define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
#define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
typedef c89atomic_uint8 c89atomic_flag;
#define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order)
#define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order)
#define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
#else
#define c89atomic_memory_order_relaxed 1
#define c89atomic_memory_order_consume 2
#define c89atomic_memory_order_acquire 3
#define c89atomic_memory_order_release 4
#define c89atomic_memory_order_acq_rel 5
#define c89atomic_memory_order_seq_cst 6
#define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory")
#if defined(__GNUC__)
#define c89atomic_thread_fence(order) __sync_synchronize(), (void)order
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
{
if (order > c89atomic_memory_order_acquire) {
__sync_synchronize();
}
return __sync_lock_test_and_set(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
{
c89atomic_uint16 oldValue;
do {
oldValue = *dst;
} while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
(void)order;
return oldValue;
}
static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
{
c89atomic_uint32 oldValue;
do {
oldValue = *dst;
} while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
(void)order;
return oldValue;
}
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
{
c89atomic_uint64 oldValue;
do {
oldValue = *dst;
} while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
(void)order;
return oldValue;
}
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_add(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_add(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_add(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_add(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_sub(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
{
(void)order;
return __sync_fetch_and_or(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_or(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_or(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_xor(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_xor(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_xor(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_xor(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_and(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_and(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_and(dst, src);
}
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
{
(void)order;
return __sync_fetch_and_and(dst, src);
}
#define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
#define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
#define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
#define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
#else
#if defined(C89ATOMIC_X86)
#define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc")
#elif defined(C89ATOMIC_X64)
#define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc")
#else
#error Unsupported architecture. Please submit a feature request.
#endif
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired)
{
c89atomic_uint8 result;
#if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
__asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
#else
#error Unsupported architecture. Please submit a feature request.
#endif
return result;
}
static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired)
{
c89atomic_uint16 result;
#if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
__asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
#else
#error Unsupported architecture. Please submit a feature request.
#endif
return result;
}
static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
{
c89atomic_uint32 result;
#if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
__asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
#else
#error Unsupported architecture. Please submit a feature request.
#endif
return result;
}
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
{
volatile c89atomic_uint64 result;
#if defined(C89ATOMIC_X86)
c89atomic_uint32 resultEAX;
c89atomic_uint32 resultEDX;
__asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc");
result = ((c89atomic_uint64)resultEDX << 32) | resultEAX;
#elif defined(C89ATOMIC_X64)
__asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
#else
#error Unsupported architecture. Please submit a feature request.
#endif
return result;
}
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
{
c89atomic_uint8 result = 0;
(void)order;
#if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
__asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
#else
#error Unsupported architecture. Please submit a feature request.
#endif
return result;
}
static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
} while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
(void)order;
return oldValue;
}
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
{
c89atomic_uint64 oldValue;
c89atomic_uint64 newValue;
do {
oldValue = *dst;
newValue = oldValue ^ src;
} while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
(void)order;
return oldValue;
}
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
{
c89atomic_uint8 oldValue;
c89atomic_uint8 newValue;
do {
oldValue = *dst;
newValue = (c89atomic_uint8)(oldValue | src);
} while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
(void)order;
return oldValue;
}
static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
{
c89atomic_uint16 oldValue;
c89atomic_uint16 newValue;
do {
oldValue = *dst;
newValue = (c89atomic_uint16)(oldValue | src);
} while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
(void)order;
return oldValue;
}
static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
{
c89atomic_uint32 oldValue;
c89atomic_uint32 newValue;
do {
oldValue = *dst;
newValue = oldValue | src;
} while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
(void)order;
return oldValue;
}
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
{
c89atomic_uint64 oldValue;
c89atomic_uint64 newValue;
do {
oldValue = *dst;
newValue = oldValue | src;
} while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
(void)order;
return oldValue;
}
#endif
#define c89atomic_signal_fence(order) c89atomic_thread_fence(order)
static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order)
{
(void)order;
return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0);
}
static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order)
{
(void)order;
return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0);
}
static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order)
{
(void)order;
return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0);
}
static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order)
{
(void)order;
return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0);
}
#define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order)
#define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order)
#define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order)
#define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order)
#define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order)
#define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order)
#define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order)
#define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order)
#define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order)
#define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order)
#define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order)
#define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order)
typedef c89atomic_uint8 c89atomic_flag;
#define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order)
#define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order)
#define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
#endif
#if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE)
#if defined(C89ATOMIC_HAS_8)
c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
{
c89atomic_uint8 expectedValue;
c89atomic_uint8 result;
(void)successOrder;
(void)failureOrder;
expectedValue = c89atomic_load_explicit_8(expected, c89atomic_memory_order_seq_cst);
result = c89atomic_compare_and_swap_8(dst, expectedValue, desired);
if (result == expectedValue) {
return 1;
} else {
c89atomic_store_explicit_8(expected, result, failureOrder);
return 0;
}
}
#endif
#if defined(C89ATOMIC_HAS_16)
c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
{
c89atomic_uint16 expectedValue;
c89atomic_uint16 result;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
case ERROR_SEM_TIMEOUT: return MA_TIMEOUT;
case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST;
default: break;
}
return MA_ERROR;
}
#endif /* MA_WIN32 */
/*******************************************************************************
Threading
*******************************************************************************/
static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield)
{
if (pSpinlock == NULL) {
return MA_INVALID_ARGS;
}
for (;;) {
if (c89atomic_exchange_explicit_32(pSpinlock, 1, c89atomic_memory_order_acquire) == 0) {
break;
}
while (c89atomic_load_explicit_32(pSpinlock, c89atomic_memory_order_relaxed) == 1) {
if (yield) {
ma_yield();
}
}
}
return MA_SUCCESS;
}
MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock)
{
return ma_spinlock_lock_ex(pSpinlock, MA_TRUE);
}
MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock)
{
return ma_spinlock_lock_ex(pSpinlock, MA_FALSE);
}
MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock)
{
if (pSpinlock == NULL) {
return MA_INVALID_ARGS;
}
c89atomic_store_explicit_32(pSpinlock, 0, c89atomic_memory_order_release);
return MA_SUCCESS;
}
#ifndef MA_NO_THREADING
#ifdef MA_WIN32
#define MA_THREADCALL WINAPI
typedef unsigned long ma_thread_result;
#else
#define MA_THREADCALL
typedef void* ma_thread_result;
#endif
typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
#ifdef MA_WIN32
static int ma_thread_priority_to_win32(ma_thread_priority priority)
{
switch (priority) {
case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
default: return THREAD_PRIORITY_NORMAL;
}
}
static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
{
*pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, NULL);
if (*pThread == NULL) {
return ma_result_from_GetLastError(GetLastError());
}
SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority));
return MA_SUCCESS;
}
static void ma_thread_wait__win32(ma_thread* pThread)
{
WaitForSingleObject((HANDLE)*pThread, INFINITE);
CloseHandle((HANDLE)*pThread);
}
static ma_result ma_mutex_init__win32(ma_mutex* pMutex)
{
*pMutex = CreateEventW(NULL, FALSE, TRUE, NULL);
if (*pMutex == NULL) {
return ma_result_from_GetLastError(GetLastError());
}
return MA_SUCCESS;
}
static void ma_mutex_uninit__win32(ma_mutex* pMutex)
{
CloseHandle((HANDLE)*pMutex);
}
static void ma_mutex_lock__win32(ma_mutex* pMutex)
{
WaitForSingleObject((HANDLE)*pMutex, INFINITE);
}
static void ma_mutex_unlock__win32(ma_mutex* pMutex)
{
SetEvent((HANDLE)*pMutex);
}
static ma_result ma_event_init__win32(ma_event* pEvent)
{
*pEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if (*pEvent == NULL) {
return ma_result_from_GetLastError(GetLastError());
}
return MA_SUCCESS;
}
static void ma_event_uninit__win32(ma_event* pEvent)
{
CloseHandle((HANDLE)*pEvent);
}
static ma_result ma_event_wait__win32(ma_event* pEvent)
{
DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE);
if (result == WAIT_OBJECT_0) {
return MA_SUCCESS;
}
if (result == WAIT_TIMEOUT) {
return MA_TIMEOUT;
}
return ma_result_from_GetLastError(GetLastError());
}
static ma_result ma_event_signal__win32(ma_event* pEvent)
{
BOOL result = SetEvent((HANDLE)*pEvent);
if (result == 0) {
return ma_result_from_GetLastError(GetLastError());
}
return MA_SUCCESS;
}
static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore)
{
*pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL);
if (*pSemaphore == NULL) {
return ma_result_from_GetLastError(GetLastError());
}
return MA_SUCCESS;
}
static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)
{
CloseHandle((HANDLE)*pSemaphore);
}
static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore)
{
DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE);
if (result == WAIT_OBJECT_0) {
return MA_SUCCESS;
}
if (result == WAIT_TIMEOUT) {
return MA_TIMEOUT;
}
return ma_result_from_GetLastError(GetLastError());
}
static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore)
{
BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL);
if (result == 0) {
return ma_result_from_GetLastError(GetLastError());
}
return MA_SUCCESS;
}
#endif
#ifdef MA_POSIX
static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
{
int result;
pthread_attr_t* pAttr = NULL;
#if !defined(__EMSCRIPTEN__)
/* Try setting the thread priority. It's not critical if anything fails here. */
pthread_attr_t attr;
if (pthread_attr_init(&attr) == 0) {
int scheduler = -1;
if (priority == ma_thread_priority_idle) {
#ifdef SCHED_IDLE
if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) {
scheduler = SCHED_IDLE;
}
#endif
} else if (priority == ma_thread_priority_realtime) {
#ifdef SCHED_FIFO
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) {
scheduler = SCHED_FIFO;
}
#endif
#ifdef MA_LINUX
} else {
scheduler = sched_getscheduler(0);
#endif
}
if (stackSize > 0) {
pthread_attr_setstacksize(&attr, stackSize);
}
if (scheduler != -1) {
int priorityMin = sched_get_priority_min(scheduler);
int priorityMax = sched_get_priority_max(scheduler);
int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
struct sched_param sched;
if (pthread_attr_getschedparam(&attr, &sched) == 0) {
if (priority == ma_thread_priority_idle) {
sched.sched_priority = priorityMin;
} else if (priority == ma_thread_priority_realtime) {
sched.sched_priority = priorityMax;
} else {
sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
if (sched.sched_priority < priorityMin) {
sched.sched_priority = priorityMin;
}
if (sched.sched_priority > priorityMax) {
sched.sched_priority = priorityMax;
}
}
if (pthread_attr_setschedparam(&attr, &sched) == 0) {
pAttr = &attr;
}
}
}
}
#else
/* It's the emscripten build. We'll have a few unused parameters. */
(void)priority;
(void)stackSize;
#endif
result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData);
/* The thread attributes object is no longer required. */
if (pAttr != NULL) {
pthread_attr_destroy(pAttr);
}
if (result != 0) {
return ma_result_from_errno(result);
}
return MA_SUCCESS;
}
static void ma_thread_wait__posix(ma_thread* pThread)
{
pthread_join((pthread_t)*pThread, NULL);
}
static ma_result ma_mutex_init__posix(ma_mutex* pMutex)
{
int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL);
if (result != 0) {
return ma_result_from_errno(result);
}
return MA_SUCCESS;
}
static void ma_mutex_uninit__posix(ma_mutex* pMutex)
{
pthread_mutex_destroy((pthread_mutex_t*)pMutex);
}
static void ma_mutex_lock__posix(ma_mutex* pMutex)
{
pthread_mutex_lock((pthread_mutex_t*)pMutex);
}
static void ma_mutex_unlock__posix(ma_mutex* pMutex)
{
pthread_mutex_unlock((pthread_mutex_t*)pMutex);
}
static ma_result ma_event_init__posix(ma_event* pEvent)
{
int result;
result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL);
if (result != 0) {
return ma_result_from_errno(result);
}
result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL);
if (result != 0) {
pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);
return ma_result_from_errno(result);
}
pEvent->value = 0;
return MA_SUCCESS;
}
static void ma_event_uninit__posix(ma_event* pEvent)
{
pthread_cond_destroy((pthread_cond_t*)&pEvent->cond);
pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);
}
static ma_result ma_event_wait__posix(ma_event* pEvent)
{
pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);
{
while (pEvent->value == 0) {
pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock);
}
pEvent->value = 0; /* Auto-reset. */
}
pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);
return MA_SUCCESS;
}
static ma_result ma_event_signal__posix(ma_event* pEvent)
{
pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);
{
pEvent->value = 1;
pthread_cond_signal((pthread_cond_t*)&pEvent->cond);
}
pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);
return MA_SUCCESS;
}
static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore)
{
int result;
if (pSemaphore == NULL) {
return MA_INVALID_ARGS;
}
pSemaphore->value = initialValue;
result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL);
if (result != 0) {
return ma_result_from_errno(result); /* Failed to create mutex. */
}
result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL);
if (result != 0) {
pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);
return ma_result_from_errno(result); /* Failed to create condition variable. */
}
return MA_SUCCESS;
}
static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)
{
if (pSemaphore == NULL) {
return;
}
pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond);
pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);
}
static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore)
{
if (pSemaphore == NULL) {
return MA_INVALID_ARGS;
}
pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);
{
/* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */
while (pSemaphore->value == 0) {
pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock);
}
pSemaphore->value -= 1;
}
pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);
return MA_SUCCESS;
}
static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore)
{
if (pSemaphore == NULL) {
return MA_INVALID_ARGS;
}
pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);
{
pSemaphore->value += 1;
pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond);
}
pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);
return MA_SUCCESS;
}
#endif
typedef struct
{
ma_thread_entry_proc entryProc;
void* pData;
ma_allocation_callbacks allocationCallbacks;
} ma_thread_proxy_data;
static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData)
{
ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData;
ma_thread_entry_proc entryProc;
void* pEntryProcData;
ma_thread_result result;
#if defined(MA_ON_THREAD_ENTRY)
MA_ON_THREAD_ENTRY
#endif
entryProc = pProxyData->entryProc;
pEntryProcData = pProxyData->pData;
/* Free the proxy data before getting into the real thread entry proc. */
ma_free(pProxyData, &pProxyData->allocationCallbacks);
result = entryProc(pEntryProcData);
#if defined(MA_ON_THREAD_EXIT)
MA_ON_THREAD_EXIT
#endif
return result;
}
static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_result result;
ma_thread_proxy_data* pProxyData;
if (pThread == NULL || entryProc == NULL) {
return MA_INVALID_ARGS;
}
pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */
if (pProxyData == NULL) {
return MA_OUT_OF_MEMORY;
}
pProxyData->entryProc = entryProc;
pProxyData->pData = pData;
ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks);
#ifdef MA_WIN32
result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
#endif
#ifdef MA_POSIX
result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
#endif
if (result != MA_SUCCESS) {
ma_free(pProxyData, pAllocationCallbacks);
return result;
}
return MA_SUCCESS;
}
static void ma_thread_wait(ma_thread* pThread)
{
if (pThread == NULL) {
return;
}
#ifdef MA_WIN32
ma_thread_wait__win32(pThread);
#endif
#ifdef MA_POSIX
ma_thread_wait__posix(pThread);
#endif
}
MA_API ma_result ma_mutex_init(ma_mutex* pMutex)
{
if (pMutex == NULL) {
MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
return MA_INVALID_ARGS;
}
#ifdef MA_WIN32
return ma_mutex_init__win32(pMutex);
#endif
#ifdef MA_POSIX
return ma_mutex_init__posix(pMutex);
#endif
}
MA_API void ma_mutex_uninit(ma_mutex* pMutex)
{
if (pMutex == NULL) {
return;
}
#ifdef MA_WIN32
ma_mutex_uninit__win32(pMutex);
#endif
#ifdef MA_POSIX
ma_mutex_uninit__posix(pMutex);
#endif
}
MA_API void ma_mutex_lock(ma_mutex* pMutex)
{
if (pMutex == NULL) {
MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
return;
}
#ifdef MA_WIN32
ma_mutex_lock__win32(pMutex);
#endif
#ifdef MA_POSIX
ma_mutex_lock__posix(pMutex);
#endif
}
MA_API void ma_mutex_unlock(ma_mutex* pMutex)
{
if (pMutex == NULL) {
MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
return;
}
#ifdef MA_WIN32
ma_mutex_unlock__win32(pMutex);
#endif
#ifdef MA_POSIX
ma_mutex_unlock__posix(pMutex);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore)
{
if (pSemaphore == NULL) {
MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
return MA_INVALID_ARGS;
}
#ifdef MA_WIN32
return ma_semaphore_init__win32(initialValue, pSemaphore);
#endif
#ifdef MA_POSIX
return ma_semaphore_init__posix(initialValue, pSemaphore);
#endif
}
MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)
{
if (pSemaphore == NULL) {
MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
return;
}
#ifdef MA_WIN32
ma_semaphore_uninit__win32(pSemaphore);
#endif
#ifdef MA_POSIX
ma_semaphore_uninit__posix(pSemaphore);
#endif
}
MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore)
{
if (pSemaphore == NULL) {
MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
return MA_INVALID_ARGS;
}
#ifdef MA_WIN32
return ma_semaphore_wait__win32(pSemaphore);
#endif
#ifdef MA_POSIX
return ma_semaphore_wait__posix(pSemaphore);
#endif
}
MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore)
{
if (pSemaphore == NULL) {
MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
return MA_INVALID_ARGS;
}
#ifdef MA_WIN32
return ma_semaphore_release__win32(pSemaphore);
#endif
#ifdef MA_POSIX
return ma_semaphore_release__posix(pSemaphore);
#endif
}
#else
/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */
#ifndef MA_NO_DEVICE_IO
#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
#endif
#endif /* MA_NO_THREADING */
#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF
MA_API ma_result ma_fence_init(ma_fence* pFence)
{
if (pFence == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pFence);
pFence->counter = 0;
#ifndef MA_NO_THREADING
{
ma_result result;
result = ma_event_init(&pFence->e);
if (result != MA_SUCCESS) {
return result;
}
}
#endif
return MA_SUCCESS;
}
MA_API void ma_fence_uninit(ma_fence* pFence)
{
if (pFence == NULL) {
return;
}
#ifndef MA_NO_THREADING
{
ma_event_uninit(&pFence->e);
}
#endif
MA_ZERO_OBJECT(pFence);
}
MA_API ma_result ma_fence_acquire(ma_fence* pFence)
{
if (pFence == NULL) {
return MA_INVALID_ARGS;
}
for (;;) {
ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter);
ma_uint32 newCounter = oldCounter + 1;
/* Make sure we're not about to exceed our maximum value. */
if (newCounter > MA_FENCE_COUNTER_MAX) {
MA_ASSERT(MA_FALSE);
return MA_OUT_OF_RANGE;
}
if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
return MA_SUCCESS;
} else {
if (oldCounter == MA_FENCE_COUNTER_MAX) {
MA_ASSERT(MA_FALSE);
return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */
}
}
}
/* Should never get here. */
/*return MA_SUCCESS;*/
}
MA_API ma_result ma_fence_release(ma_fence* pFence)
{
if (pFence == NULL) {
return MA_INVALID_ARGS;
}
for (;;) {
ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter);
ma_uint32 newCounter = oldCounter - 1;
if (oldCounter == 0) {
MA_ASSERT(MA_FALSE);
return MA_INVALID_OPERATION; /* Acquire/release mismatch. */
}
if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
#ifndef MA_NO_THREADING
{
if (newCounter == 0) {
ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */
}
}
#endif
return MA_SUCCESS;
} else {
if (oldCounter == 0) {
MA_ASSERT(MA_FALSE);
return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */
}
}
}
/* Should never get here. */
/*return MA_SUCCESS;*/
}
MA_API ma_result ma_fence_wait(ma_fence* pFence)
{
if (pFence == NULL) {
return MA_INVALID_ARGS;
}
for (;;) {
ma_uint32 counter;
counter = c89atomic_load_32(&pFence->counter);
if (counter == 0) {
/*
Counter has hit zero. By the time we get here some other thread may have acquired the
fence again, but that is where the caller needs to take care with how they se the fence.
*/
return MA_SUCCESS;
}
/* Getting here means the counter is > 0. We'll need to wait for something to happen. */
#ifndef MA_NO_THREADING
{
ma_result result;
result = ma_event_wait(&pFence->e);
if (result != MA_SUCCESS) {
return result;
}
}
#endif
}
/* Should never get here. */
/*return MA_INVALID_OPERATION;*/
}
MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification)
{
ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification;
if (pNotification == NULL) {
return MA_INVALID_ARGS;
}
if (pNotificationCallbacks->onSignal == NULL) {
return MA_NOT_IMPLEMENTED;
}
pNotificationCallbacks->onSignal(pNotification);
return MA_INVALID_ARGS;
}
static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification)
{
((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE;
}
MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll)
{
if (pNotificationPoll == NULL) {
return MA_INVALID_ARGS;
}
pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal;
pNotificationPoll->signalled = MA_FALSE;
return MA_SUCCESS;
}
MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll)
{
if (pNotificationPoll == NULL) {
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
} else {
pHeap = NULL;
}
result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator);
if (result != MA_SUCCESS) {
ma_free(pHeap, pAllocationCallbacks);
return result;
}
pAllocator->_ownsHeap = MA_TRUE;
return MA_SUCCESS;
}
MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks)
{
if (pAllocator == NULL) {
return;
}
if (pAllocator->_ownsHeap) {
ma_free(pAllocator->_pHeap, pAllocationCallbacks);
}
}
MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot)
{
ma_uint32 iAttempt;
const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */
if (pAllocator == NULL || pSlot == NULL) {
return MA_INVALID_ARGS;
}
for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) {
/* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */
ma_uint32 iGroup;
for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) {
/* CAS */
for (;;) {
ma_uint32 oldBitfield;
ma_uint32 newBitfield;
ma_uint32 bitOffset;
oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
/* Fast check to see if anything is available. */
if (oldBitfield == 0xFFFFFFFF) {
break; /* No available bits in this bitfield. */
}
bitOffset = ma_ffs_32(~oldBitfield);
MA_ASSERT(bitOffset < 32);
newBitfield = oldBitfield | (1 << bitOffset);
if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
ma_uint32 slotIndex;
/* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */
c89atomic_fetch_add_32(&pAllocator->count, 1);
/* The slot index is required for constructing the output value. */
slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */
if (slotIndex >= pAllocator->capacity) {
return MA_OUT_OF_MEMORY;
}
/* Increment the reference count before constructing the output value. */
pAllocator->pSlots[slotIndex] += 1;
/* Construct the output value. */
*pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex);
return MA_SUCCESS;
}
}
}
/* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */
if (pAllocator->count < pAllocator->capacity) {
ma_yield();
} else {
return MA_OUT_OF_MEMORY;
}
}
/* We couldn't find a slot within the maximum number of attempts. */
return MA_OUT_OF_MEMORY;
}
MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot)
{
ma_uint32 iGroup;
ma_uint32 iBit;
if (pAllocator == NULL) {
return MA_INVALID_ARGS;
}
iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */
iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */
if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) {
return MA_INVALID_ARGS;
}
MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */
while (c89atomic_load_32(&pAllocator->count) > 0) {
/* CAS */
ma_uint32 oldBitfield;
ma_uint32 newBitfield;
oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
newBitfield = oldBitfield & ~(1 << iBit);
/* Debugging for checking for double-frees. */
#if defined(MA_DEBUG_OUTPUT)
{
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
/* Jobs. */
pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes;
pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job));
return MA_SUCCESS;
}
MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes)
{
ma_result result;
ma_job_queue_heap_layout layout;
if (pHeapSizeInBytes == NULL) {
return MA_INVALID_ARGS;
}
*pHeapSizeInBytes = 0;
result = ma_job_queue_get_heap_layout(pConfig, &layout);
if (result != MA_SUCCESS) {
return result;
}
*pHeapSizeInBytes = layout.sizeInBytes;
return MA_SUCCESS;
}
MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue)
{
ma_result result;
ma_job_queue_heap_layout heapLayout;
ma_slot_allocator_config allocatorConfig;
if (pQueue == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pQueue);
result = ma_job_queue_get_heap_layout(pConfig, &heapLayout);
if (result != MA_SUCCESS) {
return result;
}
pQueue->_pHeap = pHeap;
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
pQueue->flags = pConfig->flags;
pQueue->capacity = pConfig->capacity;
pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset);
allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);
result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator);
if (result != MA_SUCCESS) {
return result;
}
/* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */
if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
#ifndef MA_NO_THREADING
{
ma_semaphore_init(0, &pQueue->sem);
}
#else
{
/* Threading is disabled and we've requested non-blocking mode. */
return MA_INVALID_OPERATION;
}
#endif
}
/*
Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is
just a dummy item for giving us the first item in the list which is stored in the "next" member.
*/
ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */
pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE;
pQueue->tail = pQueue->head;
return MA_SUCCESS;
}
MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue)
{
ma_result result;
size_t heapSizeInBytes;
void* pHeap;
result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes);
if (result != MA_SUCCESS) {
return result;
}
if (heapSizeInBytes > 0) {
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
if (pHeap == NULL) {
return MA_OUT_OF_MEMORY;
}
} else {
pHeap = NULL;
}
result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue);
if (result != MA_SUCCESS) {
ma_free(pHeap, pAllocationCallbacks);
return result;
}
pQueue->_ownsHeap = MA_TRUE;
return MA_SUCCESS;
}
MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks)
{
if (pQueue == NULL) {
return;
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (ma_job_extract_slot(next) == 0xFFFF) {
if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) {
break;
}
} else {
ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
}
}
}
ma_job_queue_cas(&pQueue->tail, tail, slot);
}
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
ma_spinlock_unlock(&pQueue->lock);
#endif
/* Signal the semaphore as the last step if we're using synchronous mode. */
if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
#ifndef MA_NO_THREADING
{
ma_semaphore_release(&pQueue->sem);
}
#else
{
MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
}
#endif
}
return MA_SUCCESS;
}
MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob)
{
ma_uint64 head;
ma_uint64 tail;
ma_uint64 next;
if (pQueue == NULL || pJob == NULL) {
return MA_INVALID_ARGS;
}
/* If we're running in synchronous mode we'll need to wait on a semaphore. */
if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
#ifndef MA_NO_THREADING
{
ma_semaphore_wait(&pQueue->sem);
}
#else
{
MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
}
#endif
}
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
ma_spinlock_lock(&pQueue->lock);
#endif
{
/*
BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below
is stored. One thread can fall through to the freeing of this item while another is still using "head" for the
retrieval of the "next" variable.
The slot allocator might need to make use of some reference counting to ensure it's only truely freed when
there are no more references to the item. This must be fixed before removing these locks.
*/
/* Now we need to remove the root item from the list. */
for (;;) {
head = c89atomic_load_64(&pQueue->head);
tail = c89atomic_load_64(&pQueue->tail);
next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next);
if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->head))) {
if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) {
if (ma_job_extract_slot(next) == 0xFFFF) {
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
ma_spinlock_unlock(&pQueue->lock);
#endif
return MA_NO_DATA_AVAILABLE;
}
ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
} else {
*pJob = pQueue->pJobs[ma_job_extract_slot(next)];
if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) {
break;
}
}
}
}
}
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
ma_spinlock_unlock(&pQueue->lock);
#endif
ma_slot_allocator_free(&pQueue->allocator, head);
/*
If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We
could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as
possible.
*/
if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) {
ma_job_queue_post(pQueue, pJob);
return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */
}
return MA_SUCCESS;
}
/************************************************************************************************************************************************************
*************************************************************************************************************************************************************
DEVICE I/O
==========
*************************************************************************************************************************************************************
************************************************************************************************************************************************************/
#ifndef MA_NO_DEVICE_IO
#ifdef MA_WIN32
#include <objbase.h>
#include <mmreg.h>
#include <mmsystem.h>
#endif
#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
#include <mach/mach_time.h> /* For mach_absolute_time() */
#endif
#ifdef MA_POSIX
#include <sys/types.h>
#include <unistd.h>
#include <dlfcn.h>
#endif
/*
Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
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
disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
*/
/*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/
/* Disable run-time linking on certain backends. */
#ifndef MA_NO_RUNTIME_LINKING
#if defined(MA_EMSCRIPTEN)
#define MA_NO_RUNTIME_LINKING
#endif
#endif
MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags)
{
if (pDeviceInfo == NULL) {
return;
}
if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) {
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
pDeviceInfo->nativeDataFormatCount += 1;
}
}
MA_API const char* ma_get_backend_name(ma_backend backend)
{
switch (backend)
{
case ma_backend_wasapi: return "WASAPI";
case ma_backend_dsound: return "DirectSound";
case ma_backend_winmm: return "WinMM";
case ma_backend_coreaudio: return "Core Audio";
case ma_backend_sndio: return "sndio";
case ma_backend_audio4: return "audio(4)";
case ma_backend_oss: return "OSS";
case ma_backend_pulseaudio: return "PulseAudio";
case ma_backend_alsa: return "ALSA";
case ma_backend_jack: return "JACK";
case ma_backend_aaudio: return "AAudio";
case ma_backend_opensl: return "OpenSL|ES";
case ma_backend_webaudio: return "Web Audio";
case ma_backend_custom: return "Custom";
case ma_backend_null: return "Null";
default: return "Unknown";
}
}
MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
{
/*
This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers
about some enums not being handled by the switch statement.
*/
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
}
}
return MA_SUCCESS;
}
/* A helper for changing the state of the device. */
static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState)
{
c89atomic_exchange_i32((ma_int32*)&pDevice->state, (ma_int32)newState);
}
#ifdef MA_WIN32
GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
/*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
/*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
#endif
MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */
{
ma_uint32 i;
for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
if (g_maFormatPriorities[i] == format) {
return i;
}
}
/* Getting here means the format could not be found or is equal to ma_format_unknown. */
return (ma_uint32)-1;
}
static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor)
{
if (pDeviceDescriptor == NULL) {
return MA_FALSE;
}
if (pDeviceDescriptor->format == ma_format_unknown) {
return MA_FALSE;
}
if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) {
return MA_FALSE;
}
if (pDeviceDescriptor->sampleRate == 0) {
return MA_FALSE;
}
return MA_TRUE;
}
static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice)
{
ma_result result = MA_SUCCESS;
ma_bool32 exitLoop = MA_FALSE;
ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint32 capturedDeviceDataCapInFrames = 0;
ma_uint32 playbackDeviceDataCapInFrames = 0;
MA_ASSERT(pDevice != NULL);
/* Just some quick validation on the device type and the available callbacks. */
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
if (pDevice->pContext->callbacks.onDeviceRead == NULL) {
return MA_NOT_IMPLEMENTED;
}
capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
}
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
if (pDevice->pContext->callbacks.onDeviceWrite == NULL) {
return MA_NOT_IMPLEMENTED;
}
playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
}
/* NOTE: The device was started outside of this function, in the worker thread. */
while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) {
switch (pDevice->type) {
case ma_device_type_duplex:
{
/* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */
ma_uint32 totalCapturedDeviceFramesProcessed = 0;
ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
ma_uint32 capturedDeviceFramesRemaining;
ma_uint32 capturedDeviceFramesProcessed;
ma_uint32 capturedDeviceFramesToProcess;
ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
}
result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
if (result != MA_SUCCESS) {
exitLoop = MA_TRUE;
break;
}
capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
capturedDeviceFramesProcessed = 0;
/* At this point we have our captured data in device format and we now need to convert it to client format. */
for (;;) {
ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
/* Convert capture data from device format to client format. */
result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
if (result != MA_SUCCESS) {
break;
}
/*
If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small
which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
*/
if (capturedClientFramesToProcessThisIteration == 0) {
break;
}
ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
/* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
for (;;) {
ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData);
framesReadThisPeriod += framesProcessed;
}
} break;
case ma_device_type_playback:
{
/* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
ma_uint32 framesWrittenThisPeriod = 0;
while (framesWrittenThisPeriod < periodSizeInFrames) {
ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
ma_uint32 framesProcessed;
ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) {
framesToWriteThisIteration = playbackDeviceDataCapInFrames;
}
ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData);
result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed);
if (result != MA_SUCCESS) {
exitLoop = MA_TRUE;
break;
}
/* Make sure we don't get stuck in the inner loop. */
if (framesProcessed == 0) {
break;
}
framesWrittenThisPeriod += framesProcessed;
}
} break;
/* Should never get here. */
default: break;
}
}
return result;
}
/*******************************************************************************
Null Backend
*******************************************************************************/
#ifdef MA_HAS_NULL
#define MA_DEVICE_OP_NONE__NULL 0
#define MA_DEVICE_OP_START__NULL 1
#define MA_DEVICE_OP_SUSPEND__NULL 2
#define MA_DEVICE_OP_KILL__NULL 3
static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
{
ma_device* pDevice = (ma_device*)pData;
MA_ASSERT(pDevice != NULL);
for (;;) { /* Keep the thread alive until the device is uninitialized. */
ma_uint32 operation;
/* Wait for an operation to be requested. */
ma_event_wait(&pDevice->null_device.operationEvent);
/* At this point an event should have been triggered. */
operation = pDevice->null_device.operation;
/* Starting the device needs to put the thread into a loop. */
if (operation == MA_DEVICE_OP_START__NULL) {
/* Reset the timer just in case. */
ma_timer_init(&pDevice->null_device.timer);
/* Getting here means a suspend or kill operation has been requested. */
pDevice->null_device.operationResult = MA_SUCCESS;
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
ma_semaphore_release(&pDevice->null_device.operationSemaphore);
continue;
}
/* Suspending the device means we need to stop the timer and just continue the loop. */
if (operation == MA_DEVICE_OP_SUSPEND__NULL) {
/* We need to add the current run time to the prior run time, then reset the timer. */
pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
ma_timer_init(&pDevice->null_device.timer);
/* We're done. */
pDevice->null_device.operationResult = MA_SUCCESS;
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
ma_semaphore_release(&pDevice->null_device.operationSemaphore);
continue;
}
/* Killing the device means we need to get out of this loop so that this thread can terminate. */
if (operation == MA_DEVICE_OP_KILL__NULL) {
pDevice->null_device.operationResult = MA_SUCCESS;
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
ma_semaphore_release(&pDevice->null_device.operationSemaphore);
break;
}
/* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
if (operation == MA_DEVICE_OP_NONE__NULL) {
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). */
pDevice->null_device.operationResult = MA_INVALID_OPERATION;
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
ma_semaphore_release(&pDevice->null_device.operationSemaphore);
continue; /* Continue the loop. Don't terminate. */
}
}
return (ma_thread_result)0;
}
static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
{
ma_result result;
/*
TODO: Need to review this and consider just using mutual exclusion. I think the original motivation
for this was to just post the event to a queue and return immediately, but that has since changed
and now this function is synchronous. I think this can be simplified to just use a mutex.
*/
/*
The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later
to support queing of operations.
*/
result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore);
if (result != MA_SUCCESS) {
return result; /* Failed to wait for the event. */
}
/*
When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to
signal an event to the worker thread to let it know that it can start work.
*/
pDevice->null_device.operation = operation;
/* Once the operation code has been set, the worker thread can start work. */
if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) {
return MA_ERROR;
}
/* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */
if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) {
return MA_ERROR;
}
return pDevice->null_device.operationResult;
}
static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
{
ma_uint32 internalSampleRate;
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
internalSampleRate = pDevice->capture.internalSampleRate;
} else {
internalSampleRate = pDevice->playback.internalSampleRate;
}
return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
}
static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
ma_bool32 cbResult = MA_TRUE;
MA_ASSERT(pContext != NULL);
MA_ASSERT(callback != NULL);
/* Playback. */
if (cbResult) {
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
}
/* Capture. */
if (cbResult) {
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
}
(void)cbResult; /* Silence a static analysis warning. */
return MA_SUCCESS;
}
static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
{
MA_ASSERT(pContext != NULL);
if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
return MA_NO_DEVICE; /* Don't know the device. */
}
/* Name / Description */
if (deviceType == ma_device_type_playback) {
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
} else {
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
}
pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
/* Support everything on the null backend. */
pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
pDeviceInfo->nativeDataFormats[0].channels = 0;
pDeviceInfo->nativeDataFormats[0].sampleRate = 0;
pDeviceInfo->nativeDataFormats[0].flags = 0;
pDeviceInfo->nativeDataFormatCount = 1;
(void)pContext;
return MA_SUCCESS;
}
static ma_result ma_device_uninit__null(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
/* Keep it clean and wait for the device thread to finish before returning. */
ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
/* Wait for the thread to finish before continuing. */
ma_thread_wait(&pDevice->null_device.deviceThread);
/* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */
ma_semaphore_uninit(&pDevice->null_device.operationSemaphore);
ma_event_uninit(&pDevice->null_device.operationCompletionEvent);
ma_event_uninit(&pDevice->null_device.operationEvent);
return MA_SUCCESS;
}
static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
{
ma_result result;
MA_ASSERT(pDevice != NULL);
MA_ZERO_OBJECT(&pDevice->null_device);
if (pConfig->deviceType == ma_device_type_loopback) {
return MA_DEVICE_TYPE_NOT_SUPPORTED;
}
/* The null backend supports everything exactly as we specify it. */
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT;
pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS;
pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE;
if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) {
ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
}
pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
}
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT;
pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;
pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE;
if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) {
ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels);
}
pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
}
/*
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
first period is "written" to it, and then stopped in ma_device_stop__null().
*/
result = ma_event_init(&pDevice->null_device.operationEvent);
if (result != MA_SUCCESS) {
return result;
}
result = ma_event_init(&pDevice->null_device.operationCompletionEvent);
if (result != MA_SUCCESS) {
return result;
}
result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */
if (result != MA_SUCCESS) {
return result;
}
result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks);
if (result != MA_SUCCESS) {
return result;
}
return MA_SUCCESS;
}
static ma_result ma_device_start__null(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE);
return MA_SUCCESS;
}
static ma_result ma_device_stop__null(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE);
return MA_SUCCESS;
}
static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
{
ma_result result = MA_SUCCESS;
ma_uint32 totalPCMFramesProcessed;
ma_bool32 wasStartedOnEntry;
if (pFramesWritten != NULL) {
*pFramesWritten = 0;
}
wasStartedOnEntry = c89atomic_load_32(&pDevice->null_device.isStarted);
/* Keep going until everything has been read. */
totalPCMFramesProcessed = 0;
while (totalPCMFramesProcessed < frameCount) {
ma_uint64 targetFrame;
/* If there are any frames remaining in the current period, consume those first. */
if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) {
ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback;
if (framesToProcess > framesRemaining) {
framesToProcess = framesRemaining;
}
/* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */
(void)pPCMFrames;
pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
totalPCMFramesProcessed += framesToProcess;
}
/* 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. */
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (totalPCMFramesProcessed == frameCount) {
break;
}
/* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames;
for (;;) {
ma_uint64 currentFrame;
/* Stop waiting if the device has been stopped. */
if (!c89atomic_load_32(&pDevice->null_device.isStarted)) {
break;
}
currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
if (currentFrame >= targetFrame) {
break;
}
/* Getting here means we haven't yet reached the target sample, so continue waiting. */
ma_sleep(10);
}
pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames;
pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames;
}
if (pFramesRead != NULL) {
*pFramesRead = totalPCMFramesProcessed;
}
return result;
}
static ma_result ma_context_uninit__null(ma_context* pContext)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pContext->backend == ma_backend_null);
(void)pContext;
return MA_SUCCESS;
}
static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
{
MA_ASSERT(pContext != NULL);
(void)pConfig;
(void)pContext;
pCallbacks->onContextInit = ma_context_init__null;
pCallbacks->onContextUninit = ma_context_uninit__null;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null;
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null;
pCallbacks->onDeviceInit = ma_device_init__null;
pCallbacks->onDeviceUninit = ma_device_uninit__null;
pCallbacks->onDeviceStart = ma_device_start__null;
pCallbacks->onDeviceStop = ma_device_stop__null;
pCallbacks->onDeviceRead = ma_device_read__null;
pCallbacks->onDeviceWrite = ma_device_write__null;
pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */
/* The null backend always works. */
return MA_SUCCESS;
}
#endif
/*******************************************************************************
WIN32 COMMON
*******************************************************************************/
#if defined(MA_WIN32)
#if defined(MA_WIN32_DESKTOP)
#define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit)
#define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
#define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
#define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
#define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
#else
#define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
#define ma_CoUninitialize(pContext) CoUninitialize()
#define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
#define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
#define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
#endif
#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__)
typedef size_t DWORD_PTR;
#endif
#if !defined(WAVE_FORMAT_44M08)
#define WAVE_FORMAT_44M08 0x00000100
#define WAVE_FORMAT_44S08 0x00000200
#define WAVE_FORMAT_44M16 0x00000400
#define WAVE_FORMAT_44S16 0x00000800
#define WAVE_FORMAT_48M08 0x00001000
#define WAVE_FORMAT_48S08 0x00002000
#define WAVE_FORMAT_48M16 0x00004000
#define WAVE_FORMAT_48S16 0x00008000
#define WAVE_FORMAT_96M08 0x00010000
#define WAVE_FORMAT_96S08 0x00020000
#define WAVE_FORMAT_96M16 0x00040000
#define WAVE_FORMAT_96S16 0x00080000
#endif
#ifndef SPEAKER_FRONT_LEFT
#define SPEAKER_FRONT_LEFT 0x1
#define SPEAKER_FRONT_RIGHT 0x2
#define SPEAKER_FRONT_CENTER 0x4
#define SPEAKER_LOW_FREQUENCY 0x8
#define SPEAKER_BACK_LEFT 0x10
#define SPEAKER_BACK_RIGHT 0x20
#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
#define SPEAKER_BACK_CENTER 0x100
#define SPEAKER_SIDE_LEFT 0x200
#define SPEAKER_SIDE_RIGHT 0x400
#define SPEAKER_TOP_CENTER 0x800
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
} ma_IAudioRenderClientVtbl;
struct ma_IAudioRenderClient
{
ma_IAudioRenderClientVtbl* lpVtbl;
};
static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
/* IAudioCaptureClient */
typedef struct
{
/* IUnknown */
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
/* IAudioRenderClient */
HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
} ma_IAudioCaptureClientVtbl;
struct ma_IAudioCaptureClient
{
ma_IAudioCaptureClientVtbl* lpVtbl;
};
static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, ...
static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
#include <mmdeviceapi.h>
typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
typedef struct
{
/* IUnknown */
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
/* IActivateAudioInterfaceCompletionHandler */
HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
} ma_completion_handler_uwp_vtbl;
struct ma_completion_handler_uwp
{
ma_completion_handler_uwp_vtbl* lpVtbl;
MA_ATOMIC(4, ma_uint32) counter;
HANDLE hEvent;
};
static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
{
/*
We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To
"implement" this, we just make sure we return pThis when the IAgileObject is requested.
*/
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)) {
*ppObject = NULL;
return E_NOINTERFACE;
}
/* Getting here means the IID is IUnknown or IMMNotificationClient. */
*ppObject = (void*)pThis;
((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
return S_OK;
}
static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
{
return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
}
static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
{
ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
if (newRefCount == 0) {
return 0; /* We don't free anything here because we never allocate the object on the heap. */
}
return (ULONG)newRefCount;
}
static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
{
(void)pActivateOperation;
SetEvent(pThis->hEvent);
return S_OK;
}
static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
ma_completion_handler_uwp_QueryInterface,
ma_completion_handler_uwp_AddRef,
ma_completion_handler_uwp_Release,
ma_completion_handler_uwp_ActivateCompleted
};
static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
{
MA_ASSERT(pHandler != NULL);
MA_ZERO_OBJECT(pHandler);
pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
pHandler->counter = 1;
pHandler->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if (pHandler->hEvent == NULL) {
return ma_result_from_GetLastError(GetLastError());
}
return MA_SUCCESS;
}
static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
{
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (pCmd->pEvent == NULL) {
isUsingLocalEvent = MA_TRUE;
result = ma_event_init(&localEvent);
if (result != MA_SUCCESS) {
return result; /* Failed to create the event for this command. */
}
}
/* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */
ma_mutex_lock(&pContext->wasapi.commandLock);
{
ma_uint32 index;
/* Spin until we've got some space available. */
while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) {
ma_yield();
}
/* Space is now available. Can safely add to the list. */
index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands);
pContext->wasapi.commands[index] = *pCmd;
pContext->wasapi.commands[index].pEvent = &localEvent;
pContext->wasapi.commandCount += 1;
/* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */
ma_semaphore_release(&pContext->wasapi.commandSem);
}
ma_mutex_unlock(&pContext->wasapi.commandLock);
if (isUsingLocalEvent) {
ma_event_wait(&localEvent);
ma_event_uninit(&localEvent);
}
return MA_SUCCESS;
}
static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd)
{
ma_result result = MA_SUCCESS;
MA_ASSERT(pContext != NULL);
MA_ASSERT(pCmd != NULL);
result = ma_semaphore_wait(&pContext->wasapi.commandSem);
if (result == MA_SUCCESS) {
ma_mutex_lock(&pContext->wasapi.commandLock);
{
*pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex];
pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands);
pContext->wasapi.commandCount -= 1;
}
ma_mutex_unlock(&pContext->wasapi.commandLock);
}
return result;
}
static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData)
{
ma_result result;
ma_context* pContext = (ma_context*)pUserData;
MA_ASSERT(pContext != NULL);
for (;;) {
ma_context_command__wasapi cmd;
result = ma_context_next_command__wasapi(pContext, &cmd);
if (result != MA_SUCCESS) {
break;
}
switch (cmd.code)
{
case MA_CONTEXT_COMMAND_QUIT__WASAPI:
{
/* Do nothing. Handled after the switch. */
} break;
case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI:
{
if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) {
*cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService));
} else {
*cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService));
}
} break;
case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI:
{
if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) {
if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) {
ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);
cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL;
}
}
if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) {
if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) {
ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture);
cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL;
}
}
} break;
default:
{
/* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */
MA_ASSERT(MA_FALSE);
} break;
}
if (cmd.pEvent != NULL) {
ma_event_signal(cmd.pEvent);
}
if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) {
break; /* Received a quit message. Get out of here. */
}
}
return (ma_thread_result)0;
}
static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService)
{
ma_result result;
ma_result cmdResult;
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI);
cmd.data.createAudioClient.deviceType = deviceType;
cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient;
cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService;
cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */
result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */
if (result != MA_SUCCESS) {
return result;
}
return *cmd.data.createAudioClient.pResult;
}
#if 0 /* Not used at the moment, but leaving here for future use. */
static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType)
{
ma_result result;
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI);
cmd.data.releaseAudioClient.pDevice = pDevice;
cmd.data.releaseAudioClient.deviceType = deviceType;
result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */
if (result != MA_SUCCESS) {
return result;
}
return MA_SUCCESS;
}
#endif
static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo)
{
MA_ASSERT(pWF != NULL);
MA_ASSERT(pInfo != NULL);
if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) {
return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */
}
pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF);
pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels;
pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec;
pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0;
pInfo->nativeDataFormatCount += 1;
}
static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo)
{
HRESULT hr;
WAVEFORMATEX* pWF = NULL;
MA_ASSERT(pAudioClient != NULL);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
#endif
done:
/* Clean up. */
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
if (pDeviceInterface != NULL) {
ma_IMMDevice_Release(pDeviceInterface);
}
#else
if (pDeviceInterface != NULL) {
ma_IUnknown_Release(pDeviceInterface);
}
#endif
if (result != MA_SUCCESS) {
if (pData->pRenderClient) {
ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
pData->pRenderClient = NULL;
}
if (pData->pCaptureClient) {
ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
pData->pCaptureClient = NULL;
}
if (pData->pAudioClient) {
ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
pData->pAudioClient = NULL;
}
if (errorMsg != NULL && errorMsg[0] != '\0') {
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s", errorMsg);
}
return result;
} else {
return MA_SUCCESS;
}
}
static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
{
ma_device_init_internal_data__wasapi data;
ma_result result;
MA_ASSERT(pDevice != NULL);
/* We only re-initialize the playback or capture device. Never a full-duplex device. */
if (deviceType == ma_device_type_duplex) {
return MA_INVALID_ARGS;
}
/*
Before reinitializing the device we need to free the previous audio clients.
There's a known memory leak here. We will be calling this from the routing change callback that
is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion
this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably
need some system where we post an event, but delay the execution of it until the callback has
returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for
a command thread which might be useful for this.
*/
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
if (pDevice->wasapi.pCaptureClient) {
ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
pDevice->wasapi.pCaptureClient = NULL;
}
if (pDevice->wasapi.pAudioClientCapture) {
/*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/
pDevice->wasapi.pAudioClientCapture = NULL;
}
}
if (deviceType == ma_device_type_playback) {
if (pDevice->wasapi.pRenderClient) {
ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
pDevice->wasapi.pRenderClient = NULL;
}
if (pDevice->wasapi.pAudioClientPlayback) {
/*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/
pDevice->wasapi.pAudioClientPlayback = NULL;
}
}
if (deviceType == ma_device_type_playback) {
data.formatIn = pDevice->playback.format;
data.channelsIn = pDevice->playback.channels;
MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
data.shareMode = pDevice->playback.shareMode;
} else {
data.formatIn = pDevice->capture.format;
data.channelsIn = pDevice->capture.channels;
MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
data.shareMode = pDevice->capture.shareMode;
}
data.sampleRateIn = pDevice->sampleRate;
data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames;
data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;
data.periodsIn = pDevice->wasapi.originalPeriods;
data.performanceProfile = pDevice->wasapi.originalPerformanceProfile;
data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
if (result != MA_SUCCESS) {
return result;
}
/* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
pDevice->wasapi.pCaptureClient = data.pCaptureClient;
pDevice->capture.internalFormat = data.formatOut;
pDevice->capture.internalChannels = data.channelsOut;
pDevice->capture.internalSampleRate = data.sampleRateOut;
MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* Special rules for exclusive mode. */
if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback;
} else {
bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback;
}
hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback);
if (hr == S_OK) {
/* We have data available. */
pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames;
pDevice->wasapi.mappedBufferPlaybackLen = 0;
} else {
if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
/* Not enough data available. We need to wait for more. */
if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
result = MA_ERROR;
break; /* Wait failed. Probably timed out. */
}
} else {
/* Some error occurred. We'll need to abort. */
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr);
result = ma_result_from_HRESULT(hr);
break;
}
}
}
}
if (pFramesWritten != NULL) {
*pFramesWritten = totalFramesProcessed;
}
return result;
}
static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
}
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
}
return MA_SUCCESS;
}
static ma_result ma_context_uninit__wasapi(ma_context* pContext)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pContext->backend == ma_backend_wasapi);
if (pContext->wasapi.commandThread != NULL) {
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
ma_context_post_command__wasapi(pContext, &cmd);
ma_thread_wait(&pContext->wasapi.commandThread);
/* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */
ma_semaphore_uninit(&pContext->wasapi.commandSem);
ma_mutex_uninit(&pContext->wasapi.commandLock);
}
return MA_SUCCESS;
}
static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
{
ma_result result = MA_SUCCESS;
MA_ASSERT(pContext != NULL);
(void)pConfig;
#ifdef MA_WIN32_DESKTOP
/*
WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
exclusive mode does not work until SP1.
Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error.
*/
{
ma_OSVERSIONINFOEXW osvi;
ma_handle kernel32DLL;
ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
ma_PFNVerSetConditionMask _VerSetConditionMask;
kernel32DLL = ma_dlopen(pContext, "kernel32.dll");
if (kernel32DLL == NULL) {
return MA_NO_BACKEND;
}
_VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW");
_VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask");
if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
ma_dlclose(pContext, kernel32DLL);
return MA_NO_BACKEND;
}
MA_ZERO_OBJECT(&osvi);
osvi.dwOSVersionInfoSize = sizeof(osvi);
osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF);
osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF);
osvi.wServicePackMajor = 1;
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_GREA...
result = MA_SUCCESS;
} else {
result = MA_NO_BACKEND;
}
ma_dlclose(pContext, kernel32DLL);
}
#endif
if (result != MA_SUCCESS) {
return result;
}
MA_ZERO_OBJECT(&pContext->wasapi);
/*
Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread
than the one that retrieved it with GetService(). This can result in a deadlock in two
situations:
1) When calling ma_device_uninit() from a different thread to ma_device_init(); and
2) When uninitializing and reinitializing the internal IAudioClient object in response to
automatic stream routing.
We could define ma_device_uninit() such that it must be called on the same thread as
ma_device_init(). We could also just not release the IAudioClient when performing automatic
stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so
we're going to have to work around this with a worker thread. This is not ideal, but I can't
think of a better way to do this.
More information about this can be found here:
https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient
Note this section:
When releasing an IAudioRenderClient interface instance, the client must call the interface's
Release method from the same thread as the call to IAudioClient::GetService that created the
object.
*/
{
result = ma_mutex_init(&pContext->wasapi.commandLock);
if (result != MA_SUCCESS) {
return result;
}
result = ma_semaphore_init(0, &pContext->wasapi.commandSem);
if (result != MA_SUCCESS) {
ma_mutex_uninit(&pContext->wasapi.commandLock);
return result;
}
result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks);
if (result != MA_SUCCESS) {
ma_semaphore_uninit(&pContext->wasapi.commandSem);
ma_mutex_uninit(&pContext->wasapi.commandLock);
return result;
}
}
pCallbacks->onContextInit = ma_context_init__wasapi;
pCallbacks->onContextUninit = ma_context_uninit__wasapi;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi;
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi;
pCallbacks->onDeviceInit = ma_device_init__wasapi;
pCallbacks->onDeviceUninit = ma_device_uninit__wasapi;
pCallbacks->onDeviceStart = ma_device_start__wasapi;
pCallbacks->onDeviceStop = ma_device_stop__wasapi;
pCallbacks->onDeviceRead = ma_device_read__wasapi;
pCallbacks->onDeviceWrite = ma_device_write__wasapi;
pCallbacks->onDeviceDataLoop = NULL;
pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi;
return MA_SUCCESS;
}
#endif
/******************************************************************************
DirectSound Backend
******************************************************************************/
#ifdef MA_HAS_DSOUND
/*#include <dsound.h>*/
/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/
/* miniaudio only uses priority or exclusive modes. */
#define MA_DSSCL_NORMAL 1
#define MA_DSSCL_PRIORITY 2
#define MA_DSSCL_EXCLUSIVE 3
#define MA_DSSCL_WRITEPRIMARY 4
#define MA_DSCAPS_PRIMARYMONO 0x00000001
#define MA_DSCAPS_PRIMARYSTEREO 0x00000002
#define MA_DSCAPS_PRIMARY8BIT 0x00000004
#define MA_DSCAPS_PRIMARY16BIT 0x00000008
#define MA_DSCAPS_CONTINUOUSRATE 0x00000010
#define MA_DSCAPS_EMULDRIVER 0x00000020
#define MA_DSCAPS_CERTIFIED 0x00000040
#define MA_DSCAPS_SECONDARYMONO 0x00000100
#define MA_DSCAPS_SECONDARYSTEREO 0x00000200
#define MA_DSCAPS_SECONDARY8BIT 0x00000400
#define MA_DSCAPS_SECONDARY16BIT 0x00000800
#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
#define MA_DSBCAPS_STATIC 0x00000002
#define MA_DSBCAPS_LOCHARDWARE 0x00000004
#define MA_DSBCAPS_LOCSOFTWARE 0x00000008
#define MA_DSBCAPS_CTRL3D 0x00000010
#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
#define MA_DSBCAPS_CTRLPAN 0x00000040
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (ma_device_get_state(pDevice) != ma_device_state_started) {
break;
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
return result;
}
static ma_result ma_context_uninit__winmm(ma_context* pContext)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pContext->backend == ma_backend_winmm);
ma_dlclose(pContext, pContext->winmm.hWinMM);
return MA_SUCCESS;
}
static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
{
MA_ASSERT(pContext != NULL);
(void)pConfig;
pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll");
if (pContext->winmm.hWinMM == NULL) {
return MA_NO_BACKEND;
}
pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs");
pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA");
pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen");
pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose");
pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader");
pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader");
pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite");
pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset");
pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs");
pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA");
pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen");
pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose");
pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader");
pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader");
pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer");
pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart");
pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset");
pCallbacks->onContextInit = ma_context_init__winmm;
pCallbacks->onContextUninit = ma_context_uninit__winmm;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm;
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm;
pCallbacks->onDeviceInit = ma_device_init__winmm;
pCallbacks->onDeviceUninit = ma_device_uninit__winmm;
pCallbacks->onDeviceStart = ma_device_start__winmm;
pCallbacks->onDeviceStop = ma_device_stop__winmm;
pCallbacks->onDeviceRead = ma_device_read__winmm;
pCallbacks->onDeviceWrite = ma_device_write__winmm;
pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */
return MA_SUCCESS;
}
#endif
/******************************************************************************
ALSA Backend
******************************************************************************/
#ifdef MA_HAS_ALSA
#include <poll.h> /* poll(), struct pollfd */
#include <sys/eventfd.h> /* eventfd() */
#ifdef MA_NO_RUNTIME_LINKING
/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
#if !defined(__cplusplus)
#if defined(__STRICT_ANSI__)
#if !defined(inline)
#define inline __inline__ __attribute__((always_inline))
#define MA_INLINE_DEFINED
#endif
#endif
#endif
#include <alsa/asoundlib.h>
#if defined(MA_INLINE_DEFINED)
#undef inline
#undef MA_INLINE_DEFINED
#endif
typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
typedef snd_pcm_format_t ma_snd_pcm_format_t;
typedef snd_pcm_access_t ma_snd_pcm_access_t;
typedef snd_pcm_t ma_snd_pcm_t;
typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
typedef snd_pcm_info_t ma_snd_pcm_info_t;
typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
typedef snd_pcm_state_t ma_snd_pcm_state_t;
/* snd_pcm_stream_t */
#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
/* snd_pcm_format_t */
#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors;
pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count;
pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents;
pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
#endif
pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock);
if (result != MA_SUCCESS) {
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.");
return result;
}
pCallbacks->onContextInit = ma_context_init__alsa;
pCallbacks->onContextUninit = ma_context_uninit__alsa;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa;
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa;
pCallbacks->onDeviceInit = ma_device_init__alsa;
pCallbacks->onDeviceUninit = ma_device_uninit__alsa;
pCallbacks->onDeviceStart = ma_device_start__alsa;
pCallbacks->onDeviceStop = ma_device_stop__alsa;
pCallbacks->onDeviceRead = ma_device_read__alsa;
pCallbacks->onDeviceWrite = ma_device_write__alsa;
pCallbacks->onDeviceDataLoop = NULL;
pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa;
return MA_SUCCESS;
}
#endif /* ALSA */
/******************************************************************************
PulseAudio Backend
******************************************************************************/
#ifdef MA_HAS_PULSEAUDIO
/*
The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on
in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion.
PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it
allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it
appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or
write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the
simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient
when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API.
Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to
get fun, and I don't mean that in a good way...
The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands
don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is
enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost
all of PulseAudio's problems stem from.
When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own
vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called
pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop
because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use
it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed.
To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer
to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded
main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely
specialized such as if you want to integrate it into your application's existing main loop infrastructure.
(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262.
It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.)
Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to
miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's
one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which
is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if
you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()`
has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can
set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop.
All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected.
This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before
attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`.
The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an
internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the
host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind.
Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device.
The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call
`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get
information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object
is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to
run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the
context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up.
All of that just to retrieve basic information about a device!
Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the
context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design
choices in PulseAudio.
PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here
because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for
writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can
set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices
straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified,
PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation)
because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback
would be where a program will want to write or read data to or from the stream, but when it's called before the application has even
requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at
that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the
stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data
callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio
doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been
started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data
callback is not fired.
This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will
continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device
is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in
PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call
`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always
writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if
you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to
*not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining
important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained
before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write
data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again!
This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not*
write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just
resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This
disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the
callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.)
Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context,
only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as
"corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think
it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you
guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is
absolutely beyond me. Would it really be that hard to just make it run synchronously?
Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that
they were initialized in.
That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're
embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to
run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche
requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is
constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a
parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These
changes alone will change PulseAudio from one of the worst audio APIs to one of the best.
*/
/*
It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header
to check for type safety. We cannot do this when linking at run time because the header might not be available.
*/
#ifdef MA_NO_RUNTIME_LINKING
/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
#if !defined(__cplusplus)
#if defined(__STRICT_ANSI__)
#if !defined(inline)
#define inline __inline__ __attribute__((always_inline))
#define MA_INLINE_DEFINED
#endif
#endif
#endif
#include <pulse/pulseaudio.h>
#if defined(MA_INLINE_DEFINED)
#undef inline
#undef MA_INLINE_DEFINED
#endif
#define MA_PA_OK PA_OK
#define MA_PA_ERR_ACCESS PA_ERR_ACCESS
#define MA_PA_ERR_INVALID PA_ERR_INVALID
#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED
#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
#define MA_PA_RATE_MAX PA_RATE_MAX
typedef pa_context_flags_t ma_pa_context_flags_t;
#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
typedef pa_stream_flags_t ma_pa_stream_flags_t;
#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
typedef pa_sample_format_t ma_pa_sample_format_t;
#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
typedef pa_mainloop ma_pa_mainloop;
typedef pa_threaded_mainloop ma_pa_threaded_mainloop;
typedef pa_mainloop_api ma_pa_mainloop_api;
typedef pa_context ma_pa_context;
typedef pa_operation ma_pa_operation;
typedef pa_stream ma_pa_stream;
typedef pa_spawn_api ma_pa_spawn_api;
typedef pa_buffer_attr ma_pa_buffer_attr;
typedef pa_channel_map ma_pa_channel_map;
typedef pa_cvolume ma_pa_cvolume;
typedef pa_sample_spec ma_pa_sample_spec;
typedef pa_sink_info ma_pa_sink_info;
typedef pa_source_info ma_pa_source_info;
typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t;
typedef pa_free_cb_t ma_pa_free_cb_t;
#else
#define MA_PA_OK 0
#define MA_PA_ERR_ACCESS 1
#define MA_PA_ERR_INVALID 2
#define MA_PA_ERR_NOENTITY 5
#define MA_PA_ERR_NOTSUPPORTED 19
#define MA_PA_CHANNELS_MAX 32
#define MA_PA_RATE_MAX 384000
typedef int ma_pa_context_flags_t;
#define MA_PA_CONTEXT_NOFLAGS 0x00000000
#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
#define MA_PA_CONTEXT_NOFAIL 0x00000002
typedef int ma_pa_stream_flags_t;
#define MA_PA_STREAM_NOFLAGS 0x00000000
#define MA_PA_STREAM_START_CORKED 0x00000001
#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
#define MA_PA_STREAM_FIX_FORMAT 0x00000040
#define MA_PA_STREAM_FIX_RATE 0x00000080
#define MA_PA_STREAM_FIX_CHANNELS 0x00000100
#define MA_PA_STREAM_DONT_MOVE 0x00000200
#define MA_PA_STREAM_VARIABLE_RATE 0x00000400
#define MA_PA_STREAM_PEAK_DETECT 0x00000800
#define MA_PA_STREAM_START_MUTED 0x00001000
#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
#define MA_PA_STREAM_START_UNMUTED 0x00010000
#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
#define MA_PA_STREAM_PASSTHROUGH 0x00080000
typedef int ma_pa_sink_flags_t;
#define MA_PA_SINK_NOFLAGS 0x00000000
#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
#define MA_PA_CHANNEL_POSITION_AUX9 21
#define MA_PA_CHANNEL_POSITION_AUX10 22
#define MA_PA_CHANNEL_POSITION_AUX11 23
#define MA_PA_CHANNEL_POSITION_AUX12 24
#define MA_PA_CHANNEL_POSITION_AUX13 25
#define MA_PA_CHANNEL_POSITION_AUX14 26
#define MA_PA_CHANNEL_POSITION_AUX15 27
#define MA_PA_CHANNEL_POSITION_AUX16 28
#define MA_PA_CHANNEL_POSITION_AUX17 29
#define MA_PA_CHANNEL_POSITION_AUX18 30
#define MA_PA_CHANNEL_POSITION_AUX19 31
#define MA_PA_CHANNEL_POSITION_AUX20 32
#define MA_PA_CHANNEL_POSITION_AUX21 33
#define MA_PA_CHANNEL_POSITION_AUX22 34
#define MA_PA_CHANNEL_POSITION_AUX23 35
#define MA_PA_CHANNEL_POSITION_AUX24 36
#define MA_PA_CHANNEL_POSITION_AUX25 37
#define MA_PA_CHANNEL_POSITION_AUX26 38
#define MA_PA_CHANNEL_POSITION_AUX27 39
#define MA_PA_CHANNEL_POSITION_AUX28 40
#define MA_PA_CHANNEL_POSITION_AUX29 41
#define MA_PA_CHANNEL_POSITION_AUX30 42
#define MA_PA_CHANNEL_POSITION_AUX31 43
#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
typedef int ma_pa_channel_map_def_t;
#define MA_PA_CHANNEL_MAP_AIFF 0
#define MA_PA_CHANNEL_MAP_ALSA 1
#define MA_PA_CHANNEL_MAP_AUX 2
#define MA_PA_CHANNEL_MAP_WAVEEX 3
#define MA_PA_CHANNEL_MAP_OSS 4
#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
typedef int ma_pa_sample_format_t;
#define MA_PA_SAMPLE_INVALID -1
#define MA_PA_SAMPLE_U8 0
#define MA_PA_SAMPLE_ALAW 1
#define MA_PA_SAMPLE_ULAW 2
#define MA_PA_SAMPLE_S16LE 3
#define MA_PA_SAMPLE_S16BE 4
#define MA_PA_SAMPLE_FLOAT32LE 5
#define MA_PA_SAMPLE_FLOAT32BE 6
#define MA_PA_SAMPLE_S32LE 7
#define MA_PA_SAMPLE_S32BE 8
#define MA_PA_SAMPLE_S24LE 9
#define MA_PA_SAMPLE_S24BE 10
#define MA_PA_SAMPLE_S24_32LE 11
#define MA_PA_SAMPLE_S24_32BE 12
typedef struct ma_pa_mainloop ma_pa_mainloop;
typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop;
typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
typedef struct ma_pa_context ma_pa_context;
typedef struct ma_pa_operation ma_pa_operation;
typedef struct ma_pa_stream ma_pa_stream;
typedef struct ma_pa_spawn_api ma_pa_spawn_api;
typedef struct
{
ma_uint32 maxlength;
ma_uint32 tlength;
ma_uint32 prebuf;
ma_uint32 minreq;
ma_uint32 fragsize;
} ma_pa_buffer_attr;
typedef struct
{
ma_uint8 channels;
ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
} ma_pa_channel_map;
typedef struct
{
ma_uint8 channels;
ma_uint32 values[MA_PA_CHANNELS_MAX];
} ma_pa_cvolume;
typedef struct
{
ma_pa_sample_format_t format;
ma_uint32 rate;
ma_uint8 channels;
} ma_pa_sample_spec;
typedef struct
{
const char* name;
ma_uint32 index;
const char* description;
ma_pa_sample_spec sample_spec;
ma_pa_channel_map channel_map;
ma_uint32 owner_module;
ma_pa_cvolume volume;
int mute;
ma_uint32 monitor_source;
const char* monitor_source_name;
ma_uint64 latency;
const char* driver;
ma_pa_sink_flags_t flags;
void* proplist;
ma_uint64 configured_latency;
ma_uint32 base_volume;
ma_pa_sink_state_t state;
ma_uint32 n_volume_steps;
ma_uint32 card;
ma_uint32 n_ports;
void** ports;
void* active_port;
ma_uint8 n_formats;
void** formats;
} ma_pa_sink_info;
typedef struct
{
const char *name;
ma_uint32 index;
const char *description;
ma_pa_sample_spec sample_spec;
ma_pa_channel_map channel_map;
ma_uint32 owner_module;
ma_pa_cvolume volume;
int mute;
ma_uint32 monitor_of_sink;
const char *monitor_of_sink_name;
ma_uint64 latency;
const char *driver;
ma_pa_source_flags_t flags;
void* proplist;
ma_uint64 configured_latency;
ma_uint32 base_volume;
ma_pa_source_state_t state;
ma_uint32 n_volume_steps;
ma_uint32 card;
ma_uint32 n_ports;
void** ports;
void* active_port;
ma_uint8 n_formats;
void** formats;
} ma_pa_source_info;
typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata);
typedef void (* ma_pa_free_cb_t) (void* p);
#endif
typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void);
typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval);
typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void);
typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m);
typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m);
typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m);
typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m);
typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m);
typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m);
typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept);
typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m);
typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m);
typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m);
typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m);
typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name);
typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
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);
typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c);
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);
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);
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);
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);
typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o);
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);
typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
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);
typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
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);
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);
typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s);
typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
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);
typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s);
typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s);
typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s);
typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
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);
typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s);
typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s);
typedef struct
{
ma_uint32 count;
ma_uint32 capacity;
ma_device_info* pInfo;
} ma_pulse_device_enum_data;
static ma_result ma_result_from_pulse(int result)
{
if (result < 0) {
return MA_ERROR;
}
switch (result) {
case MA_PA_OK: return MA_SUCCESS;
case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* NOTE: Don't stop the device here. It'll be done at a higher level. */
return MA_SUCCESS;
}
static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
return MA_SUCCESS;
}
static ma_result ma_context_uninit__pulse(ma_context* pContext)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pContext->backend == ma_backend_pulseaudio);
((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext);
((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext);
((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop);
ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
#ifndef MA_NO_RUNTIME_LINKING
ma_dlclose(pContext, pContext->pulse.pulseSO);
#endif
return MA_SUCCESS;
}
static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
{
ma_result result;
#ifndef MA_NO_RUNTIME_LINKING
const char* libpulseNames[] = {
"libpulse.so",
"libpulse.so.0"
};
size_t i;
for (i = 0; i < ma_countof(libpulseNames); ++i) {
pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]);
if (pContext->pulse.pulseSO != NULL) {
break;
}
}
if (pContext->pulse.pulseSO == NULL) {
return MA_NO_BACKEND;
}
pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new");
pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free");
pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_quit");
pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api");
pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate");
pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup");
pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_new");
pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_free");
pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_start");
pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_stop");
pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_lock");
pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock");
pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_wait");
pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_signal");
pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_accept");
pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval");
pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api");
pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread");
pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name");
pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new");
pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref");
pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect");
pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect");
pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback");
pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state");
pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list");
pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref");
pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state");
pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend");
pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid");
pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible");
pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new");
pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref");
pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback");
pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record");
pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect");
pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state");
pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map");
pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name");
pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback");
pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback");
pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback");
pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_moved_callback");
pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended");
pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush");
pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain");
pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked");
pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork");
pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger");
pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write");
pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write");
pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek");
pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop");
pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size");
pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size");
#else
/* This strange assignment system is just for type safety. */
ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit;
ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new;
ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free;
ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start;
ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop;
ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock;
ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock;
ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait;
ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal;
ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept;
ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval;
ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api;
ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread;
ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name;
ma_pa_context_new_proc _pa_context_new = pa_context_new;
ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback;
ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback;
ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended;
ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit;
pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new;
pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free;
pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start;
pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop;
pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock;
pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock;
pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait;
pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal;
pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept;
pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval;
pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api;
pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread;
pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name;
pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback;
pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback;
pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended;
pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
#endif
/* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */
pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);
if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) {
return MA_OUT_OF_MEMORY;
}
pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);
if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) {
ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
return MA_OUT_OF_MEMORY;
}
result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext);
if (result != MA_SUCCESS) {
ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_bool32 found = MA_FALSE;
NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
[[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
found = MA_TRUE;
break;
}
}
if (found == MA_FALSE) {
return MA_DOES_NOT_EXIST;
}
}
}
#endif
/*
Format. This is the hardest part of initialization because there's a few variables to take into account.
1) The format must be supported by the device.
2) The format must be supported miniaudio.
3) There's a priority that miniaudio prefers.
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
most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
*/
{
AudioStreamBasicDescription origFormat;
UInt32 origFormatSize = sizeof(origFormat);
AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
if (deviceType == ma_device_type_playback) {
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
} else {
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
}
if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return ma_result_from_OSStatus(status);
}
#if defined(MA_APPLE_DESKTOP)
result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat);
if (result != MA_SUCCESS) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return result;
}
/*
Technical Note TN2091: Device input using the HAL Output Audio Unit
https://developer.apple.com/library/archive/technotes/tn2091/_index.html
This documentation says the following:
The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY
variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate
conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with
another AudioConverter.
The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We
therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it
safe and apply the same rule to output as well.
I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender()
returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but
this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format.
Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with
this, however, is that it actually changes the sample rate at the operating system level and not just the application. This
could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a
configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample
rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run
the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is
changed by miniaudio.
*/
if (pData->allowNominalSampleRateChange) {
AudioValueRange sampleRateRange;
AudioObjectPropertyAddress propAddress;
sampleRateRange.mMinimum = bestFormat.mSampleRate;
sampleRateRange.mMaximum = bestFormat.mSampleRate;
propAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
propAddress.mElement = kAudioObjectPropertyElementMaster;
status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange);
if (status != noErr) {
bestFormat.mSampleRate = origFormat.mSampleRate;
}
} else {
bestFormat.mSampleRate = origFormat.mSampleRate;
}
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
if (status != noErr) {
/* We failed to set the format, so fall back to the current format of the audio unit. */
bestFormat = origFormat;
}
#else
bestFormat = origFormat;
/*
Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
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
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
can tell, it looks like the sample rate is shared between playback and capture for everything.
*/
@autoreleasepool {
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
MA_ASSERT(pAudioSession != NULL);
[pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
bestFormat.mSampleRate = pAudioSession.sampleRate;
/*
I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
data.shareMode = pDescriptorPlayback->shareMode;
data.performanceProfile = pConfig->performanceProfile;
/* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
if (pConfig->deviceType == ma_device_type_duplex) {
data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
data.periodsIn = pDescriptorCapture->periodCount;
data.registerStopEvent = MA_FALSE;
} else {
data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
data.periodsIn = pDescriptorPlayback->periodCount;
data.registerStopEvent = MA_TRUE;
}
result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice);
if (result != MA_SUCCESS) {
if (pConfig->deviceType == ma_device_type_duplex) {
((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
if (pDevice->coreaudio.pAudioBufferList) {
ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
}
}
return result;
}
pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
#if defined(MA_APPLE_DESKTOP)
pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
#endif
pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount;
pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
pDescriptorPlayback->format = data.formatOut;
pDescriptorPlayback->channels = data.channelsOut;
pDescriptorPlayback->sampleRate = data.sampleRateOut;
MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
pDescriptorPlayback->periodCount = data.periodsOut;
#if defined(MA_APPLE_DESKTOP)
ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
/*
If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
switch the device in the background.
*/
if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {
ma_device__track__coreaudio(pDevice);
}
#endif
}
/*
When stopping the device, a callback is called on another thread. We need to wait for this callback
before returning from ma_device_stop(). This event is used for this.
*/
ma_event_init(&pDevice->coreaudio.stopEvent);
/*
We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done
differently on non-Desktop Apple platforms.
*/
#if defined(MA_APPLE_MOBILE)
pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice];
#endif
return MA_SUCCESS;
}
static ma_result ma_device_start__coreaudio(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
if (status != noErr) {
return ma_result_from_OSStatus(status);
}
}
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
if (status != noErr) {
if (pDevice->type == ma_device_type_duplex) {
((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
}
return ma_result_from_OSStatus(status);
}
}
return MA_SUCCESS;
}
static ma_result ma_device_stop__coreaudio(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
/* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
if (status != noErr) {
return ma_result_from_OSStatus(status);
}
}
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
if (status != noErr) {
return ma_result_from_OSStatus(status);
}
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
case ma_aaudio_usage_emergency: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION;
case ma_aaudio_usage_safety: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_USAGE_ALARM;
case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_NOTIFICATION;
case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE;
case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT;
case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION;
case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_GAME;
case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_ASSISTANT;
case ma_aaudio_usage_notification_event: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY;
case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_SYSTEM_USAGE_SAFETY;
case ma_aaudio_usage_voice_communication: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS;
case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT;
default: break;
}
return MA_AAUDIO_USAGE_MEDIA;
}
static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType)
{
switch (contentType) {
case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE;
case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC;
case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION;
case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH;
default: break;
}
return MA_AAUDIO_CONTENT_TYPE_SPEECH;
}
static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset)
{
switch (inputPreset) {
case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC;
case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER;
case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED;
case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION;
case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE;
default: break;
}
return MA_AAUDIO_INPUT_PRESET_GENERIC;
}
static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
{
ma_device* pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
(void)error;
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream));
/*
From the documentation for AAudio, when a device is disconnected all we can do is stop it. However, we cannot stop it from the callback - we need
to do it from another thread. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely.
*/
if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
/* We need to post a job to the job thread for processing. This will reroute the device by reinitializing the stream. */
ma_result result;
ma_job job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE);
job.data.device.aaudio.reroute.pDevice = pDevice;
if (pStream == pDevice->aaudio.pStreamCapture) {
job.data.device.aaudio.reroute.deviceType = ma_device_type_capture;
} else {
job.data.device.aaudio.reroute.deviceType = ma_device_type_playback;
}
result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job);
if (result != MA_SUCCESS) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n");
return;
}
}
}
static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
{
ma_device* pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount);
(void)pStream;
return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
}
static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
{
ma_device* pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount);
(void)pStream;
return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
}
static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma...
{
ma_AAudioStreamBuilder* pBuilder;
ma_aaudio_result_t resultAA;
ma_uint32 bufferCapacityInFrames;
/* Safety. */
*ppBuilder = NULL;
resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
if (resultAA != MA_AAUDIO_OK) {
return ma_result_from_aaudio(resultAA);
}
if (pDeviceID != NULL) {
((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
}
((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
/* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */
if (pDescriptor != NULL) {
MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */
if (pDescriptor->sampleRate != 0) {
((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);
}
if (deviceType == ma_device_type_capture) {
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture);
if (result != MA_SUCCESS) {
ma_device_uninit__aaudio(pDevice);
return result;
}
/* We'll only ever do this in response to a reroute. */
ma_device__on_notification_rerouted(pDevice);
/* If the device is started, start the streams. Maybe make this configurable? */
if (ma_device_get_state(pDevice) == ma_device_state_started) {
if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) {
ma_device_start__aaudio(pDevice);
} else {
ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */
}
}
return MA_SUCCESS;
}
}
static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)
{
ma_AAudioStream* pStream = NULL;
MA_ASSERT(pDevice != NULL);
MA_ASSERT(type != ma_device_type_duplex);
MA_ASSERT(pDeviceInfo != NULL);
if (type == ma_device_type_playback) {
pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture;
pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio;
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */
}
if (type == ma_device_type_capture) {
pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback;
pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio;
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */
}
/* Safety. Should never happen. */
if (pStream == NULL) {
return MA_INVALID_OPERATION;
}
pDeviceInfo->nativeDataFormatCount = 0;
ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo);
return MA_SUCCESS;
}
static ma_result ma_context_uninit__aaudio(ma_context* pContext)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pContext->backend == ma_backend_aaudio);
ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks);
ma_dlclose(pContext, pContext->aaudio.hAAudio);
pContext->aaudio.hAAudio = NULL;
return MA_SUCCESS;
}
static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
{
size_t i;
const char* libNames[] = {
"libaaudio.so"
};
for (i = 0; i < ma_countof(libNames); ++i) {
pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]);
if (pContext->aaudio.hAAudio != NULL) {
break;
}
}
if (pContext->aaudio.hAAudio == NULL) {
return MA_FAILED_TO_INIT_BACKEND;
}
pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage");
pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType");
pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset");
pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close");
pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState");
pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat");
pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart");
pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop");
pCallbacks->onContextInit = ma_context_init__aaudio;
pCallbacks->onContextUninit = ma_context_uninit__aaudio;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio;
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio;
pCallbacks->onDeviceInit = ma_device_init__aaudio;
pCallbacks->onDeviceUninit = ma_device_uninit__aaudio;
pCallbacks->onDeviceStart = ma_device_start__aaudio;
pCallbacks->onDeviceStop = ma_device_stop__aaudio;
pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */
pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */
pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */
pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio;
/* We need a job thread so we can deal with rerouting. */
{
ma_result result;
ma_device_job_thread_config jobThreadConfig;
jobThreadConfig = ma_device_job_thread_config_init();
result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread);
if (result != MA_SUCCESS) {
ma_dlclose(pContext, pContext->aaudio.hAAudio);
pContext->aaudio.hAAudio = NULL;
return result;
}
}
(void)pConfig;
return MA_SUCCESS;
}
static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
{
ma_device* pDevice;
MA_ASSERT(pJob != NULL);
pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice;
MA_ASSERT(pDevice != NULL);
/* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */
return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType);
}
#else
/* Getting here means there is no AAudio backend so we need a no-op job implementation. */
static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
{
return ma_job_process__noop(pJob);
}
#endif /* AAudio */
/******************************************************************************
OpenSL|ES Backend
******************************************************************************/
#ifdef MA_HAS_OPENSL
#include <SLES/OpenSLES.h>
#ifdef MA_ANDROID
#include <SLES/OpenSLES_Android.h>
#endif
typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);
/* OpenSL|ES has one-per-application objects :( */
static SLObjectItf g_maEngineObjectSL = NULL;
static SLEngineItf g_maEngineSL = NULL;
static ma_uint32 g_maOpenSLInitCounter = 0;
static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */
#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
#ifdef MA_ANDROID
#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
#else
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* Playback. */
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) {
return MA_INVALID_ARGS;
}
pDevice->playback.internalFormat = pDescriptorPlayback->format;
pDevice->playback.internalChannels = pDescriptorPlayback->channels;
pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate;
MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount;
if (pDevice->playback.internalPeriodSizeInFrames == 0) {
pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate);
}
}
/*
The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead.
For loopback devices, we need to retrieve the name of the playback device.
*/
{
ma_device_info deviceInfo;
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo);
if (result == MA_SUCCESS) {
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
} else {
/* We failed to retrieve the device info. Fall back to a default name. */
if (pDescriptorCapture->pDeviceID == NULL) {
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
} else {
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
}
}
}
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
if (result == MA_SUCCESS) {
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
} else {
/* We failed to retrieve the device info. Fall back to a default name. */
if (pDescriptorPlayback->pDeviceID == NULL) {
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
} else {
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
}
}
}
}
/* Update data conversion. */
return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */
}
static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
{
ma_device* pDevice = (ma_device*)pData;
MA_ASSERT(pDevice != NULL);
#ifdef MA_WIN32
ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
#endif
/*
When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from
ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
thread to signal an event to know when the worker thread is ready for action.
*/
ma_device__set_state(pDevice, ma_device_state_stopped);
ma_event_signal(&pDevice->stopEvent);
for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
ma_result startResult;
ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */
/* We wait on an event to know when something has requested that the device be started and the main loop entered. */
ma_event_wait(&pDevice->wakeupEvent);
/* Default result code. */
pDevice->workResult = MA_SUCCESS;
/* If the reason for the wake up is that we are terminating, just break from the loop. */
if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
break;
}
/*
Getting to this point means the device is wanting to get started. The function that has requested that the device
be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
*/
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting);
/* If the device has a start callback, start it now. */
if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice);
} else {
startResult = MA_SUCCESS;
}
/*
If starting was not successful we'll need to loop back to the start and wait for something
to happen (pDevice->wakeupEvent).
*/
if (startResult != MA_SUCCESS) {
pDevice->workResult = startResult;
ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */
continue;
}
/* Make sure the state is set appropriately. */
ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */
ma_event_signal(&pDevice->startEvent);
ma_device__on_notification_started(pDevice);
if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {
pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);
} else {
/* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */
ma_device_audio_thread__default_read_write(pDevice);
}
/* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */
if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice);
} else {
stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */
}
/*
After the device has stopped, make sure an event is posted. Don't post a stopped event if
stopping failed. This can happen on some backends when the underlying stream has been
stopped due to the device being physically unplugged or disabled via an OS setting.
*/
if (stopResult == MA_SUCCESS) {
ma_device__on_notification_stopped(pDevice);
}
/* 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. */
ma_device__set_state(pDevice, ma_device_state_stopped);
ma_event_signal(&pDevice->stopEvent);
}
#ifdef MA_WIN32
ma_CoUninitialize(pDevice->pContext);
#endif
return (ma_thread_result)0;
}
/* Helper for determining whether or not the given device is initialized. */
static ma_bool32 ma_device__is_initialized(ma_device* pDevice)
{
if (pDevice == NULL) {
return MA_FALSE;
}
return ma_device_get_state(pDevice) != ma_device_state_uninitialized;
}
#ifdef MA_WIN32
static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
{
/* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */
#ifdef MA_WIN32_DESKTOP
ma_CoUninitialize(pContext);
ma_dlclose(pContext, pContext->win32.hUser32DLL);
ma_dlclose(pContext, pContext->win32.hOle32DLL);
ma_dlclose(pContext, pContext->win32.hAdvapi32DLL);
#else
(void)pContext;
#endif
return MA_SUCCESS;
}
static ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
{
#ifdef MA_WIN32_DESKTOP
/* Ole32.dll */
pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll");
if (pContext->win32.hOle32DLL == NULL) {
return MA_FAILED_TO_INIT_BACKEND;
}
pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx");
pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize");
pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance");
pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree");
pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear");
pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2");
/* User32.dll */
pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll");
if (pContext->win32.hUser32DLL == NULL) {
return MA_FAILED_TO_INIT_BACKEND;
}
pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow");
pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow");
/* Advapi32.dll */
pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll");
if (pContext->win32.hAdvapi32DLL == NULL) {
return MA_FAILED_TO_INIT_BACKEND;
}
pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey");
pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
#endif
ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
return MA_SUCCESS;
}
#else
static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
{
#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
ma_dlclose(pContext, pContext->posix.pthreadSO);
#else
(void)pContext;
#endif
return MA_SUCCESS;
}
static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
{
/* pthread */
#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
const char* libpthreadFileNames[] = {
"libpthread.so",
"libpthread.so.0",
"libpthread.dylib"
};
size_t i;
for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) {
pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]);
if (pContext->posix.pthreadSO != NULL) {
break;
}
}
if (pContext->posix.pthreadSO == NULL) {
return MA_FAILED_TO_INIT_BACKEND;
}
pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create");
pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join");
pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init");
pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy");
pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock");
pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock");
pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init");
pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy");
pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait");
pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal");
pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init");
pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy");
pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy");
pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam");
pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam");
#else
pContext->posix.pthread_create = (ma_proc)pthread_create;
pContext->posix.pthread_join = (ma_proc)pthread_join;
pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init;
pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy;
pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock;
pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock;
pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init;
pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy;
pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait;
pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
#if !defined(__EMSCRIPTEN__)
pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
#endif
#endif
return MA_SUCCESS;
}
#endif
static ma_result ma_context_init_backend_apis(ma_context* pContext)
{
ma_result result;
#ifdef MA_WIN32
result = ma_context_init_backend_apis__win32(pContext);
#else
result = ma_context_init_backend_apis__nix(pContext);
#endif
return result;
}
static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
{
ma_result result;
#ifdef MA_WIN32
result = ma_context_uninit_backend_apis__win32(pContext);
#else
result = ma_context_uninit_backend_apis__nix(pContext);
#endif
return result;
}
static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
{
MA_ASSERT(pContext != NULL);
if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) {
if (pContext->callbacks.onDeviceDataLoop == NULL) {
return MA_TRUE;
} else {
return MA_FALSE;
}
} else {
return MA_FALSE;
}
}
/* The default capacity doesn't need to be too big. */
#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY
#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32
#endif
MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void)
{
ma_device_job_thread_config config;
MA_ZERO_OBJECT(&config);
config.noThread = MA_FALSE;
config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY;
config.jobQueueFlags = 0;
return config;
}
static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData)
{
ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData;
MA_ASSERT(pJobThread != NULL);
for (;;) {
ma_result result;
ma_job job;
result = ma_device_job_thread_next(pJobThread, &job);
if (result != MA_SUCCESS) {
break;
}
if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
break;
}
ma_job_process(&job);
}
return (ma_thread_result)0;
}
MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread)
{
ma_result result;
ma_job_queue_config jobQueueConfig;
if (pJobThread == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pJobThread);
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
/* Initialize the job queue before the thread to ensure it's in a valid state. */
jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity);
result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue);
if (result != MA_SUCCESS) {
return result; /* Failed to initialize job queue. */
}
/* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */
if (pConfig->noThread == MA_FALSE) {
result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks);
if (result != MA_SUCCESS) {
ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
return result; /* Failed to create the job thread. */
}
pJobThread->_hasThread = MA_TRUE;
} else {
pJobThread->_hasThread = MA_FALSE;
}
return MA_SUCCESS;
}
MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks)
{
if (pJobThread == NULL) {
return;
}
/* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */
{
ma_job job = ma_job_init(MA_JOB_TYPE_QUIT);
ma_device_job_thread_post(pJobThread, &job);
}
/* Wait for the thread to terminate naturally. */
if (pJobThread->_hasThread) {
ma_thread_wait(&pJobThread->thread);
}
/* At this point the thread should be terminated so we can safely uninitialize the job queue. */
ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
}
MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob)
{
if (pJobThread == NULL || pJob == NULL) {
return MA_INVALID_ARGS;
}
return ma_job_queue_post(&pJobThread->jobQueue, pJob);
}
MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob)
{
if (pJob == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pJob);
if (pJobThread == NULL) {
return MA_INVALID_ARGS;
}
return ma_job_queue_next(&pJobThread->jobQueue, pJob);
}
MA_API ma_context_config ma_context_config_init(void)
{
ma_context_config config;
MA_ZERO_OBJECT(&config);
return config;
}
MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
{
ma_result result;
ma_context_config defaultConfig;
ma_backend defaultBackends[ma_backend_null+1];
ma_uint32 iBackend;
ma_backend* pBackendsToIterate;
ma_uint32 backendsToIterateCount;
if (pContext == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pContext);
/* Always make sure the config is set first to ensure properties are available as soon as possible. */
if (pConfig == NULL) {
defaultConfig = ma_context_config_init();
pConfig = &defaultConfig;
}
/* Allocation callbacks need to come first because they'll be passed around to other areas. */
result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks);
if (result != MA_SUCCESS) {
return result;
}
/* Get a lot set up first so we can start logging ASAP. */
if (pConfig->pLog != NULL) {
pContext->pLog = pConfig->pLog;
} else {
result = ma_log_init(&pContext->allocationCallbacks, &pContext->log);
if (result == MA_SUCCESS) {
pContext->pLog = &pContext->log;
} else {
pContext->pLog = NULL; /* Logging is not available. */
}
}
pContext->threadPriority = pConfig->threadPriority;
pContext->threadStackSize = pConfig->threadStackSize;
pContext->pUserData = pConfig->pUserData;
/* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
result = ma_context_init_backend_apis(pContext);
if (result != MA_SUCCESS) {
return result;
}
for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
defaultBackends[iBackend] = (ma_backend)iBackend;
}
pBackendsToIterate = (ma_backend*)backends;
backendsToIterateCount = backendCount;
if (pBackendsToIterate == NULL) {
pBackendsToIterate = (ma_backend*)defaultBackends;
backendsToIterateCount = ma_countof(defaultBackends);
}
MA_ASSERT(pBackendsToIterate != NULL);
for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) {
ma_backend backend = pBackendsToIterate[iBackend];
/* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */
MA_ZERO_OBJECT(&pContext->callbacks);
/* These backends are using the new callback system. */
switch (backend) {
#ifdef MA_HAS_WASAPI
case ma_backend_wasapi:
{
pContext->callbacks.onContextInit = ma_context_init__wasapi;
} break;
#endif
#ifdef MA_HAS_DSOUND
case ma_backend_dsound:
{
pContext->callbacks.onContextInit = ma_context_init__dsound;
} break;
#endif
#ifdef MA_HAS_WINMM
case ma_backend_winmm:
{
pContext->callbacks.onContextInit = ma_context_init__winmm;
} break;
#endif
#ifdef MA_HAS_COREAUDIO
case ma_backend_coreaudio:
{
pContext->callbacks.onContextInit = ma_context_init__coreaudio;
} break;
#endif
#ifdef MA_HAS_SNDIO
case ma_backend_sndio:
{
pContext->callbacks.onContextInit = ma_context_init__sndio;
} break;
#endif
#ifdef MA_HAS_AUDIO4
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
} break;
#endif
#ifdef MA_HAS_ALSA
case ma_backend_alsa:
{
pContext->callbacks.onContextInit = ma_context_init__alsa;
} break;
#endif
#ifdef MA_HAS_JACK
case ma_backend_jack:
{
pContext->callbacks.onContextInit = ma_context_init__jack;
} break;
#endif
#ifdef MA_HAS_AAUDIO
case ma_backend_aaudio:
{
pContext->callbacks.onContextInit = ma_context_init__aaudio;
} break;
#endif
#ifdef MA_HAS_OPENSL
case ma_backend_opensl:
{
pContext->callbacks.onContextInit = ma_context_init__opensl;
} break;
#endif
#ifdef MA_HAS_WEBAUDIO
case ma_backend_webaudio:
{
pContext->callbacks.onContextInit = ma_context_init__webaudio;
} break;
#endif
#ifdef MA_HAS_CUSTOM
case ma_backend_custom:
{
/* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */
pContext->callbacks = pConfig->custom;
} break;
#endif
#ifdef MA_HAS_NULL
case ma_backend_null:
{
pContext->callbacks.onContextInit = ma_context_init__null;
} break;
#endif
default: break;
}
if (pContext->callbacks.onContextInit != NULL) {
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend));
result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks);
} else {
result = MA_NO_BACKEND;
}
/* If this iteration was successful, return. */
if (result == MA_SUCCESS) {
result = ma_mutex_init(&pContext->deviceEnumLock);
if (result != MA_SUCCESS) {
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n");
}
result = ma_mutex_init(&pContext->deviceInfoLock);
if (result != MA_SUCCESS) {
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n");
}
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n");
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO");
pContext->backend = backend;
return result;
} else {
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend));
}
}
/* If we get here it means an error occurred. */
MA_ZERO_OBJECT(pContext); /* Safety. */
return MA_NO_BACKEND;
}
MA_API ma_result ma_context_uninit(ma_context* pContext)
{
if (pContext == NULL) {
return MA_INVALID_ARGS;
}
if (pContext->callbacks.onContextUninit != NULL) {
pContext->callbacks.onContextUninit(pContext);
}
ma_mutex_uninit(&pContext->deviceEnumLock);
ma_mutex_uninit(&pContext->deviceInfoLock);
ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks);
ma_context_uninit_backend_apis(pContext);
if (pContext->pLog == &pContext->log) {
ma_log_uninit(&pContext->log);
}
return MA_SUCCESS;
}
MA_API size_t ma_context_sizeof()
{
return sizeof(ma_context);
}
MA_API ma_log* ma_context_get_log(ma_context* pContext)
{
if (pContext == NULL) {
return NULL;
}
return pContext->pLog;
}
MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) {
return MA_INVALID_ARGS;
}
}
pDevice->pContext = pContext;
/* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
pDevice->pUserData = pConfig->pUserData;
pDevice->onData = pConfig->dataCallback;
pDevice->onNotification = pConfig->notificationCallback;
pDevice->onStop = pConfig->stopCallback;
if (pConfig->playback.pDeviceID != NULL) {
MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));
pDevice->playback.pID = &pDevice->playback.id;
} else {
pDevice->playback.pID = NULL;
}
if (pConfig->capture.pDeviceID != NULL) {
MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id));
pDevice->capture.pID = &pDevice->capture.id;
} else {
pDevice->capture.pID = NULL;
}
pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer;
pDevice->noClip = pConfig->noClip;
pDevice->noDisableDenormals = pConfig->noDisableDenormals;
pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback;
pDevice->masterVolumeFactor = 1;
pDevice->type = pConfig->deviceType;
pDevice->sampleRate = pConfig->sampleRate;
pDevice->resampling.algorithm = pConfig->resampling.algorithm;
pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable;
pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData;
pDevice->capture.shareMode = pConfig->capture.shareMode;
pDevice->capture.format = pConfig->capture.format;
pDevice->capture.channels = pConfig->capture.channels;
ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
pDevice->playback.shareMode = pConfig->playback.shareMode;
pDevice->playback.format = pConfig->playback.format;
pDevice->playback.channels = pConfig->playback.channels;
ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
result = ma_mutex_init(&pDevice->startStopLock);
if (result != MA_SUCCESS) {
return result;
}
/*
When the device is started, the worker thread is the one that does the actual startup of the backend device. We
use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
Each of these semaphores is released internally by the worker thread when the work is completed. The start
semaphore is also used to wake up the worker thread.
*/
result = ma_event_init(&pDevice->wakeupEvent);
if (result != MA_SUCCESS) {
ma_mutex_uninit(&pDevice->startStopLock);
return result;
}
result = ma_event_init(&pDevice->startEvent);
if (result != MA_SUCCESS) {
ma_event_uninit(&pDevice->wakeupEvent);
ma_mutex_uninit(&pDevice->startStopLock);
return result;
}
result = ma_event_init(&pDevice->stopEvent);
if (result != MA_SUCCESS) {
ma_event_uninit(&pDevice->startEvent);
ma_event_uninit(&pDevice->wakeupEvent);
ma_mutex_uninit(&pDevice->startStopLock);
return result;
}
MA_ZERO_OBJECT(&descriptorPlayback);
descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID;
descriptorPlayback.shareMode = pConfig->playback.shareMode;
descriptorPlayback.format = pConfig->playback.format;
descriptorPlayback.channels = pConfig->playback.channels;
descriptorPlayback.sampleRate = pConfig->sampleRate;
ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames;
descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
descriptorPlayback.periodCount = pConfig->periods;
if (descriptorPlayback.periodCount == 0) {
descriptorPlayback.periodCount = MA_DEFAULT_PERIODS;
}
MA_ZERO_OBJECT(&descriptorCapture);
descriptorCapture.pDeviceID = pConfig->capture.pDeviceID;
descriptorCapture.shareMode = pConfig->capture.shareMode;
descriptorCapture.format = pConfig->capture.format;
descriptorCapture.channels = pConfig->capture.channels;
descriptorCapture.sampleRate = pConfig->sampleRate;
ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames;
descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
descriptorCapture.periodCount = pConfig->periods;
if (descriptorCapture.periodCount == 0) {
descriptorCapture.periodCount = MA_DEFAULT_PERIODS;
}
result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture);
if (result != MA_SUCCESS) {
ma_event_uninit(&pDevice->startEvent);
ma_event_uninit(&pDevice->wakeupEvent);
ma_mutex_uninit(&pDevice->startStopLock);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
*/
if (pConfig->noFixedSizedCallback == MA_FALSE) {
/* We're using a fixed sized data callback so we'll need an intermediary buffer. */
ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames;
if (intermediaryBufferCap == 0) {
intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate);
}
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
ma_uint32 intermediaryBufferSizeInBytes;
pDevice->capture.intermediaryBufferLen = 0;
pDevice->capture.intermediaryBufferCap = intermediaryBufferCap;
if (pDevice->capture.intermediaryBufferCap == 0) {
pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames;
}
intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
if (pDevice->capture.pIntermediaryBuffer == NULL) {
ma_device_uninit(pDevice);
return MA_OUT_OF_MEMORY;
}
/* Silence the buffer for safety. */
ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels);
pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap;
}
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
ma_uint64 intermediaryBufferSizeInBytes;
pDevice->playback.intermediaryBufferLen = 0;
if (pConfig->deviceType == ma_device_type_duplex) {
pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */
} else {
pDevice->playback.intermediaryBufferCap = intermediaryBufferCap;
if (pDevice->playback.intermediaryBufferCap == 0) {
pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames;
}
}
intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
if (pDevice->playback.pIntermediaryBuffer == NULL) {
ma_device_uninit(pDevice);
return MA_OUT_OF_MEMORY;
}
/* Silence the buffer for safety. */
ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels);
pDevice->playback.intermediaryBufferLen = 0;
}
} else {
/* Not using a fixed sized data callback so no need for an intermediary buffer. */
}
/* Some backends don't require the worker thread. */
if (!ma_context_is_backend_asynchronous(pContext)) {
/* The worker thread. */
result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks);
if (result != MA_SUCCESS) {
ma_device_uninit(pDevice);
return result;
}
/* Wait for the worker thread to put the device into it's stopped state for real. */
ma_event_wait(&pDevice->stopEvent);
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
} else {
/*
If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done
after ma_device__post_init_setup().
*/
if (ma_context_is_backend_asynchronous(pContext)) {
if (pConfig->deviceType == ma_device_type_duplex) {
result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->d...
if (result != MA_SUCCESS) {
ma_device_uninit(pDevice);
return result;
}
}
}
ma_device__set_state(pDevice, ma_device_state_stopped);
}
/* Log device information. */
{
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend));
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL);
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format));
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels);
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate);
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.in...
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
}
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL);
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat));
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playbac...
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO");
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
}
}
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
return MA_SUCCESS;
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (pContextConfig != NULL) {
result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);
if (result != MA_SUCCESS) {
return result;
}
} else {
allocationCallbacks = ma_allocation_callbacks_init_default();
}
pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks);
if (pContext == NULL) {
return MA_OUT_OF_MEMORY;
}
for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
defaultBackends[iBackend] = (ma_backend)iBackend;
}
pBackendsToIterate = (ma_backend*)backends;
backendsToIterateCount = backendCount;
if (pBackendsToIterate == NULL) {
pBackendsToIterate = (ma_backend*)defaultBackends;
backendsToIterateCount = ma_countof(defaultBackends);
}
result = MA_NO_BACKEND;
for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
if (result == MA_SUCCESS) {
result = ma_device_init(pContext, pConfig, pDevice);
if (result == MA_SUCCESS) {
break; /* Success. */
} else {
ma_context_uninit(pContext); /* Failure. */
}
}
}
if (result != MA_SUCCESS) {
ma_free(pContext, &allocationCallbacks);
return result;
}
pDevice->isOwnerOfContext = MA_TRUE;
return result;
}
MA_API void ma_device_uninit(ma_device* pDevice)
{
if (!ma_device__is_initialized(pDevice)) {
return;
}
/* 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. */
if (ma_device_is_started(pDevice)) {
ma_device_stop(pDevice);
}
/* Putting the device into an uninitialized state will make the worker thread return. */
ma_device__set_state(pDevice, ma_device_state_uninitialized);
/* Wake up the worker thread and wait for it to properly terminate. */
if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
ma_event_signal(&pDevice->wakeupEvent);
ma_thread_wait(&pDevice->thread);
}
if (pDevice->pContext->callbacks.onDeviceUninit != NULL) {
pDevice->pContext->callbacks.onDeviceUninit(pDevice);
}
ma_event_uninit(&pDevice->stopEvent);
ma_event_uninit(&pDevice->startEvent);
ma_event_uninit(&pDevice->wakeupEvent);
ma_mutex_uninit(&pDevice->startStopLock);
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
if (pDevice->type == ma_device_type_duplex) {
ma_duplex_rb_uninit(&pDevice->duplexRB);
}
}
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks);
}
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks);
}
if (pDevice->playback.pInputCache != NULL) {
ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
}
if (pDevice->capture.pIntermediaryBuffer != NULL) {
ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
}
if (pDevice->playback.pIntermediaryBuffer != NULL) {
ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
}
if (pDevice->isOwnerOfContext) {
ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;
ma_context_uninit(pDevice->pContext);
ma_free(pDevice->pContext, &allocationCallbacks);
}
MA_ZERO_OBJECT(pDevice);
}
MA_API ma_context* ma_device_get_context(ma_device* pDevice)
{
if (pDevice == NULL) {
return NULL;
}
return pDevice->pContext;
}
MA_API ma_log* ma_device_get_log(ma_device* pDevice)
{
return ma_context_get_log(ma_device_get_context(pDevice));
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
if (pName != NULL) {
ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1);
/*
For safety, make sure the length is based on the truncated output string rather than the
source. Otherwise the caller might assume the output buffer contains more content than it
actually does.
*/
if (pLengthNotIncludingNullTerminator != NULL) {
*pLengthNotIncludingNullTerminator = strlen(pName);
}
} else {
/* Name not specified. Just report the length of the source string. */
if (pLengthNotIncludingNullTerminator != NULL) {
*pLengthNotIncludingNullTerminator = strlen(deviceInfo.name);
}
}
return MA_SUCCESS;
}
MA_API ma_result ma_device_start(ma_device* pDevice)
{
ma_result result;
if (pDevice == NULL) {
return MA_INVALID_ARGS;
}
if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
return MA_INVALID_OPERATION; /* Not initialized. */
}
if (ma_device_get_state(pDevice) == ma_device_state_started) {
return MA_SUCCESS; /* Already started. */
}
ma_mutex_lock(&pDevice->startStopLock);
{
/* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
ma_device__set_state(pDevice, ma_device_state_starting);
/* Asynchronous backends need to be handled differently. */
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
result = pDevice->pContext->callbacks.onDeviceStart(pDevice);
} else {
result = MA_INVALID_OPERATION;
}
if (result == MA_SUCCESS) {
ma_device__set_state(pDevice, ma_device_state_started);
ma_device__on_notification_started(pDevice);
}
} else {
/*
Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
thread and then wait for the start event.
*/
ma_event_signal(&pDevice->wakeupEvent);
/*
Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
into the started state. Don't call ma_device__set_state() here.
*/
ma_event_wait(&pDevice->startEvent);
result = pDevice->workResult;
}
/* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */
if (result != MA_SUCCESS) {
ma_device__set_state(pDevice, ma_device_state_stopped);
}
}
ma_mutex_unlock(&pDevice->startStopLock);
return result;
}
MA_API ma_result ma_device_stop(ma_device* pDevice)
{
ma_result result;
if (pDevice == NULL) {
return MA_INVALID_ARGS;
}
if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
return MA_INVALID_OPERATION; /* Not initialized. */
}
if (ma_device_get_state(pDevice) == ma_device_state_stopped) {
return MA_SUCCESS; /* Already stopped. */
}
ma_mutex_lock(&pDevice->startStopLock);
{
/* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started);
ma_device__set_state(pDevice, ma_device_state_stopping);
/* Asynchronous backends need to be handled differently. */
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
/* Asynchronous backends must have a stop operation. */
if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
} else {
result = MA_INVALID_OPERATION;
}
ma_device__set_state(pDevice, ma_device_state_stopped);
} else {
/*
Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If
the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make
sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super
important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.
*/
MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started);
if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) {
pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice);
}
/*
We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
*/
ma_event_wait(&pDevice->stopEvent);
result = MA_SUCCESS;
}
}
ma_mutex_unlock(&pDevice->startStopLock);
return result;
}
MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice)
{
return ma_device_get_state(pDevice) == ma_device_state_started;
}
MA_API ma_device_state ma_device_get_state(const ma_device* pDevice)
{
if (pDevice == NULL) {
return ma_device_state_uninitialized;
}
return (ma_device_state)c89atomic_load_i32((ma_int32*)&pDevice->state); /* Naughty cast to get rid of a const warning. */
}
MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume)
{
if (pDevice == NULL) {
return MA_INVALID_ARGS;
}
if (volume < 0.0f) {
return MA_INVALID_ARGS;
}
c89atomic_exchange_f32(&pDevice->masterVolumeFactor, volume);
return MA_SUCCESS;
}
MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
{
if (pVolume == NULL) {
return MA_INVALID_ARGS;
}
if (pDevice == NULL) {
*pVolume = 0;
return MA_INVALID_ARGS;
}
*pVolume = c89atomic_load_f32(&pDevice->masterVolumeFactor);
return MA_SUCCESS;
}
MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB)
{
if (gainDB > 0) {
return MA_INVALID_ARGS;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (pNewRefCount != NULL) {
*pNewRefCount = refCount;
}
return MA_SUCCESS;
}
static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)
{
ma_uint32 refCount;
MA_ASSERT(pResourceManager != NULL);
MA_ASSERT(pDataBufferNode != NULL);
(void)pResourceManager;
refCount = c89atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1;
if (pNewRefCount != NULL) {
*pNewRefCount = refCount;
}
return MA_SUCCESS;
}
static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
{
MA_ASSERT(pResourceManager != NULL);
MA_ASSERT(pDataBufferNode != NULL);
if (pDataBufferNode->isDataOwnedByResourceManager) {
if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) {
ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks);
pDataBufferNode->data.backend.encoded.pData = NULL;
pDataBufferNode->data.backend.encoded.sizeInBytes = 0;
} else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) {
ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks);
pDataBufferNode->data.backend.decoded.pData = NULL;
pDataBufferNode->data.backend.decoded.totalFrameCount = 0;
} else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) {
ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks);
} else {
/* Should never hit this if the node was successfully initialized. */
MA_ASSERT(pDataBufferNode->result != MA_SUCCESS);
}
}
/* The data buffer itself needs to be freed. */
ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
}
static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode)
{
MA_ASSERT(pDataBufferNode != NULL);
return (ma_result)c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */
}
static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager)
{
MA_ASSERT(pResourceManager != NULL);
return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0;
}
typedef struct
{
union
{
ma_async_notification_event e;
ma_async_notification_poll p;
} backend; /* Must be the first member. */
ma_resource_manager* pResourceManager;
} ma_resource_manager_inline_notification;
static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification)
{
MA_ASSERT(pResourceManager != NULL);
MA_ASSERT(pNotification != NULL);
pNotification->pResourceManager = pResourceManager;
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
return ma_async_notification_event_init(&pNotification->backend.e);
} else {
return ma_async_notification_poll_init(&pNotification->backend.p);
}
}
static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification)
{
MA_ASSERT(pNotification != NULL);
if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
ma_async_notification_event_uninit(&pNotification->backend.e);
} else {
/* No need to uninitialize a polling notification. */
}
}
static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification)
{
MA_ASSERT(pNotification != NULL);
if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
ma_async_notification_event_wait(&pNotification->backend.e);
} else {
while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) {
ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager);
if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
break;
}
}
}
}
static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification)
{
ma_resource_manager_inline_notification_wait(pNotification);
ma_resource_manager_inline_notification_uninit(pNotification);
}
static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager)
{
MA_ASSERT(pResourceManager != NULL);
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
#ifndef MA_NO_THREADING
{
ma_mutex_lock(&pResourceManager->dataBufferBSTLock);
}
#else
{
MA_ASSERT(MA_FALSE); /* Should never hit this. */
}
#endif
} else {
/* Threading not enabled. Do nothing. */
}
}
static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager)
{
MA_ASSERT(pResourceManager != NULL);
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
#ifndef MA_NO_THREADING
{
ma_mutex_unlock(&pResourceManager->dataBufferBSTLock);
}
#else
{
MA_ASSERT(MA_FALSE); /* Should never hit this. */
}
#endif
} else {
/* Threading not enabled. Do nothing. */
}
}
#ifndef MA_NO_THREADING
static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData)
{
ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;
MA_ASSERT(pResourceManager != NULL);
for (;;) {
ma_result result;
ma_job job;
result = ma_resource_manager_next_job(pResourceManager, &job);
if (result != MA_SUCCESS) {
break;
}
/* Terminate if we got a quit message. */
if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
break;
}
ma_job_process(&job);
}
return (ma_thread_result)0;
}
#endif
MA_API ma_resource_manager_config ma_resource_manager_config_init(void)
{
ma_resource_manager_config config;
MA_ZERO_OBJECT(&config);
config.decodedFormat = ma_format_unknown;
config.decodedChannels = 0;
config.decodedSampleRate = 0;
config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */
config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY;
/* Flags. */
config.flags = 0;
#ifdef MA_NO_THREADING
{
/* Threading is disabled at compile time so disable threading at runtime as well by default. */
config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
config.jobThreadCount = 0;
}
#endif
return config;
}
MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager)
{
ma_result result;
ma_job_queue_config jobQueueConfig;
if (pResourceManager == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pResourceManager);
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
#ifndef MA_NO_THREADING
{
if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) {
return MA_INVALID_ARGS; /* Requesting too many job threads. */
}
}
#endif
pResourceManager->config = *pConfig;
ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks);
/* Get the log set up early so we can start using it as soon as possible. */
if (pResourceManager->config.pLog == NULL) {
result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log);
if (result == MA_SUCCESS) {
pResourceManager->config.pLog = &pResourceManager->log;
} else {
pResourceManager->config.pLog = NULL; /* Logging is unavailable. */
}
}
if (pResourceManager->config.pVFS == NULL) {
result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks);
if (result != MA_SUCCESS) {
return result; /* Failed to initialize the default file system. */
}
pResourceManager->config.pVFS = &pResourceManager->defaultVFS;
}
/* If threading has been disabled at compile time, enfore it at run time as well. */
#ifdef MA_NO_THREADING
{
pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
}
#endif
/* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */
if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING;
/* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */
if (pResourceManager->config.jobThreadCount > 0) {
return MA_INVALID_ARGS;
}
}
/* Job queue. */
jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity;
jobQueueConfig.flags = 0;
if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) {
if (pResourceManager->config.jobThreadCount > 0) {
return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */
}
jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING;
}
result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue);
if (result != MA_SUCCESS) {
return result;
}
/* Custom decoding backends. */
if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) {
size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount;
pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks);
if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) {
ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
return MA_OUT_OF_MEMORY;
}
MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes);
pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount;
pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData;
}
/* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
#ifndef MA_NO_THREADING
{
ma_uint32 iJobThread;
/* Data buffer lock. */
result = ma_mutex_init(&pResourceManager->dataBufferBSTLock);
if (result != MA_SUCCESS) {
ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
return result;
}
/* Create the job threads last to ensure the threads has access to valid data. */
for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, 0, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks);
if (result != MA_SUCCESS) {
ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
return result;
}
}
}
#else
{
/* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */
MA_ASSERT(MA_FALSE);
}
#endif
}
return MA_SUCCESS;
}
static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager)
{
MA_ASSERT(pResourceManager);
/* If everything was done properly, there shouldn't be any active data buffers. */
while (pResourceManager->pRootDataBufferNode != NULL) {
ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode;
ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
/* The data buffer has been removed from the BST, so now we need to free it's data. */
ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
}
}
MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager)
{
if (pResourceManager == NULL) {
return;
}
/*
Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the
queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it.
*/
ma_resource_manager_post_job_quit(pResourceManager);
/* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
#ifndef MA_NO_THREADING
{
ma_uint32 iJobThread;
for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
ma_thread_wait(&pResourceManager->jobThreads[iJobThread]);
}
}
#else
{
MA_ASSERT(MA_FALSE); /* Should never hit this. */
}
#endif
}
/* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */
ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager);
/* The job queue is no longer needed. */
ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
/* We're no longer doing anything with data buffers so the lock can now be uninitialized. */
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
#ifndef MA_NO_THREADING
{
ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
}
#else
{
MA_ASSERT(MA_FALSE); /* Should never hit this. */
}
#endif
}
ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks);
if (pResourceManager->config.pLog == &pResourceManager->log) {
ma_log_uninit(&pResourceManager->log);
}
}
MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager)
{
if (pResourceManager == NULL) {
return NULL;
}
return pResourceManager->config.pLog;
}
MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void)
{
ma_resource_manager_data_source_config config;
MA_ZERO_OBJECT(&config);
config.rangeEndInPCMFrames = ~((ma_uint64)0);
config.loopPointEndInPCMFrames = ~((ma_uint64)0);
return config;
}
static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager)
{
ma_decoder_config config;
config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate);
config.allocationCallbacks = pResourceManager->config.allocationCallbacks;
config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables;
config.customBackendCount = pResourceManager->config.customDecodingBackendCount;
config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData;
return config;
}
static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder)
{
ma_result result;
ma_decoder_config config;
MA_ASSERT(pResourceManager != NULL);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
pPage->sizeInFrames = framesRead;
result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage);
if (result == MA_SUCCESS) {
pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead;
} else {
/* Failed to append the page. Just abort and set the status to MA_AT_END. */
ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
result = MA_AT_END;
}
} else {
/* No frames were read. Free the page and just set the status to MA_AT_END. */
ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
result = MA_AT_END;
}
} break;
case ma_resource_manager_data_supply_type_encoded:
case ma_resource_manager_data_supply_type_unknown:
default:
{
/* Unexpected data supply type. */
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode));
return MA_ERROR;
};
}
if (result == MA_SUCCESS && framesRead == 0) {
result = MA_AT_END;
}
return result;
}
static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pE...
{
ma_result result = MA_SUCCESS;
ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
ma_resource_manager_data_buffer_node* pInsertPoint;
if (ppDataBufferNode != NULL) {
*ppDataBufferNode = NULL;
}
result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint);
if (result == MA_ALREADY_EXISTS) {
/* The node already exists. We just need to increment the reference count. */
pDataBufferNode = pInsertPoint;
result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL);
if (result != MA_SUCCESS) {
return result; /* Should never happen. Failed to increment the reference count. */
}
result = MA_ALREADY_EXISTS;
goto done;
} else {
/*
The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This
needs to be done inside the critical section to ensure an uninitialization of the node
does not occur before initialization on another thread.
*/
pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks);
if (pDataBufferNode == NULL) {
return MA_OUT_OF_MEMORY;
}
MA_ZERO_OBJECT(pDataBufferNode);
pDataBufferNode->hashedName32 = hashedName32;
pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */
if (pExistingData == NULL) {
pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */
pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */
pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE;
} else {
pDataBufferNode->data = *pExistingData;
pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */
pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE;
}
result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
if (result != MA_SUCCESS) {
ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
return result; /* Should never happen. Failed to insert the data buffer into the BST. */
}
/*
Here is where we'll post the job, but only if we're loading asynchronously. If we're
loading synchronously we'll defer loading to a later stage, outside of the critical
section.
*/
if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
/* Loading asynchronously. Post the job. */
ma_job job;
char* pFilePathCopy = NULL;
wchar_t* pFilePathWCopy = NULL;
/* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */
if (pFilePath != NULL) {
pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks);
} else {
pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks);
}
if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
return MA_OUT_OF_MEMORY;
}
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification);
}
/* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */
if (pInitFence != NULL) { ma_fence_acquire(pInitFence); }
if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); }
/* We now have everything we need to post the job to the job thread. */
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE);
job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager;
job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode;
job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy;
job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy;
job.data.resourceManager.loadDataBufferNode.flags = flags;
job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL;
job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL;
job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence;
job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence;
result = ma_resource_manager_post_job(pResourceManager, &job);
if (result != MA_SUCCESS) {
/* Failed to post job. Probably ran out of memory. */
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result));
/*
Fences were acquired before posting the job, but since the job was not able to
be posted, we need to make sure we release them so nothing gets stuck waiting.
*/
if (pInitFence != NULL) { ma_fence_release(pInitFence); }
if (pDoneFence != NULL) { ma_fence_release(pDoneFence); }
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification);
}
ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
return result;
}
}
}
done:
if (ppDataBufferNode != NULL) {
*ppDataBufferNode = pDataBufferNode;
}
return result;
}
static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_f...
{
ma_result result = MA_SUCCESS;
ma_bool32 nodeAlreadyExists = MA_FALSE;
ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
if (ppDataBufferNode != NULL) {
*ppDataBufferNode = NULL; /* Safety. */
}
if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) {
return MA_INVALID_ARGS;
}
/* If we're specifying existing data, it must be valid. */
if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) {
return MA_INVALID_ARGS;
}
/* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */
if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
}
if (hashedName32 == 0) {
if (pFilePath != NULL) {
hashedName32 = ma_hash_string_32(pFilePath);
} else {
hashedName32 = ma_hash_string_w_32(pFilePathW);
}
}
/*
Here is where we either increment the node's reference count or allocate a new one and add it
to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is
posted inside the critical section just in case the caller immediately uninitializes the node
as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the
node is not uninitialized before initialization.
*/
ma_resource_manager_data_buffer_bst_lock(pResourceManager);
{
result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode);
}
ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
if (result == MA_ALREADY_EXISTS) {
nodeAlreadyExists = MA_TRUE;
result = MA_SUCCESS;
} else {
if (result != MA_SUCCESS) {
return result;
}
}
/*
If we're loading synchronously, we'll need to load everything now. When loading asynchronously,
a job will have been posted inside the BST critical section so that an uninitialization can be
allocated an appropriate execution order thereby preventing it from being uninitialized before
the node is initialized by the decoding thread(s).
*/
if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */
if (pFilePath == NULL && pFilePathW == NULL) {
/*
If this path is hit, it means a buffer is being copied (i.e. initialized from only the
hashed name), but that node has been freed in the meantime, probably from some other
thread. This is an invalid operation.
*/
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n");
result = MA_INVALID_OPERATION;
goto done;
}
if (pDataBufferNode->isDataOwnedByResourceManager) {
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) {
/* Loading synchronously. Load the sound in it's entirety here. */
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) {
/* No decoding. This is the simple case - just store the file contents in memory. */
result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW);
if (result != MA_SUCCESS) {
goto done;
}
} else {
/* Decoding. We do this the same way as we do when loading asynchronously. */
ma_decoder* pDecoder;
result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder);
if (result != MA_SUCCESS) {
goto done;
}
/* We have the decoder, now decode page by page just like we do when loading asynchronously. */
for (;;) {
/* Decode next page. */
result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder);
if (result != MA_SUCCESS) {
break; /* Will return MA_AT_END when the last page has been decoded. */
}
}
/* Reaching the end needs to be considered successful. */
if (result == MA_AT_END) {
result = MA_SUCCESS;
}
/*
At this point the data buffer is either fully decoded or some error occurred. Either
way, the decoder is no longer necessary.
*/
ma_decoder_uninit(pDecoder);
ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
}
/* Getting here means we were successful. Make sure the status of the node is updated accordingly. */
c89atomic_exchange_i32(&pDataBufferNode->result, result);
} else {
/* Loading asynchronously. We may need to wait for initialization. */
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_wait(&initNotification);
}
}
} else {
/* The data is not managed by the resource manager so there's nothing else to do. */
MA_ASSERT(pExistingData != NULL);
}
}
done:
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW)
{
ma_result result = MA_SUCCESS;
ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */
ma_uint32 hashedName32 = 0;
if (pResourceManager == NULL) {
return MA_INVALID_ARGS;
}
if (pDataBufferNode == NULL) {
if (pName == NULL && pNameW == NULL) {
return MA_INVALID_ARGS;
}
if (pName != NULL) {
hashedName32 = ma_hash_string_32(pName);
} else {
hashedName32 = ma_hash_string_w_32(pNameW);
}
}
/*
The first thing to do is decrement the reference counter of the node. Then, if the reference
count is zero, we need to free the node. If the node is still in the process of loading, we'll
need to post a job to the job queue to free the node. Otherwise we'll just do it here.
*/
ma_resource_manager_data_buffer_bst_lock(pResourceManager);
{
/* Might need to find the node. Must be done inside the critical section. */
if (pDataBufferNode == NULL) {
result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode);
if (result != MA_SUCCESS) {
goto stage2; /* Couldn't find the node. */
}
}
result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount);
if (result != MA_SUCCESS) {
goto stage2; /* Should never happen. */
}
if (refCount == 0) {
result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
if (result != MA_SUCCESS) {
goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */
}
}
}
ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
stage2:
if (result != MA_SUCCESS) {
return result;
}
/*
Here is where we need to free the node. We don't want to do this inside the critical section
above because we want to keep that as small as possible for multi-threaded efficiency.
*/
if (refCount == 0) {
if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
/* The sound is still loading. We need to delay the freeing of the node to a safe time. */
ma_job job;
/* We need to mark the node as unavailable for the sake of the resource manager worker threads. */
c89atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE);
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE);
job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager;
job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode;
result = ma_resource_manager_post_job(pResourceManager, &job);
if (result != MA_SUCCESS) {
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result));
return result;
}
/* If we don't support threading, process the job queue here. */
if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
result = ma_resource_manager_process_next_job(pResourceManager);
if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
result = MA_SUCCESS;
break;
}
}
} else {
/* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */
}
} else {
/* The sound isn't loading so we can just free the node here. */
ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
}
}
return result;
}
static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer)
{
MA_ASSERT(pDataBuffer != NULL);
return c89atomic_fetch_add_32(&pDataBuffer->executionCounter, 1);
}
static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
}
static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
{
return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex);
}
static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
}
static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
{
return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor);
}
static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
{
return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength);
}
static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
{
ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource;
MA_ASSERT(pDataBuffer != NULL);
c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping);
/* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */
ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping);
return MA_SUCCESS;
}
static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable =
{
ma_resource_manager_data_buffer_cb__read_pcm_frames,
ma_resource_manager_data_buffer_cb__seek_to_pcm_frame,
ma_resource_manager_data_buffer_cb__get_data_format,
ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames,
ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames,
ma_resource_manager_data_buffer_cb__set_looping,
0
};
static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer)
{
ma_result result = MA_SUCCESS;
ma_resource_manager_data_buffer_node* pDataBufferNode;
ma_data_source_config dataSourceConfig;
ma_bool32 async;
ma_uint32 flags;
ma_resource_manager_pipeline_notifications notifications;
if (pDataBuffer == NULL) {
if (pConfig != NULL && pConfig->pNotifications != NULL) {
ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
}
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pDataBuffer);
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
if (pConfig->pNotifications != NULL) {
notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */
} else {
MA_ZERO_OBJECT(¬ifications);
}
/* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */
flags = pConfig->flags;
if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
}
async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0;
/*
Fences need to be acquired before doing anything. These must be aquired and released outside of
the node to ensure there's no holes where ma_fence_wait() could prematurely return before the
data buffer has completed initialization.
When loading asynchronously, the node acquisition routine below will acquire the fences on this
thread and then release them on the async thread when the operation is complete.
These fences are always released at the "done" tag at the end of this function. They'll be
acquired a second if loading asynchronously. This double acquisition system is just done to
simplify code maintanence.
*/
ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications);
{
/* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */
result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode);
if (result != MA_SUCCESS) {
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
goto done;
}
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable;
result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds);
if (result != MA_SUCCESS) {
ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
goto done;
}
pDataBuffer->pResourceManager = pResourceManager;
pDataBuffer->pNode = pDataBufferNode;
pDataBuffer->flags = flags;
pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */
/* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */
if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {
/* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL);
c89atomic_exchange_i32(&pDataBuffer->result, result);
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
goto done;
} else {
/* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */
ma_job job;
ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);
}
/*
The status of the data buffer needs to be set to MA_BUSY before posting the job so that the
worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other
than MA_BUSY, it'll assume an error and fall through to an early exit.
*/
c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY);
/* Acquire fences a second time. These will be released by the async thread. */
ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications);
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER);
job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer;
job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;
job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification;
job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence;
job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence;
job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames;
job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames;
job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping;
result = ma_resource_manager_post_job(pResourceManager, &job);
if (result != MA_SUCCESS) {
/* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result));
c89atomic_exchange_i32(&pDataBuffer->result, result);
/* Release the fences after the result has been set on the data buffer. */
ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications);
} else {
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_wait(&initNotification);
if (notifications.init.pNotification != NULL) {
ma_async_notification_signal(notifications.init.pNotification);
}
/* NOTE: Do not release the init fence here. It will have been done by the job. */
/* Make sure we return an error if initialization failed on the async thread. */
result = ma_resource_manager_data_buffer_result(pDataBuffer);
if (result == MA_BUSY) {
result = MA_SUCCESS;
}
}
}
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_uninit(&initNotification);
}
}
if (result != MA_SUCCESS) {
ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
goto done;
}
}
done:
if (result == MA_SUCCESS) {
if (pConfig->initialSeekPointInPCMFrames > 0) {
ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames);
}
}
ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications);
return result;
}
MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer)
{
return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer);
}
MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)
{
ma_resource_manager_data_source_config config;
config = ma_resource_manager_data_source_config_init();
config.pFilePath = pFilePath;
config.flags = flags;
config.pNotifications = pNotifications;
return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
}
MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)
{
ma_resource_manager_data_source_config config;
config = ma_resource_manager_data_source_config_init();
config.pFilePathW = pFilePath;
config.flags = flags;
config.pNotifications = pNotifications;
return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
}
MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer)
{
ma_resource_manager_data_source_config config;
if (pExistingDataBuffer == NULL) {
return MA_INVALID_ARGS;
}
MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */
config = ma_resource_manager_data_source_config_init();
config.flags = pExistingDataBuffer->flags;
return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer);
}
static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer)
{
MA_ASSERT(pDataBuffer != NULL);
/* The connector should be uninitialized first. */
ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer);
/* With the connector uninitialized we can unacquire the node. */
ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL);
/* The base data source needs to be uninitialized as well. */
ma_data_source_uninit(&pDataBuffer->ds);
return MA_SUCCESS;
}
MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer)
{
ma_result result;
if (pDataBuffer == NULL) {
return MA_INVALID_ARGS;
}
if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) {
/* The data buffer can be deleted synchronously. */
return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
} else {
/*
The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will
be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event
to get processed before returning.
*/
ma_resource_manager_inline_notification notification;
ma_job job;
/*
We need to mark the node as unavailable so we don't try reading from it anymore, but also to
let the loading thread know that it needs to abort it's loading procedure.
*/
c89atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE);
result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification);
if (result != MA_SUCCESS) {
return result; /* Failed to create the notification. This should rarely, if ever, happen. */
}
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER);
job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer;
job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification;
job.data.resourceManager.freeDataBuffer.pDoneFence = NULL;
result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job);
if (result != MA_SUCCESS) {
ma_resource_manager_inline_notification_uninit(¬ification);
return result;
}
ma_resource_manager_inline_notification_wait_and_uninit(¬ification);
}
return result;
}
MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
ma_result result = MA_SUCCESS;
ma_uint64 framesRead = 0;
ma_bool32 isDecodedBufferBusy = MA_FALSE;
/* Safety. */
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
if (frameCount == 0) {
return MA_INVALID_ARGS;
}
/*
We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after
it's been uninitialized or is in the process of uninitializing.
*/
MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
/* If the node is not initialized we need to abort with a busy code. */
if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
return MA_BUSY; /* Still loading. */
}
if (pDataBuffer->seekToCursorOnNextRead) {
pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames);
if (result != MA_SUCCESS) {
return result;
}
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) {
absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames;
}
c89atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor);
}
MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream)
{
ma_result result;
ma_data_source_config dataSourceConfig;
char* pFilePathCopy = NULL;
wchar_t* pFilePathWCopy = NULL;
ma_job job;
ma_bool32 waitBeforeReturning = MA_FALSE;
ma_resource_manager_inline_notification waitNotification;
ma_resource_manager_pipeline_notifications notifications;
if (pDataStream == NULL) {
if (pConfig != NULL && pConfig->pNotifications != NULL) {
ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
}
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pDataStream);
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
if (pConfig->pNotifications != NULL) {
notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */
} else {
MA_ZERO_OBJECT(¬ifications);
}
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable;
result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds);
if (result != MA_SUCCESS) {
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
return result;
}
pDataStream->pResourceManager = pResourceManager;
pDataStream->flags = pConfig->flags;
pDataStream->result = MA_BUSY;
ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
ma_data_source_set_looping(pDataStream, pConfig->isLooping);
if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) {
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
return MA_INVALID_ARGS;
}
/* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */
/* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */
if (pConfig->pFilePath != NULL) {
pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks);
} else {
pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks);
}
if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
return MA_OUT_OF_MEMORY;
}
/*
We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we
can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same.
*/
if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
waitBeforeReturning = MA_TRUE;
ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification);
}
ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications);
/* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */
ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames);
/* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM);
job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
job.data.resourceManager.loadDataStream.pDataStream = pDataStream;
job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy;
job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy;
job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames;
job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification;
job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence;
result = ma_resource_manager_post_job(pResourceManager, &job);
if (result != MA_SUCCESS) {
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications);
if (waitBeforeReturning) {
ma_resource_manager_inline_notification_uninit(&waitNotification);
}
ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
return result;
}
/* Wait if needed. */
if (waitBeforeReturning) {
ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification);
if (notifications.init.pNotification != NULL) {
ma_async_notification_signal(notifications.init.pNotification);
}
/* NOTE: Do not release pInitFence here. That will be done by the job. */
}
return MA_SUCCESS;
}
MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)
{
ma_resource_manager_data_source_config config;
config = ma_resource_manager_data_source_config_init();
config.pFilePath = pFilePath;
config.flags = flags;
config.pNotifications = pNotifications;
return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
}
MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)
{
ma_resource_manager_data_source_config config;
config = ma_resource_manager_data_source_config_init();
config.pFilePathW = pFilePath;
config.flags = flags;
config.pNotifications = pNotifications;
return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream));
ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd);
ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd);
ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd);
ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd);
}
/* Just read straight from the decoder. It will deal with ranges and looping for us. */
result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage);
if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) {
c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE);
}
c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage);
c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE);
}
static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream)
{
ma_uint32 iPage;
MA_ASSERT(pDataStream != NULL);
for (iPage = 0; iPage < 2; iPage += 1) {
ma_resource_manager_data_stream_fill_page(pDataStream, iPage);
}
}
static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount)
{
ma_uint64 framesAvailable;
ma_uint64 frameCount = 0;
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
if (pFrameCount != NULL) {
frameCount = *pFrameCount;
*pFrameCount = 0;
}
if (ppFramesOut != NULL) {
*ppFramesOut = NULL;
}
if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
return MA_INVALID_ARGS;
}
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
return MA_INVALID_OPERATION;
}
/* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
return MA_BUSY;
}
/* If the page we're on is invalid it means we've caught up to the job thread. */
if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) {
framesAvailable = 0;
} else {
/*
The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is
that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler.
*/
ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]);
MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor);
framesAvailable = currentPageFrameCount - pDataStream->relativeCursor;
}
/* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */
if (framesAvailable == 0) {
if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) {
return MA_AT_END;
} else {
return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */
}
}
MA_ASSERT(framesAvailable > 0);
if (frameCount > framesAvailable) {
frameCount = framesAvailable;
}
*ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor);
*pFrameCount = frameCount;
return MA_SUCCESS;
}
static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount)
{
ma_uint32 newRelativeCursor;
ma_uint32 pageSizeInFrames;
ma_job job;
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
return MA_INVALID_OPERATION;
}
/* The frame count should always fit inside a 32-bit integer. */
if (frameCount > 0xFFFFFFFF) {
return MA_INVALID_ARGS;
}
pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
/* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */
ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount);
/* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */
newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount;
/* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */
if (newRelativeCursor >= pageSizeInFrames) {
newRelativeCursor -= pageSizeInFrames;
/* Here is where we post the job start decoding. */
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM);
job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
job.data.resourceManager.pageDataStream.pDataStream = pDataStream;
job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex;
/* The page needs to be marked as invalid so that the public API doesn't try reading from it. */
c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE);
/* Before posting the job we need to make sure we set some state. */
pDataStream->relativeCursor = newRelativeCursor;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesProcessed;
}
if (result == MA_SUCCESS && totalFramesProcessed == 0) {
result = MA_AT_END;
}
return result;
}
MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex)
{
ma_job job;
ma_result streamResult;
streamResult = ma_resource_manager_data_stream_result(pDataStream);
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(streamResult != MA_UNAVAILABLE);
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) {
return MA_INVALID_OPERATION;
}
/* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */
if (c89atomic_load_32(&pDataStream->seekCounter) == 0) {
if (c89atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) {
return MA_SUCCESS;
}
}
/* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */
c89atomic_fetch_add_32(&pDataStream->seekCounter, 1);
/* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */
ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex);
/*
We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public
API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of
the first page.
*/
pDataStream->relativeCursor = 0;
pDataStream->currentPageIndex = 0;
c89atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE);
c89atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE);
/* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */
c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE);
/*
The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages
are invalid and any content contained within them will be discarded and replaced with newly decoded data.
*/
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM);
job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
job.data.resourceManager.seekDataStream.pDataStream = pDataStream;
job.data.resourceManager.seekDataStream.frameIndex = frameIndex;
return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
}
MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
if (pFormat != NULL) {
*pFormat = ma_format_unknown;
}
if (pChannels != NULL) {
*pChannels = 0;
}
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pChannelMap != NULL) {
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
}
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
return MA_INVALID_OPERATION;
}
/*
We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function
such that the application is responsible for ensuring it's not called while uninitializing so it should be safe.
*/
return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
}
MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor)
{
ma_result result;
if (pCursor == NULL) {
return MA_INVALID_ARGS;
}
*pCursor = 0;
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
/*
If the stream is in an erroneous state we need to return an invalid operation. We can allow
this to be called when the data stream is in a busy state because the caller may have asked
for an initial seek position and it's convenient to return that as the cursor position.
*/
result = ma_resource_manager_data_stream_result(pDataStream);
if (result != MA_SUCCESS && result != MA_BUSY) {
return MA_INVALID_OPERATION;
}
*pCursor = c89atomic_load_64(&pDataStream->absoluteCursor);
return MA_SUCCESS;
}
MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength)
{
ma_result streamResult;
if (pLength == NULL) {
return MA_INVALID_ARGS;
}
*pLength = 0;
streamResult = ma_resource_manager_data_stream_result(pDataStream);
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(streamResult != MA_UNAVAILABLE);
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
if (streamResult != MA_SUCCESS) {
return streamResult;
}
/*
We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we
calculated when we initialized it on the job thread.
*/
*pLength = pDataStream->totalLengthInPCMFrames;
if (*pLength == 0) {
return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */
}
return MA_SUCCESS;
}
MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream)
{
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
return (ma_result)c89atomic_load_i32(&pDataStream->result);
}
MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping)
{
return ma_data_source_set_looping(pDataStream, isLooping);
}
MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream)
{
if (pDataStream == NULL) {
return MA_FALSE;
}
return c89atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */
}
MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames)
{
ma_uint32 pageIndex0;
ma_uint32 pageIndex1;
ma_uint32 relativeCursor;
ma_uint64 availableFrames;
if (pAvailableFrames == NULL) {
return MA_INVALID_ARGS;
}
*pAvailableFrames = 0;
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
pageIndex0 = pDataStream->currentPageIndex;
pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01;
relativeCursor = pDataStream->relativeCursor;
availableFrames = 0;
if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex0])) {
availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor;
if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex1])) {
availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]);
}
}
*pAvailableFrames = availableFrames;
return MA_SUCCESS;
}
static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource)
{
if (pDataSource == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pDataSource);
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
if (pResourceManager == NULL) {
return MA_INVALID_ARGS;
}
pDataSource->flags = pConfig->flags;
return MA_SUCCESS;
}
MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource)
{
ma_result result;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
}
MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob)
{
if (pResourceManager == NULL) {
return MA_INVALID_ARGS;
}
return ma_job_queue_post(&pResourceManager->jobQueue, pJob);
}
MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager)
{
ma_job job = ma_job_init(MA_JOB_TYPE_QUIT);
return ma_resource_manager_post_job(pResourceManager, &job);
}
MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob)
{
if (pResourceManager == NULL) {
return MA_INVALID_ARGS;
}
return ma_job_queue_next(&pResourceManager->jobQueue, pJob);
}
static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob)
{
ma_result result = MA_SUCCESS;
ma_resource_manager* pResourceManager;
ma_resource_manager_data_buffer_node* pDataBufferNode;
MA_ASSERT(pJob != NULL);
pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager;
MA_ASSERT(pResourceManager != NULL);
pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode;
MA_ASSERT(pDataBufferNode != NULL);
MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */
/* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */
if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */
}
/* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */
if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) {
result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */
goto done;
}
/*
We're ready to start loading. Essentially what we're doing here is initializing the data supply
of the node. Once this is complete, data buffers can have their connectors initialized which
will allow then to have audio data read from them.
Note that when the data supply type has been moved away from "unknown", that is when other threads
will determine that the node is available for data delivery and the data buffer connectors can be
initialized. Therefore, it's important that it is set after the data supply has been initialized.
*/
if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) {
/*
Decoding. This is the complex case because we're not going to be doing the entire decoding
process here. Instead it's going to be split of multiple jobs and loaded in pages. The
reason for this is to evenly distribute decoding time across multiple sounds, rather than
having one huge sound hog all the available processing resources.
The first thing we do is initialize a decoder. This is allocated on the heap and is passed
around to the paging jobs. When the last paging job has completed it's processing, it'll
free the decoder for us.
This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job
which is where the actual decoding work will be done. However, once this job is complete,
the node will be in a state where data buffer connectors can be initialized.
*/
ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */
ma_job pageDataBufferNodeJob;
/* Allocate the decoder by initializing a decoded data supply. */
result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager...
/*
Don't ever propagate an MA_BUSY result code or else the resource manager will think the
node is just busy decoding rather than in an error state. This should never happen, but
including this logic for safety just in case.
*/
if (result == MA_BUSY) {
result = MA_ERROR;
}
if (result != MA_SUCCESS) {
if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) {
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result));
} else {
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result));
#endif
}
goto done;
}
/*
At this point the node's data supply is initialized and other threads can start initializing
their data buffer connectors. However, no data will actually be available until we start to
actually decode it. To do this, we need to post a paging job which is where the decoding
work is done.
Note that if an error occurred at an earlier point, this section will have been skipped.
*/
pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE);
pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager;
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode;
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder;
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification;
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence;
/* The job has been set up so it can now be posted. */
result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob);
/*
When we get here, we want to make sure the result code is set to MA_BUSY. The reason for
this is that the result will be copied over to the node's internal result variable. In
this case, since the decoding is still in-progress, we need to make sure the result code
is set to MA_BUSY.
*/
if (result != MA_SUCCESS) {
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result));
ma_decoder_uninit(pDecoder);
ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
} else {
result = MA_BUSY;
}
} else {
/* No decoding. This is the simple case. We need only read the file content into memory and we're done. */
result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW);
}
done:
/* File paths are no longer needed. */
ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks);
ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks);
/*
We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads
are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY
because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then
immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any
other error code would cause the buffer to look like it's in a state that it's not.
*/
c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
/* At this point initialization is complete and we can signal the notification if any. */
if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification);
}
if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) {
ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence);
}
/* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */
if (result != MA_BUSY) {
if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification);
}
if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) {
ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence);
}
}
/* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */
c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
return result;
}
static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob)
{
ma_resource_manager* pResourceManager;
ma_resource_manager_data_buffer_node* pDataBufferNode;
MA_ASSERT(pJob != NULL);
pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager;
MA_ASSERT(pResourceManager != NULL);
pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode;
MA_ASSERT(pDataBufferNode != NULL);
if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
}
ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
/* The event needs to be signalled last. */
if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification);
}
if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) {
ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence);
}
c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
return MA_SUCCESS;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */
}
/*
First thing we need to do is check whether or not the data buffer is getting deleted. If so we
just abort, but making sure we increment the execution pointer.
*/
result = ma_resource_manager_data_buffer_result(pDataBuffer);
if (result != MA_BUSY) {
goto done; /* <-- This will ensure the exucution pointer is incremented. */
} else {
result = MA_SUCCESS; /* <-- Make sure this is reset. */
}
/* Try initializing the connector if we haven't already. */
isConnectorInitialized = pDataBuffer->isConnectorInitialized;
if (isConnectorInitialized == MA_FALSE) {
dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode);
if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) {
/* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */
ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */
dataSourceConfig = ma_resource_manager_data_source_config_init();
dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames;
dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames;
dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames;
dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames;
dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping;
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence);
if (result != MA_SUCCESS) {
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result));
goto done;
}
} else {
/* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */
}
} else {
/* The connector is already initialized. Nothing to do here. */
}
/*
If the data node is still loading, we need to repost the job and *not* increment the execution
pointer (i.e. we need to not fall through to the "done" label).
There is a hole between here and the where the data connector is initialized where the data
buffer node may have finished initializing. We need to check for this by checking the result of
the data buffer node and whether or not we had an unknown data supply type at the time of
trying to initialize the data connector.
*/
result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) {
return ma_resource_manager_post_job(pResourceManager, pJob);
}
done:
/* Only move away from a busy code so that we don't trash any existing error codes. */
c89atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result);
/* Only signal the other threads after the result has been set just for cleanliness sake. */
if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification);
}
if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) {
ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence);
}
/*
If at this point the data buffer has not had it's connector initialized, it means the
notification event was never signalled which means we need to signal it here.
*/
if (pDataBuffer->isConnectorInitialized == MA_FALSE && result != MA_SUCCESS) {
if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification);
}
if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) {
ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence);
}
}
c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
return result;
}
static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob)
{
ma_resource_manager* pResourceManager;
ma_resource_manager_data_buffer* pDataBuffer;
MA_ASSERT(pJob != NULL);
pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer;
MA_ASSERT(pDataBuffer != NULL);
pResourceManager = pDataBuffer->pResourceManager;
if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
}
ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
/* The event needs to be signalled last. */
if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification);
}
if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) {
ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence);
}
c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
return MA_SUCCESS;
}
static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob)
{
ma_result result = MA_SUCCESS;
ma_decoder_config decoderConfig;
ma_uint32 pageBufferSizeInBytes;
ma_resource_manager* pResourceManager;
ma_resource_manager_data_stream* pDataStream;
MA_ASSERT(pJob != NULL);
pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream;
MA_ASSERT(pDataStream != NULL);
pResourceManager = pDataStream->pResourceManager;
if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
}
if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) {
result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */
goto done;
}
/* We need to initialize the decoder first so we can determine the size of the pages. */
decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager);
if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) {
result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder);
} else {
result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder);
}
if (result != MA_SUCCESS) {
goto done;
}
/* Retrieve the total length of the file before marking the decoder are loaded. */
if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {
result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames);
if (result != MA_SUCCESS) {
goto done; /* Failed to retrieve the length. */
}
} else {
pDataStream->totalLengthInPCMFrames = 0;
}
/*
Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file
and we don't want to have another thread trying to access the decoder while it's scanning.
*/
pDataStream->isDecoderInitialized = MA_TRUE;
/* We have the decoder so we can now initialize our page buffer. */
pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels);
pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks);
if (pDataStream->pPageData == NULL) {
ma_decoder_uninit(&pDataStream->decoder);
result = MA_OUT_OF_MEMORY;
goto done;
}
/* Seek to our initial seek point before filling the initial pages. */
ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint);
/* We have our decoder and our page buffer, so now we need to fill our pages. */
ma_resource_manager_data_stream_fill_pages(pDataStream);
/* And now we're done. We want to make sure the result is MA_SUCCESS. */
result = MA_SUCCESS;
done:
ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks);
ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks);
/* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */
c89atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result);
/* Only signal the other threads after the result has been set just for cleanliness sake. */
if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification);
}
if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) {
ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence);
}
c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
return result;
}
static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob)
{
ma_resource_manager* pResourceManager;
ma_resource_manager_data_stream* pDataStream;
MA_ASSERT(pJob != NULL);
pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream;
MA_ASSERT(pDataStream != NULL);
pResourceManager = pDataStream->pResourceManager;
if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
}
/* If our status is not MA_UNAVAILABLE we have a bug somewhere. */
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE);
if (pDataStream->isDecoderInitialized) {
ma_decoder_uninit(&pDataStream->decoder);
}
if (pDataStream->pPageData != NULL) {
ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks);
pDataStream->pPageData = NULL; /* Just in case... */
}
ma_data_source_uninit(&pDataStream->ds);
/* The event needs to be signalled last. */
if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification);
}
if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) {
ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence);
}
/*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/
return MA_SUCCESS;
}
static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob)
{
ma_result result = MA_SUCCESS;
ma_resource_manager* pResourceManager;
ma_resource_manager_data_stream* pDataStream;
MA_ASSERT(pJob != NULL);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
{
return c89atomic_load_f32((float*)&pOutputBus->volume);
}
static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus)
{
MA_ASSERT(pInputBus != NULL);
MA_ASSERT(channels < 256);
MA_ZERO_OBJECT(pInputBus);
if (channels == 0) {
return MA_INVALID_ARGS;
}
pInputBus->channels = (ma_uint8)channels;
return MA_SUCCESS;
}
static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus)
{
ma_spinlock_lock(&pInputBus->lock);
}
static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus)
{
ma_spinlock_unlock(&pInputBus->lock);
}
static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus)
{
c89atomic_fetch_add_32(&pInputBus->nextCounter, 1);
}
static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus)
{
c89atomic_fetch_sub_32(&pInputBus->nextCounter, 1);
}
static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus)
{
return c89atomic_load_32(&pInputBus->nextCounter);
}
static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus)
{
return pInputBus->channels;
}
static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
{
MA_ASSERT(pInputBus != NULL);
MA_ASSERT(pOutputBus != NULL);
/*
Mark the output bus as detached first. This will prevent future iterations on the audio thread
from iterating this output bus.
*/
ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE);
/*
We cannot use the output bus lock here since it'll be getting used at a higher level, but we do
still need to use the input bus lock since we'll be updating pointers on two different output
buses. The same rules apply here as the attaching case. Although we're using a lock here, we're
*not* using a lock when iterating over the list in the audio thread. We therefore need to craft
this in a way such that the iteration on the audio thread doesn't break.
The the first thing to do is swap out the "next" pointer of the previous output bus with the
new "next" output bus. This is the operation that matters for iteration on the audio thread.
After that, the previous pointer on the new "next" pointer needs to be updated, after which
point the linked list will be in a good state.
*/
ma_node_input_bus_lock(pInputBus);
{
ma_node_output_bus* pOldPrev = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pPrev);
ma_node_output_bus* pOldNext = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext);
if (pOldPrev != NULL) {
c89atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */
}
if (pOldNext != NULL) {
c89atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */
}
}
ma_node_input_bus_unlock(pInputBus);
/* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */
c89atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */
c89atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */
pOutputBus->pInputNode = NULL;
pOutputBus->inputNodeInputBusIndex = 0;
/*
For thread-safety reasons, we don't want to be returning from this straight away. We need to
wait for the audio thread to finish with the output bus. There's two things we need to wait
for. The first is the part that selects the next output bus in the list, and the other is the
part that reads from the output bus. Basically all we're doing is waiting for the input bus
to stop referencing the output bus.
We're doing this part last because we want the section above to run while the audio thread
is finishing up with the output bus, just for efficiency reasons. We marked the output bus as
detached right at the top of this function which is going to prevent the audio thread from
iterating the output bus again.
*/
/* Part 1: Wait for the current iteration to complete. */
while (ma_node_input_bus_get_next_counter(pInputBus) > 0) {
ma_yield();
}
/* Part 2: Wait for any reads to complete. */
while (c89atomic_load_32(&pOutputBus->refCount) > 0) {
ma_yield();
}
/*
At this point we're done detaching and we can be guaranteed that the audio thread is not going
to attempt to reference this output bus again (until attached again).
*/
}
#if 0 /* Not used at the moment, but leaving here in case I need it later. */
static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
{
MA_ASSERT(pInputBus != NULL);
MA_ASSERT(pOutputBus != NULL);
ma_node_output_bus_lock(pOutputBus);
{
ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
}
ma_node_output_bus_unlock(pOutputBus);
}
#endif
static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex)
{
MA_ASSERT(pInputBus != NULL);
MA_ASSERT(pOutputBus != NULL);
ma_node_output_bus_lock(pOutputBus);
{
ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pInputNode);
/* Detach from any existing attachment first if necessary. */
if (pOldInputNode != NULL) {
ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
}
/*
At this point we can be sure the output bus is not attached to anything. The linked list in the
old input bus has been updated so that pOutputBus will not get iterated again.
*/
pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */
pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; /* As above. */
/*
Now we need to attach the output bus to the linked list. This involves updating two pointers on
two different output buses so I'm going to go ahead and keep this simple and just use a lock.
There are ways to do this without a lock, but it's just too hard to maintain for it's value.
Although we're locking here, it's important to remember that we're *not* locking when iterating
and reading audio data since that'll be running on the audio thread. As a result we need to be
careful how we craft this so that we don't break iteration. What we're going to do is always
attach the new item so that it becomes the first item in the list. That way, as we're iterating
we won't break any links in the list and iteration will continue safely. The detaching case will
also be crafted in a way as to not break list iteration. It's important to remember to use
atomic exchanges here since no locking is happening on the audio thread during iteration.
*/
ma_node_input_bus_lock(pInputBus);
{
ma_node_output_bus* pNewPrev = &pInputBus->head;
ma_node_output_bus* pNewNext = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext);
/* Update the local output bus. */
c89atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev);
c89atomic_exchange_ptr(&pOutputBus->pNext, pNewNext);
/* Update the other output buses to point back to the local output bus. */
c89atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */
/* Do the previous pointer last. This is only used for detachment. */
if (pNewNext != NULL) {
c89atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus);
}
}
ma_node_input_bus_unlock(pInputBus);
/*
Mark the node as attached last. This is used to controlling whether or the output bus will be
iterated on the audio thread. Mainly required for detachment purposes.
*/
ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE);
}
ma_node_output_bus_unlock(pOutputBus);
}
static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
{
ma_node_output_bus* pNext;
MA_ASSERT(pInputBus != NULL);
if (pOutputBus == NULL) {
return NULL;
}
ma_node_input_bus_next_begin(pInputBus);
{
pNext = pOutputBus;
for (;;) {
pNext = (ma_node_output_bus*)c89atomic_load_ptr(&pNext->pNext);
if (pNext == NULL) {
break; /* Reached the end. */
}
if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) {
continue; /* The node is not attached. Keep checking. */
}
/* The next node has been selected. */
break;
}
/* We need to increment the reference count of the selected node. */
if (pNext != NULL) {
c89atomic_fetch_add_32(&pNext->refCount, 1);
}
/* The previous node is no longer being referenced. */
c89atomic_fetch_sub_32(&pOutputBus->refCount, 1);
}
ma_node_input_bus_next_end(pInputBus);
return pNext;
}
static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus)
{
return ma_node_input_bus_next(pInputBus, &pInputBus->head);
}
static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime)
{
ma_result result = MA_SUCCESS;
ma_node_output_bus* pOutputBus;
ma_node_output_bus* pFirst;
ma_uint32 inputChannels;
ma_bool32 doesOutputBufferHaveContent = MA_FALSE;
/*
This will be called from the audio thread which means we can't be doing any locking. Basically,
this function will not perfom any locking, whereas attaching and detaching will, but crafted in
such a way that we don't need to perform any locking here. The important thing to remember is
to always iterate in a forward direction.
In order to process any data we need to first read from all input buses. That's where this
function comes in. This iterates over each of the attachments and accumulates/mixes them. We
also convert the channels to the nodes output channel count before mixing. We want to do this
channel conversion so that the caller of this function can invoke the processing callback
without having to do it themselves.
When we iterate over each of the attachments on the input bus, we need to read as much data as
we can from each of them so that we don't end up with holes between each of the attachments. To
do this, we need to read from each attachment in a loop and read as many frames as we can, up
to `frameCount`.
*/
MA_ASSERT(pInputNode != NULL);
MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */
*pFramesRead = 0; /* Safety. */
inputChannels = ma_node_input_bus_get_channels(pInputBus);
/*
We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They
are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first()
once per iteration, however we have an optimization to checks whether or not it's the first item in
the list. We therefore need to store a pointer to the first item rather than repeatedly calling
ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it
after calling ma_node_input_bus_next(), which we won't be.
*/
pFirst = ma_node_input_bus_first(pInputBus);
if (pFirst == NULL) {
return MA_SUCCESS; /* No attachments. Read nothing. */
}
for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) {
ma_uint32 framesProcessed = 0;
ma_bool32 isSilentOutput = MA_FALSE;
MA_ASSERT(pOutputBus->pNode != NULL);
isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0;
if (pFramesOut != NULL) {
/* Read. */
float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels;
while (framesProcessed < frameCount) {
float* pRunningFramesOut;
ma_uint32 framesToRead;
ma_uint32 framesJustRead;
framesToRead = frameCount - framesProcessed;
if (framesToRead > tempCapInFrames) {
framesToRead = tempCapInFrames;
}
pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels);
if (doesOutputBufferHaveContent == MA_FALSE) {
/* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */
result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed);
} else {
/* Slow path. Not the first attachment. Mixing required. */
result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed);
if (result == MA_SUCCESS || result == MA_AT_END) {
if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */
ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1);
}
}
}
framesProcessed += framesJustRead;
/* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */
if (result != MA_SUCCESS) {
break;
}
/* If we didn't read anything, abort so we don't get stuck in a loop. */
if (framesJustRead == 0) {
break;
}
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]));
}
for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]));
}
#else
/* For debugging. Default to a sine wave. */
for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000);
}
for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000);
}
#endif
}
return MA_SUCCESS;
}
MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode)
{
ma_result result;
size_t heapSizeInBytes;
void* pHeap;
result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes);
if (result != MA_SUCCESS) {
return result;
}
if (heapSizeInBytes > 0) {
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
if (pHeap == NULL) {
return MA_OUT_OF_MEMORY;
}
} else {
pHeap = NULL;
}
result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode);
if (result != MA_SUCCESS) {
ma_free(pHeap, pAllocationCallbacks);
return result;
}
((ma_node_base*)pNode)->_ownsHeap = MA_TRUE;
return MA_SUCCESS;
}
MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_node_base* pNodeBase = (ma_node_base*)pNode;
if (pNodeBase == NULL) {
return;
}
/*
The first thing we need to do is fully detach the node. This will detach all inputs and
outputs. We need to do this first because it will sever the connection with the node graph and
allow us to complete uninitialization without needing to worry about thread-safety with the
audio thread. The detachment process will wait for any local processing of the node to finish.
*/
ma_node_detach_full(pNode);
/*
At this point the node should be completely unreferenced by the node graph and we can finish up
the uninitialization process without needing to worry about thread-safety.
*/
if (pNodeBase->_ownsHeap) {
ma_free(pNodeBase->_pHeap, pAllocationCallbacks);
}
}
MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode)
{
if (pNode == NULL) {
return NULL;
}
return ((const ma_node_base*)pNode)->pNodeGraph;
}
MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode)
{
if (pNode == NULL) {
return 0;
}
return ((ma_node_base*)pNode)->inputBusCount;
}
MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode)
{
if (pNode == NULL) {
return 0;
}
return ((ma_node_base*)pNode)->outputBusCount;
}
MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex)
{
const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
if (pNode == NULL) {
return 0;
}
if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) {
return 0; /* Invalid bus index. */
}
return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]);
}
MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex)
{
const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
if (pNode == NULL) {
return 0;
}
if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
return 0; /* Invalid bus index. */
}
return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]);
}
static ma_result ma_node_detach_full(ma_node* pNode)
{
ma_node_base* pNodeBase = (ma_node_base*)pNode;
ma_uint32 iInputBus;
if (pNodeBase == NULL) {
return MA_INVALID_ARGS;
}
/*
Make sure the node is completely detached first. This will not return until the output bus is
guaranteed to no longer be referenced by the audio thread.
*/
ma_node_detach_all_output_buses(pNode);
/*
At this point all output buses will have been detached from the graph and we can be guaranteed
that none of it's input nodes will be getting processed by the graph. We can detach these
without needing to worry about the audio thread touching them.
*/
for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) {
ma_node_input_bus* pInputBus;
ma_node_output_bus* pOutputBus;
pInputBus = &pNodeBase->pInputBuses[iInputBus];
/*
This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those
functions are specifically for the audio thread. We'll instead just manually iterate using standard
linked list logic. We don't need to worry about the audio thread referencing these because the step
above severed the connection to the graph.
*/
for (pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext)) {
ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */
}
}
return MA_SUCCESS;
}
MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex)
{
ma_result result = MA_SUCCESS;
ma_node_base* pNodeBase = (ma_node_base*)pNode;
ma_node_base* pInputNodeBase;
if (pNode == NULL) {
return MA_INVALID_ARGS;
}
if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
return MA_INVALID_ARGS; /* Invalid output bus index. */
}
/* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */
ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]);
{
pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode;
if (pInputNodeBase != NULL) {
ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]);
}
}
ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]);
return result;
}
MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode)
{
ma_uint32 iOutputBus;
if (pNode == NULL) {
return MA_INVALID_ARGS;
}
for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) {
ma_node_detach_output_bus(pNode, iOutputBus);
}
return MA_SUCCESS;
}
MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex)
{
ma_node_base* pNodeBase = (ma_node_base*)pNode;
ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode;
if (pNodeBase == NULL || pOtherNodeBase == NULL) {
return MA_INVALID_ARGS;
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_uint32 frameCountIn;
ma_uint32 frameCountOut;
const float* pRunningFramesIn;
float* pRunningFramesOut;
/*
The first thing we need to do is read into the temporary buffer. We can calculate exactly
how many input frames we'll need after resampling.
*/
framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining);
if (framesToRead > tempCapInFrames) {
framesToRead = tempCapInFrames;
}
result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead);
/* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */
if (result == MA_AT_END) {
c89atomic_exchange_32(&pSound->atEnd, MA_TRUE); /* This will be set to false in ma_sound_start(). */
}
pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound)));
frameCountIn = (ma_uint32)framesJustRead;
frameCountOut = framesRemaining;
/* Convert if necessary. */
if (dataSourceFormat == ma_format_f32) {
/* Fast path. No data conversion necessary. */
pRunningFramesIn = (float*)temp;
ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
} else {
/* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */
float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */
ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none);
/* Now that we have our samples in f32 format we can process like normal. */
pRunningFramesIn = tempf32;
ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
}
/* We should have processed all of our input frames since we calculated the required number of input frames at the top. */
MA_ASSERT(frameCountIn == framesJustRead);
totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */
if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {
break; /* Might have reached the end. */
}
}
}
*pFrameCountOut = totalFramesRead;
}
static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
{
/*
Make sure the pitch is updated before trying to read anything. It's important that this is done
only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that
ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(),
and if another thread modifies the pitch just after that call it can result in a glitch due to
the input rate changing.
*/
ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
/* For groups, the input data has already been read and we just need to apply the effect. */
ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
}
static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount)
{
ma_uint64 inputFrameCount;
MA_ASSERT(pInputFrameCount != NULL);
/* Our pitch will affect this calculation. We need to update it. */
ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount);
if (inputFrameCount > 0xFFFFFFFF) {
inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */
}
*pInputFrameCount = (ma_uint32)inputFrameCount;
return MA_SUCCESS;
}
static ma_node_vtable g_ma_engine_node_vtable__sound =
{
ma_engine_node_process_pcm_frames__sound,
NULL, /* onGetRequiredInputFrameCount */
0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */
1, /* Sounds have one output bus. */
0 /* Default flags. */
};
static ma_node_vtable g_ma_engine_node_vtable__group =
{
ma_engine_node_process_pcm_frames__group,
ma_engine_node_get_required_input_frame_count__group,
1, /* Groups have one input bus. */
1, /* Groups have one output bus. */
MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */
};
static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig)
{
ma_node_config baseNodeConfig;
if (pConfig->type == ma_engine_node_type_sound) {
/* Sound. */
baseNodeConfig = ma_node_config_init();
baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound;
baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */
} else {
/* Group. */
baseNodeConfig = ma_node_config_init();
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* Free the heap last. */
if (pEngineNode->_ownsHeap) {
ma_free(pEngineNode->_pHeap, pAllocationCallbacks);
}
}
MA_API ma_sound_config ma_sound_config_init(void)
{
ma_sound_config config;
MA_ZERO_OBJECT(&config);
config.rangeEndInPCMFrames = ~((ma_uint64)0);
config.loopPointEndInPCMFrames = ~((ma_uint64)0);
return config;
}
MA_API ma_sound_group_config ma_sound_group_config_init(void)
{
ma_sound_group_config config;
MA_ZERO_OBJECT(&config);
return config;
}
MA_API ma_engine_config ma_engine_config_init(void)
{
ma_engine_config config;
MA_ZERO_OBJECT(&config);
config.listenerCount = 1; /* Always want at least one listener. */
config.monoExpansionMode = ma_mono_expansion_mode_default;
return config;
}
#if !defined(MA_NO_DEVICE_IO)
static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
{
ma_engine* pEngine = (ma_engine*)pDevice->pUserData;
(void)pFramesIn;
/*
Experiment: Try processing a resource manager job if we're on the Emscripten build.
This serves two purposes:
1) It ensures jobs are actually processed at some point since we cannot guarantee that the
caller is doing the right thing and calling ma_resource_manager_process_next_job(); and
2) It's an attempt at working around an issue where processing jobs on the Emscripten main
loop doesn't work as well as it should. When trying to load sounds without the `DECODE`
flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time
before the callback is processed. I think it's got something to do with the single-
threaded nature of Web, but I'm not entirely sure.
*/
#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN)
{
if (pEngine->pResourceManager != NULL) {
if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
ma_resource_manager_process_next_job(pEngine->pResourceManager);
}
}
}
#endif
ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL);
}
#endif
MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine)
{
ma_result result;
ma_node_graph_config nodeGraphConfig;
ma_engine_config engineConfig;
ma_spatializer_listener_config listenerConfig;
ma_uint32 iListener;
if (pEngine == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pEngine);
/* The config is allowed to be NULL in which case we use defaults for everything. */
if (pConfig != NULL) {
engineConfig = *pConfig;
} else {
engineConfig = ma_engine_config_init();
}
pEngine->monoExpansionMode = engineConfig.monoExpansionMode;
ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks);
#if !defined(MA_NO_RESOURCE_MANAGER)
{
pEngine->pResourceManager = engineConfig.pResourceManager;
}
#endif
#if !defined(MA_NO_DEVICE_IO)
{
pEngine->pDevice = engineConfig.pDevice;
/* If we don't have a device, we need one. */
if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) {
ma_device_config deviceConfig;
pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks);
if (pEngine->pDevice == NULL) {
return MA_OUT_OF_MEMORY;
}
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/*
If we're using a device, use the device's channel map for the listener. Otherwise just use
miniaudio's default channel map.
*/
#if !defined(MA_NO_DEVICE_IO)
{
if (pEngine->pDevice != NULL) {
/*
Temporarily disabled. There is a subtle bug here where front-left and front-right
will be used by the device's channel map, but this is not what we want to use for
spatialization. Instead we want to use side-left and side-right. I need to figure
out a better solution for this. For now, disabling the user of device channel maps.
*/
/*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/
}
}
#endif
result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */
if (result != MA_SUCCESS) {
goto on_error_2;
}
pEngine->listenerCount += 1;
}
/* Gain smoothing for spatialized sounds. */
pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames;
if (pEngine->gainSmoothTimeInFrames == 0) {
ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds;
if (gainSmoothTimeInMilliseconds == 0) {
gainSmoothTimeInMilliseconds = 8;
}
pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */
}
/* We need a resource manager. */
#ifndef MA_NO_RESOURCE_MANAGER
{
if (pEngine->pResourceManager == NULL) {
ma_resource_manager_config resourceManagerConfig;
pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks);
if (pEngine->pResourceManager == NULL) {
result = MA_OUT_OF_MEMORY;
goto on_error_2;
}
resourceManagerConfig = ma_resource_manager_config_init();
resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */
resourceManagerConfig.decodedFormat = ma_format_f32;
resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */
resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine);
ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks);
resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS;
/* The Emscripten build cannot use threads. */
#if defined(MA_EMSCRIPTEN)
{
resourceManagerConfig.jobThreadCount = 0;
resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
}
#endif
result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager);
if (result != MA_SUCCESS) {
goto on_error_3;
}
pEngine->ownsResourceManager = MA_TRUE;
}
}
#endif
/* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */
pEngine->inlinedSoundLock = 0;
pEngine->pInlinedSoundHead = NULL;
/* Start the engine if required. This should always be the last step. */
#if !defined(MA_NO_DEVICE_IO)
{
if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) {
result = ma_engine_start(pEngine);
if (result != MA_SUCCESS) {
goto on_error_4; /* Failed to start the engine. */
}
}
}
#endif
return MA_SUCCESS;
#if !defined(MA_NO_DEVICE_IO)
on_error_4:
#endif
#if !defined(MA_NO_RESOURCE_MANAGER)
on_error_3:
if (pEngine->ownsResourceManager) {
ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
}
#endif /* MA_NO_RESOURCE_MANAGER */
on_error_2:
for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
}
ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);
on_error_1:
#if !defined(MA_NO_DEVICE_IO)
{
if (pEngine->ownsDevice) {
ma_device_uninit(pEngine->pDevice);
ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
}
}
#endif
return result;
}
MA_API void ma_engine_uninit(ma_engine* pEngine)
{
ma_uint32 iListener;
if (pEngine == NULL) {
return;
}
/* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */
#if !defined(MA_NO_DEVICE_IO)
{
if (pEngine->ownsDevice) {
ma_device_uninit(pEngine->pDevice);
ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
} else {
if (pEngine->pDevice != NULL) {
ma_device_stop(pEngine->pDevice);
}
}
}
#endif
/*
All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case
I want to do some kind of garbage collection later on.
*/
ma_spinlock_lock(&pEngine->inlinedSoundLock);
{
for (;;) {
ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead;
if (pSoundToDelete == NULL) {
break; /* Done. */
}
pEngine->pInlinedSoundHead = pSoundToDelete->pNext;
ma_sound_uninit(&pSoundToDelete->sound);
ma_free(pSoundToDelete, &pEngine->allocationCallbacks);
}
}
ma_spinlock_unlock(&pEngine->inlinedSoundLock);
for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
}
/* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */
ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);
/* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */
#ifndef MA_NO_RESOURCE_MANAGER
if (pEngine->ownsResourceManager) {
ma_resource_manager_uninit(pEngine->pResourceManager);
ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
}
#endif
}
MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
return ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, pFramesRead);
}
MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine)
{
if (pEngine == NULL) {
return NULL;
}
return &pEngine->nodeGraph;
}
#if !defined(MA_NO_RESOURCE_MANAGER)
MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine)
{
if (pEngine == NULL) {
return NULL;
}
#if !defined(MA_NO_RESOURCE_MANAGER)
{
return pEngine->pResourceManager;
}
#else
{
return NULL;
}
#endif
}
#endif
MA_API ma_device* ma_engine_get_device(ma_engine* pEngine)
{
if (pEngine == NULL) {
return NULL;
}
#if !defined(MA_NO_DEVICE_IO)
{
return pEngine->pDevice;
}
#else
{
return NULL;
}
#endif
}
MA_API ma_log* ma_engine_get_log(ma_engine* pEngine)
{
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
if (result != MA_SUCCESS) {
ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
MA_ZERO_OBJECT(pSound);
return result;
}
return MA_SUCCESS;
}
#endif
MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound)
{
ma_sound_config config = ma_sound_config_init();
config.pDataSource = pDataSource;
config.flags = flags;
config.pInitialAttachment = pGroup;
return ma_sound_init_ex(pEngine, &config, pSound);
}
MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
{
ma_result result;
result = ma_sound_preinit(pEngine, pSound);
if (result != MA_SUCCESS) {
return result;
}
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
/* We need to load the sound differently depending on whether or not we're loading from a file. */
#ifndef MA_NO_RESOURCE_MANAGER
if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) {
return ma_sound_init_from_file_internal(pEngine, pConfig, pSound);
} else
#endif
{
/*
Getting here means we're not loading from a file. We may be loading from an already-initialized
data source, or none at all. If we aren't specifying any data source, we'll be initializing the
the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this
for us, so no special treatment required here.
*/
return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound);
}
}
MA_API void ma_sound_uninit(ma_sound* pSound)
{
if (pSound == NULL) {
return;
}
/*
Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done
so which makes thread safety beyond this point trivial.
*/
ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks);
/* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */
#ifndef MA_NO_RESOURCE_MANAGER
if (pSound->ownsDataSource) {
ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks);
pSound->pDataSource = NULL;
}
#else
MA_ASSERT(pSound->ownsDataSource == MA_FALSE);
#endif
}
MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound)
{
if (pSound == NULL) {
return NULL;
}
return pSound->engineNode.pEngine;
}
MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound)
{
if (pSound == NULL) {
return NULL;
}
return pSound->pDataSource;
}
MA_API ma_result ma_sound_start(ma_sound* pSound)
{
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
/* If the sound is already playing, do nothing. */
if (ma_sound_is_playing(pSound)) {
return MA_SUCCESS;
}
/* If the sound is at the end it means we want to start from the start again. */
if (ma_sound_at_end(pSound)) {
ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0);
if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) {
return result; /* Failed to seek back to the start. */
}
/* Make sure we clear the end indicator. */
c89atomic_exchange_32(&pSound->atEnd, MA_FALSE);
}
/* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */
ma_node_set_state(pSound, ma_node_state_started);
return MA_SUCCESS;
}
MA_API ma_result ma_sound_stop(ma_sound* pSound)
{
if (pSound == NULL) {
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
return 0;
}
return ma_node_get_time(pSound);
}
MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping)
{
if (pSound == NULL) {
return;
}
/* Looping is only a valid concept if the sound is backed by a data source. */
if (pSound->pDataSource == NULL) {
return;
}
/* The looping state needs to be applied to the data source in order for any looping to actually happen. */
ma_data_source_set_looping(pSound->pDataSource, isLooping);
}
MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound)
{
if (pSound == NULL) {
return MA_FALSE;
}
/* There is no notion of looping for sounds that are not backed by a data source. */
if (pSound->pDataSource == NULL) {
return MA_FALSE;
}
return ma_data_source_is_looping(pSound->pDataSource);
}
MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound)
{
if (pSound == NULL) {
return MA_FALSE;
}
/* There is no notion of an end of a sound if it's not backed by a data source. */
if (pSound->pDataSource == NULL) {
return MA_FALSE;
}
return c89atomic_load_32(&pSound->atEnd);
}
MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex)
{
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
/* Seeking is only valid for sounds that are backed by a data source. */
if (pSound->pDataSource == NULL) {
return MA_INVALID_OPERATION;
}
/* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */
c89atomic_exchange_64(&pSound->seekTarget, frameIndex);
return MA_SUCCESS;
}
MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
/* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */
if (pSound->pDataSource == NULL) {
ma_uint32 channels;
if (pFormat != NULL) {
*pFormat = ma_format_f32;
}
channels = ma_node_get_input_channels(&pSound->engineNode, 0);
if (pChannels != NULL) {
*pChannels = channels;
}
if (pSampleRate != NULL) {
*pSampleRate = pSound->engineNode.resampler.config.sampleRateIn;
}
if (pChannelMap != NULL) {
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels);
}
return MA_SUCCESS;
} else {
return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
}
}
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor)
{
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
/* The notion of a cursor is only valid for sounds that are backed by a data source. */
if (pSound->pDataSource == NULL) {
return MA_INVALID_OPERATION;
}
return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor);
}
MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength)
{
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
/* The notion of a sound length is only valid for sounds that are backed by a data source. */
if (pSound->pDataSource == NULL) {
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
#define DRFLAC_BAD_ADDRESS -24
#define DRFLAC_BAD_SEEK -25
#define DRFLAC_BAD_PIPE -26
#define DRFLAC_DEADLOCK -27
#define DRFLAC_TOO_MANY_LINKS -28
#define DRFLAC_NOT_IMPLEMENTED -29
#define DRFLAC_NO_MESSAGE -30
#define DRFLAC_BAD_MESSAGE -31
#define DRFLAC_NO_DATA_AVAILABLE -32
#define DRFLAC_INVALID_DATA -33
#define DRFLAC_TIMEOUT -34
#define DRFLAC_NO_NETWORK -35
#define DRFLAC_NOT_UNIQUE -36
#define DRFLAC_NOT_SOCKET -37
#define DRFLAC_NO_ADDRESS -38
#define DRFLAC_BAD_PROTOCOL -39
#define DRFLAC_PROTOCOL_UNAVAILABLE -40
#define DRFLAC_PROTOCOL_NOT_SUPPORTED -41
#define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42
#define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43
#define DRFLAC_SOCKET_NOT_SUPPORTED -44
#define DRFLAC_CONNECTION_RESET -45
#define DRFLAC_ALREADY_CONNECTED -46
#define DRFLAC_NOT_CONNECTED -47
#define DRFLAC_CONNECTION_REFUSED -48
#define DRFLAC_NO_HOST -49
#define DRFLAC_IN_PROGRESS -50
#define DRFLAC_CANCELLED -51
#define DRFLAC_MEMORY_ALREADY_MAPPED -52
#define DRFLAC_AT_END -53
#define DRFLAC_CRC_MISMATCH -128
#define DRFLAC_SUBFRAME_CONSTANT 0
#define DRFLAC_SUBFRAME_VERBATIM 1
#define DRFLAC_SUBFRAME_FIXED 8
#define DRFLAC_SUBFRAME_LPC 32
#define DRFLAC_SUBFRAME_RESERVED 255
#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision)
{
if (pMajor) {
*pMajor = DRFLAC_VERSION_MAJOR;
}
if (pMinor) {
*pMinor = DRFLAC_VERSION_MINOR;
}
if (pRevision) {
*pRevision = DRFLAC_VERSION_REVISION;
}
}
DRFLAC_API const char* drflac_version_string(void)
{
return DRFLAC_VERSION_STRING;
}
#if defined(__has_feature)
#if __has_feature(thread_sanitizer)
#define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread")))
#else
#define DRFLAC_NO_THREAD_SANITIZE
#endif
#else
#define DRFLAC_NO_THREAD_SANITIZE
#endif
#if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE;
#endif
#ifndef DRFLAC_NO_CPUID
static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE;
static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE;
DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void)
{
static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE;
if (!isCPUCapsInitialized) {
#if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
int info[4] = {0};
drflac__cpuid(info, 0x80000001);
drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
#endif
drflac__gIsSSE2Supported = drflac_has_sse2();
drflac__gIsSSE41Supported = drflac_has_sse41();
isCPUCapsInitialized = DRFLAC_TRUE;
}
}
#else
static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE;
static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void)
{
#if defined(DRFLAC_SUPPORT_NEON)
#if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON)
#if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
return DRFLAC_TRUE;
#else
return DRFLAC_FALSE;
#endif
#else
return DRFLAC_FALSE;
#endif
#else
return DRFLAC_FALSE;
#endif
}
DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void)
{
drflac__gIsNEONSupported = drflac__has_neon();
#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
drflac__gIsLZCNTSupported = DRFLAC_TRUE;
#endif
}
#endif
static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void)
{
#if defined(DRFLAC_X86) || defined(DRFLAC_X64)
return DRFLAC_TRUE;
#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
return DRFLAC_TRUE;
#else
int n = 1;