Ios 在Swift中使用AVAudioEngine轻触麦克风输入

Ios 在Swift中使用AVAudioEngine轻触麦克风输入,ios,swift,avfoundation,core-audio,ios8.1,Ios,Swift,Avfoundation,Core Audio,Ios8.1,我对新的AVAudioEngine非常兴奋。它似乎是围绕音频单元的一个很好的API包装器。不幸的是,文档到目前为止还不存在,我在使用一个简单的图表时遇到了问题 使用以下简单代码设置音频引擎图,将永远不会调用tap块。它模仿了一些在web上浮动的示例代码,尽管这些代码也不起作用 let inputNode = audioEngine.inputNode var error: NSError? let bus = 0 inputNode.installTapOnBus(bus, buffe

我对新的AVAudioEngine非常兴奋。它似乎是围绕音频单元的一个很好的API包装器。不幸的是,文档到目前为止还不存在,我在使用一个简单的图表时遇到了问题

使用以下简单代码设置音频引擎图,将永远不会调用tap块。它模仿了一些在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)")
    }
}
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")
    }
}
我要找的是原始pcm缓冲区,用于分析。我不需要任何效果或输出。根据WWDC讲座“502音频引擎在实践中”,此设置应该可以工作

现在,如果您想从输入节点捕获数据,您可以安装一个节点点击,我们已经讨论过了

但这个特殊示例的有趣之处在于,如果我只想使用输入节点,比如说从麦克风中捕获数据,并可能对其进行检查、实时分析或将其写入文件,我可以直接在输入节点上安装一个点击

轻触将完成拉入输入节点以获取数据的工作,将其填充到缓冲区中,然后将其返回给应用程序

一旦你有了这些数据,你就可以用它做任何你需要做的事情

以下是我尝试过的一些链接:

  • (开始和结束时在操场上的SIGABRT错误)
  • 编辑:这是根据Thorsten Karrer的建议实施的。不幸的是,它不起作用

    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)")
        }
    }
    
    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")
        }
    }
    

    可能的情况是,您的AVAudioEngine超出了范围,由ARC发布(“如果您喜欢它,那么您应该将retain放在它上面…”)

    以下代码(发动机移动到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)
        }
    }
    

    (为了简洁起见,我删除了错误处理)

    上面的答案对我不起作用,但下面的答案确实起作用。我正在混音器节点上安装一个水龙头

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

    更新:我实现了一个完整的录制麦克风输入的工作示例,在运行时应用一些效果(混响、延迟、失真),并将所有这些效果保存到输出文件中

    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()
    }
    
    我希望这对你有帮助。谢谢

    好话题

    嗨,布罗德尼

    在你的主题中,我找到了我的解决方案。这里有类似的话题

    参见Wwdc 2014 502-AVAudioEngine在实践中的讲座在20分钟内捕获麦克风=>在21.50中使用tap代码创建缓冲区=>

    这是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()
    
    }
    

    我尝试了这一点,并用使用的代码编辑了我的问题。你答案中的密码对你有用吗?朱普,对我有用。我还没有在操场上测试过它,但在从Xcode编译运行时它可以工作。水龙头被连续不断地调用。遗憾的是,我不得不承认它毕竟是一个未修复的对象。我的AudioProcessor类是在一个方法中实例化的,而不是作为类属性实例化的。您为什么选择带有“notification”参数的appDelegate方法?默认/事实上的选项是didFinishLaunchingWithOptions:原因是在我编写答案时(2014年),Xcode会将通知参数作为默认参数提供给我。对于手头的问题,这并不重要。这是可行的,但是有没有一种方法可以让它工作,而不让演讲者实时产生效果?@user3344977我目前正在处理一个类似的模块,我还没有成功地实现它。i、 e.带效果的音频文件的脱机渲染。以下是我关注的帖子:[离线渲染]()。希望它有帮助这些值来自哪里?:让ioBufferDuration=128.0/44100.0有用的代码运行。现在我有了关于这个AudioEngine如何在AudioUnits之间的所有连接中工作的代码。它工作得很好。但是,如果添加AVAudioUnitTimePitch节点,它会崩溃:(我也提出了类似的解决方案。事情只会运行一次,然后在5分钟内不会再次运行。当你的应用程序即将终止时,你需要移除轻触并停止音频引擎(我在调试器中执行此操作)。我还将AudioSession设置为非活动状态