从Core Audio Callback更新UI的最佳做法是什么?

我目前正在围绕Core Audio进行包装,并且遇到了如何更新GUI AudioQueueInputCallback的问题。 首先,我想用麦克风的电平表读数来更新标签。

在我的代码中,我将当前的电平表值存储在每个callback的结构中。

func MyAudioQueueInputCallback(inUserData: UnsafeMutablePointer<Void>, inAQ: AudioQueueRef, inBuffer: AudioQueueBufferRef, inStartTime: UnsafePointer<AudioTimeStamp>, var inNumberPacketDesc: UInt32, inPacketDesc: UnsafePointer<AudioStreamPacketDescription>){ var error: OSStatus if (inNumberPacketDesc > 0){ error = AudioFileWritePackets(MyRecorder.recordFile, false, inBuffer.memory.mAudioDataByteSize, inPacketDesc, MyRecorder.recordPacket, &inNumberPacketDesc, inBuffer.memory.mAudioData) checkError(error, operation: "AudioFileWritePackets Failed ") // Increment the packet index MyRecorder.recordPacket += Int64(inNumberPacketDesc) if (MyRecorder.running){ error = AudioQueueEnqueueBuffer(inAQ, inBuffer, inNumberPacketDesc, inPacketDesc) checkError(error, operation: "AudioQueueEnqueueBuffer Failed BAHHHH") var level: Float32 = 0 var levelSize = UInt32(sizeof(level.dynamicType)) error = AudioQueueGetProperty(inAQ, kAudioQueueProperty_CurrentLevelMeter, &level, &levelSize) checkError(error, operation: "AudioQueueGetProperty Failed... Get help!") MyRecorder.meterLevel = level // meter level stored in public struct } } 

}

 //MARK: User Data Struct / Class struct MyRecorder { static var recordFile: AudioFileID = nil static var recordPacket: Int64 = 0 static var running: Bool = false static var queue: AudioQueueRef = nil static var meterLevel: Float32 = 0.00 } 

在这里,meterLevelvariables在主线程上使用NSTimer进行轮询。

 func setMeterLabel() -> Void{ meter.text = String(MyRecorder.meterLevel) } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. main() var timer = NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: "setMeterLabel", userInfo: nil, repeats: true) } 

这段代码工作的很好,但我觉得好像使用NSTimer可能不是最好的方式去做。 有没有办法让我的callback直接更新标签?

使用重复的NSTimer或CADisplayLink是一个很好的标准方法。 任何其他方法都可能会阻止您的audiocallback时间过长。

您知道您的audio采样率和audio缓冲区的大小。 因此,您大概知道新数据缓冲区将出现的频率。 因此,在2速率中最慢的一个(新的audio缓冲速率或者你想要的帧animation速率,通常是60或者30Hz或者更慢),你可以设置一个重复定时器callback(或者一个CADisplayLink定时器callback)来轮询一个新的audio数据已经被logging下来,然后做一些事情,比如在某些需要更新的视图上调用setNeedsDisplay。 (UIView drawRect更新将始终在主UI线程上调用)。

将audiocallback中的状态或数据传递给定时器callback的一个好方法是使用无锁循环FIFO /缓冲区。 那么你保证永远不要阻止你的audiocallback线程。

如果您在UI线程上运行定时器callback,则不需要对同一个线程执行asynchronous调度。

看起来像在同一个控制器中发生的一切,所以你应该能够从callback函数中访问你需要的所有东西。

您可能不希望每次callback都更新GUI,但是您可以设置一个每次递增的计数器,并定期更新GUI。

唯一需要更改的是确保您调用主队列上的setMeterLabel – 不能从后台队列中更改GUI。

 dispatch_async(dispatch_get_main_queue(), { self.setMeterLabel() }) 

callback速度可能太快,所以请考虑创build一个平均filter(或平均值)。 让audiocallback在控制器中更新一个简单的基元(int,float等)。 如果你愿意,你可以创build一个委托。 将它们存储在一个队列中,只保留最后的20个项目。 摆脱最古老,保持最新。

让您的用户界面计算您select的最近n个项目的平均值,并以定期比率显示在ui上。