12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AVAudioEngineでiOSボイスチェンジャーをつくる

Last updated at Posted at 2020-12-11

ボイチェンみたいに音声を高くしたり低くしたり、速くしたり遅くしたりできます。
変換した音声をファイルに書き込むこともできます。

#手順

音声ファイルから読み込んだ音声を変換します。

###1.AVAudioEngine を初期化

var audioEngine = AVAudioEngine()

###2.AVAudioEngine に音声ファイルを流し込む PlayerNode を用意

let playerNode = AVAudioPlayerNode()
let audioFile = try! AVAudioFile(forReading: audioFileURL) // URLからファイル読み込み
playerNode.scheduleFile(audioFile, at:nil) // playerNodeにファイルをセット

###3.音声を変換する AVAudioUnitTimeEffect を用意

let audioUnitTimePitch = AVAudioUnitTimePitch()
audioUnitTimePitch.pitch = 1200 // 1200で1オクターブ高くなる。可能な値は -2400 ~ 2400

###4.AudioEngine に PlayerNodeとAudioUnitTimeEffect を取り付けて接続

|==================AudioEngine================|
 PlayerNodeAudioUnitTimePitchOutputNode
|============================================|

// 取り付け
audioEngine.attach(playerNode)
audioEngine.attach(audioUnitTimePitch)

// 接続
audioEngine.connect(playerNode, to: audioUnitTimePitch, format: audioFile.processingFormat)
audioEngine.connect(audioUnitTimePitch, to: audioEngine.outputNode, format: audioFile.processingFormat)

###5.音声を流し込む

変換された音声が再生されます。
匿名インタビューみたいな声になります。

try! audioEngine.start()
playerNode.play()

###6.変換した音声をファイルとしてエクスポート

AudioUnitTimePitch にタップ(蛇口)というのを取り付けて、音声バッファを取り出してファイルに書き込みます。
|==================AudioEngine================|
 PlayerNode → AudioUnitTimePitch → OutputNode
                ↓ Tap(蛇口)
|============================================|

Tap をつけるのは AudioUnitTimePitch ですが、その後の OutputNode も一応つけておかないと動かないようです。

// 書き込み先ファイルをつくる
let outputFile = try! AVAudioFile(forWriting: outputURL, settings: audioUnitTimePitch.outputFormat(forBus: 0).settings)

// タップ(蛇口)をつけてバッファを取り出してファイルに書き込み
audioUnitTimePitch.installTap(onBus: 0, bufferSize: AVAudioFrameCount(audioUnitTimePitch.outputFormat(forBus: 0).sampleRate), format: audioUnitTimePitch.outputFormat(forBus: 0)) { (buffer, when) in
    do {
        try outputFile.write(from: buffer)
       } catch let error {
        print(error)
       }
}

書き込みを終了するにはタップ(蛇口)を取り除く必要があります。
PlayerNode が再生し終わった時に書き込み終了したいので、上記手順2.の PlayerNode に音声ファイルをセットするときに、完了した時に呼ばれる完了ハンドラーを仕込んでおきます。録音するときは以下のよう書き換えます。

playerNode.scheduleFile(audioFile, at:nil, completionCallbackType: .dataPlayedBack) { [self] _ in // 再生完了時に呼ばれる
    audioUnitTimePitch.removeTap(onBus: 0) // これで書き込み終了の合図を送る。書き込みには音声の長さによって数秒かかります。
    }

###エフェクトの種類

それぞれ使い方は上記のピッチ変更の例と同じです(AudioEngine にアタッチして、接続)。
ディレイとディストーションとリバーブとイコイライザーはマイクからのリアルタイム音声に適用できます。
AVAudioUnitTimePitch と AVAudioUnitVarispeed はファイル音声しか無理みたいです。

AVAudioUnitTimePitch

プロパティ 効果
overlap 入力オーディオ信号のセグメント間のオーバーラップの量 Float
pitch 高さ Float
rate 速さ Float

AVAudioUnitVarispeed

プロパティ 効果
rate 高さの変化を伴った速さの変化 Float

AVAudioUnitEQ

プロパティ 効果
bands 周波数帯 [AVAudioUnitEQFilterParameters]
globalGain 増幅 Float

AVAudioUnitDistortion

プロパティ 効果
loadFactoryPreset プリセットのディストーション(エイリアンの声とか) AVAudioUnitDistortionPreset
preGain 増幅 Float
wetDryMix 歪み具合 Float

AVAudioUnitDelay

プロパティ 効果
delayTime 遅延時間 Float
feedback フィードバック Float
lowPassCutoff これ以上の周波数をカット Float
wetDryMix ウェット信号とドライ信号のブレンド具合 Float

AVAudioUnitReverb

プロパティ 効果
loadFactoryPreset プリセットの音の広がり(大きなホールなど) AVAudioUnitReverbPreset
wetDryMix ウェット信号とドライ信号のブレンド具合 Float

🐣


フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLを使ったアプリを作っています。
機械学習関連の情報を発信しています。

Twitter
Medium

12
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?