问题描述
我正在使用iOS中的AVAudioEngine从麦克风获取音频,并使用输入节点及其功能installTap将其写入缓冲区。
在installTap函数的tapBlock内部,应该在该位置读取和/或操作PCM缓冲区,我需要调用C库函数,此函数处理PCM缓冲区数据,计算音频指纹,函数还需要读取一个文件,该文件是预先计算出的音频指纹的数据库,以查找可能的匹配项。
问题是,显然(如果我错了,请纠正我),您不能在此块内进行任何文件I / O调用,因为此代码正在另一个线程中运行,并且我将传递给C的文件指针事物的一面总是null或垃圾,这不会在此函数之外发生,(在事物的主线程中)指针起作用,并且C可以读取数据库文件。
如何在主线程中操纵PCM缓冲区,以便可以进行文件I / O调用并能够计算出C端所需的匹配项?
我在做什么错了?
还有其他选择吗?谢谢。
application.conf
import Foundation
import AVFoundation
let audioEngine = AVAudioEngine()
class AudioEngineTest: NSObject {
func setupAudioEngine() {
let input = audioEngine.inputNode
let inputFormat = input.outputFormat(forBus: 0)
let inputNode = audioEngine.inputNode;
//Convert received buffer to required format
let recordingFormat = AVAudioFormat(commonFormat: .pcmFormatInt16,sampleRate: Double(44100),channels: 2,interleaved: false)
let formatConverter = AVAudioConverter(from:inputFormat,to: recordingFormat!)
let pcmBuffer = AVAudioPCMBuffer(pcmFormat: recordingFormat!,frameCapacity: AVAudioFrameCount(recordingFormat!.sampleRate * 4.0))
var error: NSError? = nil
inputNode.installTap(onBus: 0,bufferSize: AVAudioFrameCount(2048),format: inputFormat)
{
(buffer,time) in
let inputBlock: AVAudioConverterInputBlock = { inNumPackets,outStatus in
outStatus.pointee = AVAudioConverterInputStatus.haveData
return buffer
}
formatConverter?.convert(to: pcmBuffer!,error: &error,withInputFrom: inputBlock)
if error != nil {
print(error!.localizedDescription)
}
//Calling the function from the C library,passing it the buffer and the pointer to the db file: dbFilePathForC an UnsafeMutablePointer<Int8>
creatingFingerprintAndLookingForMatch(pcmbuffer,dbFilePathForC)
//In this scope,the pointer dbFilePathFoC is either null or garbage,so the C side of things cannot read the database file,outside of this scope,the same pointer works and C can read the file,but I cannot read the PCM buffer because it only exists inside this scope of this closure of installTap,called the tapBlock
}
try! audioEngine.start()
}
}
解决方法
在任何线程上都可以进行I / O调用。问题出在您将C字符串转换为UnsafeMutablePointer<Int8>
(有充分的理由称其为不安全)。您在堆栈上执行此操作,使“变量”消失,一旦您的PCM Audio非主线程完成,该变量将消失。因此,您最终会得到一个悬空的指针,指向某些随机存储器。我怀疑您似乎在主线程上没有遇到相同的问题,因为它在应用程序的整个生命周期中始终存在,并且不太可能在其堆栈上碰到悬挂的指针(但仍然绝对可能)。
解决方案是让您的UnsafeMutablePointer<Int8>
(yossan的礼貌)是这样的:
func makeCString(from str: String) -> UnsafeMutablePointer<Int8> {
let count = str.utf8CString.count
let result: UnsafeMutableBufferPointer<Int8> = UnsafeMutableBufferPointer<Int8>.allocate(capacity: count)
_ = result.initialize(from: str.utf8CString)
return result.baseAddress!
}
这样做,您可以在堆上为C字符串分配空间,以安全的方式在所有线程之间共享C字符串(只要它是只读内存)。