|  | /* | 
|  | *  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 <limits.h> | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #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<int16_t*>(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<const int16_t*>(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<int16_t[]> out_buffer( | 
|  | new int16_t[kChannels * kFrameSizeSamples]); | 
|  | std::unique_ptr<int16_t[]> in_buffer( | 
|  | new int16_t[kChannels * kFrameSizeSamples]); | 
|  |  | 
|  | for (int i = 0; i < kNumberOfFrames; ++i) { | 
|  | fine_buffer.GetPlayoutData( | 
|  | rtc::ArrayView<int16_t>(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<const int16_t>(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 |