🍎

AVAudioEngineを触ってみる

2020/10/06に公開

iOSのオーディオプログラミングをできるようになりたい!と思ったので、AVAudioEngineを触ってみました。

まず、Xcodeでプロジェクトを作って、出来上がったプロジェクトのViewController.swiftの中を次のようにしてみました。

import UIKit
import AVFoundation

class ViewController: UIViewController {

    private var audioEngine: AVAudioEngine!

    override func viewDidLoad() {
        super.viewDidLoad()
        audioEngine = AVAudioEngine()
        // audioEngine.mainMixerNode.outputVolume = 10.0
        audioEngine.connect(audioEngine.inputNode, to: audioEngine.mainMixerNode, format:audioEngine.inputNode.outputFormat(forBus: 0))
        do {
            try audioEngine.start()
        } catch {
            print("cannot start audioEngine")
            print(error)
        }
    }
}

上記のコードは、端末の音声入力をそのまま音声出力へ繋いでいます。このコードを実行すると、マイクへ入力した音声がそのまま出力されるはずです。
シミュレーターで実行するにしろ端末で実行するにしろ、イヤホンを付けるのを推奨します。 iPhoneのマイク・スピーカーは優秀なのでハウリングしにくいようになっていますが、万が一の事を考えて。
あと、純粋にスピーカーからの出力を聴くのはわかりづらい。

念のため端末の音声出力のボリュームを小さくしてから実行してください。イヤホンを付けている時に突然大きな音が出るとびっくりしちゃいますから。

audioEngine.mainMixerNode.outputVolume = 10をコメントアウトしています。これはデフォルトのままだとマイク入力の音量が小さくてイヤホンをしていても本当に動作しているかがわかりづらいときに出力音量を増幅するための指定です。必要に応じて使ってみてください。

音声を加工してみる

マイク入力をそのまま出力できることがわかったら、次はこの入力音声を加工してみましょう。
AVAudioUnitDelayを使うと、カラオケのエコーみたいな感じになります。

import UIKit
import AVFoundation

class ViewController: UIViewController {

    private var audioEngine: AVAudioEngine!

    override func viewDidLoad() {
        super.viewDidLoad()
        audioEngine = AVAudioEngine()

        let delay = AVAudioUnitDelay()
        delay.delayTime = 0.2
        audioEngine.attach(delay)

        let format = audioEngine.inputNode.outputFormat(forBus: 0)
        audioEngine.connect(audioEngine.inputNode, to: delay, format: format)
        audioEngine.connect(delay, to: audioEngine.mainMixerNode, format: format)

        do {
            try audioEngine.start()
        } catch {
            print("cannot start audioEngine")
            print(error)
        }
    }
}

お〜れ〜はジャ○アー○♪ って歌ってみたくなります。

次に、distortionをかけてみます。ギターの音声を入力したりするといい感じになるかもしれません。

import UIKit
import AVFoundation

class ViewController: UIViewController {

    private var audioEngine: AVAudioEngine!

    override func viewDidLoad() {
        super.viewDidLoad()
        audioEngine = AVAudioEngine()
        
        let distortion = AVAudioUnitDistortion()
        audioEngine.attach(distortion)

        let format = audioEngine.inputNode.outputFormat(forBus: 0)
        audioEngine.connect(audioEngine.inputNode, to: distortion, format: format)
        audioEngine.connect(distortion, to: audioEngine.mainMixerNode, format: format)

        do {
            try audioEngine.start()
        } catch {
            print("cannot start audioEngine")
            print(error)
        }
    }
}

distortionエフェクトは音量に注意して使いましょう。

エフェクトとして使えるクラスの一覧を見ていたら、AVAudioUnitTimePitchというのを見つけました。これを使えば声を高くしたり低くしたりするエフェクトが作れるのでは…?
と思ってコードを書いてみました。

import UIKit
import AVFoundation

class ViewController: UIViewController {

    private var audioEngine: AVAudioEngine!

    override func viewDidLoad() {
        super.viewDidLoad()
        audioEngine = AVAudioEngine()
        
        let pitch = AVAudioUnitTimePitch()
        pitch.pitch = 1200
        audioEngine.attach(pitch)

        let format = audioEngine.inputNode.outputFormat(forBus: 0)
        audioEngine.connect(audioEngine.inputNode, to: pitch, format: format)
        audioEngine.connect(pitch, to: audioEngine.mainMixerNode, format: format)
        
        do {
            try audioEngine.start()
        } catch {
            print("cannot start audioEngine")
            print(error)
        }
    }
}

ですが、結論からいうとこれは動きません。 コンパイルは可能ですが、実行時エラーでクラッシュします。それはAVAudioUnitTimePitchAVAudioUnitTimeEffectを継承しているからで、ドキュメントを見てみると

A class that processes audio in non-real time.

と書いてあります。つまり、マイク入力のようなリアルタイム音声の処理には使用できないということです。残念!

Discussion