Skip to content

Commit 1bdb158

Browse files
committed
Muted talker detection
1 parent 0324b22 commit 1bdb158

File tree

6 files changed

+128
-28
lines changed

6 files changed

+128
-28
lines changed

modules/audio_device/audio_engine_device.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class AudioEngineDevice : public AudioDeviceGeneric,
126126

127127
bool IsInterrupted();
128128

129+
int32_t SetObserver(AudioDeviceObserver* observer) override;
129130

130131
private:
131132
struct EngineState {
@@ -184,6 +185,8 @@ class AudioEngineDevice : public AudioDeviceGeneric,
184185
// Set to true after successful call to Init(), false otherwise.
185186
bool initialized_ RTC_GUARDED_BY(thread_);
186187

188+
AudioDeviceObserver* observer_ RTC_GUARDED_BY(thread_);
189+
187190
// Audio interruption observer instance.
188191
RTC_OBJC_TYPE(RTCNativeAudioSessionDelegateAdapter) * audio_session_observer_
189192
RTC_GUARDED_BY(thread_);

modules/audio_device/audio_engine_device.mm

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,14 @@
673673
return 0;
674674
}
675675

676+
int32_t AudioEngineDevice::SetObserver(AudioDeviceObserver* observer) {
677+
LOGI() << "SetObserver";
678+
RTC_DCHECK_RUN_ON(thread_);
679+
680+
observer_ = observer;
681+
return 0;
682+
}
683+
676684
// ----------------------------------------------------------------------------------------------------
677685
// Private - Engine Related
678686

@@ -862,6 +870,32 @@
862870
}
863871
LOGI() << "setVoiceProcessingEnabled (input) result: " << set_input_vp_result ? "YES" : "NO";
864872

873+
// Muted talker detection.
874+
if (@available(iOS 17.0, macCatalyst 17.0, macOS 14.0, tvOS 17.0, visionOS 1.0, *)) {
875+
auto listener_block = ^(AVAudioVoiceProcessingSpeechActivityEvent event) {
876+
LOGI() << "AVAudioVoiceProcessingSpeechActivityEvent: " << event;
877+
RTC_DCHECK(event == AVAudioVoiceProcessingSpeechActivityStarted ||
878+
event == AVAudioVoiceProcessingSpeechActivityEnded);
879+
AudioDeviceModule::SpeechActivityEvent rtc_event =
880+
(event == AVAudioVoiceProcessingSpeechActivityStarted
881+
? AudioDeviceModule::SpeechActivityEvent::kStarted
882+
: AudioDeviceModule::SpeechActivityEvent::kEnded);
883+
884+
thread_->PostTask(SafeTask(safety_, [this, rtc_event] {
885+
RTC_DCHECK_RUN_ON(thread_); // Silence warning.
886+
if (this->observer_ != nullptr) {
887+
this->observer_->OnSpeechActivityEvent(rtc_event);
888+
}
889+
}));
890+
};
891+
892+
BOOL set_listener_result =
893+
[audio_engine_.inputNode setMutedSpeechActivityEventListener:listener_block];
894+
LOGI() << "setMutedSpeechActivityEventListener result: " << set_listener_result ? "YES"
895+
: "NO";
896+
RTC_DCHECK(set_listener_result);
897+
}
898+
865899
// Other audio ducking.
866900
// iOS 17.0+, iPadOS 17.0+, Mac Catalyst 17.0+, macOS 14.0+, visionOS 1.0+
867901
if (@available(iOS 17.0, macCatalyst 17.0, macOS 14.0, visionOS 1.0, *)) {

modules/audio_device/include/audio_device.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,7 @@
2020
namespace webrtc {
2121

2222
class AudioDeviceModuleForTest;
23-
24-
class AudioDeviceObserver {
25-
public:
26-
virtual ~AudioDeviceObserver() = default;
27-
28-
// input/output devices updated or default device changed
29-
virtual void OnDevicesUpdated() = 0;
30-
};
23+
class AudioDeviceObserver;
3124

3225
class AudioDeviceModule : public rtc::RefCountInterface {
3326
public:
@@ -50,6 +43,11 @@ class AudioDeviceModule : public rtc::RefCountInterface {
5043
kDefaultDevice = -2
5144
};
5245

46+
enum SpeechActivityEvent {
47+
kStarted = 0,
48+
kEnded,
49+
};
50+
5351
struct Stats {
5452
// The fields below correspond to similarly-named fields in the WebRTC stats
5553
// spec. https://w3c.github.io/webrtc-stats/#playoutstats-dict*
@@ -203,6 +201,16 @@ class AudioDeviceModuleForTest : public AudioDeviceModule {
203201
virtual int SetRecordingSampleRate(uint32_t sample_rate) = 0;
204202
};
205203

204+
class AudioDeviceObserver {
205+
public:
206+
virtual ~AudioDeviceObserver() = default;
207+
208+
// input/output devices updated or default device changed
209+
virtual void OnDevicesUpdated() {}
210+
virtual void OnSpeechActivityEvent(
211+
AudioDeviceModule::SpeechActivityEvent event) {}
212+
};
213+
206214
} // namespace webrtc
207215

208216
#endif // MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_H_

sdk/objc/api/peerconnection/RTCAudioDeviceModule.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@
2222

2323
NS_ASSUME_NONNULL_BEGIN
2424

25-
typedef void (^RTCOnAudioDevicesDidUpdate)();
25+
typedef NS_ENUM(NSInteger, RTCSpeechActivityEvent) {
26+
RTCSpeechActivityEventStarted,
27+
RTCSpeechActivityEventEnded,
28+
};
29+
30+
typedef void (^RTCDevicesDidUpdateCallback)();
31+
typedef void (^RTCSpeechActivityCallback)(RTCSpeechActivityEvent);
2632

2733
RTC_OBJC_EXPORT
2834
@interface RTC_OBJC_TYPE (RTCAudioDeviceModule) : NSObject
@@ -42,7 +48,8 @@ RTC_OBJC_EXPORT
4248
- (BOOL)trySetOutputDevice:(nullable RTC_OBJC_TYPE(RTCIODevice) *)device;
4349
- (BOOL)trySetInputDevice:(nullable RTC_OBJC_TYPE(RTCIODevice) *)device;
4450

45-
- (BOOL)setDevicesUpdatedHandler: (nullable RTCOnAudioDevicesDidUpdate) handler;
51+
- (BOOL)setDevicesDidUpdateCallback:(nullable RTCDevicesDidUpdateCallback)callback;
52+
- (BOOL)setSpeechActivityCallback:(nullable RTCSpeechActivityCallback)callback;
4653

4754
- (BOOL)startPlayout;
4855
- (BOOL)stopPlayout;

sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
#include <AudioUnit/AudioUnit.h>
17+
#include <os/lock.h>
1818

1919
#import "RTCAudioDeviceModule.h"
2020
#import "RTCAudioDeviceModule+Private.h"
@@ -25,19 +25,60 @@
2525

2626
class AudioDeviceObserver : public webrtc::AudioDeviceObserver {
2727
public:
28-
AudioDeviceObserver() {}
28+
AudioDeviceObserver() : lock_(OS_UNFAIR_LOCK_INIT) {}
2929

3030
void OnDevicesUpdated() override {
31+
os_unfair_lock_lock(&lock_);
32+
if (on_devices_did_update_callback_) {
33+
on_devices_did_update_callback_();
34+
}
35+
os_unfair_lock_unlock(&lock_);
36+
}
3137

32-
RTCLogInfo(@"AudioDeviceObserver OnDevicesUpdated");
33-
34-
if (callback_handler_) {
35-
callback_handler_();
38+
void OnSpeechActivityEvent(webrtc::AudioDeviceModule::SpeechActivityEvent event) override {
39+
os_unfair_lock_lock(&lock_);
40+
if (on_speech_activity_callback_) {
41+
on_speech_activity_callback_(ConvertSpeechActivityEvent(event));
3642
}
43+
os_unfair_lock_unlock(&lock_);
44+
}
45+
46+
void SetDevicesUpdatedCallBack(RTCDevicesDidUpdateCallback cb) {
47+
os_unfair_lock_lock(&lock_);
48+
on_devices_did_update_callback_ = cb;
49+
os_unfair_lock_unlock(&lock_);
50+
}
51+
52+
void SetOnSpeechActivityCallBack(RTCSpeechActivityCallback cb) {
53+
os_unfair_lock_lock(&lock_);
54+
on_speech_activity_callback_ = cb;
55+
os_unfair_lock_unlock(&lock_);
56+
}
57+
58+
bool IsAnyCallbackAttached() {
59+
os_unfair_lock_lock(&lock_);
60+
bool result =
61+
on_devices_did_update_callback_ != nullptr || on_speech_activity_callback_ != nullptr;
62+
os_unfair_lock_unlock(&lock_);
63+
return result;
3764
}
3865

39-
// private:
40-
RTCOnAudioDevicesDidUpdate callback_handler_;
66+
private:
67+
os_unfair_lock lock_;
68+
RTCDevicesDidUpdateCallback on_devices_did_update_callback_;
69+
RTCSpeechActivityCallback on_speech_activity_callback_;
70+
71+
RTCSpeechActivityEvent ConvertSpeechActivityEvent(
72+
webrtc::AudioDeviceModule::SpeechActivityEvent event) {
73+
switch (event) {
74+
case webrtc::AudioDeviceModule::SpeechActivityEvent::kStarted:
75+
return RTCSpeechActivityEvent::RTCSpeechActivityEventStarted;
76+
case webrtc::AudioDeviceModule::SpeechActivityEvent::kEnded:
77+
return RTCSpeechActivityEvent::RTCSpeechActivityEventEnded;
78+
default:
79+
return RTCSpeechActivityEvent::RTCSpeechActivityEventEnded;
80+
}
81+
}
4182
};
4283

4384
@implementation RTC_OBJC_TYPE (RTCAudioDeviceModule) {
@@ -57,10 +98,6 @@ - (instancetype)initWithNativeModule:(rtc::scoped_refptr<webrtc::AudioDeviceModu
5798

5899
_observer = new AudioDeviceObserver();
59100

60-
_workerThread->BlockingCall([self] {
61-
_native->SetObserver(_observer);
62-
});
63-
64101
return self;
65102
}
66103

@@ -240,9 +277,19 @@ - (BOOL)initRecording {
240277
});
241278
}
242279

243-
- (BOOL)setDevicesUpdatedHandler: (nullable RTCOnAudioDevicesDidUpdate) handler {
244-
_sink->callback_handler_ = handler;
245-
_observer->callback_handler_ = callback;
280+
- (BOOL)setDevicesDidUpdateCallback:(nullable RTCDevicesDidUpdateCallback)callback {
281+
_observer->SetDevicesUpdatedCallBack(callback);
282+
webrtc::AudioDeviceObserver *observer = _observer->IsAnyCallbackAttached() ? _observer : nullptr;
283+
_workerThread->BlockingCall([self, observer] { _native->SetObserver(observer); });
284+
285+
return YES;
286+
}
287+
288+
- (BOOL)setSpeechActivityCallback:(nullable RTCSpeechActivityCallback)callback {
289+
_observer->SetOnSpeechActivityCallBack(callback);
290+
webrtc::AudioDeviceObserver *observer = _observer->IsAnyCallbackAttached() ? _observer : nullptr;
291+
_workerThread->BlockingCall([self, observer] { _native->SetObserver(observer); });
292+
246293
return YES;
247294
}
248295

sdk/objc/native/src/audio/audio_device_module_ios.mm

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -667,9 +667,10 @@
667667
#endif // WEBRTC_IOS
668668

669669
int32_t AudioDeviceModuleIOS::SetObserver(AudioDeviceObserver* observer) const {
670-
// not implemented
671-
RTC_LOG(LS_WARNING) << __FUNCTION__ << "(" << observer << ") Not implemented";
672-
return -1;
670+
RTC_DLOG(LS_INFO) << __FUNCTION__;
671+
int r = audio_device_->SetObserver(observer);
672+
RTC_DLOG(LS_INFO) << "output: " << r;
673+
return r;
673674
}
674675
}
675676
}

0 commit comments

Comments
 (0)