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_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the audio(4) backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the OSS backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the AAudio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the OpenSL|ES backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the Web Audio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the null backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_DECODING | Disables decoding APIs. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_ENCODING | Disables encoding APIs. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_WAV | Disables the built-in WAV decoder and encoder. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_FLAC | Disables the built-in FLAC decoder. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_MP3 | Disables the built-in MP3 decoder. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` |
| | and `ma_device` APIs. This is useful if you only want to use |
| | miniaudio's data conversion and/or decoding APIs. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and |
| | `ma_event` APIs. This option is useful if you only need to use |
| | miniaudio for data conversion, decoding and/or encoding. Some |
| | families of APIsrequire threading which means the following |
| | options must also be set: |
| | |
| | ``` |
| | MA_NO_DEVICE_IO |
| | ``` |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_SSE2 | Disables SSE2 optimizations. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_AVX2 | Disables AVX2 optimizations. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_NEON | Disables NEON optimizations. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's |
| | notarization process. When enabling this, you may need to avoid |
| | using `-std=c89` or `-std=c99` on Linux builds or else you may end |
| | up with compilation errors due to conflicts with `timespec` and |
| | `timeval` data types. |
| | |
| | You may need to enable this if your target platform does not allow |
| | runtime linking via `dlopen()`. |
+----------------------------------+--------------------------------------------------------------------+
| MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). |
+----------------------------------+--------------------------------------------------------------------+
| MA_COINIT_VALUE | Windows only. The value to pass to internal calls to |
| | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. |
+----------------------------------+--------------------------------------------------------------------+
| MA_API | Controls how public APIs should be decorated. Default is `extern`. |
+----------------------------------+--------------------------------------------------------------------+
3. Definitions
==============
This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity
in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio
uses each term.
3.1. Sample
-----------
A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit
floating point number.
3.2. Frame / PCM Frame
----------------------
A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2
samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame"
and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame.
If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always
clarify what it's referring to with something like "FLAC frame".
3.3. Channel
------------
A stream of monaural audio that is emitted from an individual speaker in a speaker system, or
received from an individual microphone in a microphone system. A stereo stream has two channels (a
left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio
systems refer to a channel as a complex audio stream that's mixed with other channels to produce
the final mix - this is completely different to miniaudio's use of the term "channel" and should
not be confused.
3.4. Sample Rate
----------------
The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number
of PCM frames that are processed per second.
3.5. Formats
------------
Throughout miniaudio you will see references to different sample formats:
+---------------+----------------------------------------+---------------------------+
| Symbol | Description | Range |
+---------------+----------------------------------------+---------------------------+
| ma_format_f32 | 32-bit floating point | [-1, 1] |
| ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
| ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
| ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
| ma_format_u8 | 8-bit unsigned integer | [0, 255] |
+---------------+----------------------------------------+---------------------------+
All formats are native-endian.
4. Data Sources
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
1, // 1 output bus.
0 // Default flags.
};
...
ma_node_config nodeConfig = ma_node_config_init();
nodeConfig.vtable = &my_custom_node_vtable;
nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here.
nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array.
nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array.
```
In the above example it's important to never set the `inputBusCount` and `outputBusCount` members
to anything other than their defaults if the vtable specifies an explicit count. They can only be
set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count.
Most often you'll want to create a structure to encapsulate your node with some extra data. You
need to make sure the `ma_node_base` object is your first member of the structure:
```c
typedef struct
{
ma_node_base base; // <-- Make sure this is always the first member.
float someCustomData;
} my_custom_node;
```
By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the
graph just like any other node.
In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the
number of channels for each bus is what was specified by the config when the node was initialized
with `ma_node_init()`. In addition, all attachments to each of the input buses will have been
pre-mixed by miniaudio. The config allows you to specify different channel counts for each
individual input and output bus. It's up to the effect to handle it appropriate, and if it can't,
return an error in it's initialization routine.
Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable
and include the following:
+-----------------------------------------+---------------------------------------------------+
| Flag Name | Description |
+-----------------------------------------+---------------------------------------------------+
| MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio |
| | processing, but are instead used for tracking |
| | time, handling events, etc. Also used by the |
| | internal endpoint node. It reads directly from |
| | the input bus to the output bus. Nodes with this |
| | flag must have exactly 1 input bus and 1 output |
| | bus, and both buses must have the same channel |
| | counts. |
+-----------------------------------------+---------------------------------------------------+
| MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even |
| | when no data is available to be read from input |
| | attachments. This is useful for effects like |
| | echos where there will be a tail of audio data |
| | that still needs to be processed even when the |
| | original data sources have reached their ends. |
+-----------------------------------------+---------------------------------------------------+
| MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with |
| | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this |
| | is set, the `ppFramesIn` parameter of the |
| | processing callback will be set to NULL when |
| | there are no input frames are available. When |
| | this is unset, silence will be posted to the |
| | processing callback. |
+-----------------------------------------+---------------------------------------------------+
| MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output |
| | frames are processed at different rates. You |
| | should set this for any nodes that perform |
| | resampling. |
+-----------------------------------------+---------------------------------------------------+
| MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only |
| | silent output. This is useful for nodes where you |
| | don't want the output to contribute to the final |
| | mix. An example might be if you want split your |
| | stream and have one branch be output to a file. |
| | When using this flag, you should avoid writing to |
| | the output buffer of the node's processing |
| | callback because miniaudio will ignore it anyway. |
+-----------------------------------------+---------------------------------------------------+
If you need to make a copy of an audio stream for effect processing you can use a splitter node
called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses.
You can use it like this:
```c
ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut);
ma_splitter_node splitterNode;
result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);
if (result != MA_SUCCESS) {
// Failed to create node.
}
// Attach your output buses to two different input buses (can be on two different nodes).
ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint.
ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node.
```
The volume of an output bus can be configured on a per-bus basis:
```c
ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f);
ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f);
```
In the code above we're using the splitter node from before and changing the volume of each of the
copied streams.
You can start and stop a node with the following:
```c
ma_node_set_state(&splitterNode, ma_node_state_started); // The default state.
ma_node_set_state(&splitterNode, ma_node_state_stopped);
```
By default the node is in a started state, but since it won't be connected to anything won't
actually be invoked by the node graph until it's connected. When you stop a node, data will not be
read from any of it's input connections. You can use this property to stop a group of sounds
atomically.
You can configure the initial state of a node in it's config:
```c
nodeConfig.initialState = ma_node_state_stopped;
```
Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member
which is the config to use with the base node. This is where the initial state can be configured
for specialized nodes:
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* OpenSL stream types. */
typedef enum
{
ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */
ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */
ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */
ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */
ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */
ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */
ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */
} ma_opensl_stream_type;
/* OpenSL recording presets. */
typedef enum
{
ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */
ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */
ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */
ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */
ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */
ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */
} ma_opensl_recording_preset;
/* AAudio usage types. */
typedef enum
{
ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */
ma_aaudio_usage_announcement, /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */
ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */
ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */
ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */
ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */
ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */
ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */
ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */
ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */
ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */
ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */
ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */
ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */
ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */
ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */
ma_aaudio_usage_voice_communication_signalling /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */
} ma_aaudio_usage;
/* AAudio content types. */
typedef enum
{
ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */
ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */
ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */
ma_aaudio_content_type_sonification, /* AAUDIO_CONTENT_TYPE_SONIFICATION */
ma_aaudio_content_type_speech /* AAUDIO_CONTENT_TYPE_SPEECH */
} ma_aaudio_content_type;
/* AAudio input presets. */
typedef enum
{
ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */
ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */
ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */
ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */
ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */
ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */
ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */
} ma_aaudio_input_preset;
typedef union
{
ma_int64 counter;
double counterD;
} ma_timer;
typedef union
{
wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
/*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */
char alsa[256]; /* ALSA uses a name string for identification. */
char pulse[256]; /* PulseAudio uses a name string for identification. */
int jack; /* JACK always uses default devices. */
char coreaudio[256]; /* Core Audio uses a string for identification. */
char sndio[256]; /* "snd/0", etc. */
char audio4[256]; /* "/dev/audio", etc. */
char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */
ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
union
{
int i;
char s[256];
void* p;
} custom; /* The custom backend could be anything. Give them a few options. */
int nullbackend; /* The null backend uses an integer for device IDs. */
} ma_device_id;
typedef struct ma_context_config ma_context_config;
typedef struct ma_device_config ma_device_config;
typedef struct ma_backend_callbacks ma_backend_callbacks;
#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */
#ifndef MA_MAX_DEVICE_NAME_LENGTH
#define MA_MAX_DEVICE_NAME_LENGTH 255
#endif
typedef struct
{
/* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
ma_device_id id;
char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */
ma_bool32 isDefault;
ma_uint32 nativeDataFormatCount;
struct
{
ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */
ma_uint32 channels; /* If set to 0, all channels are supported. */
ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */
ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */
} nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64]; /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */
} ma_device_info;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_proc sio_initpar;
} sndio;
#endif
#ifdef MA_SUPPORT_AUDIO4
struct
{
int _unused;
} audio4;
#endif
#ifdef MA_SUPPORT_OSS
struct
{
int versionMajor;
int versionMinor;
} oss;
#endif
#ifdef MA_SUPPORT_AAUDIO
struct
{
ma_handle hAAudio; /* libaaudio.so */
ma_proc AAudio_createStreamBuilder;
ma_proc AAudioStreamBuilder_delete;
ma_proc AAudioStreamBuilder_setDeviceId;
ma_proc AAudioStreamBuilder_setDirection;
ma_proc AAudioStreamBuilder_setSharingMode;
ma_proc AAudioStreamBuilder_setFormat;
ma_proc AAudioStreamBuilder_setChannelCount;
ma_proc AAudioStreamBuilder_setSampleRate;
ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
ma_proc AAudioStreamBuilder_setDataCallback;
ma_proc AAudioStreamBuilder_setErrorCallback;
ma_proc AAudioStreamBuilder_setPerformanceMode;
ma_proc AAudioStreamBuilder_setUsage;
ma_proc AAudioStreamBuilder_setContentType;
ma_proc AAudioStreamBuilder_setInputPreset;
ma_proc AAudioStreamBuilder_openStream;
ma_proc AAudioStream_close;
ma_proc AAudioStream_getState;
ma_proc AAudioStream_waitForStateChange;
ma_proc AAudioStream_getFormat;
ma_proc AAudioStream_getChannelCount;
ma_proc AAudioStream_getSampleRate;
ma_proc AAudioStream_getBufferCapacityInFrames;
ma_proc AAudioStream_getFramesPerDataCallback;
ma_proc AAudioStream_getFramesPerBurst;
ma_proc AAudioStream_requestStart;
ma_proc AAudioStream_requestStop;
ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */
} aaudio;
#endif
#ifdef MA_SUPPORT_OPENSL
struct
{
ma_handle libOpenSLES;
ma_handle SL_IID_ENGINE;
ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;
ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
ma_handle SL_IID_RECORD;
ma_handle SL_IID_PLAY;
ma_handle SL_IID_OUTPUTMIX;
ma_handle SL_IID_ANDROIDCONFIGURATION;
ma_proc slCreateEngine;
} opensl;
#endif
#ifdef MA_SUPPORT_WEBAUDIO
struct
{
int _unused;
} webaudio;
#endif
#ifdef MA_SUPPORT_NULL
struct
{
int _unused;
} null_backend;
#endif
};
union
{
#ifdef MA_WIN32
struct
{
/*HMODULE*/ ma_handle hOle32DLL;
ma_proc CoInitializeEx;
ma_proc CoUninitialize;
ma_proc CoCreateInstance;
ma_proc CoTaskMemFree;
ma_proc PropVariantClear;
ma_proc StringFromGUID2;
/*HMODULE*/ ma_handle hUser32DLL;
ma_proc GetForegroundWindow;
ma_proc GetDesktopWindow;
/*HMODULE*/ ma_handle hAdvapi32DLL;
ma_proc RegOpenKeyExA;
ma_proc RegCloseKey;
ma_proc RegQueryValueExA;
} win32;
#endif
#ifdef MA_POSIX
struct
{
ma_handle pthreadSO;
ma_proc pthread_create;
ma_proc pthread_join;
ma_proc pthread_mutex_init;
ma_proc pthread_mutex_destroy;
ma_proc pthread_mutex_lock;
ma_proc pthread_mutex_unlock;
ma_proc pthread_cond_init;
ma_proc pthread_cond_destroy;
ma_proc pthread_cond_wait;
ma_proc pthread_cond_signal;
ma_proc pthread_attr_init;
ma_proc pthread_attr_destroy;
ma_proc pthread_attr_setschedpolicy;
ma_proc pthread_attr_getschedparam;
ma_proc pthread_attr_setschedparam;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength);
MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping);
MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames);
/* Data Sources. */
MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex);
MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor);
MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength);
MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping);
MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames);
/* Job management. */
MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob);
MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */
MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob);
MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */
MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */
#endif /* MA_NO_RESOURCE_MANAGER */
/************************************************************************************************************************************************************
Node Graph
************************************************************************************************************************************************************/
#ifndef MA_NO_NODE_GRAPH
/* Must never exceed 254. */
#ifndef MA_MAX_NODE_BUS_COUNT
#define MA_MAX_NODE_BUS_COUNT 254
#endif
/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */
#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT
#define MA_MAX_NODE_LOCAL_BUS_COUNT 2
#endif
/* Use this when the bus count is determined by the node instance rather than the vtable. */
#define MA_NODE_BUS_COUNT_UNKNOWN 255
typedef struct ma_node_graph ma_node_graph;
typedef void ma_node;
/* Node flags. */
typedef enum
{
MA_NODE_FLAG_PASSTHROUGH = 0x00000001,
MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002,
MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004,
MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008,
MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010
} ma_node_flags;
/* The playback state of a node. Either started or stopped. */
typedef enum
{
ma_node_state_started = 0,
ma_node_state_stopped = 1
} ma_node_state;
typedef struct
{
/*
Extended processing callback. This callback is used for effects that process input and output
at different rates (i.e. they perform resampling). This is similar to the simple version, only
they take two seperate frame counts: one for input, and one for output.
On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas
`pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`.
On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set
`pFrameCountIn` to the number of input frames that were consumed.
*/
void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
/*
A callback for retrieving the number of a input frames that are required to output the
specified number of output frames. You would only want to implement this when the node performs
resampling. This is optional, even for nodes that perform resampling, but it does offer a
small reduction in latency as it allows miniaudio to calculate the exact number of input frames
to read at a time instead of having to estimate.
*/
ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount);
/*
The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn`
parameters of the callbacks above.
*/
ma_uint8 inputBusCount;
/*
The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut`
parameters of the callbacks above.
*/
ma_uint8 outputBusCount;
/*
Flags describing characteristics of the node. This is currently just a placeholder for some
ideas for later on.
*/
ma_uint32 flags;
} ma_node_vtable;
typedef struct
{
const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */
ma_node_state initialState; /* Defaults to ma_node_state_started. */
ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */
ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */
const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */
const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */
} ma_node_config;
MA_API ma_node_config ma_node_config_init(void);
/*
A node has multiple output buses. An output bus is attached to an input bus as an item in a linked
list. Think of the input bus as a linked list, with the output bus being an item in that list.
*/
typedef struct ma_node_output_bus ma_node_output_bus;
struct ma_node_output_bus
{
/* Immutable. */
ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */
ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */
ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
/* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */
MA_ATOMIC(1, ma_uint8) inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. */
MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */
MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */
MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */
MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
MA_ATOMIC(4, float) volume; /* Linear. */
MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */
MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */
MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */
};
/*
A node has multiple input buses. The output buses of a node are connecting to the input busses of
another. An input bus is essentially just a linked list of output buses.
*/
typedef struct ma_node_input_bus ma_node_input_bus;
struct ma_node_input_bus
{
/* Mutable via multiple threads. */
ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */
MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */
MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
/* Set once at startup. */
ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
};
typedef struct ma_node_base ma_node_base;
struct ma_node_base
{
/* These variables are set once at startup. */
ma_node_graph* pNodeGraph; /* The graph this node belongs to. */
const ma_node_vtable* vtable;
float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */
ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */
/* These variables are read and written only from the audio thread. */
ma_uint16 cachedFrameCountOut;
ma_uint16 cachedFrameCountIn;
ma_uint16 consumedFrameCountIn;
/* These variables are read and written between different threads. */
MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */
MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */
ma_uint32 inputBusCount;
ma_uint32 outputBusCount;
ma_node_input_bus* pInputBuses;
ma_node_output_bus* pOutputBuses;
/* Memory management. */
ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];
ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];
void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */
ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */
};
MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes);
MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode);
MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode);
MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
static void ma__free_default(void* p, void* pUserData)
{
(void)pUserData;
MA_FREE(p);
}
static ma_allocation_callbacks ma_allocation_callbacks_init_default(void)
{
ma_allocation_callbacks callbacks;
callbacks.pUserData = NULL;
callbacks.onMalloc = ma__malloc_default;
callbacks.onRealloc = ma__realloc_default;
callbacks.onFree = ma__free_default;
return callbacks;
}
static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc)
{
if (pDst == NULL) {
return MA_INVALID_ARGS;
}
if (pSrc == NULL) {
*pDst = ma_allocation_callbacks_init_default();
} else {
if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) {
*pDst = ma_allocation_callbacks_init_default();
} else {
if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) {
return MA_INVALID_ARGS; /* Invalid allocation callbacks. */
} else {
*pDst = *pSrc;
}
}
}
return MA_SUCCESS;
}
/**************************************************************************************************************************************************************
Logging
**************************************************************************************************************************************************************/
MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
{
switch (logLevel)
{
case MA_LOG_LEVEL_DEBUG: return "DEBUG";
case MA_LOG_LEVEL_INFO: return "INFO";
case MA_LOG_LEVEL_WARNING: return "WARNING";
case MA_LOG_LEVEL_ERROR: return "ERROR";
default: return "ERROR";
}
}
#if defined(MA_DEBUG_OUTPUT)
/* Customize this to use a specific tag in __android_log_print() for debug output messages. */
#ifndef MA_ANDROID_LOG_TAG
#define MA_ANDROID_LOG_TAG "miniaudio"
#endif
void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage)
{
(void)pUserData;
/* Special handling for some platforms. */
#if defined(MA_ANDROID)
{
/* Android. */
__android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage);
}
#else
{
/* Everything else. */
printf("%s: %s", ma_log_level_to_string(level), pMessage);
}
#endif
}
#endif
MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData)
{
ma_log_callback callback;
MA_ZERO_OBJECT(&callback);
callback.onLog = onLog;
callback.pUserData = pUserData;
return callback;
}
MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog)
{
if (pLog == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pLog);
ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks);
/* We need a mutex for thread safety. */
#ifndef MA_NO_THREADING
{
ma_result result = ma_mutex_init(&pLog->lock);
if (result != MA_SUCCESS) {
return result;
}
}
#endif
/* If we're using debug output, enable it. */
#if defined(MA_DEBUG_OUTPUT)
{
ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */
}
#endif
return MA_SUCCESS;
}
MA_API void ma_log_uninit(ma_log* pLog)
{
if (pLog == NULL) {
return;
}
#ifndef MA_NO_THREADING
ma_mutex_uninit(&pLog->lock);
#endif
}
static void ma_log_lock(ma_log* pLog)
{
#ifndef MA_NO_THREADING
ma_mutex_lock(&pLog->lock);
#else
(void)pLog;
#endif
}
static void ma_log_unlock(ma_log* pLog)
{
#ifndef MA_NO_THREADING
ma_mutex_unlock(&pLog->lock);
#else
(void)pLog;
#endif
}
MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback)
{
ma_result result = MA_SUCCESS;
if (pLog == NULL || callback.onLog == NULL) {
return MA_INVALID_ARGS;
}
ma_log_lock(pLog);
{
if (pLog->callbackCount == ma_countof(pLog->callbacks)) {
result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */
} else {
pLog->callbacks[pLog->callbackCount] = callback;
pLog->callbackCount += 1;
}
}
ma_log_unlock(pLog);
return result;
}
MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback)
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */
c89atomic_fetch_add_32(&pAllocator->count, 1);
/* The slot index is required for constructing the output value. */
slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */
if (slotIndex >= pAllocator->capacity) {
return MA_OUT_OF_MEMORY;
}
/* Increment the reference count before constructing the output value. */
pAllocator->pSlots[slotIndex] += 1;
/* Construct the output value. */
*pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex);
return MA_SUCCESS;
}
}
}
/* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */
if (pAllocator->count < pAllocator->capacity) {
ma_yield();
} else {
return MA_OUT_OF_MEMORY;
}
}
/* We couldn't find a slot within the maximum number of attempts. */
return MA_OUT_OF_MEMORY;
}
MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot)
{
ma_uint32 iGroup;
ma_uint32 iBit;
if (pAllocator == NULL) {
return MA_INVALID_ARGS;
}
iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */
iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */
if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) {
return MA_INVALID_ARGS;
}
MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */
while (c89atomic_load_32(&pAllocator->count) > 0) {
/* CAS */
ma_uint32 oldBitfield;
ma_uint32 newBitfield;
oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
newBitfield = oldBitfield & ~(1 << iBit);
/* Debugging for checking for double-frees. */
#if defined(MA_DEBUG_OUTPUT)
{
if ((oldBitfield & (1 << iBit)) == 0) {
MA_ASSERT(MA_FALSE); /* Double free detected.*/
}
}
#endif
if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
c89atomic_fetch_sub_32(&pAllocator->count, 1);
return MA_SUCCESS;
}
}
/* Getting here means there are no allocations available for freeing. */
return MA_INVALID_OPERATION;
}
#define MA_JOB_ID_NONE ~((ma_uint64)0)
#define MA_JOB_SLOT_NONE (ma_uint16)(~0)
static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc)
{
return (ma_uint32)(toc >> 32);
}
static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc)
{
return (ma_uint16)(toc & 0x0000FFFF);
}
static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc)
{
return (ma_uint16)((toc & 0xFFFF0000) >> 16);
}
static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc)
{
return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc);
}
static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount)
{
/* Clear the reference count first. */
toc = toc & ~((ma_uint64)0xFFFFFFFF << 32);
toc = toc | ((ma_uint64)refcount << 32);
return toc;
}
MA_API ma_job ma_job_init(ma_uint16 code)
{
ma_job job;
MA_ZERO_OBJECT(&job);
job.toc.breakup.code = code;
job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */
job.next = MA_JOB_ID_NONE;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (pHandler->hEvent == NULL) {
return ma_result_from_GetLastError(GetLastError());
}
return MA_SUCCESS;
}
static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
{
if (pHandler->hEvent != NULL) {
CloseHandle(pHandler->hEvent);
}
}
static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
{
WaitForSingleObject(pHandler->hEvent, INFINITE);
}
#endif /* !MA_WIN32_DESKTOP */
/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
{
/*
We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
*/
if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
*ppObject = NULL;
return E_NOINTERFACE;
}
/* Getting here means the IID is IUnknown or IMMNotificationClient. */
*ppObject = (void*)pThis;
((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
return S_OK;
}
static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
{
return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
}
static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
{
ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
if (newRefCount == 0) {
return 0; /* We don't free anything here because we never allocate the object on the heap. */
}
return (ULONG)newRefCount;
}
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState)
{
ma_bool32 isThisDevice = MA_FALSE;
ma_bool32 isCapture = MA_FALSE;
ma_bool32 isPlayback = MA_FALSE;
#ifdef MA_DEBUG_OUTPUT
/*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/
#endif
/*
There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect
that the device is disabled or has been unplugged.
*/
if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) {
isCapture = MA_TRUE;
if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) {
isThisDevice = MA_TRUE;
}
}
if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) {
isPlayback = MA_TRUE;
if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) {
isThisDevice = MA_TRUE;
}
}
/*
If the device ID matches our device we need to mark our device as detached and stop it. When a
device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device
was started at the time of being removed.
*/
if (isThisDevice) {
if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) {
/*
Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll
use this to determine whether or not we need to automatically start the device when it's
plugged back in again.
*/
if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) {
if (isPlayback) {
pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE;
}
if (isCapture) {
pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE;
}
ma_device_stop(pThis->pDevice);
}
}
if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) {
/* The device was activated. If we were detached, we need to start it again. */
ma_bool8 tryRestartingDevice = MA_FALSE;
if (isPlayback) {
if (pThis->pDevice->wasapi.isDetachedPlayback) {
pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
tryRestartingDevice = MA_TRUE;
}
}
if (isCapture) {
if (pThis->pDevice->wasapi.isDetachedCapture) {
pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
tryRestartingDevice = MA_TRUE;
}
}
if (tryRestartingDevice) {
if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) {
ma_device_start(pThis->pDevice);
}
}
}
}
return S_OK;
}
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
{
#ifdef MA_DEBUG_OUTPUT
/*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
#endif
/* We don't need to worry about this event for our purposes. */
(void)pThis;
(void)pDeviceID;
return S_OK;
}
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
{
#ifdef MA_DEBUG_OUTPUT
/*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
#endif
/* We don't need to worry about this event for our purposes. */
(void)pThis;
(void)pDeviceID;
return S_OK;
}
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID)
{
#ifdef MA_DEBUG_OUTPUT
/*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/
#endif
/* We only ever use the eConsole role in miniaudio. */
if (role != ma_eConsole) {
ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting: role != eConsole\n");
return S_OK;
}
/* We only care about devices with the same data flow and role as the current device. */
if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
(pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) {
ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n");
return S_OK;
}
/* Don't do automatic stream routing if we're not allowed. */
if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||
(dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) {
ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n");
return S_OK;
}
/*
Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
it's fixed.
*/
if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
(dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n");
return S_OK;
}
/*
Second attempt at device rerouting. We're going to retrieve the device's state at the time of
the route change. We're then going to stop the device, reinitialize the device, and then start
it again if the state before stopping was ma_device_state_started.
*/
{
ma_uint32 previousState = ma_device_get_state(pThis->pDevice);
ma_bool8 restartDevice = MA_FALSE;
if (previousState == ma_device_state_started) {
ma_device_stop(pThis->pDevice);
restartDevice = MA_TRUE;
}
if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */
if (dataFlow == ma_eRender) {
ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
if (pThis->pDevice->wasapi.isDetachedPlayback) {
pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) {
restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */
} else {
restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */
}
}
} else {
ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
if (pThis->pDevice->wasapi.isDetachedCapture) {
pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) {
restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */
} else {
restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */
}
}
}
if (restartDevice) {
ma_device_start(pThis->pDevice);
}
}
}
return S_OK;
}
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key)
{
#ifdef MA_DEBUG_OUTPUT
/*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
#endif
(void)pThis;
(void)pDeviceID;
(void)key;
return S_OK;
}
static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
ma_IMMNotificationClient_QueryInterface,
ma_IMMNotificationClient_AddRef,
ma_IMMNotificationClient_Release,
ma_IMMNotificationClient_OnDeviceStateChanged,
ma_IMMNotificationClient_OnDeviceAdded,
ma_IMMNotificationClient_OnDeviceRemoved,
ma_IMMNotificationClient_OnDefaultDeviceChanged,
ma_IMMNotificationClient_OnPropertyValueChanged
};
#endif /* MA_WIN32_DESKTOP */
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
typedef ma_IMMDevice ma_WASAPIDeviceInterface;
#else
typedef ma_IUnknown ma_WASAPIDeviceInterface;
#endif
#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1
#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2
#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3
static ma_context_command__wasapi ma_context_init_command__wasapi(int code)
{
ma_context_command__wasapi cmd;
MA_ZERO_OBJECT(&cmd);
cmd.code = code;
return cmd;
}
static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd)
{
/* For now we are doing everything synchronously, but I might relax this later if the need arises. */
ma_result result;
ma_bool32 isUsingLocalEvent = MA_FALSE;
ma_event localEvent;
MA_ASSERT(pContext != NULL);
MA_ASSERT(pCmd != NULL);
if (pCmd->pEvent == NULL) {
isUsingLocalEvent = MA_TRUE;
result = ma_event_init(&localEvent);
if (result != MA_SUCCESS) {
return result; /* Failed to create the event for this command. */
}
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
https://developer.apple.com/library/archive/technotes/tn2091/_index.html
******************************************************************************/
#ifdef MA_HAS_COREAUDIO
#include <TargetConditionals.h>
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
#define MA_APPLE_MOBILE
#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
#define MA_APPLE_TV
#endif
#if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
#define MA_APPLE_WATCH
#endif
#if __has_feature(objc_arc)
#define MA_BRIDGE_TRANSFER __bridge_transfer
#define MA_BRIDGE_RETAINED __bridge_retained
#else
#define MA_BRIDGE_TRANSFER
#define MA_BRIDGE_RETAINED
#endif
#else
#define MA_APPLE_DESKTOP
#endif
#if defined(MA_APPLE_DESKTOP)
#include <CoreAudio/CoreAudio.h>
#else
#include <AVFoundation/AVFoundation.h>
#endif
#include <AudioToolbox/AudioToolbox.h>
/* CoreFoundation */
typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
/* CoreAudio */
#if defined(MA_APPLE_DESKTOP)
typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
#endif
/* AudioToolbox */
typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
#define MA_COREAUDIO_OUTPUT_BUS 0
#define MA_COREAUDIO_INPUT_BUS 1
#if defined(MA_APPLE_DESKTOP)
static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
#endif
/*
Core Audio
So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
needing to figure out how this darn thing works, I'm going to outline a few things here.
Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen
that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
the central APIs for retrieving information about the system and specific devices.
To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different.
Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
*/
static ma_result ma_result_from_OSStatus(OSStatus status)
{
switch (status)
{
case noErr: return MA_SUCCESS;
#if defined(MA_APPLE_DESKTOP)
case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
case kAudioHardwareUnspecifiedError: return MA_ERROR;
case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
#endif
default: return MA_ERROR;
}
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
} else {
/* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
if (thisSampleFormat == desiredFormat) {
bestDeviceFormatSoFar = thisDeviceFormat;
break; /* Found the exact match. */
} else {
/* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
bestDeviceFormatSoFar = thisDeviceFormat;
continue;
} else {
continue; /* No change to the best format for now. */
}
}
}
} else {
/*
In this case the channel count is different to what the client has requested. If the best so far has the same channel
count as the requested count then it remains the best.
*/
if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
continue;
} else {
/*
This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
the same priority, but we need to compare the format now.
*/
if (thisSampleFormat == bestSampleFormatSoFar) {
if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
bestDeviceFormatSoFar = thisDeviceFormat;
continue;
} else {
continue; /* No change to the best format for now. */
}
}
}
}
}
}
}
*pFormat = bestDeviceFormatSoFar;
ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
return MA_SUCCESS;
}
static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
{
AudioUnitScope deviceScope;
AudioUnitElement deviceBus;
UInt32 channelLayoutSize;
OSStatus status;
AudioChannelLayout* pChannelLayout;
ma_result result;
MA_ASSERT(pContext != NULL);
if (deviceType == ma_device_type_playback) {
deviceScope = kAudioUnitScope_Input;
deviceBus = MA_COREAUDIO_OUTPUT_BUS;
} else {
deviceScope = kAudioUnitScope_Output;
deviceBus = MA_COREAUDIO_INPUT_BUS;
}
status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
if (status != noErr) {
return ma_result_from_OSStatus(status);
}
pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks);
if (pChannelLayout == NULL) {
return MA_OUT_OF_MEMORY;
}
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
if (status != noErr) {
ma_free(pChannelLayout, &pContext->allocationCallbacks);
return ma_result_from_OSStatus(status);
}
result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
if (result != MA_SUCCESS) {
ma_free(pChannelLayout, &pContext->allocationCallbacks);
return result;
}
ma_free(pChannelLayout, &pContext->allocationCallbacks);
return MA_SUCCESS;
}
#endif /* MA_APPLE_DESKTOP */
#if !defined(MA_APPLE_DESKTOP)
static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)
{
MA_ZERO_OBJECT(pInfo);
ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1);
ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1);
}
#endif
static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
#if defined(MA_APPLE_DESKTOP)
UInt32 deviceCount;
AudioObjectID* pDeviceObjectIDs;
AudioObjectID defaultDeviceObjectIDPlayback;
AudioObjectID defaultDeviceObjectIDCapture;
ma_result result;
UInt32 iDevice;
ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */
ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */
result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
if (result != MA_SUCCESS) {
return result;
}
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
ma_device_info info;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
AudioStreamBasicDescription bestFormat;
UInt32 propSize;
/* We want to ensure we use a consistent device name to device enumeration. */
if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') {
ma_bool32 found = MA_FALSE;
if (deviceType == ma_device_type_playback) {
NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
found = MA_TRUE;
break;
}
}
} else {
NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
found = MA_TRUE;
break;
}
}
}
if (!found) {
return MA_DOES_NOT_EXIST;
}
} else {
if (deviceType == ma_device_type_playback) {
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
} else {
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
}
}
/*
Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to
retrieve from the AVAudioSession shared instance.
*/
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
if (component == NULL) {
return MA_FAILED_TO_INIT_BACKEND;
}
status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
if (status != noErr) {
return ma_result_from_OSStatus(status);
}
formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
propSize = sizeof(bestFormat);
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
return ma_result_from_OSStatus(status);
}
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
audioUnit = NULL;
/* Only a single format is being reported for iOS. */
pDeviceInfo->nativeDataFormatCount = 1;
result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
if (result != MA_SUCCESS) {
return result;
}
pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
/*
It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
this we just get the shared instance and inspect.
*/
@autoreleasepool {
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
MA_ASSERT(pAudioSession != NULL);
pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;
}
}
#endif
(void)pDeviceInfo; /* Unused. */
return MA_SUCCESS;
}
static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks)
{
AudioBufferList* pBufferList;
UInt32 audioBufferSizeInBytes;
size_t allocationSize;
MA_ASSERT(sizeInFrames > 0);
MA_ASSERT(format != ma_format_unknown);
MA_ASSERT(channels > 0);
allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
if (layout == ma_stream_layout_interleaved) {
/* Interleaved case. This is the simple case because we just have one buffer. */
allocationSize += sizeof(AudioBuffer) * 1;
} else {
/* Non-interleaved case. This is the more complex case because there's more than one buffer. */
allocationSize += sizeof(AudioBuffer) * channels;
}
allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels);
pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* inten...
{
ma_result result;
OSStatus status;
UInt32 enableIOFlag;
AudioStreamBasicDescription bestFormat;
UInt32 actualPeriodSizeInFrames;
AURenderCallbackStruct callbackInfo;
#if defined(MA_APPLE_DESKTOP)
AudioObjectID deviceObjectID;
#endif
/* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
if (deviceType == ma_device_type_duplex) {
return MA_INVALID_ARGS;
}
MA_ASSERT(pContext != NULL);
MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
#if defined(MA_APPLE_DESKTOP)
pData->deviceObjectID = 0;
#endif
pData->component = NULL;
pData->audioUnit = NULL;
pData->pAudioBufferList = NULL;
#if defined(MA_APPLE_DESKTOP)
result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
if (result != MA_SUCCESS) {
return result;
}
pData->deviceObjectID = deviceObjectID;
#endif
/* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
pData->periodsOut = pData->periodsIn;
if (pData->periodsOut == 0) {
pData->periodsOut = MA_DEFAULT_PERIODS;
}
if (pData->periodsOut > 16) {
pData->periodsOut = 16;
}
/* Audio unit. */
status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
if (status != noErr) {
return ma_result_from_OSStatus(status);
}
/* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */
enableIOFlag = 1;
if (deviceType == ma_device_type_capture) {
enableIOFlag = 0;
}
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return ma_result_from_OSStatus(status);
}
enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return ma_result_from_OSStatus(status);
}
/* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
#if defined(MA_APPLE_DESKTOP)
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID));
if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return ma_result_from_OSStatus(result);
}
#else
/*
For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change
the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices.
*/
if (pDeviceID != NULL) {
if (deviceType == ma_device_type_capture) {
ma_bool32 found = MA_FALSE;
NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
[[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
found = MA_TRUE;
break;
}
}
if (found == MA_FALSE) {
return MA_DOES_NOT_EXIST;
}
}
}
#endif
/*
Format. This is the hardest part of initialization because there's a few variables to take into account.
1) The format must be supported by the device.
2) The format must be supported miniaudio.
3) There's a priority that miniaudio prefers.
Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The
most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
*/
{
AudioStreamBasicDescription origFormat;
UInt32 origFormatSize = sizeof(origFormat);
AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
if (deviceType == ma_device_type_playback) {
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
} else {
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
}
if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return ma_result_from_OSStatus(status);
}
#if defined(MA_APPLE_DESKTOP)
result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat);
if (result != MA_SUCCESS) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return result;
}
/*
Technical Note TN2091: Device input using the HAL Output Audio Unit
https://developer.apple.com/library/archive/technotes/tn2091/_index.html
This documentation says the following:
The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY
variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate
conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with
another AudioConverter.
The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We
therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it
safe and apply the same rule to output as well.
I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender()
returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but
this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format.
Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with
this, however, is that it actually changes the sample rate at the operating system level and not just the application. This
could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a
configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample
rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run
the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is
changed by miniaudio.
*/
if (pData->allowNominalSampleRateChange) {
AudioValueRange sampleRateRange;
AudioObjectPropertyAddress propAddress;
sampleRateRange.mMinimum = bestFormat.mSampleRate;
sampleRateRange.mMaximum = bestFormat.mSampleRate;
propAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
propAddress.mElement = kAudioObjectPropertyElementMaster;
status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange);
if (status != noErr) {
bestFormat.mSampleRate = origFormat.mSampleRate;
}
} else {
bestFormat.mSampleRate = origFormat.mSampleRate;
}
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
deviceName = pDeviceID->oss;
}
flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
if (shareMode == ma_share_mode_exclusive) {
flags |= O_EXCL;
}
*pfd = open(deviceName, flags, 0);
if (*pfd == -1) {
return ma_result_from_errno(errno);
}
return MA_SUCCESS;
}
static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
int fd;
oss_sysinfo si;
int result;
MA_ASSERT(pContext != NULL);
MA_ASSERT(callback != NULL);
fd = ma_open_temp_device__oss();
if (fd == -1) {
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.");
return MA_NO_BACKEND;
}
result = ioctl(fd, SNDCTL_SYSINFO, &si);
if (result != -1) {
int iAudioDevice;
for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
oss_audioinfo ai;
ai.dev = iAudioDevice;
result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
if (result != -1) {
if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
ma_device_info deviceInfo;
ma_bool32 isTerminating = MA_FALSE;
MA_ZERO_OBJECT(&deviceInfo);
/* ID */
ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
/*
The human readable device name should be in the "ai.handle" variable, but it can
sometimes be empty in which case we just fall back to "ai.name" which is less user
friendly, but usually has a value.
*/
if (ai.handle[0] != '\0') {
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
} else {
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
}
/* The device can be both playback and capture. */
if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
}
if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
}
if (isTerminating) {
break;
}
}
}
}
} else {
close(fd);
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
return MA_NO_BACKEND;
}
close(fd);
return MA_SUCCESS;
}
static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo)
{
unsigned int minChannels;
unsigned int maxChannels;
unsigned int iRate;
MA_ASSERT(pContext != NULL);
MA_ASSERT(pAudioInfo != NULL);
MA_ASSERT(pDeviceInfo != NULL);
/* If we support all channels we just report 0. */
minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
/*
OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness,
which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which
case we'll need to use min_rate and max_rate and report only standard rates.
*/
if (pAudioInfo->nrates > 0) {
for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) {
unsigned int rate = pAudioInfo->rates[iRate];
if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */
} else {
unsigned int iChannel;
for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0);
}
}
}
} else {
for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) {
ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate];
if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) {
if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */
} else {
unsigned int iChannel;
for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0);
}
}
}
}
}
}
static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
{
ma_bool32 foundDevice;
int fdTemp;
oss_sysinfo si;
int result;
MA_ASSERT(pContext != NULL);
/* Handle the default device a little differently. */
if (pDeviceID == NULL) {
if (deviceType == ma_device_type_playback) {
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
} else {
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
}
return MA_SUCCESS;
}
/* If we get here it means we are _not_ using the default device. */
foundDevice = MA_FALSE;
fdTemp = ma_open_temp_device__oss();
if (fdTemp == -1) {
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.");
return MA_NO_BACKEND;
}
result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
if (result != -1) {
int iAudioDevice;
for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
oss_audioinfo ai;
ai.dev = iAudioDevice;
result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
if (result != -1) {
if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
/* It has the same name, so now just confirm the type. */
if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
(deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
unsigned int formatMask;
/* ID */
ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
/*
The human readable device name should be in the "ai.handle" variable, but it can
sometimes be empty in which case we just fall back to "ai.name" which is less user
friendly, but usually has a value.
*/
if (ai.handle[0] != '\0') {
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
} else {
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
}
pDeviceInfo->nativeDataFormatCount = 0;
if (deviceType == ma_device_type_playback) {
formatMask = ai.oformats;
} else {
formatMask = ai.iformats;
}
if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo);
}
if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo);
}
if ((formatMask & AFMT_U8) != 0) {
ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo);
}
foundDevice = MA_TRUE;
break;
}
}
}
}
} else {
close(fdTemp);
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
return MA_NO_BACKEND;
}
close(fdTemp);
if (!foundDevice) {
return MA_NO_DEVICE;
}
return MA_SUCCESS;
}
static ma_result ma_device_uninit__oss(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* Grab the OSS version. */
ossVersion = 0;
result = ioctl(fd, OSS_GETVERSION, &ossVersion);
if (result == -1) {
close(fd);
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.");
return MA_NO_BACKEND;
}
/* The file handle to temp device is no longer needed. Close ASAP. */
close(fd);
pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
pCallbacks->onContextInit = ma_context_init__oss;
pCallbacks->onContextUninit = ma_context_uninit__oss;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss;
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss;
pCallbacks->onDeviceInit = ma_device_init__oss;
pCallbacks->onDeviceUninit = ma_device_uninit__oss;
pCallbacks->onDeviceStart = ma_device_start__oss;
pCallbacks->onDeviceStop = ma_device_stop__oss;
pCallbacks->onDeviceRead = ma_device_read__oss;
pCallbacks->onDeviceWrite = ma_device_write__oss;
pCallbacks->onDeviceDataLoop = NULL;
return MA_SUCCESS;
}
#endif /* OSS */
/******************************************************************************
AAudio Backend
******************************************************************************/
#ifdef MA_HAS_AAUDIO
/*#include <AAudio/AAudio.h>*/
typedef int32_t ma_aaudio_result_t;
typedef int32_t ma_aaudio_direction_t;
typedef int32_t ma_aaudio_sharing_mode_t;
typedef int32_t ma_aaudio_format_t;
typedef int32_t ma_aaudio_stream_state_t;
typedef int32_t ma_aaudio_performance_mode_t;
typedef int32_t ma_aaudio_usage_t;
typedef int32_t ma_aaudio_content_type_t;
typedef int32_t ma_aaudio_input_preset_t;
typedef int32_t ma_aaudio_data_callback_result_t;
typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
typedef struct ma_AAudioStream_t* ma_AAudioStream;
#define MA_AAUDIO_UNSPECIFIED 0
/* Result codes. miniaudio only cares about the success code. */
#define MA_AAUDIO_OK 0
/* Directions. */
#define MA_AAUDIO_DIRECTION_OUTPUT 0
#define MA_AAUDIO_DIRECTION_INPUT 1
/* Sharing modes. */
#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
#define MA_AAUDIO_SHARING_MODE_SHARED 1
/* Formats. */
#define MA_AAUDIO_FORMAT_PCM_I16 1
#define MA_AAUDIO_FORMAT_PCM_FLOAT 2
/* Stream states. */
#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
#define MA_AAUDIO_STREAM_STATE_OPEN 2
#define MA_AAUDIO_STREAM_STATE_STARTING 3
#define MA_AAUDIO_STREAM_STATE_STARTED 4
#define MA_AAUDIO_STREAM_STATE_PAUSING 5
#define MA_AAUDIO_STREAM_STATE_PAUSED 6
#define MA_AAUDIO_STREAM_STATE_FLUSHING 7
#define MA_AAUDIO_STREAM_STATE_FLUSHED 8
#define MA_AAUDIO_STREAM_STATE_STOPPING 9
#define MA_AAUDIO_STREAM_STATE_STOPPED 10
#define MA_AAUDIO_STREAM_STATE_CLOSING 11
#define MA_AAUDIO_STREAM_STATE_CLOSED 12
#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
/* Performance modes. */
#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
/* Usage types. */
#define MA_AAUDIO_USAGE_MEDIA 1
#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2
#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3
#define MA_AAUDIO_USAGE_ALARM 4
#define MA_AAUDIO_USAGE_NOTIFICATION 5
#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6
#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10
#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11
#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12
#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13
#define MA_AAUDIO_USAGE_GAME 14
#define MA_AAUDIO_USAGE_ASSISTANT 16
#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000
#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001
#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002
#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003
/* Content types. */
#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1
#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2
#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3
#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4
/* Input presets. */
#define MA_AAUDIO_INPUT_PRESET_GENERIC 1
#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5
#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6
#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7
#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9
#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10
/* Callback results. */
#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
#define MA_AAUDIO_CALLBACK_RESULT_STOP 1
typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);
typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType);
typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType);
typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset);
typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds);
typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
{
switch (resultAA)
{
case MA_AAUDIO_OK: return MA_SUCCESS;
default: break;
}
return MA_ERROR;
}
static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage)
{
switch (usage) {
case ma_aaudio_usage_announcement: return MA_AAUDIO_USAGE_MEDIA;
case ma_aaudio_usage_emergency: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION;
case ma_aaudio_usage_safety: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_USAGE_ALARM;
case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_NOTIFICATION;
case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE;
case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT;
case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION;
case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_GAME;
case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_ASSISTANT;
case ma_aaudio_usage_notification_event: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY;
case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_SYSTEM_USAGE_SAFETY;
case ma_aaudio_usage_voice_communication: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS;
case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT;
default: break;
}
return MA_AAUDIO_USAGE_MEDIA;
}
static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType)
{
switch (contentType) {
case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE;
case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC;
case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION;
case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH;
default: break;
}
return MA_AAUDIO_CONTENT_TYPE_SPEECH;
}
static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset)
{
switch (inputPreset) {
case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC;
case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER;
case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED;
case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION;
case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE;
default: break;
}
return MA_AAUDIO_INPUT_PRESET_GENERIC;
}
static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
{
ma_device* pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
(void)error;
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream));
/*
From the documentation for AAudio, when a device is disconnected all we can do is stop it. However, we cannot stop it from the callback - we need
to do it from another thread. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely.
*/
if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
/* We need to post a job to the job thread for processing. This will reroute the device by reinitializing the stream. */
ma_result result;
ma_job job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE);
job.data.device.aaudio.reroute.pDevice = pDevice;
if (pStream == pDevice->aaudio.pStreamCapture) {
job.data.device.aaudio.reroute.deviceType = ma_device_type_capture;
} else {
job.data.device.aaudio.reroute.deviceType = ma_device_type_playback;
}
result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job);
if (result != MA_SUCCESS) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n");
return;
}
}
}
static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
{
ma_device* pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount);
(void)pStream;
return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
}
static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
{
ma_device* pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount);
(void)pStream;
return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
}
static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma...
{
ma_AAudioStreamBuilder* pBuilder;
ma_aaudio_result_t resultAA;
ma_uint32 bufferCapacityInFrames;
/* Safety. */
*ppBuilder = NULL;
resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
if (resultAA != MA_AAUDIO_OK) {
return ma_result_from_aaudio(resultAA);
}
if (pDeviceID != NULL) {
((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
}
((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
/* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */
if (pDescriptor != NULL) {
MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */
if (pDescriptor->sampleRate != 0) {
((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);
}
if (deviceType == ma_device_type_capture) {
if (pDescriptor->channels != 0) {
((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
}
if (pDescriptor->format != ma_format_unknown) {
((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
}
} else {
if (pDescriptor->channels != 0) {
((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
}
if (pDescriptor->format != ma_format_unknown) {
((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
}
}
/*
AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you
retrieve the actual sample rate until after you've opened the stream. But you need to configure
the buffer capacity before you open the stream... :/
To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on.
*/
bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount;
((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount);
if (deviceType == ma_device_type_capture) {
if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) {
((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset));
}
((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
} else {
if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) {
((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage));
}
if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) {
((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType));
}
((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
}
/* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */
((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFOR...
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
{
ma_result result;
ma_device_job_thread_config jobThreadConfig;
jobThreadConfig = ma_device_job_thread_config_init();
result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread);
if (result != MA_SUCCESS) {
ma_dlclose(pContext, pContext->aaudio.hAAudio);
pContext->aaudio.hAAudio = NULL;
return result;
}
}
(void)pConfig;
return MA_SUCCESS;
}
static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
{
ma_device* pDevice;
MA_ASSERT(pJob != NULL);
pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice;
MA_ASSERT(pDevice != NULL);
/* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */
return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType);
}
#else
/* Getting here means there is no AAudio backend so we need a no-op job implementation. */
static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
{
return ma_job_process__noop(pJob);
}
#endif /* AAudio */
/******************************************************************************
OpenSL|ES Backend
******************************************************************************/
#ifdef MA_HAS_OPENSL
#include <SLES/OpenSLES.h>
#ifdef MA_ANDROID
#include <SLES/OpenSLES_Android.h>
#endif
typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);
/* OpenSL|ES has one-per-application objects :( */
static SLObjectItf g_maEngineObjectSL = NULL;
static SLEngineItf g_maEngineSL = NULL;
static ma_uint32 g_maOpenSLInitCounter = 0;
static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */
#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
#ifdef MA_ANDROID
#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
#else
#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
#endif
static ma_result ma_result_from_OpenSL(SLuint32 result)
{
switch (result)
{
case SL_RESULT_SUCCESS: return MA_SUCCESS;
case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR;
case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS;
case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY;
case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA;
case SL_RESULT_RESOURCE_LOST: return MA_ERROR;
case SL_RESULT_IO_ERROR: return MA_IO_ERROR;
case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE;
case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA;
case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED;
case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR;
case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED;
case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED;
case SL_RESULT_INTERNAL_ERROR: return MA_ERROR;
case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR;
case SL_RESULT_OPERATION_ABORTED: return MA_ERROR;
case SL_RESULT_CONTROL_LOST: return MA_ERROR;
default: return MA_ERROR;
}
}
/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
{
switch (id)
{
case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
default: return 0;
}
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
return ma_result_from_OpenSL(resultSL);
}
for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
deviceInfo.id.opensl = pDeviceIDs[iDevice];
SLAudioOutputDescriptor desc;
resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
if (resultSL == SL_RESULT_SUCCESS) {
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
if (cbResult == MA_FALSE) {
isTerminated = MA_TRUE;
break;
}
}
}
}
/* Capture */
if (!isTerminated) {
resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
if (resultSL != SL_RESULT_SUCCESS) {
return ma_result_from_OpenSL(resultSL);
}
for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
deviceInfo.id.opensl = pDeviceIDs[iDevice];
SLAudioInputDescriptor desc;
resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
if (resultSL == SL_RESULT_SUCCESS) {
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
if (cbResult == MA_FALSE) {
isTerminated = MA_TRUE;
break;
}
}
}
}
return MA_SUCCESS;
#else
goto return_default_device;
#endif
return_default_device:;
cbResult = MA_TRUE;
/* Playback. */
if (cbResult) {
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
}
/* Capture. */
if (cbResult) {
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
}
return MA_SUCCESS;
}
static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pDeviceInfo != NULL);
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
pDeviceInfo->nativeDataFormatCount += 1;
}
static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo)
{
ma_uint32 minChannels = 1;
ma_uint32 maxChannels = 2;
ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000;
ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000;
ma_uint32 iChannel;
ma_uint32 iSampleRate;
MA_ASSERT(pContext != NULL);
MA_ASSERT(pDeviceInfo != NULL);
/*
Each sample format can support mono and stereo, and we'll support a small subset of standard
rates (up to 48000). A better solution would be to somehow find a native sample rate.
*/
for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) {
for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo);
}
}
}
}
static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */
if (g_maOpenSLInitCounter == 0) {
return MA_INVALID_OPERATION;
}
/*
TODO: Test Me.
This is currently untested, so for now we are just returning default devices.
*/
#if 0 && !defined(MA_ANDROID)
SLAudioIODeviceCapabilitiesItf deviceCaps;
SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
if (resultSL != SL_RESULT_SUCCESS) {
/* The interface may not be supported so just report a default device. */
goto return_default_device;
}
if (deviceType == ma_device_type_playback) {
SLAudioOutputDescriptor desc;
resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
if (resultSL != SL_RESULT_SUCCESS) {
return ma_result_from_OpenSL(resultSL);
}
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
} else {
SLAudioInputDescriptor desc;
resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
if (resultSL != SL_RESULT_SUCCESS) {
return ma_result_from_OpenSL(resultSL);
}
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
}
goto return_detailed_info;
#else
goto return_default_device;
#endif
return_default_device:
if (pDeviceID != NULL) {
if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
(deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
return MA_NO_DEVICE; /* Don't know the device. */
}
}
/* ID and Name / Description */
if (deviceType == ma_device_type_playback) {
pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
} else {
pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
}
pDeviceInfo->isDefault = MA_TRUE;
goto return_detailed_info;
return_detailed_info:
/*
For now we're just outputting a set of values that are supported by the API but not necessarily supported
by the device natively. Later on we should work on this so that it more closely reflects the device's
actual native format.
*/
pDeviceInfo->nativeDataFormatCount = 0;
#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo);
#endif
ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo);
ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo);
return MA_SUCCESS;
}
#ifdef MA_ANDROID
/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
{
ma_device* pDevice = (ma_device*)pUserData;
size_t periodSizeInBytes;
ma_uint8* pBuffer;
SLresult resultSL;
MA_ASSERT(pDevice != NULL);
(void)pBufferQueue;
/*
For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
*/
/* Don't do anything if the device is not started. */
if (ma_device_get_state(pDevice) != ma_device_state_started) {
return;
}
/* Don't do anything if the device is being drained. */
if (pDevice->opensl.isDrainingCapture) {
return;
}
periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
*pChannels = pDataFormat->numChannels;
*pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);
return MA_SUCCESS;
}
static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
{
#ifdef MA_ANDROID
SLDataLocator_AndroidSimpleBufferQueue queue;
SLresult resultSL;
size_t bufferSizeInBytes;
SLInterfaceID itfIDs[2];
const SLboolean itfIDsRequired[] = {
SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */
SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */
};
#endif
MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */
if (g_maOpenSLInitCounter == 0) {
return MA_INVALID_OPERATION;
}
if (pConfig->deviceType == ma_device_type_loopback) {
return MA_DEVICE_TYPE_NOT_SUPPORTED;
}
/*
For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
been able to test with and I currently depend on Android-specific extensions (simple buffer
queues).
*/
#ifdef MA_ANDROID
itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION;
/* No exclusive mode with OpenSL|ES. */
if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
return MA_SHARE_MODE_NOT_SUPPORTED;
}
/* Now we can start initializing the device properly. */
MA_ASSERT(pDevice != NULL);
MA_ZERO_OBJECT(&pDevice->opensl);
queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
ma_SLDataFormat_PCM pcm;
SLDataLocator_IODevice locatorDevice;
SLDataSource source;
SLDataSink sink;
SLAndroidConfigurationItf pRecorderConfig;
ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm);
locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */
locatorDevice.device = NULL;
source.pLocator = &locatorDevice;
source.pFormat = NULL;
queue.numBuffers = pDescriptorCapture->periodCount;
sink.pLocator = &queue;
sink.pFormat = (SLDataFormat_PCM*)&pcm;
resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
/* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
pcm.formatType = SL_DATAFORMAT_PCM;
pcm.numChannels = 1;
((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
pcm.bitsPerSample = 16;
pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
pcm.channelMask = 0;
resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
}
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.");
return ma_result_from_OpenSL(resultSL);
}
/* Set the recording preset before realizing the player. */
if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) {
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig);
if (resultSL == SL_RESULT_SUCCESS) {
SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset);
resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32));
if (resultSL != SL_RESULT_SUCCESS) {
/* Failed to set the configuration. Just keep going. */
}
}
}
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.");
return ma_result_from_OpenSL(resultSL);
}
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.");
return ma_result_from_OpenSL(resultSL);
}
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
return ma_result_from_OpenSL(resultSL);
}
resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
return ma_result_from_OpenSL(resultSL);
}
/* The internal format is determined by the "pcm" object. */
ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap));
/* Buffer. */
pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
pDevice->opensl.currentBufferIndexCapture = 0;
bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount;
pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
if (pDevice->opensl.pBufferCapture == NULL) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
return MA_OUT_OF_MEMORY;
}
MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
}
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
ma_SLDataFormat_PCM pcm;
SLDataSource source;
SLDataLocator_OutputMix outmixLocator;
SLDataSink sink;
SLAndroidConfigurationItf pPlayerConfig;
ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm);
resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.");
return ma_result_from_OpenSL(resultSL);
}
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.");
return ma_result_from_OpenSL(resultSL);
}
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.");
return ma_result_from_OpenSL(resultSL);
}
/* Set the output device. */
if (pDescriptorPlayback->pDeviceID != NULL) {
SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl;
MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
}
queue.numBuffers = pDescriptorPlayback->periodCount;
source.pLocator = &queue;
source.pFormat = (SLDataFormat_PCM*)&pcm;
outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
sink.pLocator = &outmixLocator;
sink.pFormat = NULL;
resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
/* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
pcm.formatType = SL_DATAFORMAT_PCM;
pcm.numChannels = 2;
((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
pcm.bitsPerSample = 16;
pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
}
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.");
return ma_result_from_OpenSL(resultSL);
}
/* Set the stream type before realizing the player. */
if (pConfig->opensl.streamType != ma_opensl_stream_type_default) {
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig);
if (resultSL == SL_RESULT_SUCCESS) {
SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType);
resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
if (resultSL != SL_RESULT_SUCCESS) {
/* Failed to set the configuration. Just keep going. */
}
}
}
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.");
return ma_result_from_OpenSL(resultSL);
}
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.");
return ma_result_from_OpenSL(resultSL);
}
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
return ma_result_from_OpenSL(resultSL);
}
resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);
if (resultSL != SL_RESULT_SUCCESS) {
ma_device_uninit__opensl(pDevice);
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
#if !defined(MA_NO_RUNTIME_LINKING)
size_t i;
const char* libOpenSLESNames[] = {
"libOpenSLES.so"
};
#endif
MA_ASSERT(pContext != NULL);
(void)pConfig;
#if !defined(MA_NO_RUNTIME_LINKING)
/*
Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One
report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime
and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any
references to the symbols and will hopefully skip the checks.
*/
for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {
pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]);
if (pContext->opensl.libOpenSLES != NULL) {
break;
}
}
if (pContext->opensl.libOpenSLES == NULL) {
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so");
return MA_NO_BACKEND;
}
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE);
if (result != MA_SUCCESS) {
ma_dlclose(pContext, pContext->opensl.libOpenSLES);
return result;
}
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);
if (result != MA_SUCCESS) {
ma_dlclose(pContext, pContext->opensl.libOpenSLES);
return result;
}
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);
if (result != MA_SUCCESS) {
ma_dlclose(pContext, pContext->opensl.libOpenSLES);
return result;
}
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD);
if (result != MA_SUCCESS) {
ma_dlclose(pContext, pContext->opensl.libOpenSLES);
return result;
}
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY);
if (result != MA_SUCCESS) {
ma_dlclose(pContext, pContext->opensl.libOpenSLES);
return result;
}
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX);
if (result != MA_SUCCESS) {
ma_dlclose(pContext, pContext->opensl.libOpenSLES);
return result;
}
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION);
if (result != MA_SUCCESS) {
ma_dlclose(pContext, pContext->opensl.libOpenSLES);
return result;
}
pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine");
if (pContext->opensl.slCreateEngine == NULL) {
ma_dlclose(pContext, pContext->opensl.libOpenSLES);
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine.");
return MA_NO_BACKEND;
}
#else
pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE;
pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES;
pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD;
pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY;
pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX;
pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION;
pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine;
#endif
/* Initialize global data first if applicable. */
ma_spinlock_lock(&g_maOpenSLSpinlock);
{
result = ma_context_init_engine_nolock__opensl(pContext);
}
ma_spinlock_unlock(&g_maOpenSLSpinlock);
if (result != MA_SUCCESS) {
ma_dlclose(pContext, pContext->opensl.libOpenSLES);
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine.");
return result;
}
pCallbacks->onContextInit = ma_context_init__opensl;
pCallbacks->onContextUninit = ma_context_uninit__opensl;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl;
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl;
pCallbacks->onDeviceInit = ma_device_init__opensl;
pCallbacks->onDeviceUninit = ma_device_uninit__opensl;
pCallbacks->onDeviceStart = ma_device_start__opensl;
pCallbacks->onDeviceStop = ma_device_stop__opensl;
pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */
pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */
pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */
return MA_SUCCESS;
}
#endif /* OpenSL|ES */
/******************************************************************************
Web Audio Backend
******************************************************************************/
#ifdef MA_HAS_WEBAUDIO
#include <emscripten/emscripten.h>
static ma_bool32 ma_is_capture_supported__webaudio()
{
return EM_ASM_INT({
return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
}, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
}
#ifdef __cplusplus
extern "C" {
#endif
void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
{
ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount);
}
void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
{
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager)
{
ma_result result;
ma_job job;
if (pResourceManager == NULL) {
return MA_INVALID_ARGS;
}
/* This will return MA_CANCELLED if the next job is a quit job. */
result = ma_resource_manager_next_job(pResourceManager, &job);
if (result != MA_SUCCESS) {
return result;
}
return ma_job_process(&job);
}
#else
/* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */
static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
#endif /* MA_NO_RESOURCE_MANAGER */
#ifndef MA_NO_NODE_GRAPH
/* 10ms @ 48K = 480. Must never exceed 65535. */
#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS
#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480
#endif
static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime);
MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
{
#ifndef MA_NO_GENERATION
{
ma_waveform_config waveformConfig;
ma_waveform waveform;
waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400);
ma_waveform_init(&waveformConfig, &waveform);
ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL);
}
#else
{
(void)pFramesOut;
(void)frameCount;
(void)format;
(void)channels;
(void)sampleRate;
#if defined(MA_DEBUG_OUTPUT)
{
#if _MSC_VER
#pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.")
#endif
}
#endif
}
#endif
}
static ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
{
ma_uint64 iSample;
ma_uint64 sampleCount;
if (pDst == NULL || pSrc == NULL || channels == 0) {
return MA_INVALID_ARGS;
}
if (volume == 0) {
return MA_SUCCESS; /* No changes if the volume is 0. */
}
sampleCount = frameCount * channels;
if (volume == 1) {
for (iSample = 0; iSample < sampleCount; iSample += 1) {
pDst[iSample] += pSrc[iSample];
}
} else {
for (iSample = 0; iSample < sampleCount; iSample += 1) {
pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume);
}
}
return MA_SUCCESS;
}
MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels)
{
ma_node_graph_config config;
MA_ZERO_OBJECT(&config);
config.channels = channels;
config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
return config;
}
static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading)
{
MA_ASSERT(pNodeGraph != NULL);
c89atomic_exchange_32(&pNodeGraph->isReading, isReading);
}
#if 0
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE);
{
result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint));
}
ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE);
totalFramesRead += framesJustRead;
if (result != MA_SUCCESS) {
break;
}
/* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */
if (framesJustRead == 0) {
break;
}
}
/* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */
if (totalFramesRead < frameCount) {
ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels);
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
return result;
}
MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph)
{
if (pNodeGraph == NULL) {
return 0;
}
return ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
}
MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph)
{
if (pNodeGraph == NULL) {
return 0;
}
return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */
}
MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime)
{
if (pNodeGraph == NULL) {
return MA_INVALID_ARGS;
}
return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */
}
#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */
static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus)
{
MA_ASSERT(pOutputBus != NULL);
MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT);
MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode));
MA_ASSERT(channels < 256);
MA_ZERO_OBJECT(pOutputBus);
if (channels == 0) {
return MA_INVALID_ARGS;
}
pOutputBus->pNode = pNode;
pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex;
pOutputBus->channels = (ma_uint8)channels;
pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */
pOutputBus->volume = 1;
return MA_SUCCESS;
}
static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus)
{
ma_spinlock_lock(&pOutputBus->lock);
}
static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus)
{
ma_spinlock_unlock(&pOutputBus->lock);
}
static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus)
{
return pOutputBus->channels;
}
static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead)
{
if (hasRead) {
c89atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
} else {
c89atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
}
}
static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus)
{
return (c89atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0;
}
static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached)
{
c89atomic_exchange_32(&pOutputBus->isAttached, isAttached);
}
static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus)
{
return c89atomic_load_32(&pOutputBus->isAttached);
}
static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume)
{
MA_ASSERT(pOutputBus != NULL);
if (volume < 0.0f) {
volume = 0.0f;
}
c89atomic_exchange_f32(&pOutputBus->volume, volume);
return MA_SUCCESS;
}
static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus)
{
return c89atomic_load_f32((float*)&pOutputBus->volume);
}
static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus)
{
MA_ASSERT(pInputBus != NULL);
MA_ASSERT(channels < 256);
MA_ZERO_OBJECT(pInputBus);
if (channels == 0) {
return MA_INVALID_ARGS;
}
pInputBus->channels = (ma_uint8)channels;
return MA_SUCCESS;
}
static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus)
{
ma_spinlock_lock(&pInputBus->lock);
}
static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus)
{
ma_spinlock_unlock(&pInputBus->lock);
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus)
{
return ma_node_input_bus_next(pInputBus, &pInputBus->head);
}
static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime)
{
ma_result result = MA_SUCCESS;
ma_node_output_bus* pOutputBus;
ma_node_output_bus* pFirst;
ma_uint32 inputChannels;
ma_bool32 doesOutputBufferHaveContent = MA_FALSE;
/*
This will be called from the audio thread which means we can't be doing any locking. Basically,
this function will not perfom any locking, whereas attaching and detaching will, but crafted in
such a way that we don't need to perform any locking here. The important thing to remember is
to always iterate in a forward direction.
In order to process any data we need to first read from all input buses. That's where this
function comes in. This iterates over each of the attachments and accumulates/mixes them. We
also convert the channels to the nodes output channel count before mixing. We want to do this
channel conversion so that the caller of this function can invoke the processing callback
without having to do it themselves.
When we iterate over each of the attachments on the input bus, we need to read as much data as
we can from each of them so that we don't end up with holes between each of the attachments. To
do this, we need to read from each attachment in a loop and read as many frames as we can, up
to `frameCount`.
*/
MA_ASSERT(pInputNode != NULL);
MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */
*pFramesRead = 0; /* Safety. */
inputChannels = ma_node_input_bus_get_channels(pInputBus);
/*
We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They
are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first()
once per iteration, however we have an optimization to checks whether or not it's the first item in
the list. We therefore need to store a pointer to the first item rather than repeatedly calling
ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it
after calling ma_node_input_bus_next(), which we won't be.
*/
pFirst = ma_node_input_bus_first(pInputBus);
if (pFirst == NULL) {
return MA_SUCCESS; /* No attachments. Read nothing. */
}
for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) {
ma_uint32 framesProcessed = 0;
ma_bool32 isSilentOutput = MA_FALSE;
MA_ASSERT(pOutputBus->pNode != NULL);
isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0;
if (pFramesOut != NULL) {
/* Read. */
float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels;
while (framesProcessed < frameCount) {
float* pRunningFramesOut;
ma_uint32 framesToRead;
ma_uint32 framesJustRead;
framesToRead = frameCount - framesProcessed;
if (framesToRead > tempCapInFrames) {
framesToRead = tempCapInFrames;
}
pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels);
if (doesOutputBufferHaveContent == MA_FALSE) {
/* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */
result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed);
} else {
/* Slow path. Not the first attachment. Mixing required. */
result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed);
if (result == MA_SUCCESS || result == MA_AT_END) {
if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */
ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1);
}
}
}
framesProcessed += framesJustRead;
/* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */
if (result != MA_SUCCESS) {
break;
}
/* If we didn't read anything, abort so we don't get stuck in a loop. */
if (framesJustRead == 0) {
break;
}
}
/* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */
if (pOutputBus == pFirst && framesProcessed < frameCount) {
ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels);
}
if (isSilentOutput == MA_FALSE) {
doesOutputBufferHaveContent = MA_TRUE;
}
} else {
/* Seek. */
ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime);
}
}
/* If we didn't output anything, output silence. */
if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) {
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus);
/* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */
result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime);
if (result != MA_SUCCESS) {
/* It doesn't really matter if we fail because we'll just fill with silence. */
framesRead = 0; /* Just for safety, but I don't think it's really needed. */
}
/* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */
/* Any leftover frames need to silenced for safety. */
if (framesRead < framesToProcessIn) {
ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus));
}
maxFramesReadIn = ma_max(maxFramesReadIn, framesRead);
}
/* This was a fresh load of input data so reset our consumption counter. */
pNodeBase->consumedFrameCountIn = 0;
/*
We don't want to keep processing if there's nothing to process, so set the number of cached
input frames to the maximum number we read from each attachment (the lesser will be padded
with silence). If we didn't read anything, this will be set to 0 and the entire buffer will
have been assigned to silence. This being equal to 0 is an important property for us because
it allows us to detect when NULL can be passed into the processing callback for the input
buffer for the purpose of continuous processing.
*/
pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn;
} else {
/* We don't need to read anything, but we do need to prepare our input frame pointers. */
for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus));
}
}
/*
At this point we have our input data so now we need to do some processing. Sneaky little
optimization here - we can set the pointer to the output buffer for this output bus so
that the final copy into the output buffer is done directly by onProcess().
*/
if (pFramesOut != NULL) {
ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex));
}
/* Give the processing function the entire capacity of the output buffer. */
frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut);
/*
We need to treat nodes with continuous processing a little differently. For these ones,
we always want to fire the callback with the requested number of frames, regardless of
pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass
in NULL for the input buffer to the callback.
*/
if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) {
/* We're using continuous processing. Make sure we specify the whole frame count at all times. */
frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */
if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) {
consumeNullInput = MA_TRUE;
} else {
consumeNullInput = MA_FALSE;
}
/*
Since we're using continuous processing we're always passing in a full frame count
regardless of how much input data was read. If this is greater than what we read as
input, we'll end up with an underflow. We instead need to make sure our cached frame
count is set to the number of frames we'll be passing to the data callback. Not
doing this will result in an underflow when we "consume" the cached data later on.
Note that this check needs to be done after the "consumeNullInput" check above because
we use the property of cachedFrameCountIn being 0 to determine whether or not we
should be passing in a null pointer to the processing callback for when the node is
configured with MA_NODE_FLAG_ALLOW_NULL_INPUT.
*/
if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) {
pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn;
}
} else {
frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */
consumeNullInput = MA_FALSE;
}
/*
Process data slightly differently depending on whether or not we're consuming NULL
input (checked just above).
*/
if (consumeNullInput) {
ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
} else {
/*
We want to skip processing if there's no input data, but we can only do that safely if
we know that there is no chance of any output frames being produced. If continuous
processing is being used, this won't be a problem because the input frame count will
always be non-0. However, if continuous processing is *not* enabled and input and output
data is processed at different rates, we still need to process that last input frame
because there could be a few excess output frames needing to be produced from cached
data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for
determining whether or not we need to process the node even when there are no input
frames available right now.
*/
if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) {
ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? E...
} else {
frameCountOut = 0; /* No data was processed. */
}
}
/*
Thanks to our sneaky optimization above we don't need to do any data copying directly into
the output buffer - the onProcess() callback just did that for us. We do, however, need to
apply the number of input and output frames that were processed. Note that due to continuous
processing above, we need to do explicit checks here. If we just consumed a NULL input
buffer it means that no actual input data was processed from the internal buffers and we
don't want to be modifying any counters.
*/
if (consumeNullInput == MA_FALSE) {
pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn;
pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn;
}
/* The cached output frame count is always equal to what we just read. */
pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut;
/* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */
if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) {
break;
}
}
} else {
/*
We're not needing to read anything from the input buffer so just read directly from our
already-processed data.
*/
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
{}
#else
for (; k < n; k++)
{
float t[4][8], *x, *y = grbuf + k;
for (x = t[0], i = 0; i < 8; i++, x++)
{
float x0 = y[i*18];
float x1 = y[(15 - i)*18];
float x2 = y[(16 + i)*18];
float x3 = y[(31 - i)*18];
float t0 = x0 + x3;
float t1 = x1 + x2;
float t2 = (x1 - x2)*g_sec[3*i + 0];
float t3 = (x0 - x3)*g_sec[3*i + 1];
x[0] = t0 + t1;
x[8] = (t0 - t1)*g_sec[3*i + 2];
x[16] = t3 + t2;
x[24] = (t3 - t2)*g_sec[3*i + 2];
}
for (x = t[0], i = 0; i < 4; i++, x += 8)
{
float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt;
xt = x0 - x7; x0 += x7;
x7 = x1 - x6; x1 += x6;
x6 = x2 - x5; x2 += x5;
x5 = x3 - x4; x3 += x4;
x4 = x0 - x3; x0 += x3;
x3 = x1 - x2; x1 += x2;
x[0] = x0 + x1;
x[4] = (x0 - x1)*0.70710677f;
x5 = x5 + x6;
x6 = (x6 + x7)*0.70710677f;
x7 = x7 + xt;
x3 = (x3 + x4)*0.70710677f;
x5 -= x7*0.198912367f;
x7 += x5*0.382683432f;
x5 -= x7*0.198912367f;
x0 = xt - x6; xt += x6;
x[1] = (xt + x7)*0.50979561f;
x[2] = (x4 + x3)*0.54119611f;
x[3] = (x0 - x5)*0.60134488f;
x[5] = (x0 + x5)*0.89997619f;
x[6] = (x4 - x3)*1.30656302f;
x[7] = (xt - x7)*2.56291556f;
}
for (i = 0; i < 7; i++, y += 4*18)
{
y[0*18] = t[0][i];
y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];
y[2*18] = t[1][i] + t[1][i + 1];
y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];
}
y[0*18] = t[0][7];
y[1*18] = t[2][7] + t[3][7];
y[2*18] = t[1][7];
y[3*18] = t[3][7];
}
#endif
}
#ifndef DR_MP3_FLOAT_OUTPUT
typedef drmp3_int16 drmp3d_sample_t;
static drmp3_int16 drmp3d_scale_pcm(float sample)
{
drmp3_int16 s;
#if DRMP3_HAVE_ARMV6
drmp3_int32 s32 = (drmp3_int32)(sample + .5f);
s32 -= (s32 < 0);
s = (drmp3_int16)drmp3_clip_int16_arm(s32);
#else
if (sample >= 32766.5) return (drmp3_int16) 32767;
if (sample <= -32767.5) return (drmp3_int16)-32768;
s = (drmp3_int16)(sample + .5f);
s -= (s < 0);
#endif
return s;
}
#else
typedef float drmp3d_sample_t;
static float drmp3d_scale_pcm(float sample)
{
return sample*(1.f/32768.f);
}
#endif
static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z)
{
float a;
a = (z[14*64] - z[ 0]) * 29;
a += (z[ 1*64] + z[13*64]) * 213;
a += (z[12*64] - z[ 2*64]) * 459;
a += (z[ 3*64] + z[11*64]) * 2037;
a += (z[10*64] - z[ 4*64]) * 5153;
a += (z[ 5*64] + z[ 9*64]) * 6574;
a += (z[ 8*64] - z[ 6*64]) * 37489;
a += z[ 7*64] * 75038;
pcm[0] = drmp3d_scale_pcm(a);
z += 2;
a = z[14*64] * 104;
a += z[12*64] * 1567;
a += z[10*64] * 9727;
a += z[ 8*64] * 64019;
a += z[ 6*64] * -9975;
a += z[ 4*64] * -45;
a += z[ 2*64] * 146;
a += z[ 0*64] * -5;
pcm[16*nch] = drmp3d_scale_pcm(a);
}
static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
{
int i;
float *xr = xl + 576*(nch - 1);
drmp3d_sample_t *dstr = dstl + (nch - 1);
static const float g_win[] = {
-1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
-1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,
-1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,
-1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,
-1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,
-1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,
-2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,
-2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,
-2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,
-2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,
-3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,
-3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,
-4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,
-4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,
-5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290
};
float *zlin = lins + 15*64;
const float *w = g_win;
zlin[4*15] = xl[18*16];
zlin[4*15 + 1] = xr[18*16];
zlin[4*15 + 2] = xl[0];
zlin[4*15 + 3] = xr[0];
zlin[4*31] = xl[1 + 18*16];
zlin[4*31 + 1] = xr[1 + 18*16];
zlin[4*31 + 2] = xl[1];
zlin[4*31 + 3] = xr[1];
drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1);
drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);
drmp3d_synth_pair(dstl, nch, lins + 4*15);
drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);
#if DRMP3_HAVE_SIMD
if (drmp3_have_simd()) for (i = 14; i >= 0; i--)
{
#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]);
#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); }
#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); }
#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); }
drmp3_f4 a, b;
zlin[4*i] = xl[18*(31 - i)];
zlin[4*i + 1] = xr[18*(31 - i)];
zlin[4*i + 2] = xl[1 + 18*(31 - i)];
zlin[4*i + 3] = xr[1 + 18*(31 - i)];
zlin[4*i + 64] = xl[1 + 18*(1 + i)];
zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];
zlin[4*i - 64 + 2] = xl[18*(1 + i)];
zlin[4*i - 64 + 3] = xr[18*(1 + i)];
DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7)
{
#ifndef DR_MP3_FLOAT_OUTPUT
#if DRMP3_HAVE_SSE
static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
__m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
#else
int16x4_t pcma, pcmb;
a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);
vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);
vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);
vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);
vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);
vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);
vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
#endif
#else
#if DRMP3_HAVE_SSE
static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
#else
const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f);
#endif
a = DRMP3_VMUL(a, g_scale);
b = DRMP3_VMUL(b, g_scale);
#if DRMP3_HAVE_SSE
_mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
_mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
_mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
_mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
_mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
_mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
_mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
_mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
#else
vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
#endif
#endif
}
} else
#endif
#ifdef DR_MP3_ONLY_SIMD
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
if (*ppFile == NULL) {
return DRMP3_ERROR;
}
#endif
return DRMP3_SUCCESS;
}
static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
}
static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin)
{
return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
}
DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
{
drmp3_bool32 result;
FILE* pFile;
if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) {
return DRMP3_FALSE;
}
result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
if (result != DRMP3_TRUE) {
fclose(pFile);
return result;
}
return DRMP3_TRUE;
}
DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
{
drmp3_bool32 result;
FILE* pFile;
if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) {
return DRMP3_FALSE;
}
result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
if (result != DRMP3_TRUE) {
fclose(pFile);
return result;
}
return DRMP3_TRUE;
}
#endif
DRMP3_API void drmp3_uninit(drmp3* pMP3)
{
if (pMP3 == NULL) {
return;
}
#ifndef DR_MP3_NO_STDIO
if (pMP3->onRead == drmp3__on_read_stdio) {
FILE* pFile = (FILE*)pMP3->pUserData;
if (pFile != NULL) {
fclose(pFile);
pMP3->pUserData = NULL;
}
}
#endif
drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
}
#if defined(DR_MP3_FLOAT_OUTPUT)
static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount)
{
drmp3_uint64 i;
drmp3_uint64 i4;
drmp3_uint64 sampleCount4;
i = 0;
sampleCount4 = sampleCount >> 2;
for (i4 = 0; i4 < sampleCount4; i4 += 1) {
float x0 = src[i+0];
float x1 = src[i+1];
float x2 = src[i+2];
float x3 = src[i+3];
x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
x0 = x0 * 32767.0f;
x1 = x1 * 32767.0f;
x2 = x2 * 32767.0f;
x3 = x3 * 32767.0f;
dst[i+0] = (drmp3_int16)x0;
dst[i+1] = (drmp3_int16)x1;
dst[i+2] = (drmp3_int16)x2;
dst[i+3] = (drmp3_int16)x3;
i += 4;
}
for (; i < sampleCount; i += 1) {
float x = src[i];
x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
x = x * 32767.0f;
dst[i] = (drmp3_int16)x;
}
}
#endif
#if !defined(DR_MP3_FLOAT_OUTPUT)
static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount)
{
drmp3_uint64 i;
for (i = 0; i < sampleCount; i += 1) {
float x = (float)src[i];
x = x * 0.000030517578125f;
dst[i] = x;
}
}
#endif
static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut)
{
drmp3_uint64 totalFramesRead = 0;
DRMP3_ASSERT(pMP3 != NULL);
DRMP3_ASSERT(pMP3->onRead != NULL);
while (framesToRead > 0) {
drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
if (pBufferOut != NULL) {
#if defined(DR_MP3_FLOAT_OUTPUT)
float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels);
float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
#else
drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels);
drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels);
#endif
}
pMP3->currentPCMFrame += framesToConsume;
pMP3->pcmFramesConsumedInMP3Frame += framesToConsume;
pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;
totalFramesRead += framesToConsume;
framesToRead -= framesToConsume;
if (framesToRead == 0) {
break;
}
DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
if (drmp3_decode_next_frame(pMP3) == 0) {
break;
}
}
return totalFramesRead;
}
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
{
if (pMP3 == NULL || pMP3->onRead == NULL) {
return 0;
}
#if defined(DR_MP3_FLOAT_OUTPUT)
return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
#else
{
drmp3_int16 pTempS16[8192];
drmp3_uint64 totalPCMFramesRead = 0;
while (totalPCMFramesRead < framesToRead) {
drmp3_uint64 framesJustRead;
drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels;
if (framesToReadNow > framesRemaining) {
framesToReadNow = framesRemaining;
}
framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);
if (framesJustRead == 0) {
break;
}
drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
totalPCMFramesRead += framesJustRead;
}
return totalPCMFramesRead;
}
#endif
}
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut)
{
if (pMP3 == NULL || pMP3->onRead == NULL) {
return 0;
}
#if !defined(DR_MP3_FLOAT_OUTPUT)
return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
#else
{
float pTempF32[4096];
drmp3_uint64 totalPCMFramesRead = 0;
while (totalPCMFramesRead < framesToRead) {
drmp3_uint64 framesJustRead;
drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels;
if (framesToReadNow > framesRemaining) {
framesToReadNow = framesRemaining;
}
framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);
if (framesJustRead == 0) {
break;
}
drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
totalPCMFramesRead += framesJustRead;
}
return totalPCMFramesRead;
}
#endif
}
static void drmp3_reset(drmp3* pMP3)
{
DRMP3_ASSERT(pMP3 != NULL);
pMP3->pcmFramesConsumedInMP3Frame = 0;
pMP3->pcmFramesRemainingInMP3Frame = 0;
pMP3->currentPCMFrame = 0;
pMP3->dataSize = 0;
pMP3->atEnd = DRMP3_FALSE;
drmp3dec_init(&pMP3->decoder);
}
static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
{
DRMP3_ASSERT(pMP3 != NULL);
DRMP3_ASSERT(pMP3->onSeek != NULL);
if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
return DRMP3_FALSE;
}
drmp3_reset(pMP3);
return DRMP3_TRUE;
}
static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset)
{
drmp3_uint64 framesRead;
#if defined(DR_MP3_FLOAT_OUTPUT)
framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
#else
framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
#endif
if (framesRead != frameOffset) {
return DRMP3_FALSE;
}
return DRMP3_TRUE;
}
static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex)
{
DRMP3_ASSERT(pMP3 != NULL);
if (frameIndex == pMP3->currentPCMFrame) {
return DRMP3_TRUE;
}
if (frameIndex < pMP3->currentPCMFrame) {
if (!drmp3_seek_to_start_of_stream(pMP3)) {
return DRMP3_FALSE;
}
}
DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
}
static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex)
{
drmp3_uint32 iSeekPoint;
DRMP3_ASSERT(pSeekPointIndex != NULL);
*pSeekPointIndex = 0;
if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
return DRMP3_FALSE;
}
for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
break;
}
*pSeekPointIndex = iSeekPoint;
}
return DRMP3_TRUE;
}
static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex)
{
drmp3_seek_point seekPoint;
drmp3_uint32 priorSeekPointIndex;
drmp3_uint16 iMP3Frame;
drmp3_uint64 leftoverFrames;
DRMP3_ASSERT(pMP3 != NULL);
DRMP3_ASSERT(pMP3->pSeekPoints != NULL);
DRMP3_ASSERT(pMP3->seekPointCount > 0);
if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
} else {
seekPoint.seekPosInBytes = 0;
seekPoint.pcmFrameIndex = 0;
seekPoint.mp3FramesToDiscard = 0;
seekPoint.pcmFramesToDiscard = 0;
}
if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) {
return DRMP3_FALSE;
}
drmp3_reset(pMP3);