この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
こんにちは。モバイルアプリサービス部の平屋です。
前回の記事「[watchOS 3] AVSpeechSynthesizer による合成音声再生について」に引き続き、watchOS 3 で新規追加された AVFoundation framework を使用した実装を紹介します。
本記事では、2 つの AVAudioPlayerNode
を AVAudioMixerNode
に接続して 2 つの音源をミックスする実装を紹介します。
AVAudioEngine
, AVAudioPlayerNode
, AVAudioMixerNode
などの基本事項については以下の記事で紹介していますので参考にしてみてください!
検証環境
- Xcode Version 8.1
- iPhone 7, iOS 10.1
- Apple Watch, watchOS 3.1
実装
さっそく実装を紹介していきます。
「iOS App with WatchKit App」テンプレートからプロジェクトを作成し、WatchKit Extension の InterfaceController
にコードを追加していきました。InterfaceController
のコードは以下のようになりました。
class InterfaceController: WKInterfaceController {
// ...
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// [1] AVAudioFile を準備する
do {
let leftAudioFilePath = Bundle.main.path(forResource: "left",
ofType: "mp3")
self.leftAudioFile = try AVAudioFile.init(forReading: URL.init(fileURLWithPath: leftAudioFilePath!))
let rightAudioFilePath = Bundle.main.path(forResource: "right",
ofType: "mp3")
self.rightAudioFile = try AVAudioFile.init(forReading: URL.init(fileURLWithPath: rightAudioFilePath!))
} catch {
fatalError("\(error)")
}
// [2] AVAudioPlayerNode の設定を行う
// AVAudioEngine に装着する
self.engine.attach(self.leftAudioPlayerNode)
self.engine.attach(self.rightAudioPlayerNode)
// pan (音像定位) を設定する
self.leftAudioPlayerNode.pan = -0.6
self.rightAudioPlayerNode.pan = 0.6
// [3] AVAudioPlayerNode を AVAudioMixerNode に繋ぐ
// leftAudioPlayerNode(Bus:0) -> mainMixerNode (InputBus:0) という順で繋ぐ
self.engine.connect(self.leftAudioPlayerNode,
to: self.engine.mainMixerNode,
fromBus: 0,
toBus: 0,
format: self.leftAudioFile!.processingFormat)
// rightAudioPlayerNode(Bus:0) -> mainMixerNode (InputBus:1) という順で繋ぐ
self.engine.connect(self.rightAudioPlayerNode,
to: self.engine.mainMixerNode,
fromBus: 0,
toBus: 1,
format: self.rightAudioFile!.processingFormat)
// AVAudioEngine の処理を開始する
do {
try self.engine.start()
} catch {
fatalError("\(error)")
}
}
@IBAction func playButtonDidTap() {
// [4] オーディオファイルを再生する
self.leftAudioPlayerNode.scheduleFile(self.leftAudioFile!,
at: nil,
completionHandler: nil)
self.rightAudioPlayerNode.scheduleFile(self.rightAudioFile!,
at: nil,
completionHandler: nil)
self.leftAudioPlayerNode.play()
self.rightAudioPlayerNode.play()
}
}
[1] AVAudioFile を準備する
今回は再生するファイルが 2 つあるので AVAudioFile
を 2 つ準備します。
[2] AVAudioPlayerNode の設定を行う
AVAudioFile
と同様に AVAudioPlayerNode
を 2 つ準備し、AVAudioEngine
に装着します。
また、各チャンネルのボリュームや pan (音像定位) の設定は AVAudioMixerNode
ではなく AVAudioMixerNode
に繋ぐ Node 側で行います。ここでは pan の設定だけ行なっています。
[3] AVAudioPlayerNode を AVAudioMixerNode に繋ぐ
2 つの AVAudioPlayerNode
を AVAudioEngine
が内部に持つ AVAudioMixerNode
に繋ぎます。
ここでは、片方の AVAudioPlayerNode
を AVAudioMixerNode
の InputBus 0 に、もう片方の AVAudioPlayerNode
を InputBus 1 に繋いでいます。
Bus の数
AVAudioPlayerNode
や AVAudioMixerNode
(AVAudioNode
のサブクラス) の Bus の数は numberOfInputs または numberOfOutputs プロパティを使用して取得できます。
print("self.leftAudioPlayerNode", "numberOfOutputs:", self.leftAudioPlayerNode.numberOfOutputs)
print("self.rightAudioPlayerNode", "numberOfOutputs:", self.rightAudioPlayerNode.numberOfOutputs)
print("self.engine.mainMixerNode", "numberOfInputs:", self.engine.mainMixerNode.numberOfInputs)
AVAudioPlayerNode
の OutputBus の数は 1、AVAudioMixerNode
の InputBus の数は 8 のようです。
self.leftAudioPlayerNode numberOfOutputs: 1
self.rightAudioPlayerNode numberOfOutputs: 1
self.engine.mainMixerNode numberOfInputs: 8
[4] オーディオファイルを再生する
2 つの AVAudioFile
の再生をスケジュールし、再生を実行します。
実行結果
サンプルアプリを Apple Watch で実行し、2 つの音源のミックス結果を確認することができました。Watch に Bluetooth イヤホンを繋げば、pan の設定の効果がわかりやすくなります。
さいごに
本記事では、2 つの AVAudioPlayerNode
を AVAudioMixerNode
に接続して 2 つの音源をミックスする実装を紹介しました。
今回のサンプルに AVAudioUnitEffect
のサブクラス (リバーブやイコライザーなど) を混ぜようと思ったのですが、watchOS では使用できないようです。次のメジャーバージョンで使うことができるようになるのでしょうか。