WWDC Quick Look 💓 By SwiftGGTeam
What's new in voice processing

What's new in voice processing

元の動画を見る

ハイライト

Apple は iOS 17、macOS 14、tvOS 17 で音声処理 API に 2 つの機能を追加しました。他のオーディオのダッキング(Other Audio Ducking)の細かな制御と、ミュート中の話者検出(Muted Talker Detection)です。これにより VoIP App は FaceTime のように背景音量を賢く調整し、ユーザーにミュート解除を促せます。

主要内容

背景音のダッキング: 一律制御から細かな制御へ

音声通話 App を作る開発者なら、通話中にユーザーが音楽や他のオーディオを再生し、背景音が大きすぎて声が聞き取りにくくなる場面に出会ったことがあるはずです。Apple の音声処理 API は以前から他のオーディオ音量を自動的に下げていましたが、ダッキング量は固定で、App によっては下がりすぎたり足りなかったりしました。

03:16)今年、Apple は AUVoiceIO と AVAudioEngine の両方にダッキング設定 API を追加しました。開発者は、ダッキングスタイル(高度な動的ダッキングを有効にするか)とダッキング強度(4 段階)の 2 つの軸を制御できます。

高度な動的ダッキングの効果は FaceTime SharePlay に似ています。誰かが話している間はメディア音量が自動的に下がり、誰も話していないときは元に戻ります。これにより、通話と背景オーディオを自然に共存させられます。

ミュート中の話者検出: 「話していたのにマイクがオフだった」をなくす

オンライン会議で最も気まずい場面の 1 つは、ユーザーが熱心に話し続けたあとで、マイクがずっとミュートだったと気づくことです。

07:58)Apple は iOS 15 でミュート中の話者検出を導入し、今年は macOS 14 と tvOS 17 に拡張しました。ユーザーが音声処理 API のミュートインターフェイスでミュートしていれば、システムはミュート中に話しているかを検出して App に通知できます。App は通知を受け取ったあと、ユーザーにミュート解除を促せます。

Apple の音声処理 API にまだ対応していない macOS App 向けには、代替手段として Core Audio HAL レベルの音声アクティビティ検出プロパティも用意されています。

詳細

他のオーディオのダッキング設定

Apple は 2 つの対応方法を提供しています。低レベルの AUVoiceIO(Audio Unit)と、高レベルの AVAudioEngine です。

AUVoiceIO 方式

05:50)AUVoiceIO では C 構造体でダッキングパラメータを設定します。

typedef CF_ENUM(UInt32, AUVoiceIOOtherAudioDuckingLevel) {
    kAUVoiceIOOtherAudioDuckingLevelDefault = 0,
    kAUVoiceIOOtherAudioDuckingLevelMin = 10,
    kAUVoiceIOOtherAudioDuckingLevelMid = 20,
    kAUVoiceIOOtherAudioDuckingLevelMax = 30
};

struct AUVoiceIOOtherAudioDuckingConfiguration {
    Boolean mEnableAdvancedDucking;
    AUVoiceIOOtherAudioDuckingLevel mDuckingLevel;
};

設定を作成して適用します。

const AUVoiceIOOtherAudioDuckingConfiguration duckingConfig = {
    .mEnableAdvancedDucking = true,
    .mDuckingLevel = AUVoiceIOOtherAudioDuckingLevel::kAUVoiceIOOtherAudioDuckingLevelMin
};

OSStatus err = AudioUnitSetProperty(
    auVoiceIO,
    kAUVoiceIOProperty_OtherAudioDuckingConfiguration,
    kAudioUnitScope_Global,
    0,
    &duckingConfig,
    sizeof(duckingConfig)
);

キーポイント:

  • mEnableAdvancedDuckingtrue のとき、システムは双方の音声アクティビティに応じてダッキング量を動的に調整する
  • mDuckingLevel には 4 段階がある: default(従来の動作と同じ)、min(最小ダッキングで背景音が最大)、midmax(最大ダッキングで声が最も明瞭)
  • 2 つの制御軸は互いに独立しており、組み合わせることで多くの場面をカバーできる

AVAudioEngine 方式

07:20)AVAudioEngine は対応する Swift API を提供します。

let engine = AVAudioEngine()
let inputNode = engine.inputNode

do {
    try inputNode.setVoiceProcessingEnabled(true)
} catch {
    print("Could not enable voice processing \(error)")
}

let duckingConfig = AVAudioVoiceProcessingOtherAudioDuckingConfiguration(
    enableAdvancedDucking: false,
    duckingLevel: .max
)

inputNode.voiceProcessingOtherAudioDuckingConfiguration = duckingConfig

キーポイント:

  • まず setVoiceProcessingEnabled(true) を呼び出して音声処理モードを有効にする
  • AVAudioVoiceProcessingOtherAudioDuckingConfiguration は Swift の構造体で、引数名は C 版と少し異なる
  • inputNode.voiceProcessingOtherAudioDuckingConfiguration に直接代入すれば有効になる

ミュート中の話者検出

AUVoiceIO 実装

07:32

AUVoiceIOMutedSpeechActivityEventListener listener = ^(AUVoiceIOMutedSpeechActivityEvent event) {
    if (event == kAUVoiceIOSpeechActivityHasStarted) {
        // ユーザーがミュート中に話し始めたため、ミュート解除を促す
    } else if (event == kAUVoiceIOSpeechActivityHasEnded) {
        // ユーザーが話すのをやめた
    }
};

OSStatus err = AudioUnitSetProperty(
    auVoiceIO,
    kAUVoiceIOProperty_MutedSpeechActivityEventListener,
    kAudioUnitScope_Global,
    0,
    &listener,
    sizeof(AUVoiceIOMutedSpeechActivityEventListener)
);

// ユーザーがミュートをタップしたとき
UInt32 muteUplinkOutput = 1;
AudioUnitSetProperty(
    auVoiceIO,
    kAUVoiceIOProperty_MuteOutput,
    kAudioUnitScope_Global,
    0,
    &muteUplinkOutput,
    sizeof(muteUplinkOutput)
);

キーポイント:

  • リスナーは音声アクティビティ状態が変化したときだけ発火し、連続して繰り返し呼ばれることはない
  • システムが正しく検出できるよう、ミュートは必ず kAUVoiceIOProperty_MuteOutput API で実装する
  • イベントは kAUVoiceIOSpeechActivityHasStartedkAUVoiceIOSpeechActivityHasEnded の 2 種類だけ

AVAudioEngine 実装

11:08

let listener = { (event: AVAudioVoiceProcessingSpeechActivityEvent) in
    if event == .started {
        // ユーザーがミュート中に話し始めたため、ミュート解除を促す
    } else if event == .ended {
        // ユーザーが話すのをやめた
    }
}

inputNode.setMutedSpeechActivityEventListener(listener)

// ユーザーがミュートをタップしたとき
inputNode.isVoiceProcessingInputMuted = true

キーポイント:

  • setMutedSpeechActivityEventListener でリスナーを登録する
  • ミュートには必ず isVoiceProcessingInputMuted プロパティを使い、他の方法は使わない
  • Swift の列挙値は .started.ended で、C 版より簡潔

Core Audio HAL の音声アクティビティ検出(macOS 専用)

12:31)Apple の音声処理 API に対応していない macOS App では、HAL API を使用できます。

// 入力デバイスの音声アクティビティ検出を有効にする
const AudioObjectPropertyAddress kVoiceActivityDetectionEnable = {
    kAudioDevicePropertyVoiceActivityDetectionEnable,
    kAudioDevicePropertyScopeInput,
    kAudioObjectPropertyElementMain
};

UInt32 shouldEnable = 1;
AudioObjectSetPropertyData(deviceID, &kVoiceActivityDetectionEnable, 0, NULL, sizeof(UInt32), &shouldEnable);

// 状態リスナーを登録する
const AudioObjectPropertyAddress kVoiceActivityDetectionState = {
    kAudioDevicePropertyVoiceActivityDetectionState,
    kAudioDevicePropertyScopeInput,
    kAudioObjectPropertyElementMain
};

AudioObjectAddPropertyListener(deviceID, &kVoiceActivityDetectionState, listener_callback, NULL);

リスナーの実装:

OSStatus listener_callback(
    AudioObjectID inObjectID,
    UInt32 inNumberAddresses,
    const AudioObjectPropertyAddress* inAddresses,
    void* inClientData
) {
    UInt32 voiceDetected = 0;
    UInt32 propertySize = sizeof(UInt32);
    OSStatus status = AudioObjectGetPropertyData(
        inObjectID, &kVoiceActivityDetectionState, 0, NULL, &propertySize, &voiceDetected
    );

    if (status == kAudioHardwareNoError) {
        if (voiceDetected == 1) {
            // 音声アクティビティを検出
        } else {
            // 音声アクティビティは検出されていない
        }
    }
    return status;
}

キーポイント:

  • 検出対象はエコーキャンセル後のマイク入力で、音声通話の場面に適している
  • 検出は App のミュート状態とは独立しているため、App 側でミュート状態と組み合わせて判断する必要がある
  • HAL のプロセスミュート API の使用が推奨される。メニューバーの録音インジケータを消灯でき、ユーザーのプライバシーを守れる

重要ポイント

  1. スマート音量調整付きの通話 App

    • 何を作るか: 音声通話中に背景音楽や効果音の音量を自動調整し、話している間は下げ、話していないときは戻す
    • なぜ価値があるか: 以前は開発者が音声解析と音量制御ロジックを書く必要があったが、今は enableAdvancedDucking の 1 行で FaceTime レベルのスマートダッキングを実現できる
    • 始め方: AVAudioEngine で音声処理を有効にし、voiceProcessingOtherAudioDuckingConfiguration を設定し、通話以外のオーディオを別ノードにルーティングする
  2. 「ミュート中です」提示付きの会議 App

    • 何を作るか: ユーザーがミュート中に話しているかを検出し、画面上に提示を表示する
    • なぜ価値があるか: これは FaceTime で既に提供されている機能で、ユーザーはこの操作に慣れている。setMutedSpeechActivityEventListener を数行書くだけで実現できる
    • 始め方: リスナーを登録したあと、.started イベントで「ミュート中に話しています」というオーバーレイを表示し、ワンタップのミュート解除ボタンを添える
  3. ポッドキャスト/ライブ配信用の音声トリガーツール

    • 何を作るか: 音声アクティビティ検出を使って、配信者が話したらシーンを自動切り替えしたり字幕を表示したりする
    • なぜ価値があるか: HAL API の音声アクティビティ検出はエコーキャンセル後のクリーンな音声に基づくため、単純な音量しきい値より正確
    • 始め方: macOS で kAudioDevicePropertyVoiceActivityDetectionEnable を使って検出を有効にし、リスナーを登録し、自分の業務ロジックと組み合わせてアクションを発火する
  4. マルチプレイヤーゲームのボイスチャット

    • 何を作るか: ゲーム内ボイスチャットで、ゲーム効果音とチームメイトの声を共存させ、システムが賢く音量を調整する
    • なぜ価値があるか: tvOS 17 は初めて音声処理 API に対応し、Continuity Camera と組み合わせることで Apple TV 上のゲームでも高品質なボイスチャットを提供できる
    • 始め方: tvOS で AUVoiceIO または AVAudioEngine の音声処理モードを有効にし、ゲームシーンに応じて min(ゲームの雰囲気を残す)または max(指示の明瞭さを確保する)のダッキングレベルを選ぶ

関連セッション

コメント

GitHub Issues · utterances