我试图创建一个可以播放重音音符的固体节拍器(即每个小节开头的不同音符)。在对这个主题进行了研究之后,我了解到使用Timer(以前的NSTimer)不是因为它的不准确而走的路。
我遇到的一个解决方案是使用AVAudioEngine,它非常适合稳定的节拍器,但是我还没有弄清楚如何跟踪你在哪个酒吧,并因此发表声明根据你的位置改变你播放的音频文件似乎很棘手。这是节拍器类的代码。
import Foundation
import UIKit
import AVFoundation
class Metronome {
var audioPlayerNode:AVAudioPlayerNode
var audioFile:AVAudioFile
var audioEngine:AVAudioEngine
init (fileURL: URL) {
audioFile = try! AVAudioFile(forReading: fileURL)
audioPlayerNode = AVAudioPlayerNode()
audioEngine = AVAudioEngine()
audioEngine.attach(self.audioPlayerNode)
audioEngine.connect(audioPlayerNode, to: audioEngine.mainMixerNode, format: audioFile.processingFormat)
try! audioEngine.start()
}
func generateBuffer(forBpm bpm: Int) -> AVAudioPCMBuffer {
audioFile.framePosition = 0
let periodLength = AVAudioFrameCount(audioFile.processingFormat.sampleRate * 60 / Double(bpm))
let buffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: periodLength)
try! audioFile.read(into: buffer!)
buffer?.frameLength = periodLength
return buffer!
}
func play(bpm: Int) {
let buffer = generateBuffer(forBpm: bpm)
self.audioPlayerNode.play()
self.audioPlayerNode.scheduleBuffer(buffer, at: nil, options: .loops, completionHandler: nil)
}
func stop() {
audioPlayerNode.stop()
}
}
要实际播放声音,在ViewController中我有以下代码:
required init?(coder aDecoder: NSCoder) {
let fileUrl = Bundle.main.url(forResource: "tone", withExtension: "wav")
metronome = Metronome(fileURL: fileUrl!)
super.init(coder: aDecoder)
}
使用以下代码启动和停止我放入@IBAction按钮。
metronome.play(bpm: 120)
metronome.stop()
正如你所看到的,它不能直接计算节拍的数量,因为文件实际上是在重复播放之间设置缓冲时间(据我所知)。
感谢任何帮助/建议,因为我对Swift很新。
谢谢!