🔊

AVAudioSessionでiOSの再生の環境を整える

2024/12/07に公開

この記事はVoicy Advent Calendar 2024の7日目の記事です。

はじめに

iOSアプリで音声再生を扱う際に避けて通れないのがAVAudioSessionの設定です。
こちらのデフォルト設定をご存知でしょうか?
下記のような特徴になっています

  • アプリがアクティブな時のみ音声再生が可能
  • 他のアプリの音声を自動的に停止する(ミュージックアプリなど)
  • バックグラウンドでの再生は不可能

コードで明示的に表現するとこのような設定になっています。

try audioSession.setCategory(.soloAmbient, mode: .default, options: [])
try audioSession.setActive(true)

このデフォルト設定のまま使用すると、以下のような問題が発生する可能性があります:

  • バックグラウンドで音声が再生できない
  • 他のアプリの音声が意図せず停止する
  • 音声の優先順位が正しく制御できない

そのため、アプリの用途に応じて適切なカテゴリーとオプションを設定することが重要です。
本記事では、再生を中心にこれらの設定方法と具体的な実装例を解説していきます。

1. 標準的なバックグラウンド再生

音楽プレーヤーアプリなど、シンプルにバックグラウンド再生が必要な場合:

func configureStandardPlayback() {
    do {
        let audioSession = AVAudioSession.sharedInstance()
        try audioSession.setCategory(.playback)
        try audioSession.setActive(true)
        
        logger.info("Configured standard background playback")
    } catch {
        logger.error("Failed to configure standard playback: \(error.localizedDescription)")
    }
}

また、バックグラウンド再生を有効にするには、Info.plistの設定も必要です:

<dict>
	<key>UIBackgroundModes</key>
	<array>
		<string>audio</string>
	</array>
</dict>

こんな感じでチェックしてもいいかも?

Info.plistの設定がない場合はバックグラウンドで音声の再生を保証することはできません。
下記のような形で判定ロジックを書いておくと確実ではあります。

func checkBackgroundAudioConfiguration() throws {
    guard let backgroundModes = Bundle.main.object(forInfoDictionaryKey: "UIBackgroundModes") as? [String] else {
        logger.error("UIBackgroundModes not found in Info.plist")
        throw ConfigurationError.backgroundModesNotFound
    }
    
    let hasAudioMode = backgroundModes.contains("audio")
    if !hasAudioMode {
        logger.warning("UIBackgroundModes does not contain 'audio'. Background playback may not work.")
        throw ConfigurationError.audioModeNotEnabled
    }
}

この設定では下記のような特徴があります。

  • バックグラウンド再生が可能
  • 他のアプリの音声は停止される
  • 画面をロックしても再生継続

2. 他のアプリの音声を残しつつ再生

ゲームのBGMなど、他のアプリの音声を邪魔したくない場合下記のように設定します。

func configureRespectOtherApps() {
    do {
        let audioSession = AVAudioSession.sharedInstance()
        try audioSession.setCategory(
            .playback,
            options: [.duckOthers]
        )
        try audioSession.setActive(true)
        
        logger.info("Configured playback to respect other apps")
    } catch {
        logger.error("Failed to configure respect other apps mode: \(error.localizedDescription)")
    }
}

この設定では下記のような特徴があります

  • 他のアプリの音声は小さくなるが停止はしない
  • バックグラウンド再生も可能
  • DuckOthersオプションにより、自動的に音量調整

3. 自アプリの音声を優先

ナビゲーションアプリやアラームアプリなど、重要な通知を発する場合:

func configurePrioritizeOurAudio() {
    do {
        let audioSession = AVAudioSession.sharedInstance()
        try audioSession.setCategory(
            .playback,
            mode: .spokenAudio,
            options: [.interruptSpokenAudioAndMixWithOthers]
        )
        try audioSession.setActive(true, options: [.notifyOthersOnDeactivation])
        
        logger.info("Configured playback to prioritize our audio")
    } catch {
        logger.error("Failed to configure prioritize our audio mode: \(error.localizedDescription)")
    }
}

この設定では下記のような特徴があります。

  • 他のアプリの音声を中断
  • 音声案内などの重要な音声を確実に再生
  • 設定終了時に他のアプリに通知

まとめ

AVAudioSessionの設定は、アプリの音声体験を大きく左右する重要な機能です。主なポイントは:

  • デフォルトのsoloAmbientでは多くの場合不十分
  • アプリの用途に応じた適切なカテゴリー選択が重要
  • 他アプリとの音声の共存に注意が必要

いかがでしたでしょうか?
音声を再生する際はいろいろな設定があるので注意して設定してみてください。

今回の内容は下記にサンプルコードを書いて検証しております。このまま動くと思いますのでぜひ動かして検証してみてください。

サンプルコード:
https://github.com/entaku0818/AudioMaster/tree/main/AudioMasterApp/AudioMasterApp/AudioMasterApp/AVF/AudioSesstion

以前発表した資料:
https://speakerdeck.com/entaku/avaudiosessionnoquan-ti-xiang-woguo-mu-4b8fb926-3c10-424b-870b-9d589b057262

参考リンク

Discussion