| /* |
| * Copyright (c) 2023 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/test_audio_device_impl.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| |
| #include "api/array_view.h" |
| #include "api/task_queue/task_queue_factory.h" |
| #include "api/units/time_delta.h" |
| #include "modules/audio_device/include/test_audio_device.h" |
| #include "rtc_base/checks.h" |
| #include "rtc_base/synchronization/mutex.h" |
| #include "rtc_base/task_utils/repeating_task.h" |
| |
| namespace webrtc { |
| namespace { |
| |
| constexpr int kFrameLengthUs = 10000; |
| |
| } |
| |
| TestAudioDevice::TestAudioDevice( |
| TaskQueueFactory* task_queue_factory, |
| std::unique_ptr<TestAudioDeviceModule::Capturer> capturer, |
| std::unique_ptr<TestAudioDeviceModule::Renderer> renderer, |
| float speed) |
| : task_queue_factory_(task_queue_factory), |
| capturer_(std::move(capturer)), |
| renderer_(std::move(renderer)), |
| process_interval_us_(kFrameLengthUs / speed), |
| audio_buffer_(nullptr), |
| rendering_(false), |
| capturing_(false) { |
| 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(TestAudioDeviceModule::SamplesPerFrame(sample_rate) * |
| renderer_->NumChannels(), |
| 0); |
| RTC_CHECK(good_sample_rate(sample_rate)); |
| } |
| if (capturer_) { |
| RTC_CHECK(good_sample_rate(capturer_->SamplingFrequency())); |
| } |
| } |
| |
| AudioDeviceGeneric::InitStatus TestAudioDevice::Init() { |
| task_queue_ = task_queue_factory_->CreateTaskQueue( |
| "TestAudioDeviceModuleImpl", TaskQueueFactory::Priority::NORMAL); |
| |
| RepeatingTaskHandle::Start(task_queue_.get(), [this]() { |
| ProcessAudio(); |
| return TimeDelta::Micros(process_interval_us_); |
| }); |
| return InitStatus::OK; |
| } |
| |
| int32_t TestAudioDevice::PlayoutIsAvailable(bool& available) { |
| MutexLock lock(&lock_); |
| available = renderer_ != nullptr; |
| return 0; |
| } |
| |
| int32_t TestAudioDevice::InitPlayout() { |
| MutexLock lock(&lock_); |
| |
| if (rendering_) { |
| return -1; |
| } |
| |
| if (audio_buffer_ != nullptr && renderer_ != nullptr) { |
| // Update webrtc audio buffer with the selected parameters |
| audio_buffer_->SetPlayoutSampleRate(renderer_->SamplingFrequency()); |
| audio_buffer_->SetPlayoutChannels(renderer_->NumChannels()); |
| } |
| rendering_initialized_ = true; |
| return 0; |
| } |
| |
| bool TestAudioDevice::PlayoutIsInitialized() const { |
| MutexLock lock(&lock_); |
| return rendering_initialized_; |
| } |
| |
| int32_t TestAudioDevice::StartPlayout() { |
| MutexLock lock(&lock_); |
| RTC_CHECK(renderer_); |
| rendering_ = true; |
| return 0; |
| } |
| |
| int32_t TestAudioDevice::StopPlayout() { |
| MutexLock lock(&lock_); |
| rendering_ = false; |
| return 0; |
| } |
| |
| int32_t TestAudioDevice::RecordingIsAvailable(bool& available) { |
| MutexLock lock(&lock_); |
| available = capturer_ != nullptr; |
| return 0; |
| } |
| |
| int32_t TestAudioDevice::InitRecording() { |
| MutexLock lock(&lock_); |
| |
| if (capturing_) { |
| return -1; |
| } |
| |
| if (audio_buffer_ != nullptr && capturer_ != nullptr) { |
| // Update webrtc audio buffer with the selected parameters |
| audio_buffer_->SetRecordingSampleRate(capturer_->SamplingFrequency()); |
| audio_buffer_->SetRecordingChannels(capturer_->NumChannels()); |
| } |
| capturing_initialized_ = true; |
| return 0; |
| } |
| |
| bool TestAudioDevice::RecordingIsInitialized() const { |
| MutexLock lock(&lock_); |
| return capturing_initialized_; |
| } |
| |
| int32_t TestAudioDevice::StartRecording() { |
| MutexLock lock(&lock_); |
| capturing_ = true; |
| return 0; |
| } |
| |
| int32_t TestAudioDevice::StopRecording() { |
| MutexLock lock(&lock_); |
| capturing_ = false; |
| return 0; |
| } |
| |
| bool TestAudioDevice::Playing() const { |
| MutexLock lock(&lock_); |
| return rendering_; |
| } |
| |
| bool TestAudioDevice::Recording() const { |
| MutexLock lock(&lock_); |
| return capturing_; |
| } |
| |
| void TestAudioDevice::ProcessAudio() { |
| MutexLock lock(&lock_); |
| if (audio_buffer_ == nullptr) { |
| return; |
| } |
| if (capturing_ && capturer_ != nullptr) { |
| // Capture 10ms of audio. 2 bytes per sample. |
| const bool keep_capturing = capturer_->Capture(&recording_buffer_); |
| if (recording_buffer_.size() > 0) { |
| audio_buffer_->SetRecordedBuffer( |
| recording_buffer_.data(), |
| recording_buffer_.size() / capturer_->NumChannels(), |
| std::make_optional(rtc::TimeNanos())); |
| audio_buffer_->DeliverRecordedData(); |
| } |
| if (!keep_capturing) { |
| capturing_ = false; |
| } |
| } |
| if (rendering_) { |
| const int sampling_frequency = renderer_->SamplingFrequency(); |
| int32_t samples_per_channel = audio_buffer_->RequestPlayoutData( |
| TestAudioDeviceModule::SamplesPerFrame(sampling_frequency)); |
| audio_buffer_->GetPlayoutData(playout_buffer_.data()); |
| size_t samples_out = samples_per_channel * renderer_->NumChannels(); |
| RTC_CHECK_LE(samples_out, playout_buffer_.size()); |
| const bool keep_rendering = renderer_->Render( |
| rtc::ArrayView<const int16_t>(playout_buffer_.data(), samples_out)); |
| if (!keep_rendering) { |
| rendering_ = false; |
| } |
| } |
| } |
| |
| void TestAudioDevice::AttachAudioBuffer(AudioDeviceBuffer* audio_buffer) { |
| MutexLock lock(&lock_); |
| RTC_DCHECK(audio_buffer || audio_buffer_); |
| audio_buffer_ = audio_buffer; |
| |
| if (renderer_ != nullptr) { |
| audio_buffer_->SetPlayoutSampleRate(renderer_->SamplingFrequency()); |
| audio_buffer_->SetPlayoutChannels(renderer_->NumChannels()); |
| } |
| if (capturer_ != nullptr) { |
| audio_buffer_->SetRecordingSampleRate(capturer_->SamplingFrequency()); |
| audio_buffer_->SetRecordingChannels(capturer_->NumChannels()); |
| } |
| } |
| |
| } // namespace webrtc |