web-dev-qa-db-ja.com

Swift)でAVAudioEngineを使用してマイク入力をタップします

私は新しいAVAudioEngineに本当に興奮しています。オーディオユニットの優れたAPIラッパーのようです。残念ながら、ドキュメントは今のところ存在せず、単純なグラフを機能させるのに問題があります。

次の簡単なコードを使用してオーディオエンジングラフを設定すると、タップブロックが呼び出されることはありません。これは、Webに浮かんでいるサンプルコードの一部を模倣していますが、それらも機能しませんでした。

let inputNode = audioEngine.inputNode
var error: NSError?
let bus = 0

inputNode.installTapOnBus(bus, bufferSize: 2048, format: inputNode.inputFormatForBus(bus)) { 
    (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
    println("sfdljk")
}

audioEngine.prepare()
if audioEngine.startAndReturnError(&error) {
    println("started audio")
} else {
    if let engineStartError = error {
        println("error starting audio: \(engineStartError.localizedDescription)")
    }
}

私が探しているのは、分析用の生のpcmバッファーだけです。エフェクトや出力は必要ありません。 WWDCの講演「502AudioEngine in Practice」によると、このセットアップは機能するはずです。

ここで、入力ノードからデータをキャプチャする場合は、ノードタップをインストールできます。これについては説明しました。

しかし、この特定の例で興味深いのは、入力ノードだけで作業したい場合、たとえばマイクからデータをキャプチャして調べたり、リアルタイムで分析したり、ファイルに書き込んだりする場合は、直接インストールできます。入力ノードをタップします。

そして、タップは、データの入力ノードをプルし、それをバッファーに詰めて、それをアプリケーションに戻す作業を行います。

そのデータを取得したら、それを使って必要なことは何でもできます。

これが私が試したいくつかのリンクです:

  1. http://hondrouthoughts.blogspot.com/2014/09/avfoundation-audio-monitoring.html
  2. http://jamiebullock.com/post/89243252529/live-coding-audio-with-Swift-playgrounds (startAndReturnErrorのプレイグラウンドのSIGABRT)

編集:これは、ThorstenKarrerの提案に基づく実装です。残念ながら動作しません。

class AudioProcessor {
    let audioEngine = AVAudioEngine()

    init(){
        let inputNode = audioEngine.inputNode
        let bus = 0
        var error: NSError?

        inputNode.installTapOnBus(bus, bufferSize: 2048, format:inputNode.inputFormatForBus(bus)) {
            (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
                println("sfdljk")
        }

        audioEngine.prepare()
        audioEngine.startAndReturnError(nil)
        println("started audio")
    }
}
11
brodney

AVAudioEngineがスコープ外になり、ARCによってリリースされる場合があります(「気に入った場合は、保持する必要があります...」)。

次のコード(エンジンがivarに移動されたため、固執します)がタップを起動します。

class AppDelegate: NSObject, NSApplicationDelegate {

    let audioEngine  = AVAudioEngine()

    func applicationDidFinishLaunching(aNotification: NSNotification) {
        let inputNode = audioEngine.inputNode
        let bus = 0
        inputNode.installTapOnBus(bus, bufferSize: 2048, format: inputNode.inputFormatForBus(bus)) {
            (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
            println("sfdljk")
        }

        audioEngine.prepare()
        audioEngine.startAndReturnError(nil)
    }
}

(簡潔にするためにエラー処理を削除しました)

22
Thorsten Karrer

更新:マイク入力を録音し、実行時にいくつかのエフェクト(リバーブ、ディレイ、ディストーション)を適用し、これらすべてのエフェクトを出力ファイルに保存する完全な実例を実装しました。

var engine = AVAudioEngine()
var distortion = AVAudioUnitDistortion()
var reverb = AVAudioUnitReverb()
var audioBuffer = AVAudioPCMBuffer()
var outputFile = AVAudioFile()
var delay = AVAudioUnitDelay()

//オーディオエンジンを初期化します

func initializeAudioEngine() {

    engine.stop()
    engine.reset()
    engine = AVAudioEngine()

    isRealTime = true
    do {
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)

        let ioBufferDuration = 128.0 / 44100.0

        try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(ioBufferDuration)

    } catch {

        assertionFailure("AVAudioSession setup error: \(error)")
    }

    let fileUrl = URLFor("/NewRecording.caf")
    print(fileUrl)
    do {

        try outputFile = AVAudioFile(forWriting:  fileUrl!, settings: engine.mainMixerNode.outputFormatForBus(0).settings)
    }
    catch {

    }

    let input = engine.inputNode!
    let format = input.inputFormatForBus(0)

    //settings for reverb
    reverb.loadFactoryPreset(.MediumChamber)
    reverb.wetDryMix = 40 //0-100 range
    engine.attachNode(reverb)

    delay.delayTime = 0.2 // 0-2 range
    engine.attachNode(delay)

    //settings for distortion
    distortion.loadFactoryPreset(.DrumsBitBrush)
    distortion.wetDryMix = 20 //0-100 range
    engine.attachNode(distortion)


    engine.connect(input, to: reverb, format: format)
    engine.connect(reverb, to: distortion, format: format)
    engine.connect(distortion, to: delay, format: format)
    engine.connect(delay, to: engine.mainMixerNode, format: format)

    assert(engine.inputNode != nil)

    isReverbOn = false

    try! engine.start()
}

//これで録音機能:

func startRecording() {

    let mixer = engine.mainMixerNode
    let format = mixer.outputFormatForBus(0)

    mixer.installTapOnBus(0, bufferSize: 1024, format: format, block:
        { (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in

            print(NSString(string: "writing"))
            do{
                try self.outputFile.writeFromBuffer(buffer)
            }
            catch {
                print(NSString(string: "Write failed"));
            }
    })
}

func stopRecording() {

    engine.mainMixerNode.removeTapOnBus(0)
    engine.stop()
}

これがお役に立てば幸いです。ありがとう!

11
Junaid Mukhtar

上記の答えは私にはうまくいきませんでしたが、次の答えはうまくいきました。ミキサーノードにタップを取り付けています。

        mMixerNode?.installTapOnBus(0, bufferSize: 4096, format: mMixerNode?.outputFormatForBus(0),
    {
        (buffer: AVAudioPCMBuffer!, time:AVAudioTime!) -> Void in
            NSLog("tapped")

    }
    )
2
Pescolly

いいトピック

こんにちはブロドニー

あなたのトピックで私は私の解決策を見つけます。ここに同様のトピックがあります AVAudioRecorderでAVAudioPCMBufferを生成します

講義を参照Wwdc2014 502-AVAudioEngine inPracticeキャプチャマイク=> 20分でタップコードを使用してバッファを作成=> 21.50で

ここにSwift 3コード

@IBAction func button01Pressed(_ sender: Any) {

    let inputNode = audioEngine.inputNode
    let bus = 0
    inputNode?.installTap(onBus: bus, bufferSize: 2048, format: inputNode?.inputFormat(forBus: bus)) {
        (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in

            var theLength = Int(buffer.frameLength)
            print("theLength = \(theLength)")

            var samplesAsDoubles:[Double] = []
            for i in 0 ..< Int(buffer.frameLength)
            {
                var theSample = Double((buffer.floatChannelData?.pointee[i])!)
                samplesAsDoubles.append( theSample )
            }

            print("samplesAsDoubles.count = \(samplesAsDoubles.count)")

    }

    audioEngine.prepare()
    try! audioEngine.start()

}

オーディオを停止するには

func stopAudio()
    {

        let inputNode = audioEngine.inputNode
        let bus = 0
        inputNode?.removeTap(onBus: bus)
        self.audioEngine.stop()

    }
2
myUser