我目前正围绕着Core Audio,我遇到了如何更新GUI AudioQueueInputCallback的问题。首先,我想用麦克风上的Level Meter读取更新标签。
在我的代码中,我将当前的电平表值存储在每个回调的结构中。
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
}
从此处使用主线程上的NSTimer轮询meterLevel变量。
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可能不是最好的方法。我的回调有没有办法直接更新标签?
答案 0 :(得分:2)
使用重复的NSTimer或CADisplayLink是一种很好的标准方法。任何其他方法都会导致阻止音频回调的时间过长。
您知道音频采样率和音频缓冲区的大小。因此,您大致了解新数据缓冲区的显示频率。因此,在2个速率中最慢的(新的音频缓冲速率或您想要的帧动画速率,通常为60或30 Hz或更慢),您可以设置重复计时器回调(或CADisplayLink计时器回调)来轮询是否有新的音频数据已被记录,并执行某些操作,例如在某些需要更新的视图上调用setNeedsDisplay。 (UIView drawRect更新将始终在主UI线程上调用。)
将状态或数据从音频回调传递到计时器回调的一种好方法是使用无锁循环FIFO /缓冲区。然后你保证永远不会阻止你的音频回调线程。
如果你在UI线程上运行你的计时器回调,就不需要对同一个线程进行异步调度。
答案 1 :(得分:1)
看起来所有内容都在同一个控制器中发生,因此您应该能够在回调函数中访问所需的一切。
您可能不希望在每次回调时更新GUI,但您可以设置一个每次递增的计数器,并定期更新GUI。
您唯一需要更改的是确保调用主队列上的setMeterLabel
- 您无法从后台队列进行GUI更改。
dispatch_async(dispatch_get_main_queue(),
{
self.setMeterLabel()
})
答案 2 :(得分:1)
回调率可能过快,因此请考虑创建平均过滤器(或平均值框)。 让音频回调更新控制器中的简单原语(int,float等)。如果你愿意,你可以成为一名代表。将它们存储在一个队列中,并且只保留最后20个项目。摆脱最古老的并保持最新。
让您的UI计算您选择的最新n个项目的平均值,并定期在ui上显示。