/* * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "modules/audio_device/fine_audio_buffer.h" #include #include #include "api/array_view.h" #include "api/task_queue/default_task_queue_factory.h" #include "modules/audio_device/mock_audio_device_buffer.h" #include "test/gmock.h" #include "test/gtest.h" using ::testing::_; using ::testing::AtLeast; using ::testing::InSequence; using ::testing::Return; namespace webrtc { const int kSampleRate = 44100; const int kChannels = 2; const int kSamplesPer10Ms = kSampleRate * 10 / 1000; // The fake audio data is 0,1,..SCHAR_MAX-1,0,1,... This is to make it easy // to detect errors. This function verifies that the buffers contain such data. // E.g. if there are two buffers of size 3, buffer 1 would contain 0,1,2 and // buffer 2 would contain 3,4,5. Note that SCHAR_MAX is 127 so wrap-around // will happen. // `buffer` is the audio buffer to verify. bool VerifyBuffer(const int16_t* buffer, int buffer_number, int size) { int start_value = (buffer_number * size) % SCHAR_MAX; for (int i = 0; i < size; ++i) { if (buffer[i] != (i + start_value) % SCHAR_MAX) { return false; } } return true; } // This function replaces the real AudioDeviceBuffer::GetPlayoutData when it's // called (which is done implicitly when calling GetBufferData). It writes the // sequence 0,1,..SCHAR_MAX-1,0,1,... to the buffer. Note that this is likely a // buffer of different size than the one VerifyBuffer verifies. // `iteration` is the number of calls made to UpdateBuffer prior to this call. // `samples_per_10_ms` is the number of samples that should be written to the // buffer (`arg0`). ACTION_P2(UpdateBuffer, iteration, samples_per_10_ms) { int16_t* buffer = static_cast(arg0); int start_value = (iteration * samples_per_10_ms) % SCHAR_MAX; for (int i = 0; i < samples_per_10_ms; ++i) { buffer[i] = (i + start_value) % SCHAR_MAX; } // Should return samples per channel. return samples_per_10_ms / kChannels; } // Writes a periodic ramp pattern to the supplied `buffer`. See UpdateBuffer() // for details. void UpdateInputBuffer(int16_t* buffer, int iteration, int size) { int start_value = (iteration * size) % SCHAR_MAX; for (int i = 0; i < size; ++i) { buffer[i] = (i + start_value) % SCHAR_MAX; } } // Action macro which verifies that the recorded 10ms chunk of audio data // (in `arg0`) contains the correct reference values even if they have been // supplied using a buffer size that is smaller or larger than 10ms. // See VerifyBuffer() for details. ACTION_P2(VerifyInputBuffer, iteration, samples_per_10_ms) { const int16_t* buffer = static_cast(arg0); int start_value = (iteration * samples_per_10_ms) % SCHAR_MAX; for (int i = 0; i < samples_per_10_ms; ++i) { EXPECT_EQ(buffer[i], (i + start_value) % SCHAR_MAX); } return 0; } void RunFineBufferTest(int frame_size_in_samples) { const int kFrameSizeSamples = frame_size_in_samples; const int kNumberOfFrames = 5; // Ceiling of integer division: 1 + ((x - 1) / y) const int kNumberOfUpdateBufferCalls = 1 + ((kNumberOfFrames * frame_size_in_samples - 1) / kSamplesPer10Ms); auto task_queue_factory = CreateDefaultTaskQueueFactory(); MockAudioDeviceBuffer audio_device_buffer(task_queue_factory.get()); audio_device_buffer.SetPlayoutSampleRate(kSampleRate); audio_device_buffer.SetPlayoutChannels(kChannels); audio_device_buffer.SetRecordingSampleRate(kSampleRate); audio_device_buffer.SetRecordingChannels(kChannels); EXPECT_CALL(audio_device_buffer, RequestPlayoutData(_)) .WillRepeatedly(Return(kSamplesPer10Ms)); { InSequence s; for (int i = 0; i < kNumberOfUpdateBufferCalls; ++i) { EXPECT_CALL(audio_device_buffer, GetPlayoutData(_)) .WillOnce(UpdateBuffer(i, kChannels * kSamplesPer10Ms)) .RetiresOnSaturation(); } } { InSequence s; for (int j = 0; j < kNumberOfUpdateBufferCalls - 1; ++j) { EXPECT_CALL(audio_device_buffer, SetRecordedBuffer(_, kSamplesPer10Ms)) .WillOnce(VerifyInputBuffer(j, kChannels * kSamplesPer10Ms)) .RetiresOnSaturation(); } } EXPECT_CALL(audio_device_buffer, SetVQEData(_, _)) .Times(kNumberOfUpdateBufferCalls - 1); EXPECT_CALL(audio_device_buffer, DeliverRecordedData()) .Times(kNumberOfUpdateBufferCalls - 1) .WillRepeatedly(Return(0)); FineAudioBuffer fine_buffer(&audio_device_buffer); std::unique_ptr out_buffer( new int16_t[kChannels * kFrameSizeSamples]); std::unique_ptr in_buffer( new int16_t[kChannels * kFrameSizeSamples]); for (int i = 0; i < kNumberOfFrames; ++i) { fine_buffer.GetPlayoutData( rtc::ArrayView(out_buffer.get(), kChannels * kFrameSizeSamples), 0); EXPECT_TRUE( VerifyBuffer(out_buffer.get(), i, kChannels * kFrameSizeSamples)); UpdateInputBuffer(in_buffer.get(), i, kChannels * kFrameSizeSamples); fine_buffer.DeliverRecordedData( rtc::ArrayView(in_buffer.get(), kChannels * kFrameSizeSamples), 0); } } TEST(FineBufferTest, BufferLessThan10ms) { const int kFrameSizeSamples = kSamplesPer10Ms - 50; RunFineBufferTest(kFrameSizeSamples); } TEST(FineBufferTest, GreaterThan10ms) { const int kFrameSizeSamples = kSamplesPer10Ms + 50; RunFineBufferTest(kFrameSizeSamples); } } // namespace webrtc