view release on metacpan or search on metacpan
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer
to a pointer that will, upon output, be set to a pointer to a buffer containing a list of
`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive
the number of items in the returned buffer. Do not free the returned buffers as their memory is
managed internally by miniaudio.
The `ma_device_info` structure contains an `id` member which is the ID you pass to the device
config. It also contains the name of the device which is useful for presenting a list of devices
to the user via the UI.
When creating your own context you will want to pass it to `ma_device_init()` when initializing the
device. Passing in NULL, like we do in the first example, will result in miniaudio creating the
context for you, which you don't want to do since you've already created a context. Note that
internally the context is only tracked by it's pointer which means you must not change the location
of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for
the context.
1.2. High Level API
-------------------
The high level API consists of three main parts:
* Resource management for loading and streaming sounds.
* A node graph for advanced mixing and effect processing.
* A high level "engine" that wraps around the resource manager and node graph.
The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds
fully into memory and also streaming. It will also deal with reference counting for you which
avoids the same sound being loaded multiple times.
The node graph is used for mixing and effect processing. The idea is that you connect a number of
nodes into the graph by connecting each node's outputs to another node's inputs. Each node can
implement it's own effect. By chaining nodes together, advanced mixing and effect processing can
be achieved.
The engine encapsulates both the resource manager and the node graph to create a simple, easy to
use high level API. The resource manager and node graph APIs are covered in more later sections of
this manual.
The code below shows how you can initialize an engine using it's default configuration.
```c
ma_result result;
ma_engine engine;
result = ma_engine_init(NULL, &engine);
if (result != MA_SUCCESS) {
return result; // Failed to initialize the engine.
}
```
This creates an engine instance which will initialize a device internally which you can access with
`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed
with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which
means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a
cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast.
Note that all objects in miniaudio, including the `ma_engine` object in the example above, are
transparent structures. There are no handles to opaque structures in miniaudio which means you need
to be mindful of how you declare them. In the example above we are declaring it on the stack, but
this will result in the struct being invalidated once the function encapsulating it returns. If
allocating the engine on the heap is more appropriate, you can easily do so with a standard call
to `malloc()` or whatever heap allocation routine you like:
```c
ma_engine* pEngine = malloc(sizeof(*pEngine));
```
The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure
an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of
`ma_engine_init()`:
```c
ma_result result;
ma_engine engine;
ma_engine_config engineConfig;
engineConfig = ma_engine_config_init();
engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage.
result = ma_engine_init(&engineConfig, &engine);
if (result != MA_SUCCESS) {
return result;
}
```
This creates an engine instance using a custom config. In this particular example it's showing how
you can specify a custom resource manager rather than having the engine initialize one internally.
This is particularly useful if you want to have multiple engine's share the same resource manager.
The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed.
By default the engine will be started, but nothing will be playing because no sounds have been
initialized. The easiest but least flexible way of playing a sound is like so:
```c
ma_engine_play_sound(&engine, "my_sound.wav", NULL);
```
This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the
internal sound up for recycling. The last parameter is used to specify which sound group the sound
should be associated with which will be explained later. This particular way of playing a sound is
simple, but lacks flexibility and features. A more flexible way of playing a sound is to first
initialize a sound:
```c
ma_result result;
ma_sound sound;
result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound);
if (result != MA_SUCCESS) {
return result;
}
ma_sound_start(&sound);
```
This returns a `ma_sound` object which represents a single instance of the specified sound file. If
you want to play the same file multiple times simultaneously, you need to create one sound for each
instance.
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
Some backends and platforms may only support default playback and capture devices.
In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also,
do not try to call `ma_context_get_device_info()` from within the callback.
Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.
Example 1 - Simple Enumeration
------------------------------
ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
{
printf("Device Name: %s\n", pInfo->name);
return MA_TRUE;
}
ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);
if (result != MA_SUCCESS) {
// Error.
}
See Also
--------
ma_context_get_devices()
*/
MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
/*
Retrieves basic information about every active playback and/or capture device.
This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.
Parameters
----------
pContext (in)
A pointer to the context performing the enumeration.
ppPlaybackDeviceInfos (out)
A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.
pPlaybackDeviceCount (out)
A pointer to an unsigned integer that will receive the number of playback devices.
ppCaptureDeviceInfos (out)
A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.
pCaptureDeviceCount (out)
A pointer to an unsigned integer that will receive the number of capture devices.
Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple
threads. Instead, you need to make a copy of the returned data with your own higher level synchronization.
Remarks
-------
It is _not_ safe to assume the first device in the list is the default device.
You can pass in NULL for the playback or capture lists in which case they'll be ignored.
The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers.
See Also
--------
ma_context_get_devices()
*/
MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
/*
Retrieves information about a device of the given type, with the specified ID and share mode.
Parameters
----------
pContext (in)
A pointer to the context performing the query.
deviceType (in)
The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.
pDeviceID (in)
The ID of the device being queried.
pDeviceInfo (out)
A pointer to the `ma_device_info` structure that will receive the device information.
Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.
Thread Safety
-------------
Safe. This is guarded using a simple mutex lock.
Remarks
-------
Do _not_ call this from within the `ma_context_enumerate_devices()` callback.
It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in
shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify
which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if
the requested share mode is unsupported.
This leaves pDeviceInfo unmodified in the result of an error.
*/
MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.");
return ma_result_from_errno(-resultALSA);
}
resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);
if (resultALSA < 0) {
bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
}
if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
/*
Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
the size of a period. But for full-duplex we need to set it such that it is at least two periods.
*/
resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);
if (resultALSA < 0) {
ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.");
return ma_result_from_errno(-resultALSA);
}
resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);
if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed.");
return ma_result_from_errno(-resultALSA);
}
}
resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);
if (resultALSA < 0) {
ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.");
return ma_result_from_errno(-resultALSA);
}
ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
pSWParams = NULL;
/* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */
{
ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM);
if (pChmap != NULL) {
ma_uint32 iChannel;
/* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */
if (pChmap->channels >= internalChannels) {
/* Drop excess channels. */
for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
}
} else {
ma_uint32 i;
/*
Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
channels. If validation fails, fall back to defaults.
*/
ma_bool32 isValid = MA_TRUE;
/* Fill with defaults. */
ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
/* Overwrite first pChmap->channels channels. */
for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
}
/* Validate. */
for (i = 0; i < internalChannels && isValid; ++i) {
ma_uint32 j;
for (j = i+1; j < internalChannels; ++j) {
if (internalChannelMap[i] == internalChannelMap[j]) {
isValid = MA_FALSE;
break;
}
}
}
/* If our channel map is invalid, fall back to defaults. */
if (!isValid) {
ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
}
}
free(pChmap);
pChmap = NULL;
} else {
/* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
}
}
/*
We need to retrieve the poll descriptors so we can use poll() to wait for data to become
available for reading or writing. There's no well defined maximum for this so we're just going
to allocate this on the heap.
*/
pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM);
if (pollDescriptorCount <= 0) {
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count.");
return MA_ERROR;
}
pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */
if (pPollDescriptors == NULL) {
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors.");
return MA_OUT_OF_MEMORY;
}
/*
We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver
never returns from writei() and readi(). This has been observed with the "pulse" device.
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
MA_ASSERT(channels > 0);
/* Only resize the buffer if necessary. */
if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) {
AudioBufferList* pNewAudioBufferList;
pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);
if (pNewAudioBufferList == NULL) {
return MA_OUT_OF_MEMORY;
}
/* At this point we'll have a new AudioBufferList and we can free the old one. */
ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList;
pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames;
}
/* Getting here means the capacity of the audio is fine. */
return MA_SUCCESS;
}
static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
{
ma_device* pDevice = (ma_device*)pUserData;
ma_stream_layout layout;
MA_ASSERT(pDevice != NULL);
/*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/
/* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */
layout = ma_stream_layout_interleaved;
if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
layout = ma_stream_layout_deinterleaved;
}
if (layout == ma_stream_layout_interleaved) {
/* For now we can assume everything is interleaved. */
UInt32 iBuffer;
for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
if (frameCountForThisBuffer > 0) {
ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer);
}
/*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataBy...
} else {
/*
This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
output silence here.
*/
MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
/*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferL...
}
}
} else {
/* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */
/*
For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
very strange has happened and we're not going to support it.
*/
if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
ma_uint8 tempBuffer[4096];
UInt32 iBuffer;
for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
ma_uint32 framesRemaining = frameCountPerBuffer;
while (framesRemaining > 0) {
void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
ma_uint32 iChannel;
ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
if (framesToRead > framesRemaining) {
framesToRead = framesRemaining;
}
ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
}
ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
framesRemaining -= framesToRead;
}
}
}
}
(void)pActionFlags;
(void)pTimeStamp;
(void)busNumber;
(void)frameCount;
return noErr;
}
static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
{
ma_device* pDevice = (ma_device*)pUserData;
AudioBufferList* pRenderedBufferList;
ma_result result;
ma_stream_layout layout;
ma_uint32 iBuffer;
OSStatus status;
MA_ASSERT(pDevice != NULL);
pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
MA_ASSERT(pRenderedBufferList);
/* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */
layout = ma_stream_layout_interleaved;
if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
*/
result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout);
if (result != MA_SUCCESS) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n");
return noErr;
}
pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
MA_ASSERT(pRenderedBufferList);
/*
When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes
that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer
being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a
problem when a future call to this callback specifies a larger number of frames.
To work around this we need to explicitly set the size of each buffer to their respective size in bytes.
*/
for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels;
}
status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
if (status != noErr) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status);
return status;
}
if (layout == ma_stream_layout_interleaved) {
for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount);
/*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/
} else {
/*
This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
*/
ma_uint8 silentBuffer[4096];
ma_uint32 framesRemaining;
MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
framesRemaining = frameCount;
while (framesRemaining > 0) {
ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
if (framesToSend > framesRemaining) {
framesToSend = framesRemaining;
}
ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
framesRemaining -= framesToSend;
}
/*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)...
}
}
} else {
/* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */
MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */
/*
For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
very strange has happened and we're not going to support it.
*/
if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
ma_uint8 tempBuffer[4096];
for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
ma_uint32 framesRemaining = frameCount;
while (framesRemaining > 0) {
void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
ma_uint32 iChannel;
ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
if (framesToSend > framesRemaining) {
framesToSend = framesRemaining;
}
for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
}
ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend);
framesRemaining -= framesToSend;
}
}
}
}
(void)pActionFlags;
(void)pTimeStamp;
(void)busNumber;
(void)frameCount;
(void)pUnusedBufferList;
return noErr;
}
static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
{
ma_device* pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
/* Don't do anything if it looks like we're just reinitializing due to a device switch. */
if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
return;
}
/*
There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
can try waiting on the same lock. I'm going to try working around this by not calling any Core
Audio APIs in the callback when the device has been stopped or uninitialized.
*/
if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) {
ma_device__on_notification_stopped(pDevice);
} else {
UInt32 isRunning;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
return ma_vec3f_init_3f(0, 1, 0);
}
return pListener->config.worldUp;
}
MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled)
{
if (pListener == NULL) {
return;
}
pListener->isEnabled = isEnabled;
}
MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener)
{
if (pListener == NULL) {
return MA_FALSE;
}
return pListener->isEnabled;
}
MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut)
{
ma_spatializer_config config;
MA_ZERO_OBJECT(&config);
config.channelsIn = channelsIn;
config.channelsOut = channelsOut;
config.pChannelMapIn = NULL;
config.attenuationModel = ma_attenuation_model_inverse;
config.positioning = ma_positioning_absolute;
config.handedness = ma_handedness_right;
config.minGain = 0;
config.maxGain = 1;
config.minDistance = 1;
config.maxDistance = MA_FLT_MAX;
config.rolloff = 1;
config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */
config.coneOuterGain = 0.0f;
config.dopplerFactor = 1;
config.directionalAttenuationFactor = 1;
config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */
return config;
}
static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig)
{
MA_ASSERT(pConfig != NULL);
return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames);
}
static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig)
{
MA_ASSERT(pConfig != NULL);
if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
return MA_INVALID_ARGS;
}
return MA_SUCCESS;
}
typedef struct
{
size_t sizeInBytes;
size_t channelMapInOffset;
size_t newChannelGainsOffset;
size_t gainerOffset;
} ma_spatializer_heap_layout;
static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout)
{
ma_result result;
MA_ASSERT(pHeapLayout != NULL);
MA_ZERO_OBJECT(pHeapLayout);
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
result = ma_spatializer_validate_config(pConfig);
if (result != MA_SUCCESS) {
return result;
}
pHeapLayout->sizeInBytes = 0;
/* Channel map. */
pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */
if (pConfig->pChannelMapIn != NULL) {
pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn);
}
/* New channel gains for output. */
pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes;
pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut);
/* Gainer. */
{
size_t gainerHeapSizeInBytes;
ma_gainer_config gainerConfig;
gainerConfig = ma_spatializer_gainer_config_init(pConfig);
result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes);
if (result != MA_SUCCESS) {
return result;
}
pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes);
}
return MA_SUCCESS;
}
MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes)
{
ma_result result;
ma_spatializer_heap_layout heapLayout;
if (pHeapSizeInBytes == NULL) {
return MA_INVALID_ARGS;
}
*pHeapSizeInBytes = 0; /* Safety. */
result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
if (result != MA_SUCCESS) {
return result;
}
*pHeapSizeInBytes = heapLayout.sizeInBytes;
return MA_SUCCESS;
}
MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer)
{
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
return config;
}
MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
{
ma_decoder_config config;
MA_ZERO_OBJECT(&config);
config.format = outputFormat;
config.channels = outputChannels;
config.sampleRate = outputSampleRate;
config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */
config.encodingFormat = ma_encoding_format_unknown;
/* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */
return config;
}
MA_API ma_decoder_config ma_decoder_config_init_default()
{
return ma_decoder_config_init(ma_format_unknown, 0, 0);
}
MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
{
ma_decoder_config config;
if (pConfig != NULL) {
config = *pConfig;
} else {
MA_ZERO_OBJECT(&config);
}
return config;
}
static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
{
ma_result result;
ma_data_converter_config converterConfig;
ma_format internalFormat;
ma_uint32 internalChannels;
ma_uint32 internalSampleRate;
ma_channel internalChannelMap[MA_MAX_CHANNELS];
MA_ASSERT(pDecoder != NULL);
MA_ASSERT(pConfig != NULL);
result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap));
if (result != MA_SUCCESS) {
return result; /* Failed to retrieve the internal data format. */
}
/* Make sure we're not asking for too many channels. */
if (pConfig->channels > MA_MAX_CHANNELS) {
return MA_INVALID_ARGS;
}
/* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */
if (internalChannels > MA_MAX_CHANNELS) {
return MA_INVALID_ARGS;
}
/* Output format. */
if (pConfig->format == ma_format_unknown) {
pDecoder->outputFormat = internalFormat;
} else {
pDecoder->outputFormat = pConfig->format;
}
if (pConfig->channels == 0) {
pDecoder->outputChannels = internalChannels;
} else {
pDecoder->outputChannels = pConfig->channels;
}
if (pConfig->sampleRate == 0) {
pDecoder->outputSampleRate = internalSampleRate;
} else {
pDecoder->outputSampleRate = pConfig->sampleRate;
}
converterConfig = ma_data_converter_config_init(
internalFormat, pDecoder->outputFormat,
internalChannels, pDecoder->outputChannels,
internalSampleRate, pDecoder->outputSampleRate
);
converterConfig.pChannelMapIn = internalChannelMap;
converterConfig.pChannelMapOut = pConfig->pChannelMap;
converterConfig.channelMixMode = pConfig->channelMixMode;
converterConfig.ditherMode = pConfig->ditherMode;
converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
converterConfig.resampling = pConfig->resampling;
result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter);
if (result != MA_SUCCESS) {
return result;
}
/*
Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll
need this if the data converter does not support calculation of the required input frame count. To
determine support for this we'll just run a test.
*/
{
ma_uint64 unused;
result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused);
if (result != MA_SUCCESS) {
/*
We were unable to calculate the required input frame count which means we'll need to use
a heap-allocated cache.
*/
ma_uint64 inputCacheCapSizeInBytes;
pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels);
/* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */