• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "mixerop_tests"
19 #include <log/log.h>
20 
21 #include <inttypes.h>
22 #include <type_traits>
23 
24 #include <../AudioMixerOps.h>
25 #include <gtest/gtest.h>
26 
27 using namespace android;
28 
29 // Note: gtest templated tests require typenames, not integers.
30 template <int MIXTYPE, int NCHAN>
31 class MixerOpsBasicTest {
32 public:
testStereoVolume()33     static void testStereoVolume() {
34         using namespace android::audio_utils::channels;
35 
36         constexpr size_t FRAME_COUNT = 1000;
37         constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
38 
39         const float in[SAMPLE_COUNT] = {[0 ... (SAMPLE_COUNT - 1)] = 1.f};
40 
41         AUDIO_GEOMETRY_SIDE sides[NCHAN];
42         size_t i = 0;
43         unsigned channel = canonicalChannelMaskFromCount(NCHAN);
44         constexpr unsigned LFE_LFE2 =
45                 AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2;
46         bool has_LFE_LFE2 = (channel & LFE_LFE2) == LFE_LFE2;
47         while (channel != 0) {
48             const int index = __builtin_ctz(channel);
49             if (has_LFE_LFE2 && (1 << index) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY) {
50                 sides[i++] = AUDIO_GEOMETRY_SIDE_LEFT; // special case
51             } else if (has_LFE_LFE2 && (1 << index) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2) {
52                 sides[i++] = AUDIO_GEOMETRY_SIDE_RIGHT; // special case
53             } else {
54                 sides[i++] = sideFromChannelIdx(index);
55             }
56             channel &= ~(1 << index);
57         }
58 
59         float vola[2] = {1.f, 0.f}; // left volume at max.
60         float out[SAMPLE_COUNT]{};
61         float aux[FRAME_COUNT]{};
62         float volaux = 0.5;
63         {
64             volumeMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vola, volaux);
65             const float *outp = out;
66             const float *auxp = aux;
67             const float left = vola[0];
68             const float center = (vola[0] + vola[1]) * 0.5;
69             const float right = vola[1];
70             for (size_t i = 0; i < FRAME_COUNT; ++i) {
71                 for (size_t j = 0; j < NCHAN; ++j) {
72                     const float audio = *outp++;
73                     if (sides[j] == AUDIO_GEOMETRY_SIDE_LEFT) {
74                         EXPECT_EQ(left, audio);
75                     } else if (sides[j] == AUDIO_GEOMETRY_SIDE_CENTER) {
76                         EXPECT_EQ(center, audio);
77                     } else {
78                         EXPECT_EQ(right, audio);
79                     }
80                 }
81                 EXPECT_EQ(volaux, *auxp++);  // works if all channels contain 1.f
82             }
83         }
84         float volb[2] = {0.f, 0.5f}; // right volume at half max.
85         {
86             // this accumulates into out, aux.
87             // float out[SAMPLE_COUNT]{};
88             // float aux[FRAME_COUNT]{};
89             volumeMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, volb, volaux);
90             const float *outp = out;
91             const float *auxp = aux;
92             const float left = vola[0] + volb[0];
93             const float center = (vola[0] + vola[1] + volb[0] + volb[1]) * 0.5;
94             const float right = vola[1] + volb[1];
95             for (size_t i = 0; i < FRAME_COUNT; ++i) {
96                 for (size_t j = 0; j < NCHAN; ++j) {
97                     const float audio = *outp++;
98                     if (sides[j] == AUDIO_GEOMETRY_SIDE_LEFT) {
99                         EXPECT_EQ(left, audio);
100                     } else if (sides[j] == AUDIO_GEOMETRY_SIDE_CENTER) {
101                         EXPECT_EQ(center, audio);
102                     } else {
103                         EXPECT_EQ(right, audio);
104                     }
105                 }
106                 // aux is accumulated so 2x the amplitude
107                 EXPECT_EQ(volaux * 2.f, *auxp++);  // works if all channels contain 1.f
108             }
109         }
110 
111         { // test aux as derived from out.
112             // AUX channel is the weighted sum of all of the output channels prior to volume
113             // adjustment.  We must set L and R to the same volume to allow computation
114             // of AUX from the output values.
115             const float volmono = 0.25f;
116             const float vollr[2] = {volmono, volmono}; // all the same.
117             float out[SAMPLE_COUNT]{};
118             float aux[FRAME_COUNT]{};
119             volumeMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vollr, volaux);
120             const float *outp = out;
121             const float *auxp = aux;
122             for (size_t i = 0; i < FRAME_COUNT; ++i) {
123                 float accum = 0.f;
124                 for (size_t j = 0; j < NCHAN; ++j) {
125                     accum += *outp++;
126                 }
127                 EXPECT_EQ(accum / NCHAN * volaux / volmono, *auxp++);
128             }
129         }
130     }
131 };
132 
TEST(mixerops,stereovolume_1)133 TEST(mixerops, stereovolume_1) { // Note: mono not used for output sinks yet.
134     MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 1>::testStereoVolume();
135 }
TEST(mixerops,stereovolume_2)136 TEST(mixerops, stereovolume_2) {
137     MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 2>::testStereoVolume();
138 }
TEST(mixerops,stereovolume_3)139 TEST(mixerops, stereovolume_3) {
140     MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 3>::testStereoVolume();
141 }
TEST(mixerops,stereovolume_4)142 TEST(mixerops, stereovolume_4) {
143     MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 4>::testStereoVolume();
144 }
TEST(mixerops,stereovolume_5)145 TEST(mixerops, stereovolume_5) {
146     MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 5>::testStereoVolume();
147 }
TEST(mixerops,stereovolume_6)148 TEST(mixerops, stereovolume_6) {
149     MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 6>::testStereoVolume();
150 }
TEST(mixerops,stereovolume_7)151 TEST(mixerops, stereovolume_7) {
152     MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 7>::testStereoVolume();
153 }
TEST(mixerops,stereovolume_8)154 TEST(mixerops, stereovolume_8) {
155     MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 8>::testStereoVolume();
156 }
TEST(mixerops,stereovolume_12)157 TEST(mixerops, stereovolume_12) {
158     if constexpr (FCC_LIMIT >= 12) { // NOTE: FCC_LIMIT is an enum, so can't #if
159         MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 12>::testStereoVolume();
160     }
161 }
TEST(mixerops,stereovolume_24)162 TEST(mixerops, stereovolume_24) {
163     if constexpr (FCC_LIMIT >= 24) {
164         MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 24>::testStereoVolume();
165     }
166 }
TEST(mixerops,channel_equivalence)167 TEST(mixerops, channel_equivalence) {
168     // we must match the constexpr function with the system determined channel mask from count.
169     for (size_t i = 0; i < FCC_LIMIT; ++i) {
170         const audio_channel_mask_t actual = canonicalChannelMaskFromCount(i);
171         const audio_channel_mask_t system = audio_channel_out_mask_from_count(i);
172         if (system == AUDIO_CHANNEL_INVALID) continue;
173         EXPECT_EQ(system, actual);
174     }
175 }
176