| /* | |
| * Copyright (c) 2022 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. | |
| */ | |
| #import <AudioUnit/AudioUnit.h> | |
| #import <Foundation/Foundation.h> | |
| #import "objc_audio_device.h" | |
| #import "objc_audio_device_delegate.h" | |
| #include "api/make_ref_counted.h" | |
| #include "api/ref_counted_base.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/logging.h" | |
| #include "rtc_base/thread.h" | |
| namespace { | |
| constexpr double kPreferredInputSampleRate = 48000.0; | |
| constexpr double kPreferredOutputSampleRate = 48000.0; | |
| // WebRTC processes audio in chunks of 10ms. Preferring 20ms audio chunks | |
| // is a compromize between performance and power consumption. | |
| constexpr NSTimeInterval kPeferredInputIOBufferDuration = 0.02; | |
| constexpr NSTimeInterval kPeferredOutputIOBufferDuration = 0.02; | |
| class AudioDeviceDelegateImpl final | |
| : public webrtc::RefCountedNonVirtual<AudioDeviceDelegateImpl> { | |
| public: | |
| AudioDeviceDelegateImpl( | |
| webrtc::scoped_refptr<webrtc::objc_adm::ObjCAudioDeviceModule> | |
| audio_device_module, | |
| webrtc::Thread* thread) | |
| : audio_device_module_(audio_device_module), thread_(thread) { | |
| RTC_DCHECK(audio_device_module_); | |
| RTC_DCHECK(thread_); | |
| } | |
| webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module() const { | |
| return audio_device_module_.get(); | |
| } | |
| webrtc::Thread* thread() const { return thread_; } | |
| void reset_audio_device_module() { audio_device_module_ = nullptr; } | |
| private: | |
| webrtc::scoped_refptr<webrtc::objc_adm::ObjCAudioDeviceModule> | |
| audio_device_module_; | |
| webrtc::Thread* thread_; | |
| }; | |
| } // namespace | |
| @implementation ObjCAudioDeviceDelegate { | |
| webrtc::scoped_refptr<AudioDeviceDelegateImpl> impl_; | |
| } | |
| @synthesize getPlayoutData = getPlayoutData_; | |
| @synthesize deliverRecordedData = deliverRecordedData_; | |
| @synthesize preferredInputSampleRate = preferredInputSampleRate_; | |
| @synthesize preferredInputIOBufferDuration = preferredInputIOBufferDuration_; | |
| @synthesize preferredOutputSampleRate = preferredOutputSampleRate_; | |
| @synthesize preferredOutputIOBufferDuration = preferredOutputIOBufferDuration_; | |
| - (instancetype) | |
| initWithAudioDeviceModule: | |
| (webrtc::scoped_refptr<webrtc::objc_adm::ObjCAudioDeviceModule>) | |
| audioDeviceModule | |
| audioDeviceThread:(webrtc::Thread*)thread { | |
| RTC_DCHECK_RUN_ON(thread); | |
| self = [super init]; | |
| if (self) { | |
| impl_ = webrtc::make_ref_counted<AudioDeviceDelegateImpl>(audioDeviceModule, | |
| thread); | |
| preferredInputSampleRate_ = kPreferredInputSampleRate; | |
| preferredInputIOBufferDuration_ = kPeferredInputIOBufferDuration; | |
| preferredOutputSampleRate_ = kPreferredOutputSampleRate; | |
| preferredOutputIOBufferDuration_ = kPeferredOutputIOBufferDuration; | |
| webrtc::scoped_refptr<AudioDeviceDelegateImpl> playout_delegate = impl_; | |
| getPlayoutData_ = | |
| ^OSStatus(AudioUnitRenderActionFlags* _Nonnull actionFlags, | |
| const AudioTimeStamp* _Nonnull timestamp, | |
| NSInteger inputBusNumber, | |
| UInt32 frameCount, | |
| AudioBufferList* _Nonnull outputData) { | |
| webrtc::objc_adm::ObjCAudioDeviceModule* audio_device = | |
| playout_delegate->audio_device_module(); | |
| if (audio_device) { | |
| return audio_device->OnGetPlayoutData( | |
| actionFlags, timestamp, inputBusNumber, frameCount, outputData); | |
| } else { | |
| *actionFlags |= kAudioUnitRenderAction_OutputIsSilence; | |
| RTC_LOG(LS_VERBOSE) << "No alive audio device"; | |
| return noErr; | |
| } | |
| }; | |
| webrtc::scoped_refptr<AudioDeviceDelegateImpl> record_delegate = impl_; | |
| deliverRecordedData_ = ^OSStatus( | |
| AudioUnitRenderActionFlags* _Nonnull actionFlags, | |
| const AudioTimeStamp* _Nonnull timestamp, | |
| NSInteger inputBusNumber, | |
| UInt32 frameCount, | |
| const AudioBufferList* _Nullable inputData, | |
| void* renderContext, | |
| RTC_OBJC_TYPE( | |
| RTCAudioDeviceRenderRecordedDataBlock) _Nullable renderBlock) { | |
| webrtc::objc_adm::ObjCAudioDeviceModule* audio_device = | |
| record_delegate->audio_device_module(); | |
| if (audio_device) { | |
| return audio_device->OnDeliverRecordedData(actionFlags, | |
| timestamp, | |
| inputBusNumber, | |
| frameCount, | |
| inputData, | |
| renderContext, | |
| renderBlock); | |
| } else { | |
| RTC_LOG(LS_VERBOSE) << "No alive audio device"; | |
| return noErr; | |
| } | |
| }; | |
| } | |
| return self; | |
| } | |
| - (void)notifyAudioInputParametersChange { | |
| RTC_DCHECK_RUN_ON(impl_->thread()); | |
| webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = | |
| impl_->audio_device_module(); | |
| if (audio_device_module) { | |
| audio_device_module->HandleAudioInputParametersChange(); | |
| } | |
| } | |
| - (void)notifyAudioOutputParametersChange { | |
| RTC_DCHECK_RUN_ON(impl_->thread()); | |
| webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = | |
| impl_->audio_device_module(); | |
| if (audio_device_module) { | |
| audio_device_module->HandleAudioOutputParametersChange(); | |
| } | |
| } | |
| - (void)notifyAudioInputInterrupted { | |
| RTC_DCHECK_RUN_ON(impl_->thread()); | |
| webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = | |
| impl_->audio_device_module(); | |
| if (audio_device_module) { | |
| audio_device_module->HandleAudioInputInterrupted(); | |
| } | |
| } | |
| - (void)notifyAudioOutputInterrupted { | |
| RTC_DCHECK_RUN_ON(impl_->thread()); | |
| webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = | |
| impl_->audio_device_module(); | |
| if (audio_device_module) { | |
| audio_device_module->HandleAudioOutputInterrupted(); | |
| } | |
| } | |
| - (void)dispatchAsync:(dispatch_block_t)block { | |
| webrtc::Thread* thread = impl_->thread(); | |
| RTC_DCHECK(thread); | |
| thread->PostTask([block] { | |
| @autoreleasepool { | |
| block(); | |
| } | |
| }); | |
| } | |
| - (void)dispatchSync:(dispatch_block_t)block { | |
| webrtc::Thread* thread = impl_->thread(); | |
| RTC_DCHECK(thread); | |
| if (thread->IsCurrent()) { | |
| @autoreleasepool { | |
| block(); | |
| } | |
| } else { | |
| thread->BlockingCall([block] { | |
| @autoreleasepool { | |
| block(); | |
| } | |
| }); | |
| } | |
| } | |
| - (void)resetAudioDeviceModule { | |
| RTC_DCHECK_RUN_ON(impl_->thread()); | |
| impl_->reset_audio_device_module(); | |
| } | |
| @end |