しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

AVAudioEngineでmp3をエフェクト付きで再生する

AVAudioEngineというクラスを使って、mp3をエフェクト付きで再生してみました。

まずはプロジェクトにmp3ファイルを追加します。

f:id:llcc:20170926162616p:plain

追加したファイルがBuild Phasesにも入っているか確認します。
入っていない場合は+ボタンからmp3を追加します。

f:id:llcc:20170926162629p:plain

まずは普通にmp3を再生します。
下のようにAVAudioEngineとAVAudioPlayerNodeのインスタンスを作成、それらをconnectする事で再生します。

import UIKit
import AVFoundation

class ViewController: UIViewController {
    let engine = AVAudioEngine()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let player = AVAudioPlayerNode()
        if let path = Bundle.main.path(forResource: "music", ofType: "mp3") {
            let url = URL(fileURLWithPath: path)
            
            if let file = try? AVAudioFile(forReading: url) {
                engine.attach(player)
                engine.connect(player, to: engine.mainMixerNode, format: file.processingFormat)
                player.scheduleFile(file, at: nil, completionHandler: nil)
                try? engine.start()
                player.play()
            }
        }
    }
}

もしmp3を再生するだけならAVAudioPlayerを使う方が簡単そうでした。

www.cl9.info

続けてエフェクトをかけてみます。
ViewControllerを以下のように書き換えます。
下コードでは再生速度を2倍にしています。

class ViewController: UIViewController {
    let engine = AVAudioEngine()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let player = AVAudioPlayerNode()
        if let path = Bundle.main.path(forResource: "music", ofType: "mp3") {
            let url = URL(fileURLWithPath: path)
            
            if let file = try? AVAudioFile(forReading: url) {
                engine.attach(player)
                
                let unit = AVAudioUnitVarispeed()
                unit.rate = 2.0
                engine.attach(unit)
                engine.connect(player, to: unit, format: file.processingFormat)
                engine.connect(unit, to: engine.mainMixerNode, format: file.processingFormat)
                
                player.scheduleFile(file, at: nil, completionHandler: nil)
                try? engine.start()
                player.play()
            }
        }
    }
}

今回の変更点は下の箇所です。
AVAudioUnitVarispeedという再生スピード変更ノードを作成してそれをconnectしています。

engine.attach(player)

let unit = AVAudioUnitVarispeed()
unit.rate = 2.0
engine.attach(unit)
engine.connect(player, to: unit, format: file.processingFormat)
engine.connect(unit, to: engine.mainMixerNode, format: file.processingFormat)

続けて違うエフェクトも使ってみます。
次はAVAudioUnitTimePitchを試しました。

AVAudioUnitTimePitchは音程と速度を調整するユニットで、音の高低を変更したり音程そのままで速度アップしたりできます。

let unit = AVAudioUnitTimePitch()
unit.pitch = 1200.0
engine.attach(unit)
engine.connect(player, to: unit, format: file.processingFormat)
engine.connect(unit, to: engine.mainMixerNode, format: file.processingFormat)

ファイルへの書き込みは下のように行います。
installTapメソッドでbufferを取得できるので、それを順番にファイルに書き込んでいきます。

if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
        let settings = [
            AVFormatIDKey: kAudioFormatMPEG4AAC,
            AVSampleRateKey: 44100.0,
            AVNumberOfChannelsKey: 2 ] as [String : Any]
        if let file = try? AVAudioFile(forWriting: url.appendingPathComponent("test.aac"), settings: settings) {
            unit.installTap(onBus: 0, bufferSize: 4096, format: unit.inputFormat(forBus: 0), block: { buffer, when in
            try? file.write(from: buffer)
        })
    }
}

録音を停止したい場合はremoveTapメソッドを利用します。

unit.removeTap(onBus: 0)