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_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels);
// You must unmap the buffer.
ma_audio_buffer_unmap(pAudioBuffer, frameCount);
}
```
When you use memory mapping, the read cursor is increment by the frame count passed in to
`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller
than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is
that it does not handle looping 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.
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
#define MA_MAX_FILTER_ORDER 8
#endif
typedef enum
{
ma_stream_format_pcm = 0
} ma_stream_format;
typedef enum
{
ma_stream_layout_interleaved = 0,
ma_stream_layout_deinterleaved
} ma_stream_layout;
typedef enum
{
ma_dither_mode_none = 0,
ma_dither_mode_rectangle,
ma_dither_mode_triangle
} ma_dither_mode;
typedef enum
{
/*
I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().
*/
ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */
ma_format_u8 = 1,
ma_format_s16 = 2, /* Seems to be the most widely supported format. */
ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */
ma_format_s32 = 4,
ma_format_f32 = 5,
ma_format_count
} ma_format;
typedef enum
{
/* Standard rates need to be in priority order. */
ma_standard_sample_rate_48000 = 48000, /* Most common */
ma_standard_sample_rate_44100 = 44100,
ma_standard_sample_rate_32000 = 32000, /* Lows */
ma_standard_sample_rate_24000 = 24000,
ma_standard_sample_rate_22050 = 22050,
ma_standard_sample_rate_88200 = 88200, /* Highs */
ma_standard_sample_rate_96000 = 96000,
ma_standard_sample_rate_176400 = 176400,
ma_standard_sample_rate_192000 = 192000,
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
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z);
MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener);
MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z);
MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener);
MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z);
MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener);
MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound);
MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener);
MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z);
MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener);
MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled);
MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener);
typedef struct
{
ma_uint32 channelsIn;
ma_uint32 channelsOut;
ma_channel* pChannelMapIn;
ma_attenuation_model attenuationModel;
ma_positioning positioning;
ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */
float minGain;
float maxGain;
float minDistance;
float maxDistance;
float rolloff;
float coneInnerAngleInRadians;
float coneOuterAngleInRadians;
float coneOuterGain;
float dopplerFactor; /* Set to 0 to disable doppler effect. */
float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */
ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
} ma_spatializer_config;
MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut);
typedef struct
{
ma_uint32 channelsIn;
ma_uint32 channelsOut;
ma_channel* pChannelMapIn;
ma_attenuation_model attenuationModel;
ma_positioning positioning;
ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */
float minGain;
float maxGain;
float minDistance;
float maxDistance;
float rolloff;
float coneInnerAngleInRadians;
float coneOuterAngleInRadians;
float coneOuterGain;
float dopplerFactor; /* Set to 0 to disable doppler effect. */
float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */
ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
ma_vec3f position;
ma_vec3f direction;
ma_vec3f velocity; /* For doppler effect. */
float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */
ma_gainer gainer; /* For smooth gain transitions. */
float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */
/* Memory management. */
void* _pHeap;
ma_bool32 _ownsHeap;
} ma_spatializer;
MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes);
MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer);
MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer);
MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer);
MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel);
MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning);
MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff);
MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain);
MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain);
MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance);
MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance);
MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor);
MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor);
MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z);
MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z);
MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z);
MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer);
MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir);
/************************************************************************************************************************************************************
*************************************************************************************************************************************************************
DATA CONVERSION
===============
This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.
*************************************************************************************************************************************************************
************************************************************************************************************************************************************/
/**************************************************************************************************************************************************************
Resampling
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_uint32 sampleRateIn;
ma_uint32 sampleRateOut;
ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */
ma_resampling_backend_vtable* pBackendVTable;
void* pBackendUserData;
struct
{
ma_uint32 lpfOrder;
} linear;
};
MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm);
typedef struct
{
ma_resampling_backend* pBackend;
ma_resampling_backend_vtable* pBackendVTable;
void* pBackendUserData;
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRateIn;
ma_uint32 sampleRateOut;
union
{
ma_linear_resampler linear;
} state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */
/* Memory management. */
void* _pHeap;
ma_bool32 _ownsHeap;
} ma_resampler;
MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);
MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler);
/*
Initializes a new resampler object from a config.
*/
MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler);
/*
Uninitializes a resampler.
*/
MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks);
/*
Converts the given input data.
Both the input and output frames must be in the format specified in the config when the resampler was initilized.
On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that
were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use
ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames.
On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole
input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames
you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead.
If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of
output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input
frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be
processed. In this case, any internal filter state will be updated as if zeroes were passed in.
It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.
It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
*/
MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
/*
Sets the input and output sample sample rate.
*/
MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
/*
Sets the input and output sample rate as a ratio.
The ration is in/out.
*/
MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio);
/*
Retrieves the latency introduced by the resampler in input frames.
*/
MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler);
/*
Retrieves the latency introduced by the resampler in output frames.
*/
MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler);
/*
Calculates the number of whole input frames that would need to be read from the client in order to output the specified
number of output frames.
The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
read from the input buffer in order to output the specified number of output frames.
*/
MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
/*
Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
input frames.
*/
MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
/*
Resets the resampler's timer and clears it's internal cache.
*/
MA_API ma_result ma_resampler_reset(ma_resampler* pResampler);
/**************************************************************************************************************************************************************
Channel Conversion
**************************************************************************************************************************************************************/
typedef enum
{
ma_channel_conversion_path_unknown,
ma_channel_conversion_path_passthrough,
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/*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+ |
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
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. */
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;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* Exclusive mode is not allowed with loopback. */
if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) {
return MA_INVALID_DEVICE_CONFIG;
}
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
ma_device_init_internal_data__wasapi data;
data.formatIn = pDescriptorCapture->format;
data.channelsIn = pDescriptorCapture->channels;
data.sampleRateIn = pDescriptorCapture->sampleRate;
MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
data.periodsIn = pDescriptorCapture->periodCount;
data.shareMode = pDescriptorCapture->shareMode;
data.performanceProfile = pConfig->performanceProfile;
data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data);
if (result != MA_SUCCESS) {
return result;
}
pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
pDevice->wasapi.pCaptureClient = data.pCaptureClient;
pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount;
pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
/*
The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
however, because we want to block until we actually have something for the first call to ma_device_read().
*/
pDevice->wasapi.hEventCapture = CreateEventW(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
if (pDevice->wasapi.hEventCapture == NULL) {
result = ma_result_from_GetLastError(GetLastError());
if (pDevice->wasapi.pCaptureClient != NULL) {
ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
pDevice->wasapi.pCaptureClient = NULL;
}
if (pDevice->wasapi.pAudioClientCapture != NULL) {
ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
pDevice->wasapi.pAudioClientCapture = NULL;
}
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.");
return result;
}
ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
/* We must always have a valid ID. */
ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
/* The descriptor needs to be updated with actual values. */
pDescriptorCapture->format = data.formatOut;
pDescriptorCapture->channels = data.channelsOut;
pDescriptorCapture->sampleRate = data.sampleRateOut;
MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
pDescriptorCapture->periodCount = data.periodsOut;
}
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
ma_device_init_internal_data__wasapi data;
data.formatIn = pDescriptorPlayback->format;
data.channelsIn = pDescriptorPlayback->channels;
data.sampleRateIn = pDescriptorPlayback->sampleRate;
MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
data.periodsIn = pDescriptorPlayback->periodCount;
data.shareMode = pDescriptorPlayback->shareMode;
data.performanceProfile = pConfig->performanceProfile;
data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data);
if (result != MA_SUCCESS) {
if (pConfig->deviceType == ma_device_type_duplex) {
if (pDevice->wasapi.pCaptureClient != NULL) {
ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
pDevice->wasapi.pCaptureClient = NULL;
}
if (pDevice->wasapi.pAudioClientCapture != NULL) {
ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
pDevice->wasapi.pAudioClientCapture = NULL;
}
CloseHandle(pDevice->wasapi.hEventCapture);
pDevice->wasapi.hEventCapture = NULL;
}
return result;
}
pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
pDevice->wasapi.pRenderClient = data.pRenderClient;
pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount;
pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
/*
The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
only after the whole available space has been filled, never before.
The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
to get passed WaitForMultipleObjects().
*/
pDevice->wasapi.hEventPlayback = CreateEventW(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
if (pDevice->wasapi.hEventPlayback == NULL) {
result = ma_result_from_GetLastError(GetLastError());
if (pConfig->deviceType == ma_device_type_duplex) {
if (pDevice->wasapi.pCaptureClient != NULL) {
ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
pDevice->wasapi.pCaptureClient = NULL;
}
if (pDevice->wasapi.pAudioClientCapture != NULL) {
ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
pDevice->wasapi.pAudioClientCapture = NULL;
}
CloseHandle(pDevice->wasapi.hEventCapture);
pDevice->wasapi.hEventCapture = NULL;
}
if (pDevice->wasapi.pRenderClient != NULL) {
ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
pDevice->wasapi.pRenderClient = NULL;
}
if (pDevice->wasapi.pAudioClientPlayback != NULL) {
ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
pDevice->wasapi.pAudioClientPlayback = NULL;
}
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.");
return result;
}
ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
/* We must always have a valid ID because rerouting will look at it. */
ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
/* The descriptor needs to be updated with actual values. */
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;
}
/*
We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When
we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just
stop the device outright and let the application handle it.
*/
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID == NULL) {
pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;
}
if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
}
}
hr = ma_CoCreateInstance(pDevice->pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
if (FAILED(hr)) {
ma_device_uninit__wasapi(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
return ma_result_from_HRESULT(hr);
}
pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
pDevice->wasapi.notificationClient.counter = 1;
pDevice->wasapi.notificationClient.pDevice = pDevice;
hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
if (SUCCEEDED(hr)) {
pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
} else {
/* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
}
#endif
c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
return MA_SUCCESS;
}
static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
{
ma_uint32 paddingFramesCount;
HRESULT hr;
ma_share_mode shareMode;
MA_ASSERT(pDevice != NULL);
MA_ASSERT(pFrameCount != NULL);
*pFrameCount = 0;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
#if 0 /* Introduced in a later version of macOS. */
case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
#endif
default: return MA_CHANNEL_NONE;
}
}
static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap)
{
MA_ASSERT(pChannelLayout != NULL);
if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
UInt32 iChannel;
for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) {
pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
}
} else
#if 0
if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
/* This is the same kind of system that's used by Windows audio APIs. */
UInt32 iChannel = 0;
UInt32 iBit;
AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) {
AudioChannelBitmap bit = bitmap & (1 << iBit);
if (bit != 0) {
pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
}
}
} else
#endif
{
/*
Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should
be updated to determine the mapping based on the tag.
*/
UInt32 channelCount;
/* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */
if (channelMapCap > 0xFFFFFFFF) {
channelMapCap = 0xFFFFFFFF;
}
channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap);
switch (pChannelLayout->mChannelLayoutTag)
{
case kAudioChannelLayoutTag_Mono:
case kAudioChannelLayoutTag_Stereo:
case kAudioChannelLayoutTag_StereoHeadphones:
case kAudioChannelLayoutTag_MatrixStereo:
case kAudioChannelLayoutTag_MidSide:
case kAudioChannelLayoutTag_XY:
case kAudioChannelLayoutTag_Binaural:
case kAudioChannelLayoutTag_Ambisonic_B_Format:
{
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
} break;
case kAudioChannelLayoutTag_Octagonal:
{
pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
} /* Intentional fallthrough. */
case kAudioChannelLayoutTag_Hexagonal:
{
pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
} /* Intentional fallthrough. */
case kAudioChannelLayoutTag_Pentagonal:
{
pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
} /* Intentional fallghrough. */
case kAudioChannelLayoutTag_Quadraphonic:
{
pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
pChannelMap[1] = MA_CHANNEL_RIGHT;
pChannelMap[0] = MA_CHANNEL_LEFT;
} break;
/* TODO: Add support for more tags here. */
default:
{
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
} break;
}
}
return MA_SUCCESS;
}
static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
{
AudioObjectPropertyAddress propAddressDevices;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
break;
}
}
}
ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
return MA_SUCCESS;
}
#endif
#if defined(MA_APPLE_MOBILE)
@interface ma_ios_notification_handler:NSObject {
ma_device* m_pDevice;
}
@end
@implementation ma_ios_notification_handler
-(id)init:(ma_device*)pDevice
{
self = [super init];
m_pDevice = pDevice;
/* For route changes. */
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
/* For interruptions. */
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
return self;
}
-(void)dealloc
{
[self remove_handler];
#if defined(__has_feature)
#if !__has_feature(objc_arc)
[super dealloc];
#endif
#endif
}
-(void)remove_handler
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
}
-(void)handle_interruption:(NSNotification*)pNotification
{
NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue];
switch (type)
{
case AVAudioSessionInterruptionTypeBegan:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n");
/*
Core Audio will have stopped the internal device automatically, but we need explicitly
stop it at a higher level to ensure miniaudio-specific state is updated for consistency.
*/
ma_device_stop(m_pDevice);
/*
Fire the notification after the device has been stopped to ensure it's in the correct
state when the notification handler is invoked.
*/
ma_device__on_notification_interruption_began(m_pDevice);
} break;
case AVAudioSessionInterruptionTypeEnded:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n");
ma_device__on_notification_interruption_ended(m_pDevice);
} break;
}
}
-(void)handle_route_change:(NSNotification*)pNotification
{
AVAudioSession* pSession = [AVAudioSession sharedInstance];
NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (reason)
{
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
} break;
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
} break;
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
} break;
case AVAudioSessionRouteChangeReasonWakeFromSleep:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
} break;
case AVAudioSessionRouteChangeReasonOverride:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
} break;
case AVAudioSessionRouteChangeReasonCategoryChange:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
} break;
case AVAudioSessionRouteChangeReasonUnknown:
default:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
} break;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (result != MA_SUCCESS) {
return result;
}
result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
if (result != MA_SUCCESS) {
return result;
}
*pLength = lengthInPCMFrames / (float)sampleRate;
return MA_SUCCESS;
}
MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
{
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
if (pDataSource == NULL) {
return MA_INVALID_ARGS;
}
c89atomic_exchange_32(&pDataSourceBase->isLooping, isLooping);
/* If there's no callback for this just treat it as a successful no-op. */
if (pDataSourceBase->vtable->onSetLooping == NULL) {
return MA_SUCCESS;
}
return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping);
}
MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource)
{
const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
if (pDataSource == NULL) {
return MA_FALSE;
}
return c89atomic_load_32(&pDataSourceBase->isLooping);
}
MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames)
{
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
ma_result result;
ma_uint64 cursor;
ma_uint64 loopBegAbsolute;
ma_uint64 loopEndAbsolute;
if (pDataSource == NULL) {
return MA_INVALID_ARGS;
}
if (rangeEndInFrames < rangeBegInFrames) {
return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */
}
/*
The loop points need to be updated. We'll be storing the loop points relative to the range. We'll update
these so that they maintain their absolute positioning. The loop points will then be clamped to the range.
*/
loopBegAbsolute = pDataSourceBase->loopBegInFrames + pDataSourceBase->rangeBegInFrames;
loopEndAbsolute = pDataSourceBase->loopEndInFrames + ((pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) ? pDataSourceBase->rangeBegInFrames : 0);
pDataSourceBase->rangeBegInFrames = rangeBegInFrames;
pDataSourceBase->rangeEndInFrames = rangeEndInFrames;
/* Make the loop points relative again, and make sure they're clamped to within the range. */
if (loopBegAbsolute > pDataSourceBase->rangeBegInFrames) {
pDataSourceBase->loopBegInFrames = loopBegAbsolute - pDataSourceBase->rangeBegInFrames;
} else {
pDataSourceBase->loopBegInFrames = 0;
}
if (pDataSourceBase->loopBegInFrames > pDataSourceBase->rangeEndInFrames) {
pDataSourceBase->loopBegInFrames = pDataSourceBase->rangeEndInFrames;
}
/* Only need to update the loop end point if it's not -1. */
if (loopEndAbsolute != ~((ma_uint64)0)) {
if (loopEndAbsolute > pDataSourceBase->rangeBegInFrames) {
pDataSourceBase->loopEndInFrames = loopEndAbsolute - pDataSourceBase->rangeBegInFrames;
} else {
pDataSourceBase->loopEndInFrames = 0;
}
if (pDataSourceBase->loopEndInFrames > pDataSourceBase->rangeEndInFrames && pDataSourceBase->loopEndInFrames) {
pDataSourceBase->loopEndInFrames = pDataSourceBase->rangeEndInFrames;
}
}
/* If the new range is past the current cursor position we need to seek to it. */
result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor);
if (result == MA_SUCCESS) {
/* Seek to within range. Note that our seek positions here are relative to the new range. */
if (cursor < rangeBegInFrames) {
ma_data_source_seek_to_pcm_frame(pDataSource, 0);
} else if (cursor > rangeEndInFrames) {
ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames);
}
} else {
/* We failed to get the cursor position. Probably means the data source has no notion of a cursor such a noise data source. Just pretend the seeking worked. */
}
return MA_SUCCESS;
}
MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)
{
const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
if (pDataSource == NULL) {
return;
}
if (pRangeBegInFrames != NULL) {
*pRangeBegInFrames = pDataSourceBase->rangeBegInFrames;
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
{
return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder);
}
MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder)
{
if (pDecoder == NULL) {
return MA_INVALID_ARGS;
}
if (pDecoder->pBackend != NULL) {
if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks);
}
}
if (pDecoder->onRead == ma_decoder__on_read_vfs) {
ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file);
pDecoder->data.vfs.file = NULL;
}
ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
ma_data_source_uninit(&pDecoder->ds);
if (pDecoder->pInputCache != NULL) {
ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks);
}
return MA_SUCCESS;
}
MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
ma_result result = MA_SUCCESS;
ma_uint64 totalFramesReadOut;
void* pRunningFramesOut;
if (pFramesRead != NULL) {
*pFramesRead = 0; /* Safety. */
}
if (frameCount == 0) {
return MA_INVALID_ARGS;
}
if (pDecoder == NULL) {
return MA_INVALID_ARGS;
}
if (pDecoder->pBackend == NULL) {
return MA_INVALID_OPERATION;
}
/* Fast path. */
if (pDecoder->converter.isPassthrough) {
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut);
} else {
/*
Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we
need to run through each sample because we need to ensure it's internal cache is updated.
*/
if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut);
} else {
/* Slow path. Need to run everything through the data converter. */
ma_format internalFormat;
ma_uint32 internalChannels;
totalFramesReadOut = 0;
pRunningFramesOut = pFramesOut;
result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0);
if (result != MA_SUCCESS) {
return result; /* Failed to retrieve the internal format and channel count. */
}
/*
We run a different path depending on whether or not we are using a heap-allocated
intermediary buffer or not. If the data converter does not support the calculation of
the required number of input frames, we'll use the heap-allocated path. Otherwise we'll
use the stack-allocated path.
*/
if (pDecoder->pInputCache != NULL) {
/* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */
while (totalFramesReadOut < frameCount) {
ma_uint64 framesToReadThisIterationIn;
ma_uint64 framesToReadThisIterationOut;
/* If there's any data available in the cache, that needs to get processed first. */
if (pDecoder->inputCacheRemaining > 0) {
framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
framesToReadThisIterationIn = framesToReadThisIterationOut;
if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) {
framesToReadThisIterationIn = pDecoder->inputCacheRemaining;
}
result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesO...
if (result != MA_SUCCESS) {
break;
}
pDecoder->inputCacheConsumed += framesToReadThisIterationIn;
pDecoder->inputCacheRemaining -= framesToReadThisIterationIn;
totalFramesReadOut += framesToReadThisIterationOut;
if (pRunningFramesOut != NULL) {
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
}
if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {
break; /* We're done. */
}
}
/* Getting here means there's no data in the cache and we need to fill it up from the data source. */
if (pDecoder->inputCacheRemaining == 0) {
pDecoder->inputCacheConsumed = 0;
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* Node has one child - pChildLo != NULL. */
pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent;
if (pDataBufferNode->pParent == NULL) {
MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo;
} else {
if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo;
} else {
pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo;
}
}
} else {
/* Complex case - deleting a node with two children. */
ma_resource_manager_data_buffer_node* pReplacementDataBufferNode;
/* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */
pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode);
MA_ASSERT(pReplacementDataBufferNode != NULL);
/*
Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement
node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The
replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the
replacement node and reinserting it into the same position as the deleted node.
*/
MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */
MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */
if (pReplacementDataBufferNode->pChildHi == NULL) {
if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
pReplacementDataBufferNode->pParent->pChildLo = NULL;
} else {
pReplacementDataBufferNode->pParent->pChildHi = NULL;
}
} else {
pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent;
if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi;
} else {
pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi;
}
}
/* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */
if (pDataBufferNode->pParent != NULL) {
if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode;
} else {
pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode;
}
}
/* Now need to update the replacement node's pointers. */
pReplacementDataBufferNode->pParent = pDataBufferNode->pParent;
pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo;
pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi;
/* Now the children of the replacement node need to have their parent pointers updated. */
if (pReplacementDataBufferNode->pChildLo != NULL) {
pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode;
}
if (pReplacementDataBufferNode->pChildHi != NULL) {
pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode;
}
/* Now the root node needs to be updated. */
if (pResourceManager->pRootDataBufferNode == pDataBufferNode) {
pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode;
}
}
}
return MA_SUCCESS;
}
#if 0 /* Unused for now. */
static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32)
{
ma_result result;
ma_resource_manager_data_buffer_node* pDataBufferNode;
result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode);
if (result != MA_SUCCESS) {
return result; /* Could not find the data buffer. */
}
return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode);
}
#endif
static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode)
{
return (ma_resource_manager_data_supply_type)c89atomic_load_i32(&pDataBufferNode->data.type);
}
static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType)
{
c89atomic_exchange_i32(&pDataBufferNode->data.type, supplyType);
}
static ma_result ma_resource_manager_data_buffer_node_increment_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_add_32(&pDataBufferNode->refCount, 1) + 1;
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;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
}
/*
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:
/* If we failed to initialize the data buffer we need to free it. */
if (result != MA_SUCCESS) {
if (nodeAlreadyExists == MA_FALSE) {
ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
}
}
/*
The init notification needs to be uninitialized. This will be used if the node does not already
exist, and we've specified ASYNC | WAIT_INIT.
*/
if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_uninit(&initNotification);
}
}
if (ppDataBufferNode != NULL) {
*ppDataBufferNode = pDataBufferNode;
}
return result;
}
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);
}
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* 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;
pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01;
return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
} else {
/* We haven't moved into a new page so we can just move the cursor forward. */
pDataStream->relativeCursor = newRelativeCursor;
return MA_SUCCESS;
}
}
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_result result = MA_SUCCESS;
ma_uint64 totalFramesProcessed;
ma_format format;
ma_uint32 channels;
/* Safety. */
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
if (frameCount == 0) {
return MA_INVALID_ARGS;
}
/* 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;
}
/* 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;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
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);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (channelsIn == channelsOut) {
/* No channel conversion required. Just copy straight to the output buffer. */
ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut, ma_format_f32, channelsOut);
} else {
/* Channel conversion required. TODO: Add support for channel maps here. */
ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->pEngine->monoExpansionMode);
}
}
/* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */
/* Panning. */
if (isPanningEnabled) {
ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */
}
/* We're done for this chunk. */
totalFramesProcessedIn += framesJustProcessedIn;
totalFramesProcessedOut += framesJustProcessedOut;
/* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */
if (framesJustProcessedOut == 0) {
break;
}
}
/* At this point we're done processing. */
*pFrameCountIn = totalFramesProcessedIn;
*pFrameCountOut = totalFramesProcessedOut;
}
static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
{
/* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */
ma_result result = MA_SUCCESS;
ma_sound* pSound = (ma_sound*)pNode;
ma_uint32 frameCount = *pFrameCountOut;
ma_uint32 totalFramesRead = 0;
ma_format dataSourceFormat;
ma_uint32 dataSourceChannels;
ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint32 tempCapInFrames;
ma_uint64 seekTarget;
/* This is a data source node which means no input buses. */
(void)ppFramesIn;
(void)pFrameCountIn;
/* If we're marked at the end we need to stop the sound and do nothing. */
if (ma_sound_at_end(pSound)) {
ma_sound_stop(pSound);
*pFrameCountOut = 0;
return;
}
/* If we're seeking, do so now before reading. */
seekTarget = c89atomic_load_64(&pSound->seekTarget);
if (seekTarget != MA_SEEK_TARGET_NONE) {
ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget);
/* Any time-dependant effects need to have their times updated. */
ma_node_set_time(pSound, seekTarget);
c89atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE);
}
/*
We want to update the pitch once. For sounds, this can be either at the start or at the end. If
we don't force this to only ever be updating once, we could end up in a situation where
retrieving the required input frame count ends up being different to what we actually retrieve.
What could happen is that the required input frame count is calculated, the pitch is update,
and then this processing function is called resulting in a different number of input frames
being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else
you'll hit the aforementioned bug.
*/
ma_engine_node_update_pitch_if_required(&pSound->engineNode);
/*
For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ
from the main engine.
*/
result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0);
if (result == MA_SUCCESS) {
tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels);
/* Keep reading until we've read as much as was requested or we reach the end of the data source. */
while (totalFramesRead < frameCount) {
ma_uint32 framesRemaining = frameCount - totalFramesRead;
ma_uint32 framesToRead;
ma_uint64 framesJustRead;
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. */