| /* | |
| * 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 <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <optional> | |
| #include <utility> | |
| #include "api/array_view.h" | |
| #include "api/environment/environment.h" | |
| #include "api/task_queue/task_queue_factory.h" | |
| #include "api/units/time_delta.h" | |
| #include "modules/audio_device/audio_device_buffer.h" | |
| #include "modules/audio_device/audio_device_generic.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; | |
| } // namespace | |
| TestAudioDevice::TestAudioDevice( | |
| const Environment& env, | |
| std::unique_ptr<TestAudioDeviceModule::Capturer> capturer, | |
| std::unique_ptr<TestAudioDeviceModule::Renderer> renderer, | |
| float speed) | |
| : env_(env), | |
| 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 == 24000 || 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_ = env_.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_.empty()) { | |
| audio_buffer_->SetRecordedBuffer( | |
| recording_buffer_.data(), | |
| recording_buffer_.size() / capturer_->NumChannels(), | |
| std::make_optional(env_.clock().CurrentTime().ns())); | |
| 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( | |
| 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 |