Swift AVAudioEngine の基本
環境
- Xcode 7.3.1
- Swift 2.2
準備
AVFoundation をインポートする。
import AVFoundation
AVAudioEngine
AVAudioEngine は 接続された audio node(AVAudioNode) のグループを定義する。 Audio node を使用してオーディオシグナルを生成、それらの処理、オーディオ入力・出力を実行する。
Audioエンジンを使用する方法は、 Audio node をそれぞれ生成し、audioエンジンへアタッチする。
エンジンの利用手順
- エンジンの生成
- ノードの生成
- エンジンにノードをアタッチ
- ノード同士を接続
- エンジンをスタート
AVAudioNode
AVAudioNodeはオーディオの生成・処理・I/Oブロックの抽象クラス。 ノードは入力・出力バス(接続ポイントのようなもの)を持っている。ミキサーは複数の入力バスがあり、1つの出力バスがある。 全てのバスにはバスと関連してフォーマットがあり、AVAudioMixerNode と AVAudioOutputNode 以外はノードどうしを接続するときフォーマットが同じである必要がある。
ノードの種類
- Source (ex. Player, microphone)
- Process (ex. Mixer, effect)
- Destination (ex. Speaker)
Source ノードと Destination ノードが接続されている状態をActive Chainと言う。Active Chainでない状態をInactive Chainと言う。
Active Chain 例)
Source Node (Player) - connect -> Destination Node Source Node (Player) - connect -> Processing Node - connect -> Destination Node
出力ノード (Output Node)
エンジンには暗黙に出力ノード(Output node)と呼ばれる Desitination Node があり、出力ノードはオーディオデータを出力ハードウェアに提供する。
ミキサーノード (Mixer Node)
ミキサーノードはN個の入力をミックスし、1つの出力へ出力する。 エンジンには暗黙的にミキサーノードがあり、追加のミキサーノードを生成し、エンジンにアタッチすることができる。ミキサーのインプットには異なるオーディオフォーマットを使用することができる。
Input 1 -> | ---------------- | Input 2 -> | | Input 3 -> | AVAudioMixerNode |-> . -> | | Input N -> | ---------------- |
以下のようにSub-Mixingをすることも可能。
Input 1 -> | ------------------ | . -> | AVAudioMixerNode | -> | ---------------------------- | Input N -> | ------------------ | | AVAudioMixerNode (Main Mixer)|-> Input 1 -> | ------------------ | . -> | AVAudioMixerNode | -> | ---------------------------- | Input M -> | ------------------ |
プレイヤーノード(Player Node)
プレイヤーノード(AVPlayerNode)はファイル(AVAudioFile)またはバッファ(AVAudioBuffer)からデータをスケジュール(特定の時間に)し再生する。
エンジンとノードの生成・ノードのアタッチ
以下のようにして、エンジンとソースノード(Playerノード)を生成し、エンジンにノードをアタッチする。
// エンジンの生成 let engine = AVAudioEngine() // ソースノードの生成 let player = AVAudioPlayerNode() // エンジンにノードをアタッチ engine.attachNode(player)
プレイヤーの設定(ファイル)
ファイルからプレイヤーをスケジュールする例。
do { // オーディオファイルの取得 let audioFile = try AVAudioFile(forReading: url) // エンジンからメインミキサを取得 let mainMixer = engine.mainMixerNode // プレイヤーノードとメインミキサーを接続 engine.connect(player, to: mainMixer, format: audioFile.processingFormat) // プレイヤーをすぐに開始するようにスケジュール(atTimeをnil) player.scheduleFile(audioFile, atTime:nil, completionHandler:nil) } catch let error { print(error) }
プレイヤーの設定(バッファ)
バッファからプレイヤーをスケジュールする例。
// バッファを生成または取得 let buffer: AVAudioPCMBuffer = ... // メインミキサの取得 let mainMixer = engine.mainMixerNode // プレイヤーノードとメインミキサーを接続 engine.connect(player, to: mainMixer, format: buffer.format) // プレイヤのスケジュール player.scheduleBuffer(buffer, atTime:nil, completionHandler:nil)
エンジンの開始とプレイヤーの再生
do { // エンジンの開始 try engine.start() // プレイヤーの再生 player.start } catch let error { print(error) }
プレイヤーのスケジュールオプション
AVPlayer.scheduleFile(audioFile,atTime,options,completionHandler) のoptionsに設定する。
設定できる options
- Loops (ループ再生)
- Interrupts (再生中のオーディオをすぐに停止して、再生)
- InterruptsAtLoop (ループ再生が終了してから再生)
ループ再生オプションの例。
player.scheduleFile(audioFile, atTime:nil, Loops, completionHandler:nil)
再生の開始時間を設定する
10秒後に再生する例。
let tenSecInTheFuture = AVAudioTime.timeWithSampleTime(10 * buffer.format.sampleRate), atRate: buffer.format.sampleRate) player.scheduleFile(audioFile, atTime:tenSecInTheFuture, completionHandler:nil) player.play()
Node Tap
Node Tapを使用すればデータをプルすることができる。
Outputバス(インデックス0)にタップをインストールする例。
let format = mixier.outputFormatForBus(0) mixier.installTapOnBuss(0, bufferSize:4096, format:format) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in // Process buffer }
ノードを非接続にする
入力ノードを非接続にする例。
engine.disconnectNodeOutput(input)
オーディオの再生例
// インスタンス変数 // エンジンの生成 let audioEngine = AVAudioEngine() // Playerノードの生成 let player = AVAudioPlayerNode() func play() { if let url = NSBundle.mainBundle().URLForResource("sound-file", withExtension: "wav") { do { // オーディオファイルの取得 let audioFile = try AVAudioFile(forReading: url) // エンジンにノードをアタッチ audioEngine.attachNode(player) // メインミキサの取得 let mixer = audioEngine.mainMixerNode // Playerノードとメインミキサーを接続 audioEngine.connect(player, to: mixer, format: audioFile.processingFormat) // プレイヤのスケジュール player.scheduleFile(audioFile, atTime: nil) { print("complete") } // エンジンの開始 try audioEngine.start() // オーディオの再生 player.play() } catch let error { print(error) } } else { print("File not found") } }