Channel Converter: Stop doing a micro heap allocation.

With small channel counts, mono and stereo, the channel converter will
no longer allocate memory on the heap for the channel map, but will
instead just store it in the struct directly. For larger channel counts
it will fall back to a heap allocation. This prevents stereo channel
maps resulting in a heap allocation of 8 bytes.

With this change a stereo passthrough `ma_device` can be initialized
with a heap allocation for the internal data converter.

This only affects passthrough channel conversion. When shuffling or
weights are required, a heap allocation will still be done. This
optimization is specifically for passthrough.
This commit is contained in:
David Reid
2026-02-16 16:36:27 +10:00
parent 0d7a9f960f
commit 2f148fdd12
+27 -7
View File
@@ -5819,6 +5819,10 @@ MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format fo
typedef struct
{
/* Memory management. */
void* _pHeap;
ma_bool32 _ownsHeap;
ma_format format;
ma_uint32 channelsIn;
ma_uint32 channelsOut;
@@ -5833,9 +5837,13 @@ typedef struct
ma_int32** s16;
} weights; /* [in][out] */
/* Memory management. */
void* _pHeap;
ma_bool32 _ownsHeap;
/*
In order to avoid micro heap allocations for the channel map when using a small channel count,
we will have a small local array here for the in and out channel maps. If the channel count
exceeds the capacity of these channel maps it'll fall back to the heap.
*/
ma_channel _smallChannelMapIn[2];
ma_channel _smallChannelMapOut[2];
} ma_channel_converter;
MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes);
@@ -63128,17 +63136,19 @@ static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pHeapLayout);
pHeapLayout->sizeInBytes = 0;
/* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */
pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
if (pConfig->pChannelMapIn != NULL) {
if (pConfig->pChannelMapIn != NULL && pConfig->channelsIn > ma_countof(((ma_channel_converter*)0)->_smallChannelMapIn)) {
pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn;
}
/* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */
pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
if (pConfig->pChannelMapOut != NULL) {
if (pConfig->pChannelMapOut != NULL && pConfig->channelsOut > ma_countof(((ma_channel_converter*)0)->_smallChannelMapOut)) {
pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut;
}
@@ -63213,14 +63223,24 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert
pConverter->mixingMode = pConfig->mixingMode;
if (pConfig->pChannelMapIn != NULL) {
pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
if (pConfig->channelsIn > ma_countof(pConverter->_smallChannelMapIn)) {
pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
} else {
pConverter->pChannelMapIn = pConverter->_smallChannelMapIn;
}
ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn);
} else {
pConverter->pChannelMapIn = NULL; /* Use default channel map. */
}
if (pConfig->pChannelMapOut != NULL) {
pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
if (pConfig->channelsOut > ma_countof(pConverter->_smallChannelMapOut)) {
pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
} else {
pConverter->pChannelMapOut = pConverter->_smallChannelMapOut;
}
ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);
} else {
pConverter->pChannelMapOut = NULL; /* Use default channel map. */