我正在尝试创建一个分析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()
}
}
}
我们将不胜感激。
答案 0 :(得分: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)
}
}
首次调用水龙头时,请记住添加新参数:
let fft = AKFFTTap(compressor, activate: true)
在取消分配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()函数。