Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
miniaudio
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
miniaudio
Commits
86428055
Commit
86428055
authored
Mar 30, 2018
by
David Reid
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Early untested work on spatial blending for channel conversion.
parent
69c4a35e
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
424 additions
and
29 deletions
+424
-29
mini_al.h
mini_al.h
+424
-29
No files found.
mini_al.h
View file @
86428055
...
...
@@ -620,8 +620,9 @@ typedef enum
typedef enum
{
mal_channel_mix_mode_basic, // Drop excess channels; zeroed out extra channels.
mal_channel_mix_mode_blend, // Blend channels based on locality.
mal_channel_mix_mode_simple = 0, // Drop excess channels; zeroed out extra channels.
mal_channel_mix_mode_planar_average, // Simple averaging based on the plane(s) the channel is sitting on.
//mal_channel_mix_mode_spatial, // Blend channels based on spatial locality.
} mal_channel_mix_mode;
typedef enum
...
...
@@ -740,11 +741,7 @@ struct mal_channel_router
mal_bool32 isPassthrough : 1;
mal_bool32 isSimpleShuffle : 1;
mal_uint8 shuffleTable[MAL_MAX_CHANNELS];
struct
{
mal_uint8 iChannelOut;
mal_uint8 volume; // 0..255
} shuffleData[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS];
float weights[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS];
};
...
...
@@ -810,6 +807,7 @@ typedef struct
mal_uint32 channelsOut;
mal_uint32 sampleRateOut;
mal_channel channelMapOut[MAL_MAX_CHANNELS];
mal_channel_mix_mode channelMixMode;
mal_uint32 cacheSizeInFrames; // Applications should set this to 0 for now.
} mal_dsp_config;
...
...
@@ -1802,11 +1800,27 @@ static inline mal_device_config mal_device_config_init_playback(mal_format forma
// Helper for retrieving a standard channel map.
void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]);
// Determines whether or not a channel map is valid.
//
// A blank channel map is valid (all channels set to MAL_CHANNEL_NONE). The way a blank channel map is handled is context specific, but
// is usually treated as a passthrough.
//
// Invalid channel maps:
// - A channel map with no channels
// - A channel map with more than one channel and a mono channel
mal_bool32 mal_channel_map_valid(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]);
// Helper for comparing two channel maps for equality.
//
// This assumes the channel count is the same between the two.
mal_bool32 mal_channel_map_equal(mal_uint32 channels, mal_channel channelMapA[MAL_MAX_CHANNELS], mal_channel channelMapB[MAL_MAX_CHANNELS]);
mal_bool32 mal_channel_map_equal(mal_uint32 channels, const mal_channel channelMapA[MAL_MAX_CHANNELS], const mal_channel channelMapB[MAL_MAX_CHANNELS]);
// Helper for determining if a channel map is blank (all channels set to MAL_CHANNEL_NONE).
mal_bool32 mal_channel_map_blank(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]);
// Helper for determining whether or not a channel is present in the given channel map.
mal_bool32 mal_channel_map_contains_channel_position(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS], mal_channel channelPosition);
///////////////////////////////////////////////////////////////////////////////
...
...
@@ -15367,7 +15381,30 @@ void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, m
}
}
mal_bool32 mal_channel_map_equal(mal_uint32 channels, mal_channel channelMapA[MAL_MAX_CHANNELS], mal_channel channelMapB[MAL_MAX_CHANNELS])
mal_bool32 mal_channel_map_valid(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS])
{
if (channelMap == NULL) {
return MAL_FALSE;
}
// A channel count of 0 is invalid.
if (channels == 0) {
return MAL_FALSE;
}
// It does not make sense to have a mono channel when there is more than 1 channel.
if (channels > 1) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
if (channelMap[iChannel] == MAL_CHANNEL_MONO) {
return MAL_FALSE;
}
}
}
return MAL_TRUE;
}
mal_bool32 mal_channel_map_equal(mal_uint32 channels, const mal_channel channelMapA[MAL_MAX_CHANNELS], const mal_channel channelMapB[MAL_MAX_CHANNELS])
{
if (channelMapA == channelMapB) {
return MAL_FALSE;
...
...
@@ -15386,6 +15423,28 @@ mal_bool32 mal_channel_map_equal(mal_uint32 channels, mal_channel channelMapA[MA
return MAL_TRUE;
}
mal_bool32 mal_channel_map_blank(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS])
{
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
if (channelMap[iChannel] != MAL_CHANNEL_NONE) {
return MAL_FALSE;
}
}
return MAL_TRUE;
}
mal_bool32 mal_channel_map_contains_channel_position(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS], mal_channel channelPosition)
{
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
if (channelMap[iChannel] == channelPosition) {
return MAL_TRUE;
}
}
return MAL_FALSE;
}
...
...
@@ -17045,10 +17104,88 @@ typedef struct
float z;
} mal_vec3;
static inline mal_vec3 mal_vec3f(float x, float y, float z)
{
mal_vec3 r;
r.x = x;
r.y = y;
r.z = z;
return r;
}
static inline mal_vec3 mal_vec3_add(mal_vec3 a, mal_vec3 b)
{
return mal_vec3f(
a.x + b.x,
a.y + b.y,
a.z + b.z
);
}
static inline mal_vec3 mal_vec3_sub(mal_vec3 a, mal_vec3 b)
{
return mal_vec3f(
a.x - b.x,
a.y - b.y,
a.z - b.z
);
}
static inline mal_vec3 mal_vec3_mul(mal_vec3 a, mal_vec3 b)
{
return mal_vec3f(
a.x * b.x,
a.y * b.y,
a.z * b.z
);
}
static inline mal_vec3 mal_vec3_div(mal_vec3 a, mal_vec3 b)
{
return mal_vec3f(
a.x / b.x,
a.y / b.y,
a.z / b.z
);
}
static inline float mal_vec3_dot(mal_vec3 a, mal_vec3 b)
{
return a.x*b.x + a.y*b.y + a.z*b.z;
}
static inline float mal_vec3_length2(mal_vec3 a)
{
return mal_vec3_dot(a, a);
}
static inline float mal_vec3_length(mal_vec3 a)
{
return (float)sqrt(mal_vec3_length2(a));
}
static inline mal_vec3 mal_vec3_normalize(mal_vec3 a)
{
float len = 1 / mal_vec3_length(a);
mal_vec3 r;
r.x = a.x * len;
r.y = a.y * len;
r.z = a.z * len;
return r;
}
static inline float mal_vec3_distance(mal_vec3 a, mal_vec3 b)
{
return mal_vec3_length(mal_vec3_sub(a, b));
}
// TODO: Make physical channel positions configurable.
// The position of each speaker
relative to the head
.
mal_vec3 g_mal
ChannelPositionsRelativeToHead
[MAL_CHANNEL_POSITION_COUNT] = {
// The position of each speaker
in the room
.
mal_vec3 g_mal
DefaultChannelPositionsInRoom
[MAL_CHANNEL_POSITION_COUNT] = {
{ 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_NONE
{ 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_MONO
{-1.0f, 0.0f, -1.0f}, // MAL_CHANNEL_FRONT_LEFT
...
...
@@ -17103,6 +17240,134 @@ mal_vec3 g_malChannelPositionsRelativeToHead[MAL_CHANNEL_POSITION_COUNT] = {
{ 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_31
};
float mal_calculate_channel_position_planar_weight(mal_channel channelPositionA, mal_channel channelPositionB)
{
mal_vec3 roomPosA = g_malDefaultChannelPositionsInRoom[channelPositionA];
mal_vec3 roomPosB = g_malDefaultChannelPositionsInRoom[channelPositionB];
mal_uint32 planeCountA = 0;
if (roomPosA.x < 0 || roomPosA.x > 0) planeCountA += 1;
if (roomPosA.y < 0 || roomPosA.y > 0) planeCountA += 1;
if (roomPosA.z < 0 || roomPosA.z > 0) planeCountA += 1;
mal_uint32 sharedPlaneCount = 0;
if (roomPosA.x < 0 && roomPosB.x < 0) sharedPlaneCount += 1;
if (roomPosA.x > 0 && roomPosB.x > 0) sharedPlaneCount += 1;
if (roomPosA.y < 0 && roomPosB.y < 0) sharedPlaneCount += 1;
if (roomPosA.y > 0 && roomPosB.y > 0) sharedPlaneCount += 1;
if (roomPosA.z < 0 && roomPosB.z < 0) sharedPlaneCount += 1;
if (roomPosA.z > 0 && roomPosB.z > 0) sharedPlaneCount += 1;
mal_assert(sharedPlaneCount <= planeCountA);
mal_assert(sharedPlaneCount <= 3);
if (sharedPlaneCount == 0) {
return 0;
}
return (float)planeCountA / sharedPlaneCount;
}
float mal_calculate_channel_position_spatial_weight(mal_channel channelPositionA, mal_channel channelPositionB, mal_vec3 listenerRoomPos)
{
// The weight between two channel positions is determined by the orientation and position relative to the virtual listener.
mal_vec3 channelRoomPosA = g_malDefaultChannelPositionsInRoom[channelPositionA];
mal_vec3 channelRoomPosB = g_malDefaultChannelPositionsInRoom[channelPositionB];
mal_vec3 channelDirToHeadA = mal_vec3_normalize(mal_vec3_sub(listenerRoomPos, channelRoomPosA));
mal_vec3 channelDirToHeadB = mal_vec3_normalize(mal_vec3_sub(listenerRoomPos, channelRoomPosB));
float weight = mal_vec3_dot(channelDirToHeadA, channelDirToHeadB);
if (weight <= 0) {
return 0;
}
float distA = mal_vec3_distance(channelRoomPosA, listenerRoomPos);
float distB = mal_vec3_distance(channelRoomPosB, listenerRoomPos);
float distFalloffLinear = distB / distA;
float distFalloffExp = distFalloffLinear * distFalloffLinear;
weight = weight * distFalloffExp;
return weight;
}
mal_uint32 mal_channel_router__get_number_of_channels_on_same_planes(mal_channel channelPosition, mal_uint32 channelCount, const mal_channel channelMap[MAL_MAX_CHANNELS])
{
mal_uint32 count = 0;
for (mal_uint32 iChannel = 0; iChannel < channelCount; ++iChannel) {
mal_vec3 roomPosA = g_malDefaultChannelPositionsInRoom[channelPosition];
mal_vec3 roomPosB = g_malDefaultChannelPositionsInRoom[channelMap[iChannel]];
if (roomPosA.x < 0 && roomPosB.x < 0) {
count += 1;
continue;
}
if (roomPosA.x > 0 && roomPosB.x > 0) {
count += 1;
continue;
}
if (roomPosA.y < 0 && roomPosB.y < 0) {
count += 1;
continue;
}
if (roomPosA.y > 0 && roomPosB.y > 0) {
count += 1;
continue;
}
if (roomPosA.z < 0 && roomPosB.z < 0) {
count += 1;
continue;
}
if (roomPosA.z > 0 && roomPosB.z > 0) {
count += 1;
continue;
}
}
return count;
}
float mal_channel_router__calculate_input_channel_planar_weight(const mal_channel_router* pRouter, mal_channel channelPositionIn, mal_channel channelPositionOut)
{
mal_assert(pRouter != NULL);
float weight = mal_calculate_channel_position_planar_weight(channelPositionIn, channelPositionOut);
// At this point the weight will be 0/3, 1/3, 2/3 or 3/3, depending on how many planes are shared between the two channels. Now
// we need to find out how many input channels are sitting on the planes that channelPosIn is sitting on, then divide the weight
// by that number to find the average.
weight = weight / mal_channel_router__get_number_of_channels_on_same_planes(channelPositionIn, pRouter->config.channelsIn, pRouter->config.channelMapIn);
return weight;
}
float mal_channel_router__calculate_spatial_weight(const mal_channel_router* pRouter, mal_channel channelPositionA, mal_channel channelPositionB)
{
mal_assert(pRouter != NULL);
(void)pRouter;
return mal_calculate_channel_position_spatial_weight(channelPositionA, channelPositionB, mal_vec3f(0, 0, 0));
}
mal_bool32 mal_channel_router__is_spatial_channel_position(const mal_channel_router* pRouter, mal_channel channelPosition)
{
mal_assert(pRouter != NULL);
(void)pRouter;
if (channelPosition == MAL_CHANNEL_NONE || channelPosition == MAL_CHANNEL_MONO || channelPosition == MAL_CHANNEL_LFE) {
return MAL_FALSE;
}
if (mal_vec3_length(g_malDefaultChannelPositionsInRoom[channelPosition]) == 0) {
return MAL_FALSE;
}
return MAL_TRUE;
}
mal_result mal_channel_router_init__common(const mal_channel_router_config* pConfig, void* pUserData, mal_channel_router* pRouter)
{
if (pRouter == NULL) {
...
...
@@ -17115,6 +17380,13 @@ mal_result mal_channel_router_init__common(const mal_channel_router_config* pCon
return MAL_INVALID_ARGS;
}
if (!mal_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) {
return MAL_INVALID_ARGS; // Invalid input channel map.
}
if (!mal_channel_map_valid(pConfig->channelsIn, pConfig->channelMapOut)) {
return MAL_INVALID_ARGS; // Invalid output channel map.
}
pRouter->config = *pConfig;
pRouter->pUserData = pUserData;
...
...
@@ -17123,6 +17395,9 @@ mal_result mal_channel_router_init__common(const mal_channel_router_config* pCon
if (mal_channel_map_equal(pRouter->config.channelsIn, pRouter->config.channelMapIn, pRouter->config.channelMapOut)) {
pRouter->isPassthrough = MAL_TRUE;
}
if (mal_channel_map_blank(pRouter->config.channelsIn, pRouter->config.channelMapIn) || mal_channel_map_blank(pRouter->config.channelsOut, pRouter->config.channelMapOut)) {
pRouter->isPassthrough = MAL_TRUE;
}
}
// Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules:
...
...
@@ -17130,7 +17405,7 @@ mal_result mal_channel_router_init__common(const mal_channel_router_config* pCon
// 1) If it's a passthrough, do nothing - it's just a simple memcpy().
// 2) If the channel counts are the same and every channel position in the input map is present in the output map, use a
// simple shuffle. An example might be different 5.1 channel layouts.
// 3) LFEs are treated differently. The will only receive audio data from other LFEs, or silence.
// 3) LFEs are treated differently. The
y
will only receive audio data from other LFEs, or silence.
// 4) Mono channels are distributed evenly across all channels, except LFEs.
// 5) AUX channels will receive audio data from their matching counterpart, mono channels, or silence.
// 6) All other channels will receive audio data based on their position relative to the head position.
...
...
@@ -17167,16 +17442,137 @@ mal_result mal_channel_router_init__common(const mal_channel_router_config* pCon
}
}
}
}
// If it's not a simple shuffle it's a bit more complicated. We route the input channels to the relevant output channels based on spacial
// locality. Special cases exist for mono and LFE positions. LFEs will only receive samples from other LFEs (there should usually be a
// maximum of 1 LFE per channel map, but there can technically be more). For mono, the sound is distributed across every other channel
// except LFEs.
if (!pRouter->isSimpleShuffle) {
// Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple
// simple shuffling because we want the client to have the ability to freely modify the weights.
// The first step is to map 1:1 matching channels.
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
if (channelPosIn == channelPosOut) {
pRouter->weights[iChannelIn][iChannelOut] = 1;
}
}
}
// The mono channel is accumulated on all other channels, except LFE. Make sure in this loop we exclude output mono channels since
// they were handled in the pass above.
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
if (channelPosIn == MAL_CHANNEL_MONO) {
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
if (channelPosOut != MAL_CHANNEL_NONE && channelPosOut != MAL_CHANNEL_MONO && channelPosOut != MAL_CHANNEL_LFE) {
pRouter->weights[iChannelIn][iChannelOut] = 1;
}
}
}
}
// The output mono channel is the average of all non-none, non-mono and non-lfe input channels.
{
mal_uint32 len = 0;
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
if (channelPosIn != MAL_CHANNEL_NONE && channelPosIn != MAL_CHANNEL_MONO && channelPosIn != MAL_CHANNEL_LFE) {
len += 1;
}
}
if (len > 0) {
float monoWeight = 1.0f / len;
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
if (channelPosOut == MAL_CHANNEL_MONO) {
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
if (channelPosIn != MAL_CHANNEL_NONE && channelPosIn != MAL_CHANNEL_MONO && channelPosIn != MAL_CHANNEL_LFE) {
pRouter->weights[iChannelIn][iChannelOut] += monoWeight;
}
}
}
}
}
}
// Input and output channels that are not present on the other side need to be blended in based on spatial locality.
if (pRouter->config.mixingMode != mal_channel_mix_mode_simple) {
// Input channels that are not present in output channel map.
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
if (!mal_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) {
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
float weight = 0;
if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_average) {
weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
}
#if 0
else if (pRouter->config.mixingMode == mal_channel_mix_mode_spatial) {
weight = mal_channel_router__calculate_spatial_weight(pRouter, channelPosIn, channelPosOut);
}
#endif
// Only apply the weight if we haven't already got some contribution from the respective channels.
if (pRouter->weights[iChannelIn][iChannelOut] == 0) {
pRouter->weights[iChannelIn][iChannelOut] = weight;
}
}
}
}
}
}
// Output channels that are not present in input channel map.
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
if (!mal_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) {
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
float weight = 0;
if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_average) {
weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
}
#if 0
else if (pRouter->config.mixingMode == mal_channel_mix_mode_spatial) {
weight = mal_channel_router__calculate_spatial_weight(pRouter, channelPosIn, channelPosOut);
}
#endif
// Only apply the weight if we haven't already got some contribution from the respective channels.
if (pRouter->weights[iChannelIn][iChannelOut] == 0) {
pRouter->weights[iChannelIn][iChannelOut] = weight;
}
}
}
}
}
}
}
return MAL_SUCCESS;
}
...
...
@@ -17218,10 +17614,9 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
// Accumulate.
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
for (mal_uint64 iFrame = 0; iFrame < frameCount; ++iFrame) {
for (mal_uint32 j = 0; pRouter->shuffleData[iChannelIn][j].iChannelOut != (mal_uint8)-1; ++j) {
mal_uint8 iChannelOut = pRouter->shuffleData[iChannelIn][j].iChannelOut;
ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * (pRouter->shuffleData[iChannelIn][j].volume / 255.0f);
ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * pRouter->weights[iChannelIn][iChannelOut];
}
}
}
...
...
@@ -17925,7 +18320,7 @@ void mal_dsp_mix_channels__dec(float* pFramesOut, mal_uint32 channelsOut, const
(void)channelMapOut;
(void)channelMapIn;
if (mode == mal_channel_mix_mode_
basic
) {
if (mode == mal_channel_mix_mode_
simple
) {
// Basic mode is where we just drop excess channels.
for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) {
switch (channelsOut) {
...
...
@@ -18007,10 +18402,10 @@ void mal_dsp_mix_channels__dec(float* pFramesOut, mal_uint32 channelsOut, const
}
} else if (channelsOut == 2) {
// TODO: Implement proper stereo blending.
mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_
basic
);
mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_
simple
);
} else {
// Fall back to basic mode.
mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_
basic
);
mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_
simple
);
}
}
}
...
...
@@ -18026,7 +18421,7 @@ void mal_dsp_mix_channels__inc(float* pFramesOut, mal_uint32 channelsOut, const
(void)channelMapOut;
(void)channelMapIn;
if (mode == mal_channel_mix_mode_
basic
) {
if (mode == mal_channel_mix_mode_
simple
) {
// Basic mode is where we just zero out extra channels.
for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) {
switch (channelsIn) {
...
...
@@ -18142,10 +18537,10 @@ void mal_dsp_mix_channels__inc(float* pFramesOut, mal_uint32 channelsOut, const
}
} else if (channelsIn == 2) {
// TODO: Implement an optimized stereo conversion.
mal_dsp_mix_channels__inc(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_
basic
);
mal_dsp_mix_channels__inc(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_
simple
);
} else {
// Fall back to basic mixing mode.
mal_dsp_mix_channels__inc(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_
basic
);
mal_dsp_mix_channels__inc(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_
simple
);
}
}
}
...
...
@@ -18415,7 +18810,7 @@ mal_uint64 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pF
pFramesFormat[iFrames] = mal_format_f32;
}
mal_dsp_mix_channels((float*)(pFrames[(iFrames + 1) % 2]), pDSP->config.channelsOut, pDSP->config.channelMapOut, (const float*)(pFrames[iFrames]), pDSP->config.channelsIn, pDSP->config.channelMapIn, framesRead,
mal_channel_mix_mode_blend
);
mal_dsp_mix_channels((float*)(pFrames[(iFrames + 1) % 2]), pDSP->config.channelsOut, pDSP->config.channelMapOut, (const float*)(pFrames[iFrames]), pDSP->config.channelsIn, pDSP->config.channelMapIn, framesRead,
pDSP->config.channelMixMode
);
iFrames = (iFrames + 1) % 2;
pFramesFormat[iFrames] = mal_format_f32;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment