mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 01:04:02 +02:00
Add support for preallocation to ma_data_converter.
This commit is contained in:
+199
-56
@@ -3512,7 +3512,6 @@ struct ma_resampler_config
|
|||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
ma_uint32 lpfOrder;
|
ma_uint32 lpfOrder;
|
||||||
double lpfNyquistFactor;
|
|
||||||
} linear;
|
} linear;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3720,8 +3719,14 @@ typedef struct
|
|||||||
ma_bool8 hasChannelConverter;
|
ma_bool8 hasChannelConverter;
|
||||||
ma_bool8 hasResampler;
|
ma_bool8 hasResampler;
|
||||||
ma_bool8 isPassthrough;
|
ma_bool8 isPassthrough;
|
||||||
|
|
||||||
|
/* Memory management. */
|
||||||
|
ma_bool8 _ownsHeap;
|
||||||
|
void* _pHeap;
|
||||||
} ma_data_converter;
|
} ma_data_converter;
|
||||||
|
|
||||||
|
MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes);
|
||||||
|
MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter);
|
||||||
MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter);
|
MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter);
|
||||||
MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks);
|
MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks);
|
||||||
MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
|
MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
|
||||||
@@ -44172,8 +44177,7 @@ static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const
|
|||||||
ma_linear_resampler_config linearConfig;
|
ma_linear_resampler_config linearConfig;
|
||||||
|
|
||||||
linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
|
linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
|
||||||
linearConfig.lpfOrder = pConfig->linear.lpfOrder;
|
linearConfig.lpfOrder = pConfig->linear.lpfOrder;
|
||||||
linearConfig.lpfNyquistFactor = pConfig->linear.lpfNyquistFactor;
|
|
||||||
|
|
||||||
return linearConfig;
|
return linearConfig;
|
||||||
}
|
}
|
||||||
@@ -44286,7 +44290,6 @@ MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32
|
|||||||
|
|
||||||
/* Linear. */
|
/* Linear. */
|
||||||
config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
|
config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
|
||||||
config.linear.lpfNyquistFactor = 1;
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@@ -45844,7 +45847,6 @@ MA_API ma_data_converter_config ma_data_converter_config_init_default()
|
|||||||
|
|
||||||
/* Linear resampling defaults. */
|
/* Linear resampling defaults. */
|
||||||
config.resampling.linear.lpfOrder = 1;
|
config.resampling.linear.lpfOrder = 1;
|
||||||
config.resampling.linear.lpfNyquistFactor = 1;
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@@ -45862,36 +45864,24 @@ MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter)
|
|
||||||
|
typedef struct
|
||||||
{
|
{
|
||||||
ma_result result;
|
size_t sizeInBytes;
|
||||||
ma_format midFormat;
|
size_t channelConverterOffset;
|
||||||
ma_bool32 isResamplingRequired;
|
size_t resamplerOffset;
|
||||||
|
} ma_data_converter_heap_layout;
|
||||||
|
|
||||||
if (pConverter == NULL) {
|
static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig)
|
||||||
return MA_INVALID_ARGS;
|
{
|
||||||
}
|
MA_ASSERT(pConfig != NULL);
|
||||||
|
|
||||||
MA_ZERO_OBJECT(pConverter);
|
return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut;
|
||||||
|
}
|
||||||
|
|
||||||
if (pConfig == NULL) {
|
static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig)
|
||||||
return MA_INVALID_ARGS;
|
{
|
||||||
}
|
MA_ASSERT(pConfig != NULL);
|
||||||
|
|
||||||
pConverter->config = *pConfig;
|
|
||||||
|
|
||||||
/* Basic validation. */
|
|
||||||
if (pConfig->channelsIn < MA_MIN_CHANNELS || pConfig->channelsOut < MA_MIN_CHANNELS ||
|
|
||||||
pConfig->channelsIn > MA_MAX_CHANNELS || pConfig->channelsOut > MA_MAX_CHANNELS) {
|
|
||||||
return MA_INVALID_ARGS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Determine if resampling is required. We need to do this so we can determine an appropriate
|
|
||||||
mid format to use. If resampling is required, the mid format must be ma_format_f32 since
|
|
||||||
that is the only one that is guaranteed to supported by custom resampling backends.
|
|
||||||
*/
|
|
||||||
isResamplingRequired = pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We want to avoid as much data conversion as possible. The channel converter and linear
|
We want to avoid as much data conversion as possible. The channel converter and linear
|
||||||
@@ -45902,17 +45892,148 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig,
|
|||||||
custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced
|
custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced
|
||||||
to use that if resampling is required.
|
to use that if resampling is required.
|
||||||
*/
|
*/
|
||||||
if (isResamplingRequired && pConfig->resampling.algorithm != ma_resample_algorithm_linear) {
|
if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) {
|
||||||
midFormat = ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */
|
return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */
|
||||||
} else {
|
} else {
|
||||||
/* */ if (pConverter->config.formatOut == ma_format_s16 || pConverter->config.formatOut == ma_format_f32) {
|
/* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) {
|
||||||
midFormat = pConverter->config.formatOut;
|
return pConfig->formatOut;
|
||||||
} else if (pConverter->config.formatIn == ma_format_s16 || pConverter->config.formatIn == ma_format_f32) {
|
} else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) {
|
||||||
midFormat = pConverter->config.formatIn;
|
return pConfig->formatIn;
|
||||||
} else {
|
} else {
|
||||||
midFormat = ma_format_f32;
|
return ma_format_f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
|
||||||
|
{
|
||||||
|
MA_ASSERT(pConfig != NULL);
|
||||||
|
|
||||||
|
return ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->channelMapIn, pConfig->channelsOut, pConfig->channelMapOut, pConfig->channelMixMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
|
||||||
|
{
|
||||||
|
ma_resampler_config resamplerConfig;
|
||||||
|
ma_uint32 resamplerChannels;
|
||||||
|
|
||||||
|
MA_ASSERT(pConfig != NULL);
|
||||||
|
|
||||||
|
/* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */
|
||||||
|
if (pConfig->channelsIn < pConfig->channelsOut) {
|
||||||
|
resamplerChannels = pConfig->channelsIn;
|
||||||
|
} else {
|
||||||
|
resamplerChannels = pConfig->channelsOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm);
|
||||||
|
resamplerConfig.linear = pConfig->resampling.linear;
|
||||||
|
resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable;
|
||||||
|
resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData;
|
||||||
|
|
||||||
|
return resamplerConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout)
|
||||||
|
{
|
||||||
|
ma_result result;
|
||||||
|
|
||||||
|
MA_ASSERT(pHeapLayout != NULL);
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(pHeapLayout);
|
||||||
|
|
||||||
|
if (pConfig == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
pHeapLayout->sizeInBytes = 0;
|
||||||
|
|
||||||
|
/* Channel converter. */
|
||||||
|
pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes;
|
||||||
|
{
|
||||||
|
size_t heapSizeInBytes;
|
||||||
|
ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
|
||||||
|
|
||||||
|
result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pHeapLayout->sizeInBytes += heapSizeInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resampler. */
|
||||||
|
pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
|
||||||
|
if (ma_data_converter_config_is_resampler_required(pConfig)) {
|
||||||
|
size_t heapSizeInBytes;
|
||||||
|
ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
|
||||||
|
|
||||||
|
result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pHeapLayout->sizeInBytes += heapSizeInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes)
|
||||||
|
{
|
||||||
|
ma_result result;
|
||||||
|
ma_data_converter_heap_layout heapLayout;
|
||||||
|
|
||||||
|
if (pHeapSizeInBytes == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pHeapSizeInBytes = 0;
|
||||||
|
|
||||||
|
result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter)
|
||||||
|
{
|
||||||
|
ma_result result;
|
||||||
|
ma_data_converter_heap_layout heapLayout;
|
||||||
|
ma_format midFormat;
|
||||||
|
ma_bool32 isResamplingRequired;
|
||||||
|
|
||||||
|
if (pConverter == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(pConverter);
|
||||||
|
|
||||||
|
result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pConverter->_pHeap = pHeap;
|
||||||
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
||||||
|
|
||||||
|
pConverter->config = *pConfig;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Determine if resampling is required. We need to do this so we can determine an appropriate
|
||||||
|
mid format to use. If resampling is required, the mid format must be ma_format_f32 since
|
||||||
|
that is the only one that is guaranteed to supported by custom resampling backends.
|
||||||
|
*/
|
||||||
|
isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig);
|
||||||
|
midFormat = ma_data_converter_config_get_mid_format(pConfig);
|
||||||
|
|
||||||
|
|
||||||
/* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */
|
/* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */
|
||||||
@@ -45921,7 +46042,7 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig,
|
|||||||
ma_uint32 iChannelOut;
|
ma_uint32 iChannelOut;
|
||||||
ma_channel_converter_config channelConverterConfig;
|
ma_channel_converter_config channelConverterConfig;
|
||||||
|
|
||||||
channelConverterConfig = ma_channel_converter_config_init(midFormat, pConverter->config.channelsIn, pConverter->config.channelMapIn, pConverter->config.channelsOut, pConverter->config.channelMapOut, pConverter->config.channelMixMode);
|
channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
|
||||||
|
|
||||||
/* Channel weights. */
|
/* Channel weights. */
|
||||||
for (iChannelIn = 0; iChannelIn < pConverter->config.channelsIn; iChannelIn += 1) {
|
for (iChannelIn = 0; iChannelIn < pConverter->config.channelsIn; iChannelIn += 1) {
|
||||||
@@ -45930,7 +46051,7 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = ma_channel_converter_init(&channelConverterConfig, pAllocationCallbacks, &pConverter->channelConverter);
|
result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter);
|
||||||
if (result != MA_SUCCESS) {
|
if (result != MA_SUCCESS) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -45949,23 +46070,9 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig,
|
|||||||
|
|
||||||
/* Resampler. */
|
/* Resampler. */
|
||||||
if (isResamplingRequired) {
|
if (isResamplingRequired) {
|
||||||
ma_resampler_config resamplerConfig;
|
ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
|
||||||
ma_uint32 resamplerChannels;
|
|
||||||
|
|
||||||
/* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */
|
result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler);
|
||||||
if (pConverter->config.channelsIn < pConverter->config.channelsOut) {
|
|
||||||
resamplerChannels = pConverter->config.channelsIn;
|
|
||||||
} else {
|
|
||||||
resamplerChannels = pConverter->config.channelsOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
resamplerConfig = ma_resampler_config_init(midFormat, resamplerChannels, pConverter->config.sampleRateIn, pConverter->config.sampleRateOut, pConverter->config.resampling.algorithm);
|
|
||||||
resamplerConfig.linear.lpfOrder = pConverter->config.resampling.linear.lpfOrder;
|
|
||||||
resamplerConfig.linear.lpfNyquistFactor = pConverter->config.resampling.linear.lpfNyquistFactor;
|
|
||||||
resamplerConfig.pBackendVTable = pConverter->config.resampling.pBackendVTable;
|
|
||||||
resamplerConfig.pBackendUserData = pConverter->config.resampling.pBackendUserData;
|
|
||||||
|
|
||||||
result = ma_resampler_init(&resamplerConfig, pAllocationCallbacks, &pConverter->resampler);
|
|
||||||
if (result != MA_SUCCESS) {
|
if (result != MA_SUCCESS) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -46040,6 +46147,36 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig,
|
|||||||
return MA_SUCCESS;
|
return MA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter)
|
||||||
|
{
|
||||||
|
ma_result result;
|
||||||
|
size_t heapSizeInBytes;
|
||||||
|
void* pHeap;
|
||||||
|
|
||||||
|
result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heapSizeInBytes > 0) {
|
||||||
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
||||||
|
if (pHeap == NULL) {
|
||||||
|
return MA_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pHeap = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
ma_free(pHeap, pAllocationCallbacks);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pConverter->_ownsHeap = MA_TRUE;
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
|
MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||||
{
|
{
|
||||||
if (pConverter == NULL) {
|
if (pConverter == NULL) {
|
||||||
@@ -46049,6 +46186,12 @@ MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_all
|
|||||||
if (pConverter->hasResampler) {
|
if (pConverter->hasResampler) {
|
||||||
ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks);
|
ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks);
|
||||||
|
|
||||||
|
if (pConverter->_ownsHeap) {
|
||||||
|
ma_free(pConverter->_pHeap, pAllocationCallbacks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
||||||
|
|||||||
Reference in New Issue
Block a user