我正在尝试使用给定频率的正弦波播放简单的提示音。我正在为此目的使用Swift 4.2和AudioQueues并在iOS 12上为iPhone进行构建。但是,在进行此设置时遇到很多困难。
我目前看到的问题是,当我排队初始缓冲区时,在播放这些缓冲区时会产生一点“喀哒”声。但是,当重新使用缓冲区时,什么也不会发生。我已经通过使用过多的缓冲区(50,000个奇数)证明了这一点,该缓冲区在大约我期望的水平上产生了一秒钟的闪烁。对于给定的采样率,这是正确的。但是,一旦所有初始缓冲区都播放了一次,样本就会停止。
我尝试使用内部循环以外的运行循环。我已经检查了我的回调函数是否被调用,正在接收正确的对象并且似乎正在为缓冲区的内容设置正确的值,但是它只是停止工作。任何AudioQueue函数也不返回任何异常的OSStatus代码。我已经尝试了基本描述中的各种选项来说明数据格式和字节序。我只是不认为还有什么可能是错误的,因为我所拥有的与我发现的大多数示例相似。
(注:为简洁起见,我已经省略了用于启动和停止音频队列的命令。足以称呼createAudioQueue()
。)
这是我的代码:
func synthesizerCallback(userData: UnsafeMutableRawPointer?, audioQueue: AudioQueueRef, buffer: AudioQueueBufferRef) {
guard let rawSynth = userData else { return }
let synth = Unmanaged<Synthesizer>.fromOpaque(rawSynth).takeUnretainedValue()
synth.write(withAudioQueue: audioQueue, toBuffer: buffer)
}
class Synthesizer {
enum Errors: Error {
case coreAudioError(OSStatus)
case couldNotInitialiseAudioQueue
}
private var format: AudioStreamBasicDescription
private var outputQueue: AudioQueueRef?
private var outputBuffers: [AudioQueueBufferRef?]
private var offset: Double = 0
let sampleRate: Double
let channels: Int
init(sampleRate: Double = 44100, channels: Int = 2, bufferCount: Int = 3) {
assert(bufferCount > 2, "Need at least three buffers!")
self.sampleRate = sampleRate
self.channels = channels
self.outputBuffers = Array<AudioQueueBufferRef?>(repeating: nil, count: bufferCount)
let uChannels = UInt32(channels)
let channelBytes = UInt32(MemoryLayout<Int16>.size)
let bytesPerFrame = uChannels * channelBytes
self.format = AudioStreamBasicDescription(
mSampleRate: Float64(sampleRate),
mFormatID: kAudioFormatLinearPCM,
mFormatFlags: kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked,
mBytesPerPacket: bytesPerFrame,
mFramesPerPacket: 1,
mBytesPerFrame: bytesPerFrame,
mChannelsPerFrame: uChannels,
mBitsPerChannel: channelBytes * 8,
mReserved: 0
)
}
private func createAudioQueue() throws {
let selfPointer = unsafeBitCast(self, to: UnsafeMutableRawPointer.self)
let createOutputResult = AudioQueueNewOutput(&format, synthesizerCallback, selfPointer, nil, nil, 0, &outputQueue)
if createOutputResult != 0 { throw Errors.coreAudioError(createOutputResult) }
guard let outputQueue = outputQueue else { throw Errors.couldNotInitialiseAudioQueue }
for i in 0 ..< outputBuffers.count {
let createBufferResult = AudioQueueAllocateBuffer(outputQueue, format.mBytesPerFrame, &outputBuffers[i])
if createBufferResult != 0 { throw Errors.coreAudioError(createBufferResult) }
guard let outputBuffer = outputBuffers[i] else { throw Errors.couldNotInitialiseAudioQueue }
synthesizerCallback(userData: selfPointer, audioQueue: outputQueue, buffer: outputBuffer)
}
let primeQueueResult = AudioQueuePrime(outputQueue, 0, nil)
if primeQueueResult != 0 { throw Errors.coreAudioError(primeQueueResult) }
let startQueueResult = AudioQueueStart(outputQueue, nil)
if startQueueResult != 0 { throw Errors.coreAudioError(startQueueResult) }
}
func write(withAudioQueue audioQueue: AudioQueueRef, toBuffer buffer: AudioQueueBufferRef) {
let dataSize = MemoryLayout<Int16>.stride * channels
let phase = offset / sampleRate * 10000 * Double.pi * 2
let value = Int16(sin(phase) * Double(Int16.max))
var allChannels = Array<Int16>(repeatElement(value, count: channels))
buffer.pointee.mAudioData.copyMemory(from: &allChannels, byteCount: dataSize)
buffer.pointee.mAudioDataByteSize = UInt32(dataSize)
let enqueueResult = AudioQueueEnqueueBuffer(audioQueue, buffer, 0, nil)
if enqueueResult != 0 { print(enqueueResult) }
offset += 1
}
}
UPDATE 奇怪的是,如果我提供大约550-12000个缓冲区,声音似乎是连续的。高得多或低得多,它要么消失,严重故障或同时出现。我想在某些情况下可能无法准备填充缓冲区,因此我需要采取一些措施来规避此问题,但是我不知道这可能是什么。