[watchOS 3] AVFoundation の AVAudioEngine と AVAudioPlayerNode を使用して音楽ファイルを再生する #wwdc
はじめに
こんにちは。モバイルアプリサービス部の平屋です。
前回の記事「新クラス「WKCrownSequencer」を使用して Apple Watch のデジタルクラウンの値を取得する」に引き続き、watchOS 3 の新機能を使用した実装を紹介します。
watchOS 3 の API の差分を解説している資料「watchOS 3.0 API Diffs」によると、watchOS 3 からは AVFoundation framework を使えるようになったようです。
本記事では、AVFoundation framework の AVAudioEngine と AVAudioPlayerNode を使用して音楽ファイルを再生する実装を紹介していきます。
本記事は Apple からベータ版として公開されているドキュメントを情報源としています。 そのため、正式版と異なる情報になる可能性があります。ご留意の上、お読みください。
iOS との比較
watchOS で AVAudioEngine と AVAudioPlayerNode を使用して音楽ファイルを再生する手順は iOS の場合と同様です。以下の記事もあわせてご覧ください。
実装内容
AVAudioEngine
を使用した実装では Node と呼ばれる部品を繋ぎあわせて、音を鳴らしたり、エフェクトをかけたりすることができます。Node には AVAudioPlayerNode
や AVAudioMixerNode
などがあります。
AVAudioEngine
は Node のグラフの管理、Node の接続のセットアップなどを担当します。
今回は「[iOS 8] AVFoundationのAVAudioPlayerNodeで音楽ファイルを再生してみる」と同様の実装を watchOS で試してみました。Node の構成は以下の通りです (Mixer Node や Output Node は AVAudioEngine が内部的に持つ Nodeです)
実装
それでは、音楽ファイルを再生する実装を紹介していきます。
「iOS App with WatchKit App」テンプレートからプロジェクトを作成し、WatchKit Extension の InterfaceController
にコードを追加していきました。InterfaceController
のコードは以下のようになりました。
import WatchKit import Foundation import AVFoundation class InterfaceController: WKInterfaceController { let engine = AVAudioEngine() let audioPlayerNode = AVAudioPlayerNode() var audioFile:AVAudioFile! override func awake(withContext context: AnyObject?) { super.awake(withContext: context) // [1] AVAudioFile を作成する do { let path = Bundle.main().pathForResource("beat", ofType: "aif") self.audioFile = try AVAudioFile.init(forReading: URL.init(fileURLWithPath: path!)) } catch { fatalError("\(error)") } // [2] AVAudioPlayerNode を AVAudioEngine に装着する self.engine.attach(self.audioPlayerNode) // [3] Node 同士を繋ぐ self.engine.connect(self.audioPlayerNode, to: self.engine.mainMixerNode, format: self.audioFile!.processingFormat) do { // [4] AVAudioEngine の処理を開始する try self.engine.start() } catch { fatalError("\(error)") } } @IBAction func buttonDidTap() { // [5] オーディオファイルの再生をスケジュールする self.audioPlayerNode.scheduleFile(self.audioFile!, at: nil, completionHandler: nil) // [6] 再生する self.audioPlayerNode.play() } }
上記コードのそれぞれについて解説していきます。
[1] AVAudioFile を作成する
まずは、AVAudioFile
を作成します。これはオーディオファイルを表すオブジェクトであり、ファイルのフォーマット情報などを保持しています。
[2] AVAudioPlayerNode を AVAudioEngine に装着する
attach(AVAudioNode)
メソッドを使用して、AVAudioPlayerNode
を、AVAudioEngine
に装着します。AVAudioPlayerNode
はファイルやバッファからのオーディオ再生を行うための Node です。
[3] Node 同士を繋ぐ
connect(AVAudioNode, to: AVAudioNode, format: AVAudioFormat?)
メソッドを使用して、[2]で作成した AVAudioPlayerNode
と AVAudioEngine
が内部に持つ AVAudioMixerNode
を接続します。
これでファイルを再生するための Node は全て接続完了になります。
[4] AVAudioEngine の処理を開始する
start()
メソッドを使用して AVAudioEngine
の処理を開始します。Node の接続等に問題がなければこれで準備完了です。
[5] オーディオファイルの再生をスケジュールする
AVAudioPlayerNode
の scheduleFile(AVAudioFile, at: AVAudioTime?, completionHandler: AVAudioNodeCompletionHandler? = nil)
メソッドを使用してオーディオファイルの再生をスケジュールします。
今回は引数 at
に nil
を指定しているので (また、他に再生がスケジュールされてもいないので)、「すぐに再生する」という処理がスケジュールされます。時間を指定することで指定時間後に再生することもできます。
[6] 再生する
AVAudioPlayerNode
の play()
メソッドを使用して音楽を再生します。
[5]でスケジュールされた処理が実行されます。
実行結果
アプリにバンドルした、長さ 2 秒程度の音楽ファイル (AIFF-C audio ファイル) を再生できました!
さいごに
AVFoundation の AVAudioEngine と AVAudioPlayerNode を使用して音楽ファイルを再生する実装をご紹介しました。
今後も引き続き、watchOS 3 の新機能を試していきます。