Swift中AudioQueue的问题:重新使用缓冲区似乎不起作用

时间:2019-01-30 09:43:39

标签: ios swift audio audioqueue

我正在尝试使用给定频率的正弦波播放简单的提示音。我正在为此目的使用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个缓冲区,声音似乎是连续的。高得多或低得多,它要么消失,严重故障或同时出现。我想在某些情况下可能无法准备填充缓冲区,因此我需要采取一些措施来规避此问题,但是我不知道这可能是什么。

0 个答案:

没有答案