ボクココ

少人数でのサービス開発運営に関するテックブログ

Swift での HTTPS での音声再生をさせる方法

ども、@kimihom です。

SwiftUI でアプリ開発をしていると、その情報はもう古かったりして正しく動作しないことが多々あるので、執筆した現時点で動作を確認できたコードとともに解説していこうと思う。

やりたいこと

  • API で取ってきた 音声URLを再生させる
  • 再生した録音を停止ボタンで停止、再開ボタンで再開させる
  • 音声の再生速度を早めたり遅めたりする
  • 15秒先に進んだり、15秒前に戻れたりする

Swift でのAudio実装を調べていると、AVPlayer を使う方法と AVAudioPlayer を使う方法がある。ぱっと見 AVAudioPlayer を使うのが正解に見えそうだが、どうやら AVAudioPlayer は Web経由でのHTTPでの再生が実装できない?ようで、AVPlayer を使うのが正解だった。

Apple Developer Documentation

iOS 公式ドキュメントでは標準の定義しか書かれておらず、実際に実装するには、Web上にあるいろいろな事例を見ながら組み合わせていく流れになるだろう・・。

実装

var audioPlayer: AVPlayer?

func startPlay() {
    let url = URL.init(string: "https://www.hello.com/sample.wav")
    audioPlayer = AVPlayer.init(playerItem: AVPlayerItem(url: url))

    // 再生が終わった際のイベント定義
    NotificationCenter.default
        .addObserver(self, selector: #selector(playerDidFinishPlaying),
                     name: .AVPlayerItemDidPlayToEndTime,
                     object: audioPlayer?.currentItem)

    // 秒数の表示
    Timer.scheduledTimer(withTimeInterval: 1/60, repeats: true) { timer in
        if let audioPlayer = self.audioPlayer,
           let currentItem = audioPlayer.currentItem,
           currentItem.status == .readyToPlay {
            let timeElapsed = CMTimeGetSeconds(audioPlayer.currentTime()) // 現在の再生時間の取得
            let timeDuration = currentItem.duration.seconds
            // 再生中のUI処理 秒数取得されるので / 60 で分数、% 60 で秒数
        }
    }

    audioPlayer?.play()
}

@objc func playerDidFinishPlaying(note: NSNotification) {
    // 再生が終わった際のイベント処理
}

func stop() {
    audioPlayer?.pause()
    // audioPlayer?.play() で再開
}

// 音声再生スピードの変更
func changeSpeed(speed: Float) {
    audioPlayer?.rate = speed
}

// 音声再生位置の移動
func changeLocation(seconds: Double) {
    guard let audioPlayer = self.audioPlayer else { return }

    let timeScale = CMTimeScale(NSEC_PER_SEC)
    let rhs = CMTime(seconds: seconds, preferredTimescale: timeScale)
    let time = CMTimeAdd(audioPlayer.currentTime(), rhs)
    audioPlayer.seek(to: time)
}

SwiftUI の部分を記載していないけども、ひとまずSwift側だけ記しておいた。単に音を流すだけであれば、AVPlayer を初期化して play するだけでシンプルに実装できる。

より細かく音声再生の制御をすることも AVPlayer ではサポートしており、今回はより音声再生を便利に使える他の機能の実装例も記した。

Timer.scheduledTimer を使って今の再生秒数を取得したり、全体の再生時間を取得できたりする。

audioPlayer.rate の数字を 0.25 ~ 2.0 くらいの間で操作すると、音声再生のスピードを変えることができる。

audioPlayer.seek で特定の秒数に遷移させることができる。

再生が終わった後の定義で謎に objc なコードが出てきてしまうのは、今後 iOS バージョンアップできっとなくなっていくことだろう。

終わりに

Swift5 での現状はこれでうまく動くということで記事としておいた。

objc 周りのコードを無くしたかったり、最終的にゲージの表示などもできるようにしたいけど、ひとまず音声再生プレイヤーとしてまともに動く動作にすることができた。

そうして私は開発を続けていくのである。