1 // Copyright (c) 2012 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 "base/strings/stringprintf.h" 6 #include "base/time/time.h" 7 #include "build/build_config.h" 8 #include "media/audio/audio_parameters.h" 9 #include "media/base/audio_bus.h" 10 #include "media/base/channel_layout.h" 11 #include "media/base/fake_audio_render_callback.h" 12 #include "testing/gtest/include/gtest/gtest.h" 13 14 namespace media { 15 16 static const int kChannels = 6; 17 static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_5_1; 18 // Use a buffer size which is intentionally not a multiple of kChannelAlignment. 19 static const int kFrameCount = media::AudioBus::kChannelAlignment * 32 - 1; 20 static const int kSampleRate = 48000; 21 22 class AudioBusTest : public testing::Test { 23 public: 24 AudioBusTest() {} 25 virtual ~AudioBusTest() { 26 for (size_t i = 0; i < data_.size(); ++i) 27 base::AlignedFree(data_[i]); 28 } 29 30 // Validate parameters returned by AudioBus v.s. the constructed parameters. 31 void VerifyParams(AudioBus* bus) { 32 EXPECT_EQ(kChannels, bus->channels()); 33 EXPECT_EQ(kFrameCount, bus->frames()); 34 } 35 36 void VerifyValue(const float data[], int size, float value) { 37 for (int i = 0; i < size; ++i) 38 ASSERT_FLOAT_EQ(value, data[i]) << "i=" << i; 39 } 40 41 // Verify values for each channel in |result| are within |epsilon| of 42 // |expected|. If |epsilon| exactly equals 0, uses FLOAT_EQ macro. 43 void VerifyBusWithEpsilon(const AudioBus* result, const AudioBus* expected, 44 float epsilon) { 45 ASSERT_EQ(expected->channels(), result->channels()); 46 ASSERT_EQ(expected->frames(), result->frames()); 47 for (int ch = 0; ch < result->channels(); ++ch) { 48 for (int i = 0; i < result->frames(); ++i) { 49 SCOPED_TRACE(base::StringPrintf("ch=%d, i=%d", ch, i)); 50 if (epsilon == 0) { 51 ASSERT_FLOAT_EQ(expected->channel(ch)[i], result->channel(ch)[i]); 52 } else { 53 ASSERT_NEAR(expected->channel(ch)[i], result->channel(ch)[i], 54 epsilon); 55 } 56 } 57 } 58 } 59 60 // Verify values for each channel in |result| against |expected|. 61 void VerifyBus(const AudioBus* result, const AudioBus* expected) { 62 VerifyBusWithEpsilon(result, expected, 0); 63 } 64 65 // Read and write to the full extent of the allocated channel data. Also test 66 // the Zero() method and verify it does as advertised. Also test data if data 67 // is 16-byte aligned as advertised (see kChannelAlignment in audio_bus.h). 68 void VerifyChannelData(AudioBus* bus) { 69 for (int i = 0; i < bus->channels(); ++i) { 70 ASSERT_EQ(0U, reinterpret_cast<uintptr_t>( 71 bus->channel(i)) & (AudioBus::kChannelAlignment - 1)); 72 std::fill(bus->channel(i), bus->channel(i) + bus->frames(), i); 73 } 74 75 for (int i = 0; i < bus->channels(); ++i) 76 VerifyValue(bus->channel(i), bus->frames(), i); 77 78 bus->Zero(); 79 for (int i = 0; i < bus->channels(); ++i) 80 VerifyValue(bus->channel(i), bus->frames(), 0); 81 } 82 83 // Verify copying to and from |bus1| and |bus2|. 84 void CopyTest(AudioBus* bus1, AudioBus* bus2) { 85 // Fill |bus1| with dummy data. 86 for (int i = 0; i < bus1->channels(); ++i) 87 std::fill(bus1->channel(i), bus1->channel(i) + bus1->frames(), i); 88 89 // Verify copy from |bus1| to |bus2|. 90 bus2->Zero(); 91 bus1->CopyTo(bus2); 92 VerifyBus(bus1, bus2); 93 94 // Verify copy from |bus2| to |bus1|. 95 bus1->Zero(); 96 bus2->CopyTo(bus1); 97 VerifyBus(bus2, bus1); 98 } 99 100 protected: 101 std::vector<float*> data_; 102 103 DISALLOW_COPY_AND_ASSIGN(AudioBusTest); 104 }; 105 106 // Verify basic Create(...) method works as advertised. 107 TEST_F(AudioBusTest, Create) { 108 scoped_ptr<AudioBus> bus = AudioBus::Create(kChannels, kFrameCount); 109 VerifyParams(bus.get()); 110 VerifyChannelData(bus.get()); 111 } 112 113 // Verify Create(...) using AudioParameters works as advertised. 114 TEST_F(AudioBusTest, CreateUsingAudioParameters) { 115 scoped_ptr<AudioBus> bus = AudioBus::Create(AudioParameters( 116 AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate, 32, 117 kFrameCount)); 118 VerifyParams(bus.get()); 119 VerifyChannelData(bus.get()); 120 } 121 122 // Verify an AudioBus created via wrapping a vector works as advertised. 123 TEST_F(AudioBusTest, WrapVector) { 124 data_.reserve(kChannels); 125 for (int i = 0; i < kChannels; ++i) { 126 data_.push_back(static_cast<float*>(base::AlignedAlloc( 127 sizeof(*data_[i]) * kFrameCount, AudioBus::kChannelAlignment))); 128 } 129 130 scoped_ptr<AudioBus> bus = AudioBus::WrapVector(kFrameCount, data_); 131 VerifyParams(bus.get()); 132 VerifyChannelData(bus.get()); 133 } 134 135 // Verify an AudioBus created via wrapping a memory block works as advertised. 136 TEST_F(AudioBusTest, WrapMemory) { 137 AudioParameters params( 138 AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate, 32, 139 kFrameCount); 140 int data_size = AudioBus::CalculateMemorySize(params); 141 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data(static_cast<float*>( 142 base::AlignedAlloc(data_size, AudioBus::kChannelAlignment))); 143 144 // Fill the memory with a test value we can check for after wrapping. 145 static const float kTestValue = 3; 146 std::fill( 147 data.get(), data.get() + data_size / sizeof(*data.get()), kTestValue); 148 149 scoped_ptr<AudioBus> bus = AudioBus::WrapMemory(params, data.get()); 150 // Verify the test value we filled prior to wrapping. 151 for (int i = 0; i < bus->channels(); ++i) 152 VerifyValue(bus->channel(i), bus->frames(), kTestValue); 153 VerifyParams(bus.get()); 154 VerifyChannelData(bus.get()); 155 156 // Verify the channel vectors lie within the provided memory block. 157 EXPECT_GE(bus->channel(0), data.get()); 158 EXPECT_LT(bus->channel(bus->channels() - 1) + bus->frames(), 159 data.get() + data_size / sizeof(*data.get())); 160 } 161 162 // Simulate a shared memory transfer and verify results. 163 TEST_F(AudioBusTest, CopyTo) { 164 // Create one bus with AudioParameters and the other through direct values to 165 // test for parity between the Create() functions. 166 AudioParameters params( 167 AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate, 32, 168 kFrameCount); 169 scoped_ptr<AudioBus> bus1 = AudioBus::Create(kChannels, kFrameCount); 170 scoped_ptr<AudioBus> bus2 = AudioBus::Create(params); 171 172 { 173 SCOPED_TRACE("Created"); 174 CopyTest(bus1.get(), bus2.get()); 175 } 176 { 177 SCOPED_TRACE("Wrapped Vector"); 178 // Try a copy to an AudioBus wrapping a vector. 179 data_.reserve(kChannels); 180 for (int i = 0; i < kChannels; ++i) { 181 data_.push_back(static_cast<float*>(base::AlignedAlloc( 182 sizeof(*data_[i]) * kFrameCount, AudioBus::kChannelAlignment))); 183 } 184 185 bus2 = AudioBus::WrapVector(kFrameCount, data_); 186 CopyTest(bus1.get(), bus2.get()); 187 } 188 { 189 SCOPED_TRACE("Wrapped Memory"); 190 // Try a copy to an AudioBus wrapping a memory block. 191 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data( 192 static_cast<float*>(base::AlignedAlloc( 193 AudioBus::CalculateMemorySize(params), 194 AudioBus::kChannelAlignment))); 195 196 bus2 = AudioBus::WrapMemory(params, data.get()); 197 CopyTest(bus1.get(), bus2.get()); 198 } 199 } 200 201 // Verify Zero() and ZeroFrames(...) utility methods work as advertised. 202 TEST_F(AudioBusTest, Zero) { 203 scoped_ptr<AudioBus> bus = AudioBus::Create(kChannels, kFrameCount); 204 205 // Fill the bus with dummy data. 206 for (int i = 0; i < bus->channels(); ++i) 207 std::fill(bus->channel(i), bus->channel(i) + bus->frames(), i + 1); 208 209 // Zero first half the frames of each channel. 210 bus->ZeroFrames(kFrameCount / 2); 211 for (int i = 0; i < bus->channels(); ++i) { 212 SCOPED_TRACE("First Half Zero"); 213 VerifyValue(bus->channel(i), kFrameCount / 2, 0); 214 VerifyValue(bus->channel(i) + kFrameCount / 2, 215 kFrameCount - kFrameCount / 2, i + 1); 216 } 217 218 // Fill the bus with dummy data. 219 for (int i = 0; i < bus->channels(); ++i) 220 std::fill(bus->channel(i), bus->channel(i) + bus->frames(), i + 1); 221 222 // Zero the last half of the frames. 223 bus->ZeroFramesPartial(kFrameCount / 2, kFrameCount - kFrameCount / 2); 224 for (int i = 0; i < bus->channels(); ++i) { 225 SCOPED_TRACE("Last Half Zero"); 226 VerifyValue(bus->channel(i) + kFrameCount / 2, 227 kFrameCount - kFrameCount / 2, 0); 228 VerifyValue(bus->channel(i), kFrameCount / 2, i + 1); 229 } 230 231 // Fill the bus with dummy data. 232 for (int i = 0; i < bus->channels(); ++i) 233 std::fill(bus->channel(i), bus->channel(i) + bus->frames(), i + 1); 234 235 // Zero all the frames of each channel. 236 bus->Zero(); 237 for (int i = 0; i < bus->channels(); ++i) { 238 SCOPED_TRACE("All Zero"); 239 VerifyValue(bus->channel(i), bus->frames(), 0); 240 } 241 } 242 243 // Each test vector represents two channels of data in the following arbitrary 244 // layout: <min, zero, max, min, max / 2, min / 2, zero, max, zero, zero>. 245 static const int kTestVectorSize = 10; 246 static const uint8 kTestVectorUint8[kTestVectorSize] = { 247 0, -kint8min, kuint8max, 0, kint8max / 2 + 128, kint8min / 2 + 128, 248 -kint8min, kuint8max, -kint8min, -kint8min }; 249 static const int16 kTestVectorInt16[kTestVectorSize] = { 250 kint16min, 0, kint16max, kint16min, kint16max / 2, kint16min / 2, 251 0, kint16max, 0, 0 }; 252 static const int32 kTestVectorInt32[kTestVectorSize] = { 253 kint32min, 0, kint32max, kint32min, kint32max / 2, kint32min / 2, 254 0, kint32max, 0, 0 }; 255 256 // Expected results. 257 static const int kTestVectorFrames = kTestVectorSize / 2; 258 static const float kTestVectorResult[][kTestVectorFrames] = { 259 { -1, 1, 0.5, 0, 0 }, { 0, -1, -0.5, 1, 0 }}; 260 static const int kTestVectorChannels = arraysize(kTestVectorResult); 261 262 // Verify FromInterleaved() deinterleaves audio in supported formats correctly. 263 TEST_F(AudioBusTest, FromInterleaved) { 264 scoped_ptr<AudioBus> bus = AudioBus::Create( 265 kTestVectorChannels, kTestVectorFrames); 266 scoped_ptr<AudioBus> expected = AudioBus::Create( 267 kTestVectorChannels, kTestVectorFrames); 268 for (int ch = 0; ch < kTestVectorChannels; ++ch) { 269 memcpy(expected->channel(ch), kTestVectorResult[ch], 270 kTestVectorFrames * sizeof(*expected->channel(ch))); 271 } 272 { 273 SCOPED_TRACE("uint8"); 274 bus->Zero(); 275 bus->FromInterleaved( 276 kTestVectorUint8, kTestVectorFrames, sizeof(*kTestVectorUint8)); 277 // Biased uint8 calculations have poor precision, so the epsilon here is 278 // slightly more permissive than int16 and int32 calculations. 279 VerifyBusWithEpsilon(bus.get(), expected.get(), 1.0f / (kuint8max - 1)); 280 } 281 { 282 SCOPED_TRACE("int16"); 283 bus->Zero(); 284 bus->FromInterleaved( 285 kTestVectorInt16, kTestVectorFrames, sizeof(*kTestVectorInt16)); 286 VerifyBusWithEpsilon(bus.get(), expected.get(), 1.0f / (kuint16max + 1.0f)); 287 } 288 { 289 SCOPED_TRACE("int32"); 290 bus->Zero(); 291 bus->FromInterleaved( 292 kTestVectorInt32, kTestVectorFrames, sizeof(*kTestVectorInt32)); 293 VerifyBusWithEpsilon(bus.get(), expected.get(), 1.0f / (kuint32max + 1.0f)); 294 } 295 } 296 297 // Verify FromInterleavedPartial() deinterleaves audio correctly. 298 TEST_F(AudioBusTest, FromInterleavedPartial) { 299 // Only deinterleave the middle two frames in each channel. 300 static const int kPartialStart = 1; 301 static const int kPartialFrames = 2; 302 ASSERT_LE(kPartialStart + kPartialFrames, kTestVectorFrames); 303 304 scoped_ptr<AudioBus> bus = AudioBus::Create( 305 kTestVectorChannels, kTestVectorFrames); 306 scoped_ptr<AudioBus> expected = AudioBus::Create( 307 kTestVectorChannels, kTestVectorFrames); 308 expected->Zero(); 309 for (int ch = 0; ch < kTestVectorChannels; ++ch) { 310 memcpy(expected->channel(ch) + kPartialStart, 311 kTestVectorResult[ch] + kPartialStart, 312 kPartialFrames * sizeof(*expected->channel(ch))); 313 } 314 315 bus->Zero(); 316 bus->FromInterleavedPartial( 317 kTestVectorInt32 + kPartialStart * bus->channels(), kPartialStart, 318 kPartialFrames, sizeof(*kTestVectorInt32)); 319 VerifyBus(bus.get(), expected.get()); 320 } 321 322 // Verify ToInterleaved() interleaves audio in suported formats correctly. 323 TEST_F(AudioBusTest, ToInterleaved) { 324 scoped_ptr<AudioBus> bus = AudioBus::Create( 325 kTestVectorChannels, kTestVectorFrames); 326 // Fill the bus with our test vector. 327 for (int ch = 0; ch < bus->channels(); ++ch) { 328 memcpy(bus->channel(ch), kTestVectorResult[ch], 329 kTestVectorFrames * sizeof(*bus->channel(ch))); 330 } 331 { 332 SCOPED_TRACE("uint8"); 333 uint8 test_array[arraysize(kTestVectorUint8)]; 334 bus->ToInterleaved(bus->frames(), sizeof(*kTestVectorUint8), test_array); 335 ASSERT_EQ(memcmp( 336 test_array, kTestVectorUint8, sizeof(kTestVectorUint8)), 0); 337 } 338 { 339 SCOPED_TRACE("int16"); 340 int16 test_array[arraysize(kTestVectorInt16)]; 341 bus->ToInterleaved(bus->frames(), sizeof(*kTestVectorInt16), test_array); 342 ASSERT_EQ(memcmp( 343 test_array, kTestVectorInt16, sizeof(kTestVectorInt16)), 0); 344 } 345 { 346 SCOPED_TRACE("int32"); 347 int32 test_array[arraysize(kTestVectorInt32)]; 348 bus->ToInterleaved(bus->frames(), sizeof(*kTestVectorInt32), test_array); 349 350 // Some compilers get better precision than others on the half-max test, so 351 // let the test pass with an off by one check on the half-max. 352 int32 fixed_test_array[arraysize(kTestVectorInt32)]; 353 memcpy(fixed_test_array, kTestVectorInt32, sizeof(kTestVectorInt32)); 354 ASSERT_EQ(fixed_test_array[4], kint32max / 2); 355 fixed_test_array[4]++; 356 357 ASSERT_TRUE( 358 memcmp(test_array, kTestVectorInt32, sizeof(kTestVectorInt32)) == 0 || 359 memcmp(test_array, fixed_test_array, sizeof(fixed_test_array)) == 0); 360 } 361 } 362 363 // Verify ToInterleavedPartial() interleaves audio correctly. 364 TEST_F(AudioBusTest, ToInterleavedPartial) { 365 // Only interleave the middle two frames in each channel. 366 static const int kPartialStart = 1; 367 static const int kPartialFrames = 2; 368 ASSERT_LE(kPartialStart + kPartialFrames, kTestVectorFrames); 369 370 scoped_ptr<AudioBus> expected = AudioBus::Create( 371 kTestVectorChannels, kTestVectorFrames); 372 for (int ch = 0; ch < kTestVectorChannels; ++ch) { 373 memcpy(expected->channel(ch), kTestVectorResult[ch], 374 kTestVectorFrames * sizeof(*expected->channel(ch))); 375 } 376 377 int16 test_array[arraysize(kTestVectorInt16)]; 378 expected->ToInterleavedPartial( 379 kPartialStart, kPartialFrames, sizeof(*kTestVectorInt16), test_array); 380 ASSERT_EQ(memcmp( 381 test_array, kTestVectorInt16 + kPartialStart * kTestVectorChannels, 382 kPartialFrames * sizeof(*kTestVectorInt16) * kTestVectorChannels), 0); 383 } 384 385 TEST_F(AudioBusTest, Scale) { 386 scoped_ptr<AudioBus> bus = AudioBus::Create(kChannels, kFrameCount); 387 388 // Fill the bus with dummy data. 389 static const float kFillValue = 1; 390 for (int i = 0; i < bus->channels(); ++i) 391 std::fill(bus->channel(i), bus->channel(i) + bus->frames(), kFillValue); 392 393 // Adjust by an invalid volume and ensure volume is unchanged. 394 bus->Scale(-1); 395 for (int i = 0; i < bus->channels(); ++i) { 396 SCOPED_TRACE("Invalid Scale"); 397 VerifyValue(bus->channel(i), bus->frames(), kFillValue); 398 } 399 400 // Verify correct volume adjustment. 401 static const float kVolume = 0.5; 402 bus->Scale(kVolume); 403 for (int i = 0; i < bus->channels(); ++i) { 404 SCOPED_TRACE("Half Scale"); 405 VerifyValue(bus->channel(i), bus->frames(), kFillValue * kVolume); 406 } 407 408 // Verify zero volume case. 409 bus->Scale(0); 410 for (int i = 0; i < bus->channels(); ++i) { 411 SCOPED_TRACE("Zero Scale"); 412 VerifyValue(bus->channel(i), bus->frames(), 0); 413 } 414 } 415 416 // Benchmark the FromInterleaved() and ToInterleaved() methods. 417 TEST_F(AudioBusTest, DISABLED_InterleaveBench) { 418 scoped_ptr<AudioBus> bus = AudioBus::Create(2, 48000 * 120); 419 const int frame_size = bus->frames() * bus->channels(); 420 FakeAudioRenderCallback callback(0.2); 421 callback.Render(bus.get(), 0); 422 { 423 SCOPED_TRACE("uint8"); 424 scoped_ptr<uint8> interleaved(new uint8[frame_size]); 425 const int byte_size = sizeof(*interleaved); 426 427 base::TimeTicks start = base::TimeTicks::HighResNow(); 428 bus->ToInterleaved(bus->frames(), byte_size, interleaved.get()); 429 double total_time_ms = 430 (base::TimeTicks::HighResNow() - start).InMillisecondsF(); 431 printf("ToInterleaved uint8 took %.2fms.\n", total_time_ms); 432 433 start = base::TimeTicks::HighResNow(); 434 bus->FromInterleaved(interleaved.get(), bus->frames(), byte_size); 435 total_time_ms = (base::TimeTicks::HighResNow() - start).InMillisecondsF(); 436 printf("FromInterleaved uint8 took %.2fms.\n", total_time_ms); 437 } 438 { 439 SCOPED_TRACE("int16"); 440 scoped_ptr<int16> interleaved(new int16[frame_size]); 441 const int byte_size = sizeof(*interleaved); 442 443 base::TimeTicks start = base::TimeTicks::HighResNow(); 444 bus->ToInterleaved(bus->frames(), byte_size, interleaved.get()); 445 double total_time_ms = 446 (base::TimeTicks::HighResNow() - start).InMillisecondsF(); 447 printf("ToInterleaved int16 took %.2fms.\n", total_time_ms); 448 449 start = base::TimeTicks::HighResNow(); 450 bus->FromInterleaved(interleaved.get(), bus->frames(), byte_size); 451 total_time_ms = (base::TimeTicks::HighResNow() - start).InMillisecondsF(); 452 printf("FromInterleaved int16 took %.2fms.\n", total_time_ms); 453 } 454 { 455 SCOPED_TRACE("int32"); 456 scoped_ptr<int32> interleaved(new int32[frame_size]); 457 const int byte_size = sizeof(*interleaved); 458 459 base::TimeTicks start = base::TimeTicks::HighResNow(); 460 bus->ToInterleaved(bus->frames(), byte_size, interleaved.get()); 461 double total_time_ms = 462 (base::TimeTicks::HighResNow() - start).InMillisecondsF(); 463 printf("ToInterleaved int32 took %.2fms.\n", total_time_ms); 464 465 start = base::TimeTicks::HighResNow(); 466 bus->FromInterleaved(interleaved.get(), bus->frames(), byte_size); 467 total_time_ms = (base::TimeTicks::HighResNow() - start).InMillisecondsF(); 468 printf("FromInterleaved int32 took %.2fms.\n", total_time_ms); 469 } 470 } 471 472 } // namespace media 473