Chromium Code Reviews| Index: webrtc/test/fake_audio_device.cc |
| diff --git a/webrtc/test/fake_audio_device.cc b/webrtc/test/fake_audio_device.cc |
| index 623ff518d7b0e9f3aba9e0e98e4d0505f77dbb3f..ad7e49bf0471735b8cdef7be146b169fdbdf9113 100644 |
| --- a/webrtc/test/fake_audio_device.cc |
| +++ b/webrtc/test/fake_audio_device.cc |
| @@ -11,10 +11,11 @@ |
| #include "webrtc/test/fake_audio_device.h" |
| #include <algorithm> |
| +#include <utility> |
| -#include "webrtc/base/array_view.h" |
| #include "webrtc/base/checks.h" |
| #include "webrtc/base/random.h" |
| +#include "webrtc/common_audio/wav_file.h" |
| #include "webrtc/system_wrappers/include/event_wrapper.h" |
| namespace webrtc { |
| @@ -24,58 +25,171 @@ namespace { |
| constexpr int kFrameLengthMs = 10; |
| constexpr int kFramesPerSecond = 1000 / kFrameLengthMs; |
| -} // namespace |
| -namespace test { |
| - |
| // Assuming 10ms audio packets.. |
| -class FakeAudioDevice::PulsedNoiseCapturer { |
| +class PulsedNoiseCapturer final : public test::FakeAudioDevice::Capturer { |
| public: |
| - PulsedNoiseCapturer(size_t num_samples_per_frame, int16_t max_amplitude) |
| - : fill_with_zero_(false), |
| + PulsedNoiseCapturer(int16_t max_amplitude, int sampling_frequency_in_hz) |
| + : sampling_frequency_in_hz_(sampling_frequency_in_hz), |
| + fill_with_zero_(false), |
| random_generator_(1), |
| - max_amplitude_(max_amplitude), |
| - random_audio_(num_samples_per_frame), |
| - silent_audio_(num_samples_per_frame, 0) { |
| + max_amplitude_(max_amplitude) { |
| RTC_DCHECK_GT(max_amplitude, 0); |
| } |
| - rtc::ArrayView<const int16_t> Capture() { |
| + int SamplingFrequency() const override { |
| + return sampling_frequency_in_hz_; |
| + } |
| + |
| + bool Capture(rtc::BufferT<int16_t>* buffer) override { |
| fill_with_zero_ = !fill_with_zero_; |
| - if (!fill_with_zero_) { |
| - std::generate(random_audio_.begin(), random_audio_.end(), [&]() { |
| - return random_generator_.Rand(-max_amplitude_, max_amplitude_); |
| - }); |
| - } |
| - return fill_with_zero_ ? silent_audio_ : random_audio_; |
| + buffer->SetData(buffer->size(), [&](rtc::ArrayView<int16_t> data) { |
| + if (fill_with_zero_) { |
| + std::fill(data.begin(), data.end(), 0); |
| + } else { |
| + std::generate(data.begin(), data.end(), [&]() { |
| + return random_generator_.Rand(-max_amplitude_, max_amplitude_); |
| + }); |
| + } |
| + return data.size(); |
| + }); |
| + return true; |
| } |
| private: |
| + int sampling_frequency_in_hz_; |
| bool fill_with_zero_; |
| Random random_generator_; |
| const int16_t max_amplitude_; |
| - std::vector<int16_t> random_audio_; |
| - std::vector<int16_t> silent_audio_; |
| }; |
| -FakeAudioDevice::FakeAudioDevice(float speed, |
| - int sampling_frequency_in_hz, |
| - int16_t max_amplitude) |
| - : sampling_frequency_in_hz_(sampling_frequency_in_hz), |
| - num_samples_per_frame_( |
| - rtc::CheckedDivExact(sampling_frequency_in_hz_, kFramesPerSecond)), |
| +class WavFileReader final : public test::FakeAudioDevice::Capturer { |
| + public: |
| + WavFileReader(std::string filename, int sampling_frequency_in_hz) |
| + : sampling_frequency_in_hz_(sampling_frequency_in_hz), |
| + wav_reader_(filename) { |
| + RTC_CHECK_EQ(wav_reader_.sample_rate(), sampling_frequency_in_hz); |
| + RTC_CHECK_EQ(wav_reader_.num_channels(), 1); |
| + } |
| + |
| + int SamplingFrequency() const override { |
| + return sampling_frequency_in_hz_; |
| + } |
| + |
| + bool Capture(rtc::BufferT<int16_t>* buffer) override { |
| + buffer->SetData(buffer->size(), [&](rtc::ArrayView<int16_t> data) { |
| + return wav_reader_.ReadSamples(data.size(), data.data()); |
| + }); |
| + return buffer->size() > 0; |
| + } |
| + |
| + private: |
| + int sampling_frequency_in_hz_; |
| + WavReader wav_reader_; |
| +}; |
| + |
| +class WavFileWriter final : public test::FakeAudioDevice::Renderer { |
| + public: |
| + WavFileWriter(std::string filename, int sampling_frequency_in_hz) |
| + : sampling_frequency_in_hz_(sampling_frequency_in_hz), |
| + wav_writer_(filename, sampling_frequency_in_hz, 1) {} |
| + |
| + int SamplingFrequency() const override { |
| + return sampling_frequency_in_hz_; |
| + } |
| + |
| + bool Render(rtc::ArrayView<const int16_t> data) override { |
| + wav_writer_.WriteSamples(data.data(), data.size()); |
| + return true; |
| + } |
| + |
| + private: |
| + int sampling_frequency_in_hz_; |
| + WavWriter wav_writer_; |
| +}; |
| + |
| +class DiscardRenderer final : public test::FakeAudioDevice::Renderer { |
| + public: |
| + DiscardRenderer(int sampling_frequency_in_hz) |
| + : sampling_frequency_in_hz_(sampling_frequency_in_hz) {} |
| + |
| + int SamplingFrequency() const override { |
| + return sampling_frequency_in_hz_; |
| + } |
| + |
| + bool Render(rtc::ArrayView<const int16_t> data) override { |
| + return true; |
| + } |
| + |
| + private: |
| + int sampling_frequency_in_hz_; |
| +}; |
| + |
| +} // namespace |
| +namespace test { |
| + |
| +size_t FakeAudioDevice::SamplesPerFrame(int sampling_frequency_in_hz) { |
| + return rtc::CheckedDivExact(sampling_frequency_in_hz, kFramesPerSecond); |
| +} |
| + |
| +std::unique_ptr<FakeAudioDevice::Capturer> |
| + FakeAudioDevice::CreatePulsedNoiseCapturer( |
| + int16_t max_amplitude, int sampling_frequency_in_hz) { |
| + return std::unique_ptr<FakeAudioDevice::Capturer>( |
| + new PulsedNoiseCapturer(max_amplitude, sampling_frequency_in_hz)); |
| +} |
| + |
| +std::unique_ptr<FakeAudioDevice::Capturer> FakeAudioDevice::CreateWavFileReader( |
| + std::string filename, int sampling_frequency_in_hz) { |
| + return std::unique_ptr<FakeAudioDevice::Capturer>( |
| + new WavFileReader(filename, sampling_frequency_in_hz)); |
| +} |
| + |
| +std::unique_ptr<FakeAudioDevice::Capturer> FakeAudioDevice::CreateWavFileReader( |
| + std::string filename) { |
| + int sampling_frequency_in_hz = WavReader(filename).sample_rate(); |
| + return std::unique_ptr<FakeAudioDevice::Capturer>( |
| + new WavFileReader(filename, sampling_frequency_in_hz)); |
| +} |
| + |
| +std::unique_ptr<FakeAudioDevice::Renderer> FakeAudioDevice::CreateWavFileWriter( |
| + std::string filename, int sampling_frequency_in_hz) { |
| + return std::unique_ptr<FakeAudioDevice::Renderer>( |
| + new WavFileWriter(filename, sampling_frequency_in_hz)); |
| +} |
| + |
| +std::unique_ptr<FakeAudioDevice::Renderer> |
| + FakeAudioDevice::CreateDiscardRenderer(int sampling_frequency_in_hz) { |
| + return std::unique_ptr<FakeAudioDevice::Renderer>( |
| + new DiscardRenderer(sampling_frequency_in_hz)); |
| +} |
| + |
| + |
| +FakeAudioDevice::FakeAudioDevice(std::unique_ptr<Capturer> capturer, |
| + std::unique_ptr<Renderer> renderer, |
| + float speed) |
| + : capturer_(std::move(capturer)), |
| + renderer_(std::move(renderer)), |
| speed_(speed), |
| audio_callback_(nullptr), |
| rendering_(false), |
| capturing_(false), |
| - capturer_(new FakeAudioDevice::PulsedNoiseCapturer(num_samples_per_frame_, |
| - max_amplitude)), |
| - playout_buffer_(num_samples_per_frame_, 0), |
| + done_rendering_(true, true), |
| + done_capturing_(true, true), |
| tick_(EventTimerWrapper::Create()), |
| thread_(FakeAudioDevice::Run, this, "FakeAudioDevice") { |
| - RTC_DCHECK( |
| - sampling_frequency_in_hz == 8000 || sampling_frequency_in_hz == 16000 || |
| - sampling_frequency_in_hz == 32000 || sampling_frequency_in_hz == 44100 || |
| - sampling_frequency_in_hz == 48000); |
| + if (renderer_) { |
| + const int sample_rate = renderer_->SamplingFrequency(); |
| + playout_buffer_.resize(SamplesPerFrame(sample_rate), 0); |
| + RTC_CHECK( |
| + sample_rate == 8000 || sample_rate == 16000 || sample_rate == 32000 || |
| + sample_rate == 44100 || sample_rate == 48000); |
| + } |
| + if (capturer_) { |
| + const int sample_rate = capturer_->SamplingFrequency(); |
| + RTC_CHECK( |
| + sample_rate == 8000 || sample_rate == 16000 || sample_rate == 32000 || |
| + sample_rate == 44100 || sample_rate == 48000); |
|
kwiberg-webrtc
2017/03/13 14:22:28
Do the CHECK first in this function, so you don't
oprypin_webrtc
2017/03/13 15:11:36
I'm checking two different sample rates.
kwiberg-webrtc
2017/03/14 10:02:03
Right, I would have seen that if I hadn't been so
oprypin_webrtc
2017/03/14 11:58:13
Done.
|
| + } |
| } |
| FakeAudioDevice::~FakeAudioDevice() { |
| @@ -86,25 +200,31 @@ FakeAudioDevice::~FakeAudioDevice() { |
| int32_t FakeAudioDevice::StartPlayout() { |
| rtc::CritScope cs(&lock_); |
| + RTC_CHECK(renderer_); |
| rendering_ = true; |
| + done_rendering_.Reset(); |
| return 0; |
| } |
| int32_t FakeAudioDevice::StopPlayout() { |
| rtc::CritScope cs(&lock_); |
| rendering_ = false; |
| + done_rendering_.Set(); |
| return 0; |
| } |
| int32_t FakeAudioDevice::StartRecording() { |
| rtc::CritScope cs(&lock_); |
| + RTC_CHECK(capturer_); |
| capturing_ = true; |
| + done_capturing_.Reset(); |
| return 0; |
| } |
| int32_t FakeAudioDevice::StopRecording() { |
| rtc::CritScope cs(&lock_); |
| capturing_ = false; |
| + done_capturing_.Set(); |
| return 0; |
| } |
| @@ -117,7 +237,7 @@ int32_t FakeAudioDevice::Init() { |
| int32_t FakeAudioDevice::RegisterAudioCallback(AudioTransport* callback) { |
| rtc::CritScope cs(&lock_); |
| - RTC_DCHECK(callback || audio_callback_ != nullptr); |
| + RTC_DCHECK(callback || audio_callback_); |
| audio_callback_ = callback; |
| return 0; |
| } |
| @@ -132,6 +252,14 @@ bool FakeAudioDevice::Recording() const { |
| return capturing_; |
| } |
| +bool FakeAudioDevice::WaitForPlayoutEnd(int timeout_ms) { |
| + return done_rendering_.Wait(timeout_ms); |
| +} |
| + |
| +bool FakeAudioDevice::WaitForRecordingEnd(int timeout_ms) { |
| + return done_capturing_.Wait(timeout_ms); |
| +} |
| + |
| bool FakeAudioDevice::Run(void* obj) { |
| static_cast<FakeAudioDevice*>(obj)->ProcessAudio(); |
| return true; |
| @@ -142,19 +270,34 @@ void FakeAudioDevice::ProcessAudio() { |
| rtc::CritScope cs(&lock_); |
| if (capturing_) { |
| // Capture 10ms of audio. 2 bytes per sample. |
| - rtc::ArrayView<const int16_t> audio_data = capturer_->Capture(); |
| - uint32_t new_mic_level = 0; |
| - audio_callback_->RecordedDataIsAvailable( |
| - audio_data.data(), audio_data.size(), 2, 1, sampling_frequency_in_hz_, |
| - 0, 0, 0, false, new_mic_level); |
| + recording_buffer_.SetSize( |
| + SamplesPerFrame(capturer_->SamplingFrequency())); |
| + const bool keep_capturing = capturer_->Capture(&recording_buffer_); |
| + uint32_t new_mic_level; |
| + if (recording_buffer_.size() > 0) { |
|
kwiberg-webrtc
2017/03/13 14:22:27
This condition is always true, right?
(You could
oprypin_webrtc
2017/03/13 15:11:36
It is not true at the end of the recording. Don't
kwiberg-webrtc
2017/03/14 10:02:03
On line 273, you SetSize() the buffer to SamplesPe
oprypin_webrtc
2017/03/14 11:58:13
No. The Capture method is free to change the buffe
kwiberg-webrtc
2017/03/14 13:46:47
Ah, right! Sorry for being dense.
However, since
oprypin_webrtc
2017/03/14 14:01:40
Yes, I removed that line in a later patchset.
In
kwiberg-webrtc
2017/03/14 14:08:56
Acknowledged.
|
| + audio_callback_->RecordedDataIsAvailable( |
| + recording_buffer_.data(), recording_buffer_.size(), 2, 1, |
| + capturer_->SamplingFrequency(), 0, 0, 0, false, new_mic_level); |
| + } |
| + if (!keep_capturing) { |
| + capturing_ = false; |
| + done_capturing_.Set(); |
| + } |
| } |
| if (rendering_) { |
| - size_t samples_out = 0; |
| - int64_t elapsed_time_ms = -1; |
| - int64_t ntp_time_ms = -1; |
| + size_t samples_out; |
| + int64_t elapsed_time_ms; |
| + int64_t ntp_time_ms; |
| + const int sampling_frequency = renderer_->SamplingFrequency(); |
| audio_callback_->NeedMorePlayData( |
| - num_samples_per_frame_, 2, 1, sampling_frequency_in_hz_, |
| + SamplesPerFrame(sampling_frequency), 2, 1, sampling_frequency, |
| playout_buffer_.data(), samples_out, &elapsed_time_ms, &ntp_time_ms); |
| + const bool keep_rendering = renderer_->Render( |
| + rtc::ArrayView<const int16_t>(playout_buffer_.data(), samples_out)); |
| + if (!keep_rendering) { |
| + rendering_ = false; |
| + done_rendering_.Set(); |
| + } |
| } |
| } |
| tick_->Wait(WEBRTC_EVENT_INFINITE); |