ios - 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)
}
'''
先感谢您。
菲利波
解决方案
尽量不要使用 scheduleBuffer 的循环功能。使用完成处理程序来重新触发 play() 函数。这使您可以选择将代码添加到 play() 函数以获得视觉反馈,例如显示当前节拍。并记住始终使用 DispatchQueue.main.async {...} 处理 UI 内容。
推荐阅读
- python - 即使我没有处理文件,我也会收到“ValueError: I/O operation on closed file”
- docker - apk add for alpine Docker image 加载旧版本的纱线(1.16)
- node.js - 无法连接到 MongoDB 并通过 Node / Postman 创建新的 Collections 文件夹
- vue.js - 如何将路径添加到“vue-cli-service lint”
- java - DLL JAVA调用嵌套DLL方法
- wikipedia - 将 Wikidata QID 映射到 Wikipedia CurID
- typescript - 如何为具有默认导出的模块创建 TSD?
- mysql - Mysql 触发 if 语句更新第二张表
- python - 不同的语法错误隐藏输出中的行
- reactjs - 父组件中的表单提交时如何使子组件更新