/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "mixerop_tests" #include #include #include #include <../AudioMixerOps.h> #include using namespace android; // Note: gtest templated tests require typenames, not integers. template class MixerOpsBasicTest { public: static void testStereoVolume() { using namespace android::audio_utils::channels; constexpr size_t FRAME_COUNT = 1000; constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN; const float in[SAMPLE_COUNT] = {[0 ... (SAMPLE_COUNT - 1)] = 1.f}; AUDIO_GEOMETRY_SIDE sides[NCHAN]; size_t i = 0; unsigned channel = canonicalChannelMaskFromCount(NCHAN); constexpr unsigned LFE_LFE2 = AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2; bool has_LFE_LFE2 = (channel & LFE_LFE2) == LFE_LFE2; while (channel != 0) { const int index = __builtin_ctz(channel); if (has_LFE_LFE2 && (1 << index) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY) { sides[i++] = AUDIO_GEOMETRY_SIDE_LEFT; // special case } else if (has_LFE_LFE2 && (1 << index) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2) { sides[i++] = AUDIO_GEOMETRY_SIDE_RIGHT; // special case } else { sides[i++] = sideFromChannelIdx(index); } channel &= ~(1 << index); } float vola[2] = {1.f, 0.f}; // left volume at max. float out[SAMPLE_COUNT]{}; float aux[FRAME_COUNT]{}; float volaux = 0.5; { volumeMulti(out, FRAME_COUNT, in, aux, vola, volaux); const float *outp = out; const float *auxp = aux; const float left = vola[0]; const float center = (vola[0] + vola[1]) * 0.5; const float right = vola[1]; for (size_t i = 0; i < FRAME_COUNT; ++i) { for (size_t j = 0; j < NCHAN; ++j) { const float audio = *outp++; if (sides[j] == AUDIO_GEOMETRY_SIDE_LEFT) { EXPECT_EQ(left, audio); } else if (sides[j] == AUDIO_GEOMETRY_SIDE_CENTER) { EXPECT_EQ(center, audio); } else { EXPECT_EQ(right, audio); } } EXPECT_EQ(volaux, *auxp++); // works if all channels contain 1.f } } float volb[2] = {0.f, 0.5f}; // right volume at half max. { // this accumulates into out, aux. // float out[SAMPLE_COUNT]{}; // float aux[FRAME_COUNT]{}; volumeMulti(out, FRAME_COUNT, in, aux, volb, volaux); const float *outp = out; const float *auxp = aux; const float left = vola[0] + volb[0]; const float center = (vola[0] + vola[1] + volb[0] + volb[1]) * 0.5; const float right = vola[1] + volb[1]; for (size_t i = 0; i < FRAME_COUNT; ++i) { for (size_t j = 0; j < NCHAN; ++j) { const float audio = *outp++; if (sides[j] == AUDIO_GEOMETRY_SIDE_LEFT) { EXPECT_EQ(left, audio); } else if (sides[j] == AUDIO_GEOMETRY_SIDE_CENTER) { EXPECT_EQ(center, audio); } else { EXPECT_EQ(right, audio); } } // aux is accumulated so 2x the amplitude EXPECT_EQ(volaux * 2.f, *auxp++); // works if all channels contain 1.f } } { // test aux as derived from out. // AUX channel is the weighted sum of all of the output channels prior to volume // adjustment. We must set L and R to the same volume to allow computation // of AUX from the output values. const float volmono = 0.25f; const float vollr[2] = {volmono, volmono}; // all the same. float out[SAMPLE_COUNT]{}; float aux[FRAME_COUNT]{}; volumeMulti(out, FRAME_COUNT, in, aux, vollr, volaux); const float *outp = out; const float *auxp = aux; for (size_t i = 0; i < FRAME_COUNT; ++i) { float accum = 0.f; for (size_t j = 0; j < NCHAN; ++j) { accum += *outp++; } EXPECT_EQ(accum / NCHAN * volaux / volmono, *auxp++); } } } }; TEST(mixerops, stereovolume_1) { // Note: mono not used for output sinks yet. MixerOpsBasicTest::testStereoVolume(); } TEST(mixerops, stereovolume_2) { MixerOpsBasicTest::testStereoVolume(); } TEST(mixerops, stereovolume_3) { MixerOpsBasicTest::testStereoVolume(); } TEST(mixerops, stereovolume_4) { MixerOpsBasicTest::testStereoVolume(); } TEST(mixerops, stereovolume_5) { MixerOpsBasicTest::testStereoVolume(); } TEST(mixerops, stereovolume_6) { MixerOpsBasicTest::testStereoVolume(); } TEST(mixerops, stereovolume_7) { MixerOpsBasicTest::testStereoVolume(); } TEST(mixerops, stereovolume_8) { MixerOpsBasicTest::testStereoVolume(); } TEST(mixerops, stereovolume_12) { if constexpr (FCC_LIMIT >= 12) { // NOTE: FCC_LIMIT is an enum, so can't #if MixerOpsBasicTest::testStereoVolume(); } } TEST(mixerops, stereovolume_24) { if constexpr (FCC_LIMIT >= 24) { MixerOpsBasicTest::testStereoVolume(); } } TEST(mixerops, channel_equivalence) { // we must match the constexpr function with the system determined channel mask from count. for (size_t i = 0; i < FCC_LIMIT; ++i) { const audio_channel_mask_t actual = canonicalChannelMaskFromCount(i); const audio_channel_mask_t system = audio_channel_out_mask_from_count(i); if (system == AUDIO_CHANNEL_INVALID) continue; EXPECT_EQ(system, actual); } }