【发布时间】:2018-12-26 09:06:19
【问题描述】:
我正在尝试通过实现苹果提供的示例代码来创建节拍器应用。一切正常,但我发现节拍视觉效果与播放器时间不正确同步。这是苹果提供的示例代码
let secondsPerBeat = 60.0 / tempoBPM
let samplesPerBeat = Float(secondsPerBeat * Float(bufferSampleRate))
let beatSampleTime: AVAudioFramePosition = AVAudioFramePosition(nextBeatSampleTime)
let playerBeatTime: AVAudioTime = AVAudioTime(sampleTime: AVAudioFramePosition(beatSampleTime), atRate: bufferSampleRate)
// This time is relative to the player's start time.
player.scheduleBuffer(soundBuffer[bufferNumber]!, at: playerBeatTime, options: AVAudioPlayerNodeBufferOptions(rawValue: 0), completionHandler: {
self.syncQueue!.sync() {
self.beatsScheduled -= 1
self.bufferNumber ^= 1
self.scheduleBeats()
}
})
beatsScheduled += 1
if (!playerStarted) {
// We defer the starting of the player so that the first beat will play precisely
// at player time 0. Having scheduled the first beat, we need the player to be running
// in order for nodeTimeForPlayerTime to return a non-nil value.
player.play()
playerStarted = true
}
let callbackBeat = beatNumber
beatNumber += 1
// calculate the beattime for animating the UI based on the playerbeattime.
let nodeBeatTime: AVAudioTime = player.nodeTime(forPlayerTime: playerBeatTime)!
let output: AVAudioIONode = engine.outputNode
let latencyHostTicks: UInt64 = AVAudioTime.hostTime(forSeconds: output.presentationLatency)
//calcualte the final dispatch time which will update the UI in particualr intervals
let dispatchTime = DispatchTime(uptimeNanoseconds: nodeBeatTime.hostTime + latencyHostTicks)**
// Visuals.
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: dispatchTime) {
if (self.isPlaying) {
// send current call back beat.
self.delegate!.metronomeTicking!(self, bar: (callbackBeat / 4) + 1, beat: (callbackBeat % 4) + 1)
}
}
}
// my view controller class where i'm showing the beat number
class ViewController: UIViewController ,UIGestureRecognizerDelegate,Metronomedelegate{
@IBOutlet var rhythmlabel: UILabel!
//view did load method
override func viewDidLoad() {
}
//delegate method for getting the beat value from metronome engine and showing in the UI label.
func metronomeTicking(_ metronome: Metronome, bar: Int, beat: Int) {
DispatchQueue.main.async {
print("Playing Beat \(beat)")
//show beat in label
self.rhythmlabel.text = "\(beat)"
}
}
}
【问题讨论】:
-
您的代码中没有“视觉效果”。所以不清楚这个问题是关于什么的。
-
您好,我已经编辑了代码,请检查一次 (callbackBeat % 4) + 1 是我需要在视图中显示的节拍。
-
但是您没有显示任何在任何视图中显示任何内容的代码。并且您标记 Visuals 的代码位于后台线程上,因此 course 存在延迟。
-
@matt 我已经在我的视图控制器中实现了委托方法。 ** MetronomeTicking** 将在我的视图控制器中被调用,我正在显示延迟出现的动画。
-
我还是不明白
DispatchQueue.global(qos: .userInitiated)的意思。这会将您置于后台线程上,并授予运行时权限以在需要时执行您的代码。在我看来,这与您想要做的完全相反。当然,如果您想要以最高准确度获得最低延迟,那么您想进入 main 线程。当然我还是不能保证asyncAfter的准确性;在我看来,这似乎是另一个风险来源。理想情况下,您应该在声音播放时直接“打勾”。
标签: ios iphone swift metronome