首页 > 解决方案 > Swift:来自 AVAudioPCMBuffer 的触发函数

问题描述

我正在开发适用于 iOS 的 Metronome 应用程序。引擎正在工作,目前非常准确,但是,它只提供音频反馈,而我也想提供视觉反馈。

我制作了一个 AVAudioPCMBuffer 以按设定的时间间隔播放一个刻度,我如何在播放音频时触发一个函数?

这是我目前拥有的代码:

''' private func generateBuffer(bpm: Double, beats: Int) -> AVAudioPCMBuffer {

    audioFileMainClick.framePosition = 0
    audioFileAccentedClick.framePosition = 0
    
    let beatLength = AVAudioFrameCount(audioFileMainClick.processingFormat.sampleRate * 60 / bpm)
    let bufferMainClick = AVAudioPCMBuffer(pcmFormat: audioFileMainClick.processingFormat, frameCapacity: beatLength)!
    try! audioFileMainClick.read(into: bufferMainClick)
    bufferMainClick.frameLength = beatLength
    
    let bufferAccentedClick = AVAudioPCMBuffer(pcmFormat: audioFileMainClick.processingFormat, frameCapacity: beatLength)!
    try! audioFileAccentedClick.read(into: bufferAccentedClick)
    
    let bufferBar = AVAudioPCMBuffer(pcmFormat: audioFileMainClick.processingFormat, frameCapacity: UInt32(beats) * beatLength)!
    bufferBar.frameLength = UInt32(beats) * beatLength
    
    let channelCount = Int(audioFileMainClick.processingFormat.channelCount)
    let accentedClickArray = Array(
        UnsafeBufferPointer(start: bufferAccentedClick.floatChannelData![0],
                            count: channelCount * Int(beatLength))
    )
    let mainClickArray = Array(
        UnsafeBufferPointer(start: bufferMainClick.floatChannelData![0],
                            count: channelCount * Int(beatLength))
    )
    
    var barArray = [Float]()
    // one time for first beat
    barArray.append(contentsOf: accentedClickArray)
    // n times for regular clicks
    for _ in 1...(beats - 1) {
        barArray.append(contentsOf: mainClickArray)
    }
    bufferBar.floatChannelData!.pointee.assign(from: barArray,
                                               count: channelCount * Int(bufferBar.frameLength))
    return bufferBar
    
}

func play(bpm: Double, beats: Int) {
    
    let buffer = generateBuffer(bpm: bpm, beats: beats)
    
    if audioPlayerNode.isPlaying {
        audioPlayerNode.scheduleBuffer(buffer, at: nil, options: .interruptsAtLoop, completionHandler: nil)
    } else {
        self.audioPlayerNode.play()
    }
    
    self.audioPlayerNode.scheduleBuffer(buffer, at: nil, options: .loops, completionHandler: nil)
    
}

'''

先感谢您。

菲利波

标签: iosswiftaudiobuffer

解决方案


尽量不要使用 scheduleBuffer 的循环功能。使用完成处理程序来重新触发 play() 函数。这使您可以选择将代码添加到 play() 函数以获得视觉反馈,例如显示当前节拍。并记住始终使用 DispatchQueue.main.async {...} 处理 UI 内容。


推荐阅读