如果你使用 AudioQueuePause,你可以让 AudioQueue 等待。
在这个例子中,在 Swift 5 中,我使用了一个通用队列。当这个队列为空时,就像你做的那样,我在回调中用空数据填充我的缓冲区并调用 AudioQueuePause。需要注意的是,在播放调用 AudioQueuePause 之前,所有 AudioQueueBuffer 都会使用 AudioQueueEnqueueBuffer 发送到 AudioQueueRef。
创建一个 userData 类以将您需要的所有内容发送到您的回调:
class UserData {
let dataQueue = Queue<Data>()
let semaphore = DispatchSemaphore(value: 1)
}
private var inQueue: AudioQueueRef!
private var userData = UserData()
在创建 AudioQueue 并启动它时提供此类的实例:
AudioQueueNewOutput(&inFormat, audioQueueOutputCallback, &userData, nil, nil, 0, &inQueue)
AudioQueueStart(inQueue, nil)
生成所有缓冲区,不要直接将它们排入队列:调用回调函数:
for _ in 0...2 {
var bufferRef: AudioQueueBufferRef!
AudioQueueAllocateBuffer(inQueue, 320, &bufferRef)
audioQueueOutputCallback(&userData, inQueue, bufferRef)
}
当你收到音频数据时,你可以调用一个方法让你的数据入队并让它等待回调函数获取它:
func audioReceived(_ audio: Data) {
let dataQueue = userData.dataQueue
let semaphore = userData.semaphore
semaphore.wait()
dataQueue.enqueue(audio)
semaphore.signal()
// Start AudioQueue every time, if it's already started this call do nothing
AudioQueueStart(inQueue, nil)
}
终于可以像这样实现回调函数了:
private let audioQueueOutputCallback: AudioQueueOutputCallback = { (inUserData, inAQ, inBuffer) in
// Get data from UnsageMutableRawPointer
let userData: UserData = (inUserData!.bindMemory(to: UserData.self, capacity: 1).pointee)
let queue = userData.dataQueue
let semaphore = userData.semaphore
// bind UnsafeMutableRawPointer to UnsafeMutablePointer<UInt8> for data copy
let audioBuffer = inBuffer.pointee.mAudioData.bindMemory(to: UInt8.self, capacity: 320)
if queue.isEmpty {
print("Queue is empty: pause")
AudioQueuePause(inAQ)
audioBuffer.assign(repeating: 0, count: 320)
inBuffer.pointee.mAudioDataByteSize = 320
} else {
semaphore.wait()
if let data = queue.dequeue() {
data.copyBytes(to: audioBuffer, count: data.count)
inBuffer.pointee.mAudioDataByteSize = data.count
} else {
print("Error: queue is empty")
semaphore.signal()
return
}
semaphore.signal()
}
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil)
}
在我的例子中,我使用 320 字节的缓冲区来存储 20 毫秒的 16 位、8kHz、单声道的 PCM 数据。
此解决方案比 CPU 的空音频数据的伪无限循环更复杂但更好。苹果对贪婪的应用程序非常严厉;)
我希望这个解决方案会有所帮助。