diff --git a/mini_al.h b/mini_al.h index b3a8833e..85540bba 100644 --- a/mini_al.h +++ b/mini_al.h @@ -621,7 +621,7 @@ typedef enum typedef enum { 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_planar_blend, // 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; @@ -17243,6 +17243,56 @@ mal_vec3 g_malDefaultChannelPositionsInRoom[MAL_CHANNEL_POSITION_COUNT] = { float mal_calculate_channel_position_planar_weight(mal_channel channelPositionA, mal_channel channelPositionB) { + // Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to + // the following output configuration: + // + // - front/left + // - side/left + // - back/left + // + // The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount + // of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. + // + // Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left + // speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted + // from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would + // receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between + // the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works + // across 3 spatial dimensions. + // + // The first thing to do is figure out how each speaker's volume is spread over each of plane: + // - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane + // - side/left: 1 plane (left only) = 1/1 = entire volume from left plane + // - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane + // - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane + // + // Now that we know how much volume each speaker emits for each of the planes it emits audio from we need to know how many planes are shared + // between the two speakers: + // - front/left (in) and front/left (out): 2 shared planes (front and left) + // - front/left (in) and side/left (out): 1 shared plane (left) + // - front/left (in) and back/left (out): 1 shared plane (left) + // - front/left (in) and top/front/left (out): 2 shared planes (front and left) + // + // We now have enough information to know how much audio the input speaker gives to each of it's output: + // + // volumeToGive = volumePerInputSpeakerPlane * sharedPlaneCount + // + // We can also determine how much volume an output speaker should take: + // + // volumeToTake = volumePerOutputSpeakerPlane * sharedPlaneCount + // + // Thus, the final contribution is: + // + // contribution = volumeToGive * volumeToTake + // + // Contributions for each of our examples: + // + // front/left to front/left = (1/2 * 2) * (1/2 * 2) = 0.5*2.0 * 0.5*2.0 = 1.0*1.0 = 1.0 + // front/left to side/left = (1/2 * 1) * (1/1 * 1) = 0.5*1.0 * 1.0*1.0 = 0.5*1.0 = 0.5 + // front/left to back/left = (1/2 * 1) * (1/2 * 1) = 0.5*1.0 * 0.5*1.0 = 0.5*0.5 = 0.25 + // front/left to top/front/left = (1/2 * 2) * (1/3 * 2) = 0.5*2.0 * 0.33*2.0 = 1.0*0.66 = 0.66 + + mal_vec3 roomPosA = g_malDefaultChannelPositionsInRoom[channelPositionA]; mal_vec3 roomPosB = g_malDefaultChannelPositionsInRoom[channelPositionB]; @@ -17251,6 +17301,11 @@ float mal_calculate_channel_position_planar_weight(mal_channel channelPositionA, if (roomPosA.y < 0 || roomPosA.y > 0) planeCountA += 1; if (roomPosA.z < 0 || roomPosA.z > 0) planeCountA += 1; + mal_uint32 planeCountB = 0; + if (roomPosB.x < 0 || roomPosB.x > 0) planeCountB += 1; + if (roomPosB.y < 0 || roomPosB.y > 0) planeCountB += 1; + if (roomPosB.z < 0 || roomPosB.z > 0) planeCountB += 1; + mal_uint32 sharedPlaneCount = 0; if (roomPosA.x < 0 && roomPosB.x < 0) sharedPlaneCount += 1; if (roomPosA.x > 0 && roomPosB.x > 0) sharedPlaneCount += 1; @@ -17266,9 +17321,16 @@ float mal_calculate_channel_position_planar_weight(mal_channel channelPositionA, return 0; } - return (float)planeCountA / sharedPlaneCount; + mal_assert(planeCountA > 0); + mal_assert(planeCountB > 0); + + float contributionA = 1.0f/planeCountA * sharedPlaneCount; + float contributionB = 1.0f/planeCountB * sharedPlaneCount; + + return contributionA * contributionB; } +#if 0 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. @@ -17291,7 +17353,9 @@ float mal_calculate_channel_position_spatial_weight(mal_channel channelPositionA weight = weight * distFalloffExp; return weight; } +#endif +#if 0 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; @@ -17330,20 +17394,17 @@ mal_uint32 mal_channel_router__get_number_of_channels_on_same_planes(mal_channel return count; } +#endif float mal_channel_router__calculate_input_channel_planar_weight(const mal_channel_router* pRouter, mal_channel channelPositionIn, mal_channel channelPositionOut) { mal_assert(pRouter != NULL); + (void)pRouter; - 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; + return mal_calculate_channel_position_planar_weight(channelPositionIn, channelPositionOut); } +#if 0 float mal_channel_router__calculate_spatial_weight(const mal_channel_router* pRouter, mal_channel channelPositionA, mal_channel channelPositionB) { mal_assert(pRouter != NULL); @@ -17351,6 +17412,7 @@ float mal_channel_router__calculate_spatial_weight(const mal_channel_router* pRo return mal_calculate_channel_position_spatial_weight(channelPositionA, channelPositionB, mal_vec3f(0, 0, 0)); } +#endif mal_bool32 mal_channel_router__is_spatial_channel_position(const mal_channel_router* pRouter, mal_channel channelPosition) { @@ -17446,9 +17508,13 @@ mal_result mal_channel_router_init__common(const mal_channel_router_config* pCon // 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. + // shuffling. We use different algorithms for calculating weights depending on our mixing mode. + // + // In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just + // map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel + // map, nothing will be heard! + + // In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; @@ -17510,7 +17576,7 @@ mal_result mal_channel_router_init__common(const mal_channel_router_config* pCon // 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. + // Unmapped input channels. for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; @@ -17521,7 +17587,7 @@ mal_result mal_channel_router_init__common(const mal_channel_router_config* pCon if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) { float weight = 0; - if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_average) { + if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) { weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut); } #if 0 @@ -17540,8 +17606,7 @@ mal_result mal_channel_router_init__common(const mal_channel_router_config* pCon } } - - // Output channels that are not present in input channel map. + // Unmapped output channels. for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; @@ -17552,7 +17617,7 @@ mal_result mal_channel_router_init__common(const mal_channel_router_config* pCon if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) { float weight = 0; - if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_average) { + if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) { weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut); } #if 0 diff --git a/tests/mal_test_0.c b/tests/mal_test_0.c index 7ed28c52..72afcd62 100644 --- a/tests/mal_test_0.c +++ b/tests/mal_test_0.c @@ -1261,6 +1261,365 @@ int do_format_converter_tests() } +mal_uint32 channel_router_callback__passthrough_test(mal_channel_router* pRouter, mal_uint32 frameCount, void** ppSamplesOut, void* pUserData) +{ + (void)pUserData; + + for (mal_uint32 iChannel = 0; iChannel < pRouter->config.channelsIn; ++iChannel) { + mal_zero_memory(ppSamplesOut[iChannel], sizeof(float)*frameCount); + } + + return frameCount; +} + +int do_channel_routing_tests() +{ + mal_bool32 hasError = MAL_FALSE; + + printf("Passthrough... "); + { + mal_channel_router_config routerConfig; + mal_zero_object(&routerConfig); + routerConfig.mixingMode = mal_channel_mix_mode_planar_blend; + routerConfig.channelsIn = 6; + routerConfig.channelsOut = 6; + mal_get_standard_channel_map(mal_standard_channel_map_microsoft, routerConfig.channelsIn, routerConfig.channelMapIn); + mal_get_standard_channel_map(mal_standard_channel_map_microsoft, routerConfig.channelsOut, routerConfig.channelMapOut); + + + mal_channel_router router; + mal_result result = mal_channel_router_init_separated(&routerConfig, channel_router_callback__passthrough_test, NULL, &router); + if (result == MAL_SUCCESS) { + // Expecing a passthrough. + if (!router.isPassthrough) { + printf("Failed to init router as passthrough.\n"); + hasError = MAL_TRUE; + } + + // Expecting the weights to all be equal to 1 for each channel. + for (mal_uint32 iChannelIn = 0; iChannelIn < routerConfig.channelsIn; ++iChannelIn) { + for (mal_uint32 iChannelOut = 0; iChannelOut < routerConfig.channelsOut; ++iChannelOut) { + float expectedWeight = 0; + if (iChannelIn == iChannelOut) { + expectedWeight = 1; + } + + if (router.weights[iChannelIn][iChannelOut] != expectedWeight) { + printf("Failed. Channel weight incorrect: %f\n", expectedWeight); + hasError = MAL_TRUE; + } + } + } + } else { + printf("Failed to init router.\n"); + hasError = MAL_TRUE; + } + + if (!hasError) { + printf("PASSED\n"); + } + } + + printf("Shuffle... "); + { + // The shuffle is tested by simply reversing the order of the channels. Doing a reversal just makes it easier to + // check that everything is working. + + mal_channel_router_config routerConfig; + mal_zero_object(&routerConfig); + routerConfig.mixingMode = mal_channel_mix_mode_planar_blend; + routerConfig.channelsIn = 6; + routerConfig.channelsOut = routerConfig.channelsIn; + mal_get_standard_channel_map(mal_standard_channel_map_microsoft, routerConfig.channelsIn, routerConfig.channelMapIn); + for (mal_uint32 iChannel = 0; iChannel < routerConfig.channelsIn; ++iChannel) { + routerConfig.channelMapOut[iChannel] = routerConfig.channelMapIn[routerConfig.channelsIn - iChannel - 1]; + } + + mal_channel_router router; + mal_result result = mal_channel_router_init_separated(&routerConfig, channel_router_callback__passthrough_test, NULL, &router); + if (result == MAL_SUCCESS) { + // Expecing a shuffle, but not a passthrough. + if (router.isPassthrough) { + printf("Router incorrectly configured as a passthrough.\n"); + hasError = MAL_TRUE; + } + if (!router.isSimpleShuffle) { + printf("Router not configured as a simple shuffle.\n"); + hasError = MAL_TRUE; + } + + // Expecting the weights to all be equal to 1 for each channel. + for (mal_uint32 iChannelIn = 0; iChannelIn < routerConfig.channelsIn; ++iChannelIn) { + for (mal_uint32 iChannelOut = 0; iChannelOut < routerConfig.channelsOut; ++iChannelOut) { + float expectedWeight = 0; + if (iChannelIn == (routerConfig.channelsOut - iChannelOut - 1)) { + expectedWeight = 1; + } + + if (router.weights[iChannelIn][iChannelOut] != expectedWeight) { + printf("Failed. Channel weight incorrect: %f\n", expectedWeight); + hasError = MAL_TRUE; + } + } + } + } else { + printf("Failed to init router.\n"); + hasError = MAL_TRUE; + } + + if (!hasError) { + printf("PASSED\n"); + } + } + + printf("Simple Conversion (Stereo -> 5.1)... "); + { + // This tests takes a Stereo to 5.1 conversion using the simple mixing mode. We should expect 0 and 1 (front/left, front/right) to have + // weights of 1, and the others to have a weight of 0. + + mal_channel_router_config routerConfig; + mal_zero_object(&routerConfig); + routerConfig.mixingMode = mal_channel_mix_mode_simple; + routerConfig.channelsIn = 2; + routerConfig.channelsOut = 6; + mal_get_standard_channel_map(mal_standard_channel_map_microsoft, routerConfig.channelsIn, routerConfig.channelMapIn); + mal_get_standard_channel_map(mal_standard_channel_map_microsoft, routerConfig.channelsOut, routerConfig.channelMapOut); + + mal_channel_router router; + mal_result result = mal_channel_router_init_separated(&routerConfig, channel_router_callback__passthrough_test, NULL, &router); + if (result == MAL_SUCCESS) { + // Expecing a shuffle, but not a passthrough. + if (router.isPassthrough) { + printf("Router incorrectly configured as a passthrough.\n"); + hasError = MAL_TRUE; + } + if (router.isSimpleShuffle) { + printf("Router incorrectly configured as a simple shuffle.\n"); + hasError = MAL_TRUE; + } + + // Expecting the weights to all be equal to 1 for each channel. + for (mal_uint32 iChannelIn = 0; iChannelIn < routerConfig.channelsIn; ++iChannelIn) { + for (mal_uint32 iChannelOut = 0; iChannelOut < routerConfig.channelsOut; ++iChannelOut) { + float expectedWeight = 0; + if (routerConfig.channelMapIn[iChannelIn] == routerConfig.channelMapOut[iChannelOut]) { + expectedWeight = 1; + } + + if (router.weights[iChannelIn][iChannelOut] != expectedWeight) { + printf("Failed. Channel weight incorrect: %f\n", expectedWeight); + hasError = MAL_TRUE; + } + } + } + } else { + printf("Failed to init router.\n"); + hasError = MAL_TRUE; + } + + if (!hasError) { + printf("PASSED\n"); + } + } + + printf("Simple Conversion (5.1 -> Stereo)... "); + { + mal_channel_router_config routerConfig; + mal_zero_object(&routerConfig); + routerConfig.mixingMode = mal_channel_mix_mode_simple; + routerConfig.channelsIn = 6; + routerConfig.channelsOut = 2; + mal_get_standard_channel_map(mal_standard_channel_map_microsoft, routerConfig.channelsIn, routerConfig.channelMapIn); + mal_get_standard_channel_map(mal_standard_channel_map_microsoft, routerConfig.channelsOut, routerConfig.channelMapOut); + + mal_channel_router router; + mal_result result = mal_channel_router_init_separated(&routerConfig, channel_router_callback__passthrough_test, NULL, &router); + if (result == MAL_SUCCESS) { + // Expecing a shuffle, but not a passthrough. + if (router.isPassthrough) { + printf("Router incorrectly configured as a passthrough.\n"); + hasError = MAL_TRUE; + } + if (router.isSimpleShuffle) { + printf("Router incorrectly configured as a simple shuffle.\n"); + hasError = MAL_TRUE; + } + + // Expecting the weights to all be equal to 1 for each channel. + for (mal_uint32 iChannelIn = 0; iChannelIn < routerConfig.channelsIn; ++iChannelIn) { + for (mal_uint32 iChannelOut = 0; iChannelOut < routerConfig.channelsOut; ++iChannelOut) { + float expectedWeight = 0; + if (routerConfig.channelMapIn[iChannelIn] == routerConfig.channelMapOut[iChannelOut]) { + expectedWeight = 1; + } + + if (router.weights[iChannelIn][iChannelOut] != expectedWeight) { + printf("Failed. Channel weight incorrect: %f\n", expectedWeight); + hasError = MAL_TRUE; + } + } + } + } else { + printf("Failed to init router.\n"); + hasError = MAL_TRUE; + } + + if (!hasError) { + printf("PASSED\n"); + } + } + + printf("Planar Blend Conversion (Stereo -> 5.1)... "); + { + mal_channel_router_config routerConfig; + mal_zero_object(&routerConfig); + routerConfig.mixingMode = mal_channel_mix_mode_planar_blend; + + // Use very specific mappings for this test. + routerConfig.channelsIn = 2; + routerConfig.channelMapIn[0] = MAL_CHANNEL_FRONT_LEFT; + routerConfig.channelMapIn[1] = MAL_CHANNEL_FRONT_RIGHT; + + routerConfig.channelsOut = 8; + routerConfig.channelMapOut[0] = MAL_CHANNEL_FRONT_LEFT; + routerConfig.channelMapOut[1] = MAL_CHANNEL_FRONT_RIGHT; + routerConfig.channelMapOut[2] = MAL_CHANNEL_FRONT_CENTER; + routerConfig.channelMapOut[3] = MAL_CHANNEL_LFE; + routerConfig.channelMapOut[4] = MAL_CHANNEL_BACK_LEFT; + routerConfig.channelMapOut[5] = MAL_CHANNEL_BACK_RIGHT; + routerConfig.channelMapOut[6] = MAL_CHANNEL_SIDE_LEFT; + routerConfig.channelMapOut[7] = MAL_CHANNEL_SIDE_RIGHT; + + mal_channel_router router; + mal_result result = mal_channel_router_init_separated(&routerConfig, channel_router_callback__passthrough_test, NULL, &router); + if (result == MAL_SUCCESS) { + // Expecing a shuffle, but not a passthrough. + if (router.isPassthrough) { + printf("Router incorrectly configured as a passthrough.\n"); + hasError = MAL_TRUE; + } + if (router.isSimpleShuffle) { + printf("Router incorrectly configured as a simple shuffle.\n"); + hasError = MAL_TRUE; + } + + float expectedWeights[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS]; + mal_zero_memory(expectedWeights, sizeof(expectedWeights)); + expectedWeights[0][0] = 1.0f; // FRONT_LEFT -> FRONT_LEFT + expectedWeights[0][1] = 0.0f; // FRONT_LEFT -> FRONT_RIGHT + expectedWeights[0][2] = 0.5f; // FRONT_LEFT -> FRONT_CENTER + expectedWeights[0][3] = 0.0f; // FRONT_LEFT -> LFE + expectedWeights[0][4] = 0.25f; // FRONT_LEFT -> BACK_LEFT + expectedWeights[0][5] = 0.0f; // FRONT_LEFT -> BACK_RIGHT + expectedWeights[0][6] = 0.5f; // FRONT_LEFT -> SIDE_LEFT + expectedWeights[0][7] = 0.0f; // FRONT_LEFT -> SIDE_RIGHT + expectedWeights[1][0] = 0.0f; // FRONT_RIGHT -> FRONT_LEFT + expectedWeights[1][1] = 1.0f; // FRONT_RIGHT -> FRONT_RIGHT + expectedWeights[1][2] = 0.5f; // FRONT_RIGHT -> FRONT_CENTER + expectedWeights[1][3] = 0.0f; // FRONT_RIGHT -> LFE + expectedWeights[1][4] = 0.0f; // FRONT_RIGHT -> BACK_LEFT + expectedWeights[1][5] = 0.25f; // FRONT_RIGHT -> BACK_RIGHT + expectedWeights[1][6] = 0.0f; // FRONT_RIGHT -> SIDE_LEFT + expectedWeights[1][7] = 0.5f; // FRONT_RIGHT -> SIDE_RIGHT + + for (mal_uint32 iChannelIn = 0; iChannelIn < routerConfig.channelsIn; ++iChannelIn) { + for (mal_uint32 iChannelOut = 0; iChannelOut < routerConfig.channelsOut; ++iChannelOut) { + if (router.weights[iChannelIn][iChannelOut] != expectedWeights[iChannelIn][iChannelOut]) { + printf("Failed. Channel weight incorrect for [%d][%d]. Expected %f, got %f\n", iChannelIn, iChannelOut, expectedWeights[iChannelIn][iChannelOut], router.weights[iChannelIn][iChannelOut]); + hasError = MAL_TRUE; + } + } + } + } else { + printf("Failed to init router.\n"); + hasError = MAL_TRUE; + } + + if (!hasError) { + printf("PASSED\n"); + } + } + + printf("Planar Blend Conversion (5.1 -> Stereo)... "); + { + mal_channel_router_config routerConfig; + mal_zero_object(&routerConfig); + routerConfig.mixingMode = mal_channel_mix_mode_planar_blend; + + // Use very specific mappings for this test. + routerConfig.channelsIn = 8; + routerConfig.channelMapIn[0] = MAL_CHANNEL_FRONT_LEFT; + routerConfig.channelMapIn[1] = MAL_CHANNEL_FRONT_RIGHT; + routerConfig.channelMapIn[2] = MAL_CHANNEL_FRONT_CENTER; + routerConfig.channelMapIn[3] = MAL_CHANNEL_LFE; + routerConfig.channelMapIn[4] = MAL_CHANNEL_BACK_LEFT; + routerConfig.channelMapIn[5] = MAL_CHANNEL_BACK_RIGHT; + routerConfig.channelMapIn[6] = MAL_CHANNEL_SIDE_LEFT; + routerConfig.channelMapIn[7] = MAL_CHANNEL_SIDE_RIGHT; + + routerConfig.channelsOut = 2; + routerConfig.channelMapOut[0] = MAL_CHANNEL_FRONT_LEFT; + routerConfig.channelMapOut[1] = MAL_CHANNEL_FRONT_RIGHT; + + mal_channel_router router; + mal_result result = mal_channel_router_init_separated(&routerConfig, channel_router_callback__passthrough_test, NULL, &router); + if (result == MAL_SUCCESS) { + // Expecing a shuffle, but not a passthrough. + if (router.isPassthrough) { + printf("Router incorrectly configured as a passthrough.\n"); + hasError = MAL_TRUE; + } + if (router.isSimpleShuffle) { + printf("Router incorrectly configured as a simple shuffle.\n"); + hasError = MAL_TRUE; + } + + float expectedWeights[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS]; + mal_zero_memory(expectedWeights, sizeof(expectedWeights)); + expectedWeights[0][0] = 1.0f; // FRONT_LEFT -> FRONT_LEFT + expectedWeights[1][0] = 0.0f; // FRONT_RIGHT -> FRONT_LEFT + expectedWeights[2][0] = 0.5f; // FRONT_CENTER -> FRONT_LEFT + expectedWeights[3][0] = 0.0f; // LFE -> FRONT_LEFT + expectedWeights[4][0] = 0.25f; // BACK_LEFT -> FRONT_LEFT + expectedWeights[5][0] = 0.0f; // BACK_RIGHT -> FRONT_LEFT + expectedWeights[6][0] = 0.5f; // SIDE_LEFT -> FRONT_LEFT + expectedWeights[7][0] = 0.0f; // SIDE_RIGHT -> FRONT_LEFT + expectedWeights[0][1] = 0.0f; // FRONT_LEFT -> FRONT_RIGHT + expectedWeights[1][1] = 1.0f; // FRONT_RIGHT -> FRONT_RIGHT + expectedWeights[2][1] = 0.5f; // FRONT_CENTER -> FRONT_RIGHT + expectedWeights[3][1] = 0.0f; // LFE -> FRONT_RIGHT + expectedWeights[4][1] = 0.0f; // BACK_LEFT -> FRONT_RIGHT + expectedWeights[5][1] = 0.25f; // BACK_RIGHT -> FRONT_RIGHT + expectedWeights[6][1] = 0.0f; // SIDE_LEFT -> FRONT_RIGHT + expectedWeights[7][1] = 0.5f; // SIDE_RIGHT -> FRONT_RIGHT + + for (mal_uint32 iChannelIn = 0; iChannelIn < routerConfig.channelsIn; ++iChannelIn) { + for (mal_uint32 iChannelOut = 0; iChannelOut < routerConfig.channelsOut; ++iChannelOut) { + if (router.weights[iChannelIn][iChannelOut] != expectedWeights[iChannelIn][iChannelOut]) { + printf("Failed. Channel weight incorrect for [%d][%d]. Expected %f, got %f\n", iChannelIn, iChannelOut, expectedWeights[iChannelIn][iChannelOut], router.weights[iChannelIn][iChannelOut]); + hasError = MAL_TRUE; + } + } + } + } else { + printf("Failed to init router.\n"); + hasError = MAL_TRUE; + } + + if (!hasError) { + printf("PASSED\n"); + } + } + + + if (hasError) { + return -1; + } else { + return 0; + } +} + + int do_backend_test(mal_backend backend) { mal_result result = MAL_SUCCESS; @@ -1491,6 +1850,7 @@ int main(int argc, char** argv) mal_bool32 hasErrorOccurred = MAL_FALSE; int result = 0; + // Format Conversion printf("=== TESTING FORMAT CONVERSION ===\n"); result = do_format_conversion_tests(); if (result < 0) { @@ -1500,6 +1860,7 @@ int main(int argc, char** argv) printf("\n"); + // Interleaving / Deinterleaving printf("=== TESTING INTERLEAVING/DEINTERLEAVING ===\n"); result = do_interleaving_tests(); if (result < 0) { @@ -1509,6 +1870,7 @@ int main(int argc, char** argv) printf("\n"); + // mal_format_convert printf("=== TESTING FORMAT CONVERTER ===\n"); result = do_format_converter_tests(); if (result < 0) { @@ -1517,7 +1879,18 @@ int main(int argc, char** argv) printf("=== END TESTING FORMAT CONVERTER ===\n"); printf("\n"); + + // Channel Routing + printf("=== TESTING CHANNEL ROUTING ===\n"); + result = do_channel_routing_tests(); + if (result < 0) { + hasErrorOccurred = MAL_TRUE; + } + printf("=== END TESTING CHANNEL ROUTING ===\n"); + printf("\n"); + + // Backends printf("=== TESTING BACKENDS ===\n"); result = do_backend_tests(); if (result < 0) { @@ -1527,6 +1900,7 @@ int main(int argc, char** argv) printf("\n"); + // Default Playback Devices printf("=== TESTING DEFAULT PLAYBACK DEVICES ===\n"); result = do_playback_tests(); if (result < 0) {