重新启动时,AudioKit的FFTTap在物理设备上提供零

时间:2018-09-19 18:21:07

标签: audiokit

我正在尝试创建一个分析FFT流并将原始流与后续流进行比较的应用。为此,我需要能够关闭并重新启动流。

在模拟器上可以正常工作。在物理设备(iPad 12.9第一代)上,只有第一个会话会提供有效数据,而从第二个会话开始,我只会得到零。

我创建了一个演示该问题的示例Xcode项目。可以从这里下载: https://drive.google.com/file/d/1rR2zWPREwXbXfZFocubwMgQ8SyFrXt2V/view?usp=sharing

这是ViewController的代码:

import UIKit
import AudioKit

class ViewController: UIViewController, UIApplicationDelegate {
    @IBOutlet weak var toggleLearn: UIButton!

    var listenTimer : Timer?

    let mic = AKMicrophone()

    var compressor = AKCompressor()

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func listen() {

        compressor.start()
        compressor = AKCompressor(mic)

        if let inputs = AudioKit.inputDevices {
            do {
                try AudioKit.setInputDevice(inputs[0])
            } catch {
                print ("Could not set audio inputs: \(error)")
            }
            do {
                try mic.setDevice(inputs[0])
            } catch {
                print ("Could not set the audio input device to the AKMic: \(error)")
            }
        }

        AudioKit.output = AKBooster(compressor, gain: 0)

        if !AudioKit.engine.isRunning {
            do {
                try AudioKit.start()
            } catch {
                print ("Could not start AudioKit: \(error)")
            }
        }

        compressor.threshold = 3
        compressor.headRoom = 3
        compressor.masterGain = 1
        compressor.attackDuration = 0.001
        compressor.releaseDuration = 0.01

        mic.start()

        let fft = AKFFTTap(compressor)

        if listenTimer == nil {
            listenTimer = Timer.scheduledTimer(withTimeInterval: 0.08, repeats: true, block: { _ in

                let i = fft.fftData

                print (i[100...200])

            })
        }
    }

    @IBAction func learnPage(_ sender: UIButton) {
        if AudioKit.engine.isRunning {
            if listenTimer != nil {
                listenTimer?.invalidate()
                listenTimer = nil
            }
            mic.stop()
            compressor.stop()
            try! AudioKit.stop()

            toggleLearn.setTitle("Listen", for: .normal)
        } else {
            toggleLearn.setTitle("Stop", for: .normal)
            listen()
        }
    }
}

我们将不胜感激。

1 个答案:

答案 0 :(得分:1)

我确实发现了一个非常难看的骇客,暂时是:

  1. 编辑AKFFTTap.swift文件的公共初始化,并向其中添加另一个参数,该参数将删除水龙头:

    public init(_ input: AKNode, activate: Bool) {
        super.init()
    
        if activate == true {
    
            fft = EZAudioFFT(maximumBufferSize: vDSP_Length(bufferSize), sampleRate: Float(AKSettings.sampleRate), delegate: self)
            input.avAudioNode.installTap(onBus: 0, bufferSize: bufferSize, format: AudioKit.format) { [weak self] (buffer, time) -> Void in
                guard let strongSelf = self else { return }
                buffer.frameLength = strongSelf.bufferSize
                let offset = Int(buffer.frameCapacity - buffer.frameLength)
                let tail = buffer.floatChannelData?[0]
                strongSelf.fft!.computeFFT(withBuffer: &tail![offset],
                                       withBufferSize: strongSelf.bufferSize)
            }
    
        } else {
    
            input.avAudioNode.removeTap(onBus: 0)
        }
    }
    
  2. 首次调用水龙头时,请记住添加新参数:

    let fft = AKFFTTap(compressor, activate: true)
    
  3. 在取消分配AudioKit资源的代码中,使用参数删除抽头:

    func stopListening() {
        // The first block nullifies the timer from my example above. Not necessary unless you use the timer.
        if listenTimer != nil {
            listenTimer?.invalidate()
            listenTimer = nil
        }
        if AudioKit.engine.isRunning {
            mic.stop()
            compressor.stop()
    
            let _ = AKFFTTap(compressor, activate: false)
        }
    }
    

此黑客弄乱了AudioKit建立的链,如果您停止并重新启动AudioKit,您将获得非常奇怪的行为(在我的情况下-我的虚拟输出变得非常虚假)。但是,如果在不停止AudioKit的情况下重复所有设置,则结果保持不变。这就是说,就我最初的问题中的代码而言,请调用listen()函数。