OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <cmath> | |
6 #include <cstdint> | |
7 #include <limits> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/logging.h" | |
12 #include "base/macros.h" | |
13 #include "base/memory/ptr_util.h" | |
14 #include "chromecast/media/cma/backend/alsa/slew_volume.h" | |
15 #include "media/base/audio_bus.h" | |
16 #include "media/base/vector_math.h" | |
17 #include "testing/gtest/include/gtest/gtest.h" | |
18 | |
19 namespace chromecast { | |
20 namespace media { | |
21 | |
22 namespace { | |
23 | |
24 const int kNumChannels = 2; | |
25 const int kNumFrames = 100; | |
26 const float kSinFrequency = 1.0f / kNumFrames; | |
27 const int kBytesPerSample = sizeof(int32_t); | |
28 | |
29 // Frequency is in frames (frequency = frequency_in_hz / sample rate) | |
30 std::unique_ptr<::media::AudioBus> GetSineData(size_t frames, float frequency) { | |
31 auto data = ::media::AudioBus::Create(kNumChannels, frames); | |
32 std::vector<int32_t> sine(frames * 2); | |
33 for (size_t i = 0; i < frames; ++i) { | |
34 // Offset by 1 because sin(0) = 0 and the first value is a special case. | |
35 sine[i * 2] = sin(static_cast<float>(i + 1) * frequency * 2 * M_PI) * | |
36 std::numeric_limits<int32_t>::max(); | |
37 sine[i * 2 + 1] = cos(static_cast<float>(i + 1) * frequency * 2 * M_PI) * | |
38 std::numeric_limits<int32_t>::max(); | |
39 } | |
40 data->FromInterleaved(sine.data(), frames, kBytesPerSample); | |
41 return data; | |
42 } | |
43 | |
44 // Gets pointers to the data in an audiobus. | |
45 // If |swapped| is true, the channel order will be swapped. | |
46 std::vector<float*> GetDataChannels(::media::AudioBus* audio, | |
47 bool swapped = false) { | |
48 std::vector<float*> data(kNumChannels); | |
49 for (int i = 0; i < kNumChannels; ++i) { | |
50 int source_channel = swapped ? (i + 1) % kNumChannels : i; | |
51 data[i] = audio->channel(source_channel); | |
52 } | |
53 return data; | |
54 } | |
55 | |
56 void ScaleData(const std::vector<float*>& data, int frames, float scale) { | |
57 for (size_t ch = 0; ch < data.size(); ++ch) { | |
58 for (int f = 0; f < frames; ++f) { | |
59 data[ch][f] *= scale; | |
60 } | |
61 } | |
62 } | |
63 | |
64 void CompareDataPartial(const std::vector<float*>& expected, | |
65 const std::vector<float*>& actual, | |
66 int start, | |
67 int end) { | |
68 ASSERT_GE(start, 0); | |
69 ASSERT_LT(start, end); | |
70 ASSERT_EQ(expected.size(), actual.size()); | |
71 | |
72 for (size_t ch = 0; ch < expected.size(); ++ch) { | |
73 for (int f = start; f < end; ++f) { | |
74 EXPECT_FLOAT_EQ(expected[ch][f], actual[ch][f]) | |
75 << "ch: " << ch << " f: " << f; | |
76 } | |
77 } | |
78 } | |
79 | |
80 } // namespace | |
81 | |
82 class SlewVolumeBaseTest : public ::testing::Test { | |
83 protected: | |
84 SlewVolumeBaseTest() = default; | |
85 ~SlewVolumeBaseTest() override = default; | |
86 | |
87 void SetUp() override { | |
88 slew_volume_ = base::MakeUnique<SlewVolume>(); | |
89 slew_volume_->Interrupted(); | |
90 MakeData(kNumFrames); | |
91 } | |
92 | |
93 void MakeData(int num_frames) { | |
94 num_frames_ = num_frames; | |
95 data_bus_ = GetSineData(num_frames_, kSinFrequency); | |
96 data_bus_2_ = GetSineData(num_frames_, kSinFrequency); | |
97 expected_bus_ = GetSineData(num_frames_, kSinFrequency); | |
98 data_ = GetDataChannels(data_bus_.get()); | |
99 data_2_ = GetDataChannels(data_bus_2_.get(), true /* swapped */); | |
100 expected_ = GetDataChannels(expected_bus_.get()); | |
101 } | |
102 | |
103 void CompareBuffers(int start = 0, int end = -1) { | |
104 if (end == -1) { | |
105 end = num_frames_; | |
106 } | |
107 ASSERT_LE(end, num_frames_); | |
bcf
2017/05/04 03:45:06
ASSERT_GE(end, 0)
bshaya
2017/05/04 18:15:41
Done.
| |
108 CompareDataPartial(expected_, data_, start, end); | |
109 } | |
110 | |
111 void ClearInterrupted() { | |
112 float throwaway __attribute__((__aligned__(16))) = 0.0f; | |
113 slew_volume_->ProcessFMUL(false, &throwaway, 1, &throwaway); | |
114 } | |
115 | |
116 int num_frames_; | |
117 | |
118 std::unique_ptr<SlewVolume> slew_volume_; | |
119 std::unique_ptr<::media::AudioBus> data_bus_; | |
120 std::unique_ptr<::media::AudioBus> data_bus_2_; | |
121 std::unique_ptr<::media::AudioBus> expected_bus_; | |
122 std::vector<float*> data_; | |
123 std::vector<float*> data_2_; | |
124 std::vector<float*> expected_; | |
125 | |
126 private: | |
127 DISALLOW_COPY_AND_ASSIGN(SlewVolumeBaseTest); | |
128 }; | |
129 | |
130 TEST_F(SlewVolumeBaseTest, BadSampleRate) { | |
131 ASSERT_DEATH(slew_volume_->SetSampleRate(0), "sample_rate"); | |
132 } | |
133 | |
134 TEST_F(SlewVolumeBaseTest, BadSlewTime) { | |
135 ASSERT_DEATH(slew_volume_->SetMaxSlewTimeMs(-1), ""); | |
136 } | |
137 | |
138 class SlewVolumeSteadyStateTest : public SlewVolumeBaseTest { | |
139 protected: | |
140 SlewVolumeSteadyStateTest() = default; | |
141 ~SlewVolumeSteadyStateTest() override = default; | |
142 | |
143 void SetUp() override { | |
144 SlewVolumeBaseTest::SetUp(); | |
145 slew_volume_->Interrupted(); | |
146 } | |
147 | |
148 private: | |
149 DISALLOW_COPY_AND_ASSIGN(SlewVolumeSteadyStateTest); | |
150 }; | |
151 | |
152 TEST_F(SlewVolumeSteadyStateTest, FMULNoOp) { | |
153 slew_volume_->SetVolume(1.0f); | |
154 | |
155 slew_volume_->ProcessFMUL(false /* repeat transition */, data_[0], | |
156 num_frames_, data_[0]); | |
157 slew_volume_->ProcessFMUL(true /* repeat transition */, data_[1], num_frames_, | |
158 data_[1]); | |
159 CompareBuffers(); | |
160 } | |
161 | |
162 TEST_F(SlewVolumeSteadyStateTest, FMULCopy) { | |
163 slew_volume_->SetVolume(1.0f); | |
164 | |
165 slew_volume_->ProcessFMUL(false /* repeat transition */, data_2_[0], | |
166 num_frames_, data_[0]); | |
167 slew_volume_->ProcessFMUL(true /* repeat transition */, data_2_[1], | |
168 num_frames_, data_[1]); | |
169 CompareDataPartial(data_2_, data_, 0, num_frames_); | |
170 } | |
171 | |
172 TEST_F(SlewVolumeSteadyStateTest, FMULZero) { | |
173 slew_volume_->SetVolume(0.0f); | |
174 slew_volume_->ProcessFMUL(false, /* repeat transition */ | |
175 data_[0], num_frames_, data_[0]); | |
176 slew_volume_->ProcessFMUL(true /* repeat transition */, data_[1], num_frames_, | |
177 data_[1]); | |
178 | |
179 for (size_t ch = 0; ch < data_.size(); ++ch) { | |
180 for (int f = 0; f < num_frames_; ++f) { | |
181 EXPECT_EQ(0.0f, data_[ch][f]) << "at ch " << ch << "frame " << f; | |
182 } | |
183 } | |
184 } | |
185 | |
186 TEST_F(SlewVolumeSteadyStateTest, FMULInterrupted) { | |
187 float volume = 0.6f; | |
188 slew_volume_->SetVolume(volume); | |
189 | |
190 slew_volume_->ProcessFMUL(false /* repeat transition */, | |
191 data_[0] /* source */, num_frames_, | |
192 data_[0] /* dst */); | |
193 slew_volume_->ProcessFMUL(true /* repeat transition */, data_[1] /* source */, | |
194 num_frames_, data_[1] /* dst */); | |
195 ScaleData(expected_, num_frames_, volume); | |
196 CompareBuffers(); | |
197 } | |
198 | |
199 TEST_F(SlewVolumeSteadyStateTest, FMACNoOp) { | |
200 slew_volume_->SetVolume(0.0f); | |
201 slew_volume_->ProcessFMAC(false /* repeat transition */, | |
202 data_2_[0] /* source */, num_frames_, | |
203 data_[0] /* dst */); | |
204 slew_volume_->ProcessFMAC(false /* repeat transition */, | |
205 data_2_[1] /* source */, num_frames_, | |
206 data_[1] /*dst */); | |
207 CompareBuffers(); | |
208 } | |
209 | |
210 class SlewVolumeDynamicTest | |
211 : public SlewVolumeBaseTest, | |
212 public ::testing::WithParamInterface<std::tuple<int, int>> { | |
213 protected: | |
214 SlewVolumeDynamicTest() = default; | |
215 ~SlewVolumeDynamicTest() override = default; | |
216 | |
217 void SetUp() override { | |
218 SlewVolumeBaseTest::SetUp(); | |
219 sample_rate_ = std::get<0>(GetParam()); | |
220 slew_time_ms_ = std::get<1>(GetParam()); | |
221 slew_time_frames_ = sample_rate_ * slew_time_ms_ / 1000; | |
222 slew_volume_->SetSampleRate(sample_rate_); | |
223 slew_volume_->SetMaxSlewTimeMs(slew_time_ms_); | |
224 | |
225 int num_frames = slew_time_frames_ + 2; // +2 frames for numeric errors. | |
226 ASSERT_GE(num_frames, 1); | |
227 MakeData(num_frames); | |
228 } | |
229 | |
230 // Checks data_ = slew_volume_(expected_) | |
231 void CheckSlewMUL(double start_vol, double end_vol) { | |
232 for (size_t ch = 0; ch < data_.size(); ++ch) { | |
233 // First value should have original scaling applied. | |
234 EXPECT_FLOAT_EQ(expected_[ch][0] * start_vol, data_[ch][0]) << ch; | |
235 | |
236 // Steady state have final scaling applied | |
237 int f = num_frames_ - 1; | |
238 EXPECT_FLOAT_EQ(expected_[ch][f] * end_vol, data_[ch][f]) << ch; | |
239 } | |
240 } | |
241 | |
242 // Checks data_ = expected_ + slew_volume_(data_2_) | |
243 void CheckSlewMAC(double start_vol, double end_vol) { | |
244 for (size_t ch = 0; ch < data_.size(); ++ch) { | |
245 // First value should have original scaling applied. | |
246 EXPECT_FLOAT_EQ(expected_[ch][0] + data_2_[ch][0] * start_vol, | |
247 data_[ch][0]) | |
248 << ch; | |
249 | |
250 // Steady state have final scaling applied | |
251 int f = num_frames_ - 1; | |
252 EXPECT_FLOAT_EQ(expected_[ch][f] + data_2_[ch][f] * end_vol, data_[ch][f]) | |
253 << ch << " " << f; | |
254 } | |
255 } | |
256 | |
257 int sample_rate_; | |
258 int slew_time_ms_; | |
259 int slew_time_frames_; | |
260 | |
261 private: | |
262 DISALLOW_COPY_AND_ASSIGN(SlewVolumeDynamicTest); | |
263 }; | |
264 | |
265 TEST_P(SlewVolumeDynamicTest, FMULRampUp) { | |
266 double start = 0.0; | |
267 double end = 1.0; | |
268 slew_volume_->SetVolume(start); | |
269 ClearInterrupted(); | |
270 | |
271 slew_volume_->SetVolume(end); | |
272 slew_volume_->ProcessFMUL(false, data_[0], num_frames_, data_[0]); | |
273 slew_volume_->ProcessFMUL(true, data_[1], num_frames_, data_[1]); | |
274 CheckSlewMUL(start, end); | |
275 } | |
276 | |
277 TEST_P(SlewVolumeDynamicTest, FMULRampDown) { | |
278 double start = 1.0; | |
279 double end = 0.0; | |
280 slew_volume_->SetVolume(start); | |
281 ClearInterrupted(); | |
282 | |
283 slew_volume_->SetVolume(end); | |
284 slew_volume_->ProcessFMUL(false, data_[0], num_frames_, data_[0]); | |
285 slew_volume_->ProcessFMUL(true, data_[1], num_frames_, data_[1]); | |
286 CheckSlewMUL(start, end); | |
287 } | |
288 | |
289 // Provide data as small buffers. | |
290 TEST_P(SlewVolumeDynamicTest, FMULRampDownByParts) { | |
291 double start = 1.0; | |
292 double end = 0.0; | |
293 slew_volume_->SetVolume(start); | |
294 ClearInterrupted(); | |
295 | |
296 slew_volume_->SetVolume(end); | |
297 int frame_step = ::media::vector_math::kRequiredAlignment / kBytesPerSample; | |
298 int f; | |
299 for (f = 0; f < num_frames_; f += frame_step) { | |
300 // Process any remaining samples in the last step. | |
301 if (num_frames_ - f < frame_step * 2) { | |
302 frame_step = num_frames_ - f; | |
303 } | |
304 slew_volume_->ProcessFMUL(false, expected_[0] + f, frame_step, | |
305 data_[0] + f); | |
306 slew_volume_->ProcessFMUL(true, expected_[1] + f, frame_step, data_[1] + f); | |
307 } | |
308 ASSERT_EQ(num_frames_, f); | |
309 CheckSlewMUL(start, end); | |
310 } | |
311 | |
312 TEST_P(SlewVolumeDynamicTest, FMACRampUp) { | |
313 double start = 0.0; | |
314 double end = 1.0; | |
315 slew_volume_->SetVolume(start); | |
316 ClearInterrupted(); | |
317 | |
318 slew_volume_->SetVolume(end); | |
319 slew_volume_->ProcessFMAC(false, data_2_[0], num_frames_, data_[0]); | |
320 slew_volume_->ProcessFMAC(true, data_2_[1], num_frames_, data_[1]); | |
321 CheckSlewMAC(start, end); | |
322 } | |
323 | |
324 TEST_P(SlewVolumeDynamicTest, FMACRampDown) { | |
325 double start = 1.0; | |
326 double end = 0.0; | |
327 slew_volume_->SetVolume(start); | |
328 ClearInterrupted(); | |
329 | |
330 slew_volume_->SetVolume(end); | |
331 slew_volume_->ProcessFMAC(false, data_2_[0], num_frames_, data_[0]); | |
332 slew_volume_->ProcessFMAC(true, data_2_[1], num_frames_, data_[1]); | |
333 CheckSlewMAC(start, end); | |
334 } | |
335 | |
336 // Provide data as small buffers. | |
337 TEST_P(SlewVolumeDynamicTest, FMACRampUpByParts) { | |
338 double start = 0.0; | |
339 double end = 1.0; | |
340 slew_volume_->SetVolume(start); | |
341 ClearInterrupted(); | |
342 | |
343 slew_volume_->SetVolume(end); | |
344 int frame_step = ::media::vector_math::kRequiredAlignment / kBytesPerSample; | |
345 int f; | |
346 for (f = 0; f < num_frames_; f += frame_step) { | |
347 // Process any remaining samples in the last step. | |
348 if (num_frames_ - f < frame_step * 2) { | |
349 frame_step = num_frames_ - f; | |
350 } | |
351 slew_volume_->ProcessFMAC(false, data_2_[0] + f, frame_step, data_[0] + f); | |
352 slew_volume_->ProcessFMAC(true, data_2_[1] + f, frame_step, data_[1] + f); | |
353 } | |
354 ASSERT_EQ(num_frames_, f); | |
355 CheckSlewMAC(start, end); | |
356 } | |
357 | |
358 INSTANTIATE_TEST_CASE_P(SingleBufferSlew, | |
359 SlewVolumeDynamicTest, | |
360 ::testing::Combine(::testing::Values(44100, 48000), | |
361 ::testing::Values(0, 15, 100))); | |
362 } // namespace media | |
363 } // namespace chromecast | |
OLD | NEW |