iOS
Swift
Swift2.0
Swift2.2

AVAudioEngineを使って音声にリアルタイムにエフェクトをかけて保存する方法(サンプルあり)

More than 1 year has passed since last update.


はじめに

録音している音声にリアルタイムでエフェクトをかけて、その音声データをサーバにアップロードしたかったので、AVAudioEngineを使ってできないものかな〜と思ってサンプルを作ってみた時のメモです。

最後に簡単なサンプルのURLを貼っておりますので興味のある方は触ってみてください〜


AVAudioEngineとは?

AppleのAVAudioEngine Class Referenceには以下のように記述されています。


The AVAudioEngine class defines a group of connected AVAudioNode objects, known as audio nodes. You use audio nodes to generate audio signals, process them, and perform audio input and output.


すなわち、複数のnodeで構成されておりそれらを組み合わせて使うことでオーディオ信号の生成や処理、音声の入出力が行えるクラスと言えそうです。

※AVAudioEngine はiOS8から追加された(iOS8以降で使える)クラスです。


AVAudioEngineの基本的な使い方

これはとっても簡単です。

AVAudioEngineのインスタンスをつくってあげてNodeをつなげるだけで音声の入力と出力が行われます。


audioEngineManager.swift

let audioEngine = AVAudioEngine()

let input = audioEngine.inputNode
let mixer = audioEngine.mainMixerNode
audioEngine.connect(input!, to: mixer, format: input!.inputFormatForBus(0))
try! audioEngine.start()


AVAudioEngineを使って録音する

音声の録音には - installTapOnBus:bufferSize:format:block: という関数を使います。

audioEngine.start()を呼び出す前に以下のようにinstallTapOnBusを読んであげると音声を録音している間、逐一コールバックが呼ばれるのでその中で保存したい場所に書き込んであげればOKです!


audioEngineManager.swift

outputFile = try! AVAudioFile(forWriting: recFileURL, settings: recSettings)

let input = audioEngine.inputNode
input!.volume = 0
input!.installTapOnBus(0, bufferSize: 4096, format: input?.inputFormatForBus(0)) { (buffer, when) in
try! self.outputFile.writeFromBuffer(buffer)
}



エフェクトの種類

エフェクトは大きく分けると以下の2種類があります。

AVAudioUnitEffect

AVAudioUnitTimeEffect

この2つの大きな違いはリアルタイムで処理をできるかどうかです。

AVAudioUnitEffectクラス内のエフェクトはリアルタイムで処理ができるので入力した音声にエフェクトをかけてそのまますぐにスピーカーで出力するなどといったことが可能です。

一方で、AVAudioUnitTimeEffectクラス内のエフェクトは、


"processes audio in non-realtime"


なので入力した音声にリアルタイムでエフェクトをかけることが不可能です。

(AVAudioUnit Class Referenceにはこのことがはっきり書いてあるのですが、私はちゃんと読まなかったのでここの違いに気付かずにハマってしまいました、、、)


AVAudioUnitEffect

AVAudioUnitEffectには以下の4つのクラスが用意されています。

AVAudioUnitDelay

AVAudioUnitDistortion

AVAudioUnitEQ

AVAudioUnitReverb

この4つのエフェクトはリアルタイムで処理を行うことができます。

特に、AVAudioUnitDistortionというのが面白くて、これはDistortion(歪み)のエフェクトクラスなのですがloadFactoryPresetというメソッドの引数にたくさんのパラメーターが用意されており、簡単にロボット風の音声などに変換できたりします。

使い方はNodeを作ってつなげるだけです。


audioEngineManager.swift

let input = audioEngine.inputNode

let mixer = audioEngine.mainMixerNode

// Distortion
let distortion = AVAudioUnitDistortion()
distortion.loadFactoryPreset(.SpeechGoldenPi)
audioEngine.attachNode(distortion)

audioEngine.connect(input!, to: distortion, format: input!.inputFormatForBus(0))
audioEngine.connect(distortion, to: mixer, format: input!.inputFormatForBus(0))
try! audioEngine.start()



AVAudioUnitTimeEffect

AVAudioUnitTimeEffectには以下の2つのクラスが用意されています。

AVAudioUnitTimePitch

AVAudioUnitVarispeed

この2つに関してはリアルタイムで音声の変換を行うことができません。

使い方(使いどころ)としては、一度録音した音声をplayerNodeを使って再生する際にエフェクトをかけるというのが一番現実的な使い方だと思っています。


まとめ

AVAudioEngineは手軽に実装できて便利ではありますが、手の込んだことやリッチな機能を実装しようとするとAUGraphなどを使う必要があるなーと思いました。

iOS10で追加される音声認識APIのAppleが公開しているサンプルでもAVAudioEngineが使われていましたし、AVAudioEngineは今後に期待です:grinning:

音声にエフェクトをかけて録音・保存を行い再生するまでの簡単なサンプルプログラムはgitにありますので興味のある方は覗いてみてください。