🔉

【iOS】音声の再生を非同期で扱うメリット

2024/02/16に公開

概要

このテキストでは、Swiftにおける音声の再生を非同期で行うメリットについて解説しています。
再生して終わりではなく、再生を最後まで保証できる処理の書き方をサンプルコードを見ながら確認していきましょう。

こちらに今回のサンプルがまとまっています。
https://github.com/entaku0818/AudioMaster

非同期でない音声再生処理は?

ではまず非同期でない音声再生処理を見てみましょう。
下記は簡単なaudioPlayerを利用した音声再生処理です。

ここでplayAudio から返却されるBoolは何を表しているでしょうか?

    public func playAudio(atTime time: TimeInterval) -> Bool {
        audioPlayer.play(atTime: time)
        return audioPlayer.isPlaying
    }

ここでは 音声再生処理が開始されたこと を表しています
もちろんこれだけでも十分なケースもありますが、再生する場合は最後まで保証させる処理を書きたいなと思うのではないでしょうか?(思いますよね?)

非同期な音声再生処理とは?

それでは非同期な音声再生処理見てみましょう。下記のコードは音声を非同期再生する処理です。
ポイントは Delegate で定義している didFinishPlayingdecodeErrorDidOccur
です。

    public func playAudioAsync(atTime time: TimeInterval) async throws -> Bool {

        let stream = AsyncThrowingStream<Bool, Error> { continuation in
          do {
              self.delegate = try Delegate(didFinishPlaying: { flag in
                  continuation.yield(flag)
                  continuation.finish()
                  try? AVAudioSession.sharedInstance().setActive(false)
              }, decodeErrorDidOccur: { error in
                  continuation.finish(throwing: error)
                  try? AVAudioSession.sharedInstance().setActive(false)
              })


              try AVAudioSession.sharedInstance().setActive(true)
              try AVAudioSession.sharedInstance().setCategory(.playback)
              audioPlayer.delegate = delegate

              audioPlayer.play(atTime: time)
          } catch {
            continuation.finish(throwing: error)
          }
        }

        for try await didFinish in stream {
          return didFinish
        }
        throw CancellationError()
    }

AudioPlayerを作ったことがある方はご存知かと思いますが。再生中に問題があり停止した場合もしくは音声情報の最後まで行き停止する場合はこの didFinishPlaying や decodeErrorDidOccur が呼び出されます。

このDelegateをAsyncThrowingStreamで扱うことで非同期な音声再生ができます。

こうすることで上の例で保証できなかった音声の再生完了まで保証することができます。

参考

ここまで自分が考えたように書きましたが、この非同期処理は swift-composable-architectureのサンプルに利用されているコードをほぼ参考にまとめました。
下記のGitHubも合わせて確認ください

https://github.com/pointfreeco/swift-composable-architecture/blob/main/Examples/VoiceMemos/VoiceMemos/AudioPlayerClient/LiveAudioPlayerClient.swift

Discussion