1 /*
2  * Copyright (C) 2017 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 #include <math.h>
18 #include <vector>
19 
20 #include <gtest/gtest.h>
21 
22 #include <audio_utils/channels.h>
23 
24 // TODO: Make a common include file for helper functions.
25 
26 template<typename T>
checkMonotone(const T * ary,size_t size)27 void checkMonotone(const T *ary, size_t size)
28 {
29     for (size_t i = 1; i < size; ++i) {
30         EXPECT_LT(ary[i-1], ary[i]);
31     }
32 }
33 
34 template<typename T>
checkUnsignedMonotoneOrZero(const T * ary,size_t size)35 void checkUnsignedMonotoneOrZero(const T *ary, size_t size)
36 {
37     if (size == 0) return;
38 
39     T least = ary[0];
40     for (size_t i = 1; i < size; ++i) {
41         if (ary[i]) {
42             EXPECT_LT(least, ary[i]);
43             least = ary[i];
44         }
45     }
46 }
47 
48 template<typename T>
expectEq(const T & c1,const T & c2)49 void expectEq(const T &c1, const T &c2) {
50     EXPECT_EQ(c1.size(), c2.size());
51     EXPECT_EQ(0, memcmp(c1.data(), c2.data(), sizeof(c1[0]) * std::min(c1.size(), c2.size())));
52 }
53 
TEST(audio_utils_channels,geometry_constexpr)54 TEST(audio_utils_channels, geometry_constexpr) {
55     using namespace android::audio_utils::channels;
56     // fails to compile if not const.
57     constexpr size_t RIGHT_IDX = 1;  // bit position of AUDIO_CHANNEL_OUT_FRONT_RIGHT;
58     static constexpr AUDIO_GEOMETRY_SIDE checkConstexprSide = sideFromChannelIdx(RIGHT_IDX);
59     static constexpr AUDIO_GEOMETRY_HEIGHT checkConstexprHeight = heightFromChannelIdx(RIGHT_IDX);
60     static constexpr AUDIO_GEOMETRY_DEPTH checkConstexprDepth = depthFromChannelIdx(RIGHT_IDX);
61     (void) checkConstexprSide;
62     (void) checkConstexprHeight;
63     (void) checkConstexprDepth;
64 
65     static constexpr ssize_t leftIdx = pairIdxFromChannelIdx(RIGHT_IDX);
66     ASSERT_EQ(0, leftIdx);
67 }
68 
TEST(audio_utils_channels,geometry_range)69 TEST(audio_utils_channels, geometry_range) {
70     using namespace android::audio_utils::channels;
71     for (size_t i = 0; i < FCC_24 + 2 /* sic */; ++i) {
72         const AUDIO_GEOMETRY_SIDE side = sideFromChannelIdx(i);
73         const AUDIO_GEOMETRY_HEIGHT height = heightFromChannelIdx(i);
74         const AUDIO_GEOMETRY_DEPTH depth = depthFromChannelIdx(i);
75         ASSERT_TRUE(side == AUDIO_GEOMETRY_SIDE_LEFT
76                 || side == AUDIO_GEOMETRY_SIDE_RIGHT
77                 || side == AUDIO_GEOMETRY_SIDE_CENTER);
78         ASSERT_TRUE(height == AUDIO_GEOMETRY_HEIGHT_BOTTOM
79                 || height == AUDIO_GEOMETRY_HEIGHT_MIDDLE
80                 || height == AUDIO_GEOMETRY_HEIGHT_TOP);
81         ASSERT_TRUE(depth == AUDIO_GEOMETRY_DEPTH_FRONT
82                 || depth == AUDIO_GEOMETRY_DEPTH_MIDDLE
83                 || depth == AUDIO_GEOMETRY_DEPTH_BACK);
84     }
85 }
86 
TEST(audio_utils_channels,array_lr_pair_matching)87 TEST(audio_utils_channels, array_lr_pair_matching) {
88     using namespace android::audio_utils::channels;
89     for (size_t i = 0; i < FCC_24; ++i) {
90         const AUDIO_GEOMETRY_SIDE side = sideFromChannelIdx(i);
91         const ssize_t pairIdx = pairIdxFromChannelIdx(i);
92         switch (side) {
93         case AUDIO_GEOMETRY_SIDE_LEFT:
94         case AUDIO_GEOMETRY_SIDE_RIGHT: {
95             ASSERT_GE(pairIdx, 0);
96             ASSERT_LT(pairIdx, FCC_24);
97             const AUDIO_GEOMETRY_SIDE pairSide = side == AUDIO_GEOMETRY_SIDE_LEFT
98                     ? AUDIO_GEOMETRY_SIDE_RIGHT : AUDIO_GEOMETRY_SIDE_LEFT;
99             ASSERT_EQ(pairSide, sideFromChannelIdx(pairIdx));
100         } break;
101         case AUDIO_GEOMETRY_SIDE_CENTER:
102             ASSERT_EQ(-1, pairIdx);
103             break;
104         }
105     }
106 }
107 
TEST(audio_utils_channels,adjust_channels)108 TEST(audio_utils_channels, adjust_channels) {
109     constexpr size_t size = 65536;
110     std::vector<uint16_t> u16ref(size);
111     std::vector<uint16_t> u16expand(size * 2);
112     std::vector<uint16_t> u16ary(size);
113 
114     // reference buffer is monotonic.
115     for (size_t i = 0; i < u16ref.size(); ++i) {
116         u16ref[i] = i;
117     }
118 
119     // expand channels from stereo to quad.
120     adjust_channels(
121             u16ref.data() /*in_buff*/,
122             2 /*in_channels*/,
123             u16expand.data() /*out_buff*/,
124             4 /*out_channels*/,
125             sizeof(u16ref[0]) /*sample_size_in_bytes*/,
126             sizeof(u16ref[0]) * u16ref.size() /*num_in_bytes*/);
127 
128     // expanded buffer must increase (or be zero).
129     checkUnsignedMonotoneOrZero(u16expand.data(), u16expand.size());
130 
131     // contract channels back to stereo.
132     adjust_channels(
133             u16expand.data() /*in_buff*/,
134             4 /*in_channels*/,
135             u16ary.data() /*out_buff*/,
136             2 /*out_channels*/,
137             sizeof(u16expand[0]) /*sample_size_in_bytes*/,
138             sizeof(u16expand[0]) * u16expand.size() /*num_in_bytes*/);
139 
140     // contracted array must be identical to original.
141     expectEq(u16ary, u16ref);
142 }
143 
TEST(audio_utils_channels,adjust_selected_channels)144 TEST(audio_utils_channels, adjust_selected_channels) {
145     constexpr size_t size = 65536;
146     std::vector<uint16_t> u16ref(size);
147     std::vector<uint16_t> u16contract(size / 2);
148     std::vector<uint16_t> u16ary(size);
149 
150     // reference buffer is monotonic.
151     for (size_t i = 0; i < u16ref.size(); ++i) {
152         u16ref[i] = i;
153     }
154 
155     // contract from quad to stereo.
156     adjust_selected_channels(
157             u16ref.data() /*in_buff*/,
158             4 /*in_channels*/,
159             u16contract.data() /*out_buff*/,
160             2 /*out_channels*/,
161             sizeof(u16ref[0]) /*sample_size_in_bytes*/,
162             sizeof(u16ref[0]) * u16ref.size() /*num_in_bytes*/);
163 
164     // contracted buffer must increase.
165     checkMonotone(u16contract.data(), u16contract.size());
166 
167     // initialize channels 3 and 4 of final comparison array.
168     for (size_t i = 0; i < u16ary.size() / 4; ++i) {
169         u16ary[i * 4 + 2] = u16ref[i * 4 + 2];
170         u16ary[i * 4 + 3] = u16ref[i * 4 + 3];
171     }
172 
173     // expand stereo into channels 1 and 2 of quad comparison array.
174     adjust_selected_channels(
175             u16contract.data() /*in_buff*/,
176             2 /*in_channels*/,
177             u16ary.data() /*out_buff*/,
178             4 /*out_channels*/,
179             sizeof(u16contract[0]) /*sample_size_in_bytes*/,
180             sizeof(u16contract[0]) * u16contract.size() /*num_in_bytes*/);
181 
182     // comparison array must be identical to original.
183     expectEq(u16ary, u16ref);
184 }
185 
TEST(audio_utils_channels,adjust_channels_non_destructive)186 TEST(audio_utils_channels, adjust_channels_non_destructive) {
187     constexpr size_t size = 65536; /* arbitrary large multiple of 8 */
188     std::vector<uint16_t> u16ref(size);
189     std::vector<uint16_t> u16contracted(size);
190     std::vector<uint16_t> u16expanded(size);
191     std::vector<uint16_t> u16inout(size);
192 
193     // Reference buffer increases monotonically.
194     // For second test, in/out buffer begins identical to ref.
195     for (size_t i = 0; i < u16ref.size(); ++i) {
196         u16ref[i] = i;
197         u16inout[i] = i;
198     }
199 
200     // *** First test: different in/out buffers ***
201 
202     // Contract from quad to stereo.
203     adjust_channels_non_destructive(
204             u16ref.data() /*in_buff*/,
205             4 /*in_channels*/,
206             u16contracted.data() /*out_buff*/,
207             2 /*out_channels*/,
208             sizeof(u16ref[0]) /*sample_size_in_bytes*/,
209             sizeof(u16ref[0]) * u16ref.size() /*num_in_bytes*/);
210 
211     // Each half of contracted buffer should increase monotonically.
212     checkMonotone(u16contracted.data(), u16contracted.size() / 2);
213     checkMonotone(&u16contracted[u16contracted.size() / 2], u16contracted.size() / 2);
214 
215     // Expand stereo to quad
216     adjust_channels_non_destructive(
217             u16contracted.data() /*in_buff*/,
218             2 /*in_channels*/,
219             u16expanded.data() /*out_buff*/,
220             4 /*out_channels*/,
221             sizeof(u16contracted[0]) /*sample_size_in_bytes*/,
222             sizeof(u16contracted[0]) * (u16contracted.size() / 2) /*num_in_bytes*/);
223 
224     // Comparison array must be identical to reference.
225     expectEq(u16expanded, u16ref);
226 
227     // *** Second test: in_buff == out_buff ***
228 
229     // Contract from eight channels to stereo.
230     adjust_channels_non_destructive(
231             u16inout.data() /*in_buff*/,
232             8 /*in_channels*/,
233             u16inout.data() /*out_buff*/,
234             2 /*out_channels*/,
235             sizeof(u16inout[0]) /*sample_size_in_bytes*/,
236             sizeof(u16inout[0]) * u16inout.size() /*num_in_bytes*/);
237 
238     // Each section [1/4][3/4] of contracted buffer should increase monotonically.
239     checkMonotone(u16inout.data(), u16inout.size() / 4);
240     checkMonotone(&u16inout[u16inout.size() / 4], (u16inout.size() * 3) / 4);
241 
242     // Expand stereo to eight channels.
243     adjust_channels_non_destructive(
244             u16inout.data() /*in_buff*/,
245             2 /*in_channels*/,
246             u16inout.data() /*out_buff*/,
247             8 /*out_channels*/,
248             sizeof(u16inout[0]) /*sample_size_in_bytes*/,
249             sizeof(u16inout[0]) * (u16inout.size() / 4) /*num_in_bytes*/);
250 
251     // Comparison array must be identical to reference.
252     expectEq(u16inout, u16ref);
253 }
254