view release on metacpan or search on metacpan
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_device_set_master_volume()
ma_device_get_master_volume()
*/
MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB);
/*
Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback.
Parameters
----------
pDevice (in)
A pointer to device whose processing the data callback.
pOutput (out)
A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device
this can be NULL, in which case pInput must not be NULL.
pInput (in)
A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be
NULL, in which case `pOutput` must not be NULL.
frameCount (in)
The number of frames being processed.
Return Value
------------
MA_SUCCESS if successful; any other result code otherwise.
Thread Safety
-------------
This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a
playback and capture device in duplex setups.
Callback Safety
---------------
Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend.
Remarks
-------
If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in
which case `pInput` will be processed first, followed by `pOutput`.
If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that
callback.
*/
MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
/*
Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile.
This function is used by backends for helping determine an appropriately sized buffer to use with
the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the
`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a
best guess at the device's native sample rate is also required which is where `nativeSampleRate`
comes in. In addition, the performance profile is also needed for cases where both the period size
in frames and milliseconds are both zero.
Parameters
----------
pDescriptor (in)
A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members
will be used for the calculation of the buffer size.
nativeSampleRate (in)
The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of
`pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which
case a sample rate is required to convert to a size in frames.
performanceProfile (in)
When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are
zero, miniaudio will fall back to a buffer size based on the performance profile. The profile
to use for this calculation is determine by this parameter.
Return Value
------------
The calculated buffer size in frames.
Thread Safety
-------------
This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function
should only ever be called from within the backend's device initialization routine and therefore
shouldn't have any multithreading concerns.
Callback Safety
---------------
This is safe to call within the data callback, but there is no reason to ever do this.
Remarks
-------
If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that
is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead.
*/
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile);
/*
Retrieves a friendly name for a backend.
*/
MA_API const char* ma_get_backend_name(ma_backend backend);
/*
Determines whether or not the given backend is available by the compilation environment.
*/
MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend);
/*
Retrieves compile-time enabled backends.
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop.
All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected.
This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before
attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`.
The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an
internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the
host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind.
Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device.
The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call
`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get
information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object
is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to
run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the
context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up.
All of that just to retrieve basic information about a device!
Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the
context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design
choices in PulseAudio.
PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here
because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for
writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can
set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices
straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified,
PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation)
because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback
would be where a program will want to write or read data to or from the stream, but when it's called before the application has even
requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at
that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the
stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data
callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio
doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been
started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data
callback is not fired.
This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will
continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device
is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in
PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call
`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always
writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if
you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to
*not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining
important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained
before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write
data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again!
This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not*
write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just
resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This
disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the
callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.)
Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context,
only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as
"corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think
it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you
guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is
absolutely beyond me. Would it really be that hard to just make it run synchronously?
Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that
they were initialized in.
That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're
embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to
run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche
requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is
constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a
parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These
changes alone will change PulseAudio from one of the worst audio APIs to one of the best.
*/
/*
It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header
to check for type safety. We cannot do this when linking at run time because the header might not be available.
*/
#ifdef MA_NO_RUNTIME_LINKING
/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
#if !defined(__cplusplus)
#if defined(__STRICT_ANSI__)
#if !defined(inline)
#define inline __inline__ __attribute__((always_inline))
#define MA_INLINE_DEFINED
#endif
#endif
#endif
#include <pulse/pulseaudio.h>
#if defined(MA_INLINE_DEFINED)
#undef inline
#undef MA_INLINE_DEFINED
#endif
#define MA_PA_OK PA_OK
#define MA_PA_ERR_ACCESS PA_ERR_ACCESS
#define MA_PA_ERR_INVALID PA_ERR_INVALID
#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED
#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
#define MA_PA_RATE_MAX PA_RATE_MAX
typedef pa_context_flags_t ma_pa_context_flags_t;
#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
typedef pa_stream_flags_t ma_pa_stream_flags_t;
#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
result = ma_device_get_master_volume(pDevice, &factor);
if (result != MA_SUCCESS) {
*pGainDB = 0;
return result;
}
*pGainDB = ma_volume_linear_to_db(factor);
return MA_SUCCESS;
}
MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
if (pDevice == NULL) {
return MA_INVALID_ARGS;
}
if (pOutput == NULL && pInput == NULL) {
return MA_INVALID_ARGS;
}
if (pDevice->type == ma_device_type_duplex) {
if (pInput != NULL) {
ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb);
}
if (pOutput != NULL) {
ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb);
}
} else {
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) {
if (pInput == NULL) {
return MA_INVALID_ARGS;
}
ma_device__send_frames_to_client(pDevice, frameCount, pInput);
}
if (pDevice->type == ma_device_type_playback) {
if (pOutput == NULL) {
return MA_INVALID_ARGS;
}
ma_device__read_frames_from_client(pDevice, frameCount, pOutput);
}
}
return MA_SUCCESS;
}
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
{
if (pDescriptor == NULL) {
return 0;
}
/*
We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the
time when the size of the buffer needs to be determined. In this case we need to just take a best
guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll
just fall back to MA_DEFAULT_SAMPLE_RATE.
*/
if (nativeSampleRate == 0) {
nativeSampleRate = pDescriptor->sampleRate;
}
if (nativeSampleRate == 0) {
nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
}
MA_ASSERT(nativeSampleRate != 0);
if (pDescriptor->periodSizeInFrames == 0) {
if (pDescriptor->periodSizeInMilliseconds == 0) {
if (performanceProfile == ma_performance_profile_low_latency) {
return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate);
} else {
return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate);
}
} else {
return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
}
} else {
return pDescriptor->periodSizeInFrames;
}
}
#endif /* MA_NO_DEVICE_IO */
MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
{
/* Prevent a division by zero. */
if (sampleRate == 0) {
return 0;
}
return bufferSizeInFrames*1000 / sampleRate;
}
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
{
/* Prevent a division by zero. */
if (sampleRate == 0) {
return 0;
}
return bufferSizeInMilliseconds*sampleRate / 1000;
}
MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
{
if (dst == src) {
return; /* No-op. */
}
ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels));
}
MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
{
if (format == ma_format_u8) {
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn);
MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut);
frameCountIn = 0;
if (pFrameCountIn != NULL) {
frameCountIn = *pFrameCountIn;
}
frameCountOut = 0;
if (pFrameCountOut != NULL) {
frameCountOut = *pFrameCountOut;
}
framesProcessedIn = 0;
framesProcessedOut = 0;
tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
while (framesProcessedOut < frameCountOut) {
ma_uint64 frameCountInThisIteration;
ma_uint64 frameCountOutThisIteration;
const void* pRunningFramesIn = NULL;
void* pRunningFramesOut = NULL;
const void* pResampleBufferIn;
void* pChannelsBufferOut;
if (pFramesIn != NULL) {
pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
}
if (pFramesOut != NULL) {
pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
}
/* Run input data through the resampler and output it to the temporary buffer. */
frameCountInThisIteration = (frameCountIn - framesProcessedIn);
if (pConverter->hasPreFormatConversion) {
if (frameCountInThisIteration > tempBufferInCap) {
frameCountInThisIteration = tempBufferInCap;
}
}
frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
if (frameCountOutThisIteration > tempBufferMidCap) {
frameCountOutThisIteration = tempBufferMidCap;
}
/* We can't read more frames than can fit in the output buffer. */
if (pConverter->hasPostFormatConversion) {
if (frameCountOutThisIteration > tempBufferOutCap) {
frameCountOutThisIteration = tempBufferOutCap;
}
}
/* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */
/*
We need to try to predict how many input frames will be required for the resampler. If the
resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further
off we are from this, the more wasted format conversions we'll end up doing.
*/
#if 1
{
ma_uint64 requiredInputFrameCount;
result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
if (result != MA_SUCCESS) {
/* Fall back to a best guess. */
requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
}
if (frameCountInThisIteration > requiredInputFrameCount) {
frameCountInThisIteration = requiredInputFrameCount;
}
}
#endif
if (pConverter->hasPreFormatConversion) {
if (pFramesIn != NULL) {
ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
pResampleBufferIn = pTempBufferIn;
} else {
pResampleBufferIn = NULL;
}
} else {
pResampleBufferIn = pRunningFramesIn;
}
result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);
if (result != MA_SUCCESS) {
return result;
}
/*
The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do
this part if we have an output buffer.
*/
if (pFramesOut != NULL) {
if (pConverter->hasPostFormatConversion) {
pChannelsBufferOut = pTempBufferOut;
} else {
pChannelsBufferOut = pRunningFramesOut;
}
result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);
if (result != MA_SUCCESS) {
return result;
}
/* Finally we do post format conversion. */
if (pConverter->hasPostFormatConversion) {
ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
}
}
framesProcessedIn += frameCountInThisIteration;
framesProcessedOut += frameCountOutThisIteration;
MA_ASSERT(framesProcessedIn <= frameCountIn);
MA_ASSERT(framesProcessedOut <= frameCountOut);
if (frameCountOutThisIteration == 0) {
break; /* Consumed all of our input data. */
}
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
{
ma_result result;
ma_uint64 frameCountIn;
ma_uint64 frameCountOut;
ma_uint64 framesProcessedIn;
ma_uint64 framesProcessedOut;
ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
ma_uint64 tempBufferInCap;
ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
ma_uint64 tempBufferMidCap;
ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
ma_uint64 tempBufferOutCap;
MA_ASSERT(pConverter != NULL);
MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut);
MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn);
frameCountIn = 0;
if (pFrameCountIn != NULL) {
frameCountIn = *pFrameCountIn;
}
frameCountOut = 0;
if (pFrameCountOut != NULL) {
frameCountOut = *pFrameCountOut;
}
framesProcessedIn = 0;
framesProcessedOut = 0;
tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
while (framesProcessedOut < frameCountOut) {
ma_uint64 frameCountInThisIteration;
ma_uint64 frameCountOutThisIteration;
const void* pRunningFramesIn = NULL;
void* pRunningFramesOut = NULL;
const void* pChannelsBufferIn;
void* pResampleBufferOut;
if (pFramesIn != NULL) {
pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
}
if (pFramesOut != NULL) {
pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
}
/*
Before doing any processing we need to determine how many frames we should try processing
this iteration, for both input and output. The resampler requires us to perform format and
channel conversion before passing any data into it. If we get our input count wrong, we'll
end up peforming redundant pre-processing. This isn't the end of the world, but it does
result in some inefficiencies proportionate to how far our estimates are off.
If the resampler has a means to calculate exactly how much we'll need, we'll use that.
Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output
frame count first.
*/
frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
if (frameCountOutThisIteration > tempBufferMidCap) {
frameCountOutThisIteration = tempBufferMidCap;
}
if (pConverter->hasPostFormatConversion) {
if (frameCountOutThisIteration > tempBufferOutCap) {
frameCountOutThisIteration = tempBufferOutCap;
}
}
/* Now that we have the output frame count we can determine the input frame count. */
frameCountInThisIteration = (frameCountIn - framesProcessedIn);
if (pConverter->hasPreFormatConversion) {
if (frameCountInThisIteration > tempBufferInCap) {
frameCountInThisIteration = tempBufferInCap;
}
}
if (frameCountInThisIteration > tempBufferMidCap) {
frameCountInThisIteration = tempBufferMidCap;
}
#if 1
{
ma_uint64 requiredInputFrameCount;
result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
if (result != MA_SUCCESS) {
/* Fall back to a best guess. */
requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
}
if (frameCountInThisIteration > requiredInputFrameCount) {
frameCountInThisIteration = requiredInputFrameCount;
}
}
#endif
/* Pre format conversion. */
if (pConverter->hasPreFormatConversion) {
if (pRunningFramesIn != NULL) {
ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
pChannelsBufferIn = pTempBufferIn;
} else {
pChannelsBufferIn = NULL;
}
} else {
pChannelsBufferIn = pRunningFramesIn;
}
/* Channel conversion. */
result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);
if (result != MA_SUCCESS) {
return result;
}
/* Resampling. */
if (pConverter->hasPostFormatConversion) {
pResampleBufferOut = pTempBufferOut;
} else {
pResampleBufferOut = pRunningFramesOut;
}
result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);
if (result != MA_SUCCESS) {
return result;
}
/* Post format conversion. */
if (pConverter->hasPostFormatConversion) {
if (pRunningFramesOut != NULL) {
ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode);
}
}
framesProcessedIn += frameCountInThisIteration;
framesProcessedOut += frameCountOutThisIteration;
MA_ASSERT(framesProcessedIn <= frameCountIn);
MA_ASSERT(framesProcessedOut <= frameCountOut);
if (frameCountOutThisIteration == 0) {
break; /* Consumed all of our input data. */
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
return ma_resampler_reset(&pConverter->resampler);
}
/**************************************************************************************************************************************************************
Channel Maps
**************************************************************************************************************************************************************/
static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
{
if (pChannelMap == NULL) {
return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex);
} else {
if (channelIndex >= channelCount) {
return MA_CHANNEL_NONE;
}
return pChannelMap[channelIndex];
}
}
MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels)
{
if (pChannelMap == NULL) {
return;
}
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels);
}
static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex)
{
if (channelCount == 0 || channelIndex >= channelCount) {
return MA_CHANNEL_NONE;
}
/* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */
switch (channelCount)
{
case 0: return MA_CHANNEL_NONE;
case 1:
{
return MA_CHANNEL_MONO;
} break;
case 2:
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
}
} break;
case 3: /* No defined, but best guess. */
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_FRONT_CENTER;
}
} break;
case 4:
{
switch (channelIndex) {
#ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
/* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_FRONT_CENTER;
case 3: return MA_CHANNEL_BACK_CENTER;
#else
/* Quad. */
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_BACK_LEFT;
case 3: return MA_CHANNEL_BACK_RIGHT;
#endif
}
} break;
case 5: /* Not defined, but best guess. */
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_FRONT_CENTER;
case 3: return MA_CHANNEL_BACK_LEFT;
case 4: return MA_CHANNEL_BACK_RIGHT;
}
} break;
case 6:
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_FRONT_CENTER;
case 3: return MA_CHANNEL_LFE;
case 4: return MA_CHANNEL_SIDE_LEFT;
case 5: return MA_CHANNEL_SIDE_RIGHT;
}
} break;
case 7: /* Not defined, but best guess. */
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_FRONT_CENTER;
case 3: return MA_CHANNEL_LFE;
case 4: return MA_CHANNEL_BACK_CENTER;
case 5: return MA_CHANNEL_SIDE_LEFT;
case 6: return MA_CHANNEL_SIDE_RIGHT;
}
} break;
case 8:
default:
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_FRONT_CENTER;
case 3: return MA_CHANNEL_LFE;
case 4: return MA_CHANNEL_BACK_LEFT;
case 5: return MA_CHANNEL_BACK_RIGHT;
case 6: return MA_CHANNEL_SIDE_LEFT;
case 7: return MA_CHANNEL_SIDE_RIGHT;
}
} break;
}
if (channelCount > 8) {
if (channelIndex < 32) { /* We have 32 AUX channels. */
return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
}
}
/* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
return MA_CHANNEL_NONE;
}
static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex)
{
switch (channelCount)
{
case 0: return MA_CHANNEL_NONE;
case 1:
{
return MA_CHANNEL_MONO;
} break;
case 2:
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
}
} break;
case 3:
{
switch (channelIndex) {
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
} break;
case 7:
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_CENTER;
case 2: return MA_CHANNEL_FRONT_RIGHT;
case 3: return MA_CHANNEL_SIDE_LEFT;
case 4: return MA_CHANNEL_SIDE_RIGHT;
case 5: return MA_CHANNEL_BACK_CENTER;
case 6: return MA_CHANNEL_LFE;
}
} break;
case 8:
default:
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_CENTER;
case 2: return MA_CHANNEL_FRONT_RIGHT;
case 3: return MA_CHANNEL_SIDE_LEFT;
case 4: return MA_CHANNEL_SIDE_RIGHT;
case 5: return MA_CHANNEL_BACK_LEFT;
case 6: return MA_CHANNEL_BACK_RIGHT;
case 7: return MA_CHANNEL_LFE;
}
} break;
}
if (channelCount > 8) {
if (channelIndex < 32) { /* We have 32 AUX channels. */
return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
}
}
/* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
return MA_CHANNEL_NONE;
}
static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex)
{
switch (channelCount)
{
case 0: return MA_CHANNEL_NONE;
case 1:
{
return MA_CHANNEL_MONO;
} break;
case 2:
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
}
} break;
case 3: /* No defined, but best guess. */
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_FRONT_CENTER;
}
} break;
case 4:
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_BACK_LEFT;
case 3: return MA_CHANNEL_BACK_RIGHT;
}
} break;
case 5: /* Not defined, but best guess. */
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_BACK_LEFT;
case 3: return MA_CHANNEL_BACK_RIGHT;
case 4: return MA_CHANNEL_FRONT_CENTER;
}
} break;
case 6:
default:
{
switch (channelIndex) {
case 0: return MA_CHANNEL_FRONT_LEFT;
case 1: return MA_CHANNEL_FRONT_RIGHT;
case 2: return MA_CHANNEL_BACK_LEFT;
case 3: return MA_CHANNEL_BACK_RIGHT;
case 4: return MA_CHANNEL_FRONT_CENTER;
case 5: return MA_CHANNEL_LFE;
}
} break;
}
if (channelCount > 6) {
if (channelIndex < 32) { /* We have 32 AUX channels. */
return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
}
}
/* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
return MA_CHANNEL_NONE;
}
static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
{
if (channelCount == 0 || channelIndex >= channelCount) {
return MA_CHANNEL_NONE;
}
switch (standardChannelMap)
{
case ma_standard_channel_map_alsa:
{
return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex);
} break;
case ma_standard_channel_map_rfc3551:
{
return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex);
} break;
case ma_standard_channel_map_flac:
{
return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex);
} break;
case ma_standard_channel_map_vorbis:
{