mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
API CHANGE: Remove ma_pcm_converter.
This has been replaced with ma_data_converter.
This commit is contained in:
-560
@@ -1550,53 +1550,6 @@ struct ma_src
|
||||
ma_bool32 useNEON : 1;
|
||||
};
|
||||
|
||||
typedef struct ma_pcm_converter ma_pcm_converter;
|
||||
typedef ma_uint32 (* ma_pcm_converter_read_proc)(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ma_format formatIn;
|
||||
ma_uint32 channelsIn;
|
||||
ma_uint32 sampleRateIn;
|
||||
ma_channel channelMapIn[MA_MAX_CHANNELS];
|
||||
ma_format formatOut;
|
||||
ma_uint32 channelsOut;
|
||||
ma_uint32 sampleRateOut;
|
||||
ma_channel channelMapOut[MA_MAX_CHANNELS];
|
||||
ma_channel_mix_mode channelMixMode;
|
||||
ma_dither_mode ditherMode;
|
||||
ma_src_algorithm srcAlgorithm;
|
||||
ma_bool32 allowDynamicSampleRate;
|
||||
ma_bool32 neverConsumeEndOfInput : 1; /* <-- For SRC. */
|
||||
ma_bool32 noSSE2 : 1;
|
||||
ma_bool32 noAVX2 : 1;
|
||||
ma_bool32 noAVX512 : 1;
|
||||
ma_bool32 noNEON : 1;
|
||||
ma_pcm_converter_read_proc onRead;
|
||||
void* pUserData;
|
||||
union
|
||||
{
|
||||
ma_src_config_sinc sinc;
|
||||
};
|
||||
} ma_pcm_converter_config;
|
||||
|
||||
struct ma_pcm_converter
|
||||
{
|
||||
ma_pcm_converter_read_proc onRead;
|
||||
void* pUserData;
|
||||
ma_format_converter formatConverterIn; /* For converting data to f32 in preparation for further processing. */
|
||||
ma_format_converter formatConverterOut; /* For converting data to the requested output format. Used as the final step in the processing pipeline. */
|
||||
ma_channel_router channelRouter; /* For channel conversion. */
|
||||
ma_src src; /* For sample rate conversion. */
|
||||
ma_bool32 isDynamicSampleRateAllowed : 1; /* ma_pcm_converter_set_input_sample_rate() and ma_pcm_converter_set_output_sample_rate() will fail if this is set to false. */
|
||||
ma_bool32 isPreFormatConversionRequired : 1;
|
||||
ma_bool32 isPostFormatConversionRequired : 1;
|
||||
ma_bool32 isChannelRoutingRequired : 1;
|
||||
ma_bool32 isSRCRequired : 1;
|
||||
ma_bool32 isChannelRoutingAtStart : 1;
|
||||
ma_bool32 isPassthrough : 1; /* <-- Will be set to true when the conversion pipeline is an optimized passthrough. */
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************************************************************************************************
|
||||
*************************************************************************************************************************************************************
|
||||
@@ -1895,54 +1848,6 @@ Conversion
|
||||
|
||||
************************************************************************************************************************************************************/
|
||||
|
||||
/*
|
||||
Initializes a DSP object.
|
||||
*/
|
||||
ma_result ma_pcm_converter_init(const ma_pcm_converter_config* pConfig, ma_pcm_converter* pDSP);
|
||||
|
||||
/*
|
||||
Dynamically adjusts the input sample rate.
|
||||
|
||||
This will fail is the DSP was not initialized with allowDynamicSampleRate.
|
||||
|
||||
DEPRECATED. Use ma_pcm_converter_set_sample_rate() instead.
|
||||
*/
|
||||
ma_result ma_pcm_converter_set_input_sample_rate(ma_pcm_converter* pDSP, ma_uint32 sampleRateOut);
|
||||
|
||||
/*
|
||||
Dynamically adjusts the output sample rate.
|
||||
|
||||
This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
|
||||
is not acceptable you will need to use your own algorithm.
|
||||
|
||||
This will fail is the DSP was not initialized with allowDynamicSampleRate.
|
||||
|
||||
DEPRECATED. Use ma_pcm_converter_set_sample_rate() instead.
|
||||
*/
|
||||
ma_result ma_pcm_converter_set_output_sample_rate(ma_pcm_converter* pDSP, ma_uint32 sampleRateOut);
|
||||
|
||||
/*
|
||||
Dynamically adjusts the output sample rate.
|
||||
|
||||
This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
|
||||
is not acceptable you will need to use your own algorithm.
|
||||
|
||||
This will fail if the DSP was not initialized with allowDynamicSampleRate.
|
||||
*/
|
||||
ma_result ma_pcm_converter_set_sample_rate(ma_pcm_converter* pDSP, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
|
||||
|
||||
/*
|
||||
Reads a number of frames and runs them through the DSP processor.
|
||||
*/
|
||||
ma_uint64 ma_pcm_converter_read(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint64 frameCount);
|
||||
|
||||
/*
|
||||
Helper for initializing a ma_pcm_converter_config object.
|
||||
*/
|
||||
ma_pcm_converter_config ma_pcm_converter_config_init_new(void);
|
||||
ma_pcm_converter_config ma_pcm_converter_config_init(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_pcm_converter_read_proc onRead, void* pUserData);
|
||||
ma_pcm_converter_config ma_pcm_converter_config_init_ex(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], ma_pcm_converter_read_proc onRead, void* pUserData);
|
||||
|
||||
/*
|
||||
High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to
|
||||
determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is
|
||||
@@ -36122,471 +36027,6 @@ void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 fr
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ma_pcm_converter* pDSP;
|
||||
void* pUserDataForClient;
|
||||
} ma_pcm_converter_callback_data;
|
||||
|
||||
ma_uint32 ma_pcm_converter__pre_format_converter_on_read(ma_format_converter* pConverter, ma_uint32 frameCount, void* pFramesOut, void* pUserData)
|
||||
{
|
||||
ma_pcm_converter_callback_data* pData;
|
||||
ma_pcm_converter* pDSP;
|
||||
|
||||
(void)pConverter;
|
||||
|
||||
pData = (ma_pcm_converter_callback_data*)pUserData;
|
||||
ma_assert(pData != NULL);
|
||||
|
||||
pDSP = pData->pDSP;
|
||||
ma_assert(pDSP != NULL);
|
||||
|
||||
return pDSP->onRead(pDSP, pFramesOut, frameCount, pData->pUserDataForClient);
|
||||
}
|
||||
|
||||
ma_uint32 ma_pcm_converter__post_format_converter_on_read(ma_format_converter* pConverter, ma_uint32 frameCount, void* pFramesOut, void* pUserData)
|
||||
{
|
||||
ma_pcm_converter_callback_data* pData;
|
||||
ma_pcm_converter* pDSP;
|
||||
|
||||
(void)pConverter;
|
||||
|
||||
pData = (ma_pcm_converter_callback_data*)pUserData;
|
||||
ma_assert(pData != NULL);
|
||||
|
||||
pDSP = pData->pDSP;
|
||||
ma_assert(pDSP != NULL);
|
||||
|
||||
/* When this version of this callback is used it means we're reading directly from the client. */
|
||||
ma_assert(pDSP->isPreFormatConversionRequired == MA_FALSE);
|
||||
ma_assert(pDSP->isChannelRoutingRequired == MA_FALSE);
|
||||
ma_assert(pDSP->isSRCRequired == MA_FALSE);
|
||||
|
||||
return pDSP->onRead(pDSP, pFramesOut, frameCount, pData->pUserDataForClient);
|
||||
}
|
||||
|
||||
ma_uint32 ma_pcm_converter__post_format_converter_on_read_deinterleaved(ma_format_converter* pConverter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
|
||||
{
|
||||
ma_pcm_converter_callback_data* pData;
|
||||
ma_pcm_converter* pDSP;
|
||||
|
||||
(void)pConverter;
|
||||
|
||||
pData = (ma_pcm_converter_callback_data*)pUserData;
|
||||
ma_assert(pData != NULL);
|
||||
|
||||
pDSP = pData->pDSP;
|
||||
ma_assert(pDSP != NULL);
|
||||
|
||||
if (!pDSP->isChannelRoutingAtStart) {
|
||||
return (ma_uint32)ma_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
|
||||
} else {
|
||||
if (pDSP->isSRCRequired) {
|
||||
return (ma_uint32)ma_src_read_deinterleaved(&pDSP->src, frameCount, ppSamplesOut, pUserData);
|
||||
} else {
|
||||
return (ma_uint32)ma_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ma_uint32 ma_pcm_converter__src_on_read_deinterleaved(ma_src* pSRC, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
|
||||
{
|
||||
ma_pcm_converter_callback_data* pData;
|
||||
ma_pcm_converter* pDSP;
|
||||
|
||||
(void)pSRC;
|
||||
|
||||
pData = (ma_pcm_converter_callback_data*)pUserData;
|
||||
ma_assert(pData != NULL);
|
||||
|
||||
pDSP = pData->pDSP;
|
||||
ma_assert(pDSP != NULL);
|
||||
|
||||
/* If the channel routing stage is at the front we need to read from that. Otherwise we read from the pre format converter. */
|
||||
if (pDSP->isChannelRoutingAtStart) {
|
||||
return (ma_uint32)ma_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
|
||||
} else {
|
||||
return (ma_uint32)ma_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
|
||||
}
|
||||
}
|
||||
|
||||
ma_uint32 ma_pcm_converter__channel_router_on_read_deinterleaved(ma_channel_router* pRouter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
|
||||
{
|
||||
ma_pcm_converter_callback_data* pData;
|
||||
ma_pcm_converter* pDSP;
|
||||
|
||||
(void)pRouter;
|
||||
|
||||
pData = (ma_pcm_converter_callback_data*)pUserData;
|
||||
ma_assert(pData != NULL);
|
||||
|
||||
pDSP = pData->pDSP;
|
||||
ma_assert(pDSP != NULL);
|
||||
|
||||
/* If the channel routing stage is at the front of the pipeline we read from the pre format converter. Otherwise we read from the sample rate converter. */
|
||||
if (pDSP->isChannelRoutingAtStart) {
|
||||
return (ma_uint32)ma_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
|
||||
} else {
|
||||
if (pDSP->isSRCRequired) {
|
||||
return (ma_uint32)ma_src_read_deinterleaved(&pDSP->src, frameCount, ppSamplesOut, pUserData);
|
||||
} else {
|
||||
return (ma_uint32)ma_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ma_result ma_pcm_converter_init(const ma_pcm_converter_config* pConfig, ma_pcm_converter* pDSP)
|
||||
{
|
||||
ma_result result;
|
||||
|
||||
if (pDSP == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
ma_zero_object(pDSP);
|
||||
pDSP->onRead = pConfig->onRead;
|
||||
pDSP->pUserData = pConfig->pUserData;
|
||||
pDSP->isDynamicSampleRateAllowed = pConfig->allowDynamicSampleRate;
|
||||
|
||||
/*
|
||||
In general, this is the pipeline used for data conversion. Note that this can actually change which is explained later.
|
||||
|
||||
Pre Format Conversion -> Sample Rate Conversion -> Channel Routing -> Post Format Conversion
|
||||
|
||||
Pre Format Conversion
|
||||
---------------------
|
||||
This is where the sample data is converted to a format that's usable by the later stages in the pipeline. Input data
|
||||
is converted to deinterleaved floating-point.
|
||||
|
||||
Channel Routing
|
||||
---------------
|
||||
Channel routing is where stereo is converted to 5.1, mono is converted to stereo, etc. This stage depends on the
|
||||
pre format conversion stage.
|
||||
|
||||
Sample Rate Conversion
|
||||
----------------------
|
||||
Sample rate conversion depends on the pre format conversion stage and as the name implies performs sample rate conversion.
|
||||
|
||||
Post Format Conversion
|
||||
----------------------
|
||||
This stage is where our deinterleaved floating-point data from the previous stages are converted to the requested output
|
||||
format.
|
||||
|
||||
|
||||
Optimizations
|
||||
-------------
|
||||
Sometimes the conversion pipeline is rearranged for efficiency. The first obvious optimization is to eliminate unnecessary
|
||||
stages in the pipeline. When no channel routing nor sample rate conversion is necessary, the entire pipeline is optimized
|
||||
down to just this:
|
||||
|
||||
Post Format Conversion
|
||||
|
||||
When sample rate conversion is not unnecessary:
|
||||
|
||||
Pre Format Conversion -> Channel Routing -> Post Format Conversion
|
||||
|
||||
When channel routing is unnecessary:
|
||||
|
||||
Pre Format Conversion -> Sample Rate Conversion -> Post Format Conversion
|
||||
|
||||
A slightly less obvious optimization is used depending on whether or not we are increasing or decreasing the number of
|
||||
channels. Because everything in the pipeline works on a per-channel basis, the efficiency of the pipeline is directly
|
||||
proportionate to the number of channels that need to be processed. Therefore, it's can be more efficient to move the
|
||||
channel conversion stage to an earlier or later stage. When the channel count is being reduced, we move the channel
|
||||
conversion stage to the start of the pipeline so that later stages can work on a smaller number of channels at a time.
|
||||
Otherwise, we move the channel conversion stage to the end of the pipeline. When reducing the channel count, the pipeline
|
||||
will look like this:
|
||||
|
||||
Pre Format Conversion -> Channel Routing -> Sample Rate Conversion -> Post Format Conversion
|
||||
|
||||
Notice how the Channel Routing and Sample Rate Conversion stages are swapped so that the SRC stage has less data to process.
|
||||
*/
|
||||
|
||||
/* First we need to determine what's required and what's not. */
|
||||
if (pConfig->sampleRateIn != pConfig->sampleRateOut || pConfig->allowDynamicSampleRate) {
|
||||
pDSP->isSRCRequired = MA_TRUE;
|
||||
}
|
||||
if (pConfig->channelsIn != pConfig->channelsOut || !ma_channel_map_equal(pConfig->channelsIn, pConfig->channelMapIn, pConfig->channelMapOut)) {
|
||||
pDSP->isChannelRoutingRequired = MA_TRUE;
|
||||
}
|
||||
|
||||
/* If neither a sample rate conversion nor channel conversion is necessary we can skip the pre format conversion. */
|
||||
if (!pDSP->isSRCRequired && !pDSP->isChannelRoutingRequired) {
|
||||
/* We don't need a pre format conversion stage, but we may still need a post format conversion stage. */
|
||||
if (pConfig->formatIn != pConfig->formatOut) {
|
||||
pDSP->isPostFormatConversionRequired = MA_TRUE;
|
||||
}
|
||||
} else {
|
||||
pDSP->isPreFormatConversionRequired = MA_TRUE;
|
||||
pDSP->isPostFormatConversionRequired = MA_TRUE;
|
||||
}
|
||||
|
||||
/* Use a passthrough if none of the stages are being used. */
|
||||
if (!pDSP->isPreFormatConversionRequired && !pDSP->isPostFormatConversionRequired && !pDSP->isChannelRoutingRequired && !pDSP->isSRCRequired) {
|
||||
pDSP->isPassthrough = MA_TRUE;
|
||||
}
|
||||
|
||||
/* Move the channel conversion stage to the start of the pipeline if we are reducing the channel count. */
|
||||
if (pConfig->channelsOut < pConfig->channelsIn) {
|
||||
pDSP->isChannelRoutingAtStart = MA_TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
We always initialize every stage of the pipeline regardless of whether or not the stage is used because it simplifies
|
||||
a few things when it comes to dynamically changing properties post-initialization.
|
||||
*/
|
||||
result = MA_SUCCESS;
|
||||
|
||||
/* Pre format conversion. */
|
||||
{
|
||||
ma_format_converter_config preFormatConverterConfig = ma_format_converter_config_init(
|
||||
pConfig->formatIn,
|
||||
ma_format_f32,
|
||||
pConfig->channelsIn,
|
||||
ma_pcm_converter__pre_format_converter_on_read,
|
||||
pDSP
|
||||
);
|
||||
preFormatConverterConfig.ditherMode = pConfig->ditherMode;
|
||||
preFormatConverterConfig.noSSE2 = pConfig->noSSE2;
|
||||
preFormatConverterConfig.noAVX2 = pConfig->noAVX2;
|
||||
preFormatConverterConfig.noAVX512 = pConfig->noAVX512;
|
||||
preFormatConverterConfig.noNEON = pConfig->noNEON;
|
||||
|
||||
result = ma_format_converter_init(&preFormatConverterConfig, &pDSP->formatConverterIn);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Post format conversion. The exact configuration for this depends on whether or not we are reading data directly from the client
|
||||
or from an earlier stage in the pipeline.
|
||||
*/
|
||||
{
|
||||
ma_format_converter_config postFormatConverterConfig = ma_format_converter_config_init_new();
|
||||
postFormatConverterConfig.formatIn = pConfig->formatIn;
|
||||
postFormatConverterConfig.formatOut = pConfig->formatOut;
|
||||
postFormatConverterConfig.channels = pConfig->channelsOut;
|
||||
postFormatConverterConfig.ditherMode = pConfig->ditherMode;
|
||||
postFormatConverterConfig.noSSE2 = pConfig->noSSE2;
|
||||
postFormatConverterConfig.noAVX2 = pConfig->noAVX2;
|
||||
postFormatConverterConfig.noAVX512 = pConfig->noAVX512;
|
||||
postFormatConverterConfig.noNEON = pConfig->noNEON;
|
||||
if (pDSP->isPreFormatConversionRequired) {
|
||||
postFormatConverterConfig.onReadDeinterleaved = ma_pcm_converter__post_format_converter_on_read_deinterleaved;
|
||||
postFormatConverterConfig.formatIn = ma_format_f32;
|
||||
} else {
|
||||
postFormatConverterConfig.onRead = ma_pcm_converter__post_format_converter_on_read;
|
||||
}
|
||||
|
||||
result = ma_format_converter_init(&postFormatConverterConfig, &pDSP->formatConverterOut);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/* SRC */
|
||||
{
|
||||
ma_src_config srcConfig = ma_src_config_init(
|
||||
pConfig->sampleRateIn,
|
||||
pConfig->sampleRateOut,
|
||||
((pConfig->channelsIn < pConfig->channelsOut) ? pConfig->channelsIn : pConfig->channelsOut),
|
||||
ma_pcm_converter__src_on_read_deinterleaved,
|
||||
pDSP
|
||||
);
|
||||
srcConfig.algorithm = pConfig->srcAlgorithm;
|
||||
srcConfig.neverConsumeEndOfInput = pConfig->neverConsumeEndOfInput;
|
||||
srcConfig.noSSE2 = pConfig->noSSE2;
|
||||
srcConfig.noAVX2 = pConfig->noAVX2;
|
||||
srcConfig.noAVX512 = pConfig->noAVX512;
|
||||
srcConfig.noNEON = pConfig->noNEON;
|
||||
ma_copy_memory(&srcConfig.sinc, &pConfig->sinc, sizeof(pConfig->sinc));
|
||||
|
||||
result = ma_src_init(&srcConfig, &pDSP->src);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Channel conversion */
|
||||
{
|
||||
ma_channel_router_config routerConfig = ma_channel_router_config_init(
|
||||
pConfig->channelsIn,
|
||||
pConfig->channelMapIn,
|
||||
pConfig->channelsOut,
|
||||
pConfig->channelMapOut,
|
||||
pConfig->channelMixMode,
|
||||
ma_pcm_converter__channel_router_on_read_deinterleaved,
|
||||
pDSP);
|
||||
routerConfig.noSSE2 = pConfig->noSSE2;
|
||||
routerConfig.noAVX2 = pConfig->noAVX2;
|
||||
routerConfig.noAVX512 = pConfig->noAVX512;
|
||||
routerConfig.noNEON = pConfig->noNEON;
|
||||
|
||||
result = ma_channel_router_init(&routerConfig, &pDSP->channelRouter);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
ma_result ma_pcm_converter_refresh_sample_rate(ma_pcm_converter* pDSP)
|
||||
{
|
||||
/* The SRC stage will already have been initialized so we can just set it there. */
|
||||
ma_src_set_sample_rate(&pDSP->src, pDSP->src.config.sampleRateIn, pDSP->src.config.sampleRateOut);
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
ma_result ma_pcm_converter_set_input_sample_rate(ma_pcm_converter* pDSP, ma_uint32 sampleRateIn)
|
||||
{
|
||||
if (pDSP == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
/* Must have a sample rate of > 0. */
|
||||
if (sampleRateIn == 0) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
/* Must have been initialized with allowDynamicSampleRate. */
|
||||
if (!pDSP->isDynamicSampleRateAllowed) {
|
||||
return MA_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
ma_atomic_exchange_32(&pDSP->src.config.sampleRateIn, sampleRateIn);
|
||||
return ma_pcm_converter_refresh_sample_rate(pDSP);
|
||||
}
|
||||
|
||||
ma_result ma_pcm_converter_set_output_sample_rate(ma_pcm_converter* pDSP, ma_uint32 sampleRateOut)
|
||||
{
|
||||
if (pDSP == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
/* Must have a sample rate of > 0. */
|
||||
if (sampleRateOut == 0) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
/* Must have been initialized with allowDynamicSampleRate. */
|
||||
if (!pDSP->isDynamicSampleRateAllowed) {
|
||||
return MA_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
ma_atomic_exchange_32(&pDSP->src.config.sampleRateOut, sampleRateOut);
|
||||
return ma_pcm_converter_refresh_sample_rate(pDSP);
|
||||
}
|
||||
|
||||
ma_result ma_pcm_converter_set_sample_rate(ma_pcm_converter* pDSP, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
|
||||
{
|
||||
if (pDSP == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
/* Must have a sample rate of > 0. */
|
||||
if (sampleRateIn == 0 || sampleRateOut == 0) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
/* Must have been initialized with allowDynamicSampleRate. */
|
||||
if (!pDSP->isDynamicSampleRateAllowed) {
|
||||
return MA_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
ma_atomic_exchange_32(&pDSP->src.config.sampleRateIn, sampleRateIn);
|
||||
ma_atomic_exchange_32(&pDSP->src.config.sampleRateOut, sampleRateOut);
|
||||
|
||||
return ma_pcm_converter_refresh_sample_rate(pDSP);
|
||||
}
|
||||
|
||||
ma_uint64 ma_pcm_converter_read(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint64 frameCount)
|
||||
{
|
||||
ma_pcm_converter_callback_data data;
|
||||
|
||||
if (pDSP == NULL || pFramesOut == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fast path. */
|
||||
if (pDSP->isPassthrough) {
|
||||
if (frameCount <= 0xFFFFFFFF) {
|
||||
return (ma_uint32)pDSP->onRead(pDSP, pFramesOut, (ma_uint32)frameCount, pDSP->pUserData);
|
||||
} else {
|
||||
ma_uint8* pNextFramesOut = (ma_uint8*)pFramesOut;
|
||||
|
||||
ma_uint64 totalFramesRead = 0;
|
||||
while (totalFramesRead < frameCount) {
|
||||
ma_uint32 framesRead;
|
||||
ma_uint64 framesRemaining = (frameCount - totalFramesRead);
|
||||
ma_uint64 framesToReadRightNow = framesRemaining;
|
||||
if (framesToReadRightNow > 0xFFFFFFFF) {
|
||||
framesToReadRightNow = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
framesRead = pDSP->onRead(pDSP, pNextFramesOut, (ma_uint32)framesToReadRightNow, pDSP->pUserData);
|
||||
if (framesRead == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
pNextFramesOut += framesRead * pDSP->channelRouter.config.channelsOut * ma_get_bytes_per_sample(pDSP->formatConverterOut.config.formatOut);
|
||||
totalFramesRead += framesRead;
|
||||
}
|
||||
|
||||
return totalFramesRead;
|
||||
}
|
||||
}
|
||||
|
||||
/* Slower path. The real work is done here. To do this all we need to do is read from the last stage in the pipeline. */
|
||||
ma_assert(pDSP->isPostFormatConversionRequired == MA_TRUE);
|
||||
|
||||
data.pDSP = pDSP;
|
||||
data.pUserDataForClient = pDSP->pUserData;
|
||||
return ma_format_converter_read(&pDSP->formatConverterOut, frameCount, pFramesOut, &data);
|
||||
}
|
||||
|
||||
|
||||
ma_pcm_converter_config ma_pcm_converter_config_init_new()
|
||||
{
|
||||
ma_pcm_converter_config config;
|
||||
ma_zero_object(&config);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
ma_pcm_converter_config ma_pcm_converter_config_init(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_pcm_converter_read_proc onRead, void* pUserData)
|
||||
{
|
||||
return ma_pcm_converter_config_init_ex(formatIn, channelsIn, sampleRateIn, NULL, formatOut, channelsOut, sampleRateOut, NULL, onRead, pUserData);
|
||||
}
|
||||
|
||||
ma_pcm_converter_config ma_pcm_converter_config_init_ex(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], ma_pcm_converter_read_proc onRead, void* pUserData)
|
||||
{
|
||||
ma_pcm_converter_config config;
|
||||
ma_zero_object(&config);
|
||||
config.formatIn = formatIn;
|
||||
config.channelsIn = channelsIn;
|
||||
config.sampleRateIn = sampleRateIn;
|
||||
config.formatOut = formatOut;
|
||||
config.channelsOut = channelsOut;
|
||||
config.sampleRateOut = sampleRateOut;
|
||||
if (channelMapIn != NULL) {
|
||||
ma_copy_memory(config.channelMapIn, channelMapIn, sizeof(config.channelMapIn));
|
||||
}
|
||||
if (channelMapOut != NULL) {
|
||||
ma_copy_memory(config.channelMapOut, channelMapOut, sizeof(config.channelMapOut));
|
||||
}
|
||||
config.onRead = onRead;
|
||||
config.pUserData = pUserData;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn)
|
||||
{
|
||||
ma_data_converter_config config;
|
||||
|
||||
Reference in New Issue
Block a user