iOS8からAVAudioEngineの登場により、CoreAudioがもっと使いやすくなりました。
今回はSwiftとPlaygroundを用いて音で遊んでみたいと思います。
最初に使用するクラスについて軽く説明です。
-
AVAudioEngine
AVAudioNodeを管理するクラス
AVAudioEngineに対してAVAudioNodeをattachすることで入力にエフェクトなどをかけることができます。 -
AVAudioNode
音の生成、処理、入出力のための抽象クラス
エフェクトなどはAVAudioUnitEffectクラスを使用しますが、これらもAVAudioNodeのサブクラスです。
基本的には上記の2つクラスが元となっているようです。
では、簡単にマイク入力にエフェクトをかけてみましょう。
まず適当なPlaygroundを作成します。
このときPlatformは
Mac OS
にしてください。
iOSにしてしまうと入力nodeの生成ができません。
ではとりあえず下のコードを一気に貼り付けてしまってください。
これでマイク入力にdelayとreverbがかかります。
import AVFoundation
import XCPlayground
XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)
var engine = AVAudioEngine()
//effectNodeの用意
var delay = AVAudioUnitDelay()
var reverb = AVAudioUnitReverb()
var input = engine.inputNode
var output = engine.outputNode
var format = input.inputFormatForBus(0)
var error:NSError?
//delayの設定
delay.delayTime = 1.5
delay.feedback = 20
//reverbの設定
reverb.loadFactoryPreset(.LargeRoom2)
reverb.wetDryMix = 80
//engineにdelayとreverbを追加
engine.attachNode(delay)
engine.attachNode(reverb)
//effectをつなぐ順番を指定 入力 -> delay -> reverb -> 出力
engine.connect(input, to: delay, format: format)
engine.connect(delay, to: reverb, format: format)
engine.connect(reverb, to: output, format: format)
// engineを実行
engine.startAndReturnError(&error)
比較的簡単そうに見えますよね。
特にエフェクトを繋いでいく部分はギターのエフェクターを繋いでいくような感覚ですね。
マイクの入力はAVAudioEngine生成時に確保してくれるようです。
次は任意のオーディオファイルを再生してみます。
これもPlaygroundでやりたかったのですが、Playgroundでファイルの読み込みがうまくできなかったのでCocoaAppliationで実装します。
CocoaApplicationテンプレートからプロジェクトを作ります。
import Cocoa
import AVFoundation
class ViewController: NSViewController {
var engine = AVAudioEngine()
//playerNodeの準備
var player = AVAudioPlayerNode()
//revebNodeの準備
var reverb = AVAudioUnitReverb()
override func viewDidLoad() {
super.viewDidLoad()
self.playAudio()
}
func playAudio() {
var fileUrl = NSURL(fileURLWithPath: "/Users/Muukii/Desktop/sample.m4a")
// オーディオファイルの読み込み
var audioFile = AVAudioFile(forReading: fileUrl!, error: nil)
if let file = audioFile {
// reverbの設定
reverb.loadFactoryPreset(.LargeHall2)
// AudioEngineにnodeを設定
engine.attachNode(player)
engine.attachNode(reverb)
engine.connect(player, to: reverb, format: file.processingFormat)
engine.connect(reverb, to: engine.outputNode, format: file.processingFormat)
engine.startAndReturnError(nil)
// playerにオーディオファイルを設定
self.player.scheduleFile(file, atTime: nil, completionHandler: { () -> Void in
// 再生が終了すると呼ばれる
println("Completion")
})
// 再生開始
self.player.play()
}
}
}
これで実行すると指定したファイルが再生されます。
ファイルフォーマットはwav,mp3,aacあたりなら大体再生できそうです。
簡単なサンプルはこれで以上となります。
今回エフェクトはDelayとReverbを使用しましたが、他には
- AVAudioUnitDistortion
- AVAudioUnitEQ
など用意されています。全部で4種類なのでまだちょっと少ないですね。
AVAudioUnitEffectのサブクラスを作ることで独自のエフェクトは作れそうですが、
結局のところ内部ではCoreAudioが使用されているようで、
エフェクト処理はC,C++で書く必要がありそうです。
ただ、今までは音にエフェクトをかけるにはCoreAudioを直接使う必要がありましたが、
AVAudioEngineの登場によりObjective-C,Swiftで簡単に記述できるようになったのは素晴らしいと思います。
今後のAPI追加にも期待したいと思います。