| /* | 
 |  *  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 "test/fake_audio_device.h" | 
 |  | 
 | #include <algorithm> | 
 | #include <utility> | 
 |  | 
 | #include "common_audio/wav_file.h" | 
 | #include "rtc_base/checks.h" | 
 | #include "system_wrappers/include/event_wrapper.h" | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | namespace { | 
 |  | 
 | constexpr int kFrameLengthMs = 10; | 
 | constexpr int kFramesPerSecond = 1000 / kFrameLengthMs; | 
 |  | 
 | 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( | 
 |         test::FakeAudioDevice::SamplesPerFrame(sampling_frequency_in_hz_), | 
 |         [&](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 BoundedWavFileWriter : public test::FakeAudioDevice::Renderer { | 
 |  public: | 
 |   BoundedWavFileWriter(std::string filename, int sampling_frequency_in_hz) | 
 |       : sampling_frequency_in_hz_(sampling_frequency_in_hz), | 
 |         wav_writer_(filename, sampling_frequency_in_hz, 1), | 
 |         silent_audio_(test::FakeAudioDevice::SamplesPerFrame( | 
 |             sampling_frequency_in_hz), 0), | 
 |         started_writing_(false), | 
 |         trailing_zeros_(0) {} | 
 |  | 
 |   int SamplingFrequency() const override { | 
 |     return sampling_frequency_in_hz_; | 
 |   } | 
 |  | 
 |   bool Render(rtc::ArrayView<const int16_t> data) override { | 
 |     const int16_t kAmplitudeThreshold = 5; | 
 |  | 
 |     const int16_t* begin = data.begin(); | 
 |     const int16_t* end = data.end(); | 
 |     if (!started_writing_) { | 
 |       // Cut off silence at the beginning. | 
 |       while (begin < end) { | 
 |         if (std::abs(*begin) > kAmplitudeThreshold) { | 
 |           started_writing_ = true; | 
 |           break; | 
 |         } | 
 |         ++begin; | 
 |       } | 
 |     } | 
 |     if (started_writing_) { | 
 |       // Cut off silence at the end. | 
 |       while (begin < end) { | 
 |         if (*(end - 1) != 0) { | 
 |           break; | 
 |         } | 
 |         --end; | 
 |       } | 
 |       if (begin < end) { | 
 |         // If it turns out that the silence was not final, need to write all the | 
 |         // skipped zeros and continue writing audio. | 
 |         while (trailing_zeros_ > 0) { | 
 |           const size_t zeros_to_write = std::min(trailing_zeros_, | 
 |                                                  silent_audio_.size()); | 
 |           wav_writer_.WriteSamples(silent_audio_.data(), zeros_to_write); | 
 |           trailing_zeros_ -= zeros_to_write; | 
 |         } | 
 |         wav_writer_.WriteSamples(begin, end - begin); | 
 |       } | 
 |       // Save the number of zeros we skipped in case this needs to be restored. | 
 |       trailing_zeros_ += data.end() - end; | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |  private: | 
 |   int sampling_frequency_in_hz_; | 
 |   WavWriter wav_writer_; | 
 |   std::vector<int16_t> silent_audio_; | 
 |   bool started_writing_; | 
 |   size_t trailing_zeros_; | 
 | }; | 
 |  | 
 |  | 
 | class DiscardRenderer final : public test::FakeAudioDevice::Renderer { | 
 |  public: | 
 |   explicit 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 { | 
 |  | 
 | // Assuming 10ms audio packets. | 
 | FakeAudioDevice::PulsedNoiseCapturer::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) { | 
 |   RTC_DCHECK_GT(max_amplitude, 0); | 
 | } | 
 |  | 
 | bool FakeAudioDevice::PulsedNoiseCapturer::Capture( | 
 |     rtc::BufferT<int16_t>* buffer) { | 
 |   fill_with_zero_ = !fill_with_zero_; | 
 |   int16_t max_amplitude; | 
 |   { | 
 |     rtc::CritScope cs(&lock_); | 
 |     max_amplitude = max_amplitude_; | 
 |   } | 
 |   buffer->SetData(FakeAudioDevice::SamplesPerFrame(sampling_frequency_in_hz_), | 
 |                   [&](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; | 
 | } | 
 |  | 
 | void FakeAudioDevice::PulsedNoiseCapturer::SetMaxAmplitude(int16_t amplitude) { | 
 |   rtc::CritScope cs(&lock_); | 
 |   max_amplitude_ = amplitude; | 
 | } | 
 |  | 
 | size_t FakeAudioDevice::SamplesPerFrame(int sampling_frequency_in_hz) { | 
 |   return rtc::CheckedDivExact(sampling_frequency_in_hz, kFramesPerSecond); | 
 | } | 
 |  | 
 | std::unique_ptr<FakeAudioDevice::PulsedNoiseCapturer> | 
 | FakeAudioDevice::CreatePulsedNoiseCapturer(int16_t max_amplitude, | 
 |                                            int sampling_frequency_in_hz) { | 
 |   return std::unique_ptr<FakeAudioDevice::PulsedNoiseCapturer>( | 
 |       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::CreateBoundedWavFileWriter( | 
 |         std::string filename, int sampling_frequency_in_hz) { | 
 |   return std::unique_ptr<FakeAudioDevice::Renderer>( | 
 |       new BoundedWavFileWriter(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), | 
 |       done_rendering_(true, true), | 
 |       done_capturing_(true, true), | 
 |       tick_(EventTimerWrapper::Create()), | 
 |       thread_(FakeAudioDevice::Run, this, "FakeAudioDevice") { | 
 |   auto good_sample_rate = [](int sr) { | 
 |     return sr == 8000 || sr == 16000 || sr == 32000 | 
 |         || sr == 44100 || sr == 48000; | 
 |   }; | 
 |  | 
 |   if (renderer_) { | 
 |     const int sample_rate = renderer_->SamplingFrequency(); | 
 |     playout_buffer_.resize(SamplesPerFrame(sample_rate), 0); | 
 |     RTC_CHECK(good_sample_rate(sample_rate)); | 
 |   } | 
 |   if (capturer_) { | 
 |     RTC_CHECK(good_sample_rate(capturer_->SamplingFrequency())); | 
 |   } | 
 | } | 
 |  | 
 | FakeAudioDevice::~FakeAudioDevice() { | 
 |   StopPlayout(); | 
 |   StopRecording(); | 
 |   thread_.Stop(); | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | int32_t FakeAudioDevice::Init() { | 
 |   RTC_CHECK(tick_->StartTimer(true, kFrameLengthMs / speed_)); | 
 |   thread_.Start(); | 
 |   thread_.SetPriority(rtc::kHighPriority); | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t FakeAudioDevice::RegisterAudioCallback(AudioTransport* callback) { | 
 |   rtc::CritScope cs(&lock_); | 
 |   RTC_DCHECK(callback || audio_callback_); | 
 |   audio_callback_ = callback; | 
 |   return 0; | 
 | } | 
 |  | 
 | bool FakeAudioDevice::Playing() const { | 
 |   rtc::CritScope cs(&lock_); | 
 |   return rendering_; | 
 | } | 
 |  | 
 | bool FakeAudioDevice::Recording() const { | 
 |   rtc::CritScope cs(&lock_); | 
 |   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; | 
 | } | 
 |  | 
 | void FakeAudioDevice::ProcessAudio() { | 
 |   { | 
 |     rtc::CritScope cs(&lock_); | 
 |     if (capturing_) { | 
 |       // Capture 10ms of audio. 2 bytes per sample. | 
 |       const bool keep_capturing = capturer_->Capture(&recording_buffer_); | 
 |       uint32_t new_mic_level; | 
 |       if (recording_buffer_.size() > 0) { | 
 |         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; | 
 |       int64_t elapsed_time_ms; | 
 |       int64_t ntp_time_ms; | 
 |       const int sampling_frequency = renderer_->SamplingFrequency(); | 
 |       audio_callback_->NeedMorePlayData( | 
 |           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); | 
 | } | 
 |  | 
 |  | 
 | }  // namespace test | 
 | }  // namespace webrtc |