[英]First note played in AKSequencer is off
我正在使用AKSequencer創建一個由AKMidiSampler播放的音符序列。 我的問題是,無論我做什么,在更高的節奏下,第一個音符總是會有一點延遲。
我嘗試預先滾動序列但它沒有幫助。 用AKSampler或AKSamplePlayer替換AKMidiSampler(並使用回調軌道來播放它們)也沒有幫助,盡管它讓我認為問題可能存在於音序器或我創建音符的方式中。
這是我正在做的事情的一個例子(我試圖讓它變得盡可能簡單):
import UIKit
import AudioKit
class ViewController: UIViewController {
let sequencer = AKSequencer()
let sampler = AKMIDISampler()
let callbackInst = AKCallbackInstrument()
var metronomeTrack : AKMusicTrack?
var callbackTrack : AKMusicTrack?
let numberOfBeats = 8
let tempo = 280.0
var startTime : TimeInterval = 0
override func viewDidLoad() {
super.viewDidLoad()
print("Begin setup.")
// Load .wav sample in AKMidiSampler
do {
try sampler.loadWav("tick")
} catch {
print("sampler.loadWav() failed")
}
// Create tracks for the sequencer and set midi outputs
metronomeTrack = sequencer.newTrack("metronomeTrack")
callbackTrack = sequencer.newTrack("callbackTrack")
metronomeTrack?.setMIDIOutput(sampler.midiIn)
callbackTrack?.setMIDIOutput(callbackInst.midiIn)
// Setup and start AudioKit
AudioKit.output = sampler
do {
try AudioKit.start()
} catch {
print("AudioKit.start() failed")
}
// Set sequencer tempo
sequencer.setTempo(tempo)
// Create the notes
var midiSequenceIndex = 0
for i in 0 ..< numberOfBeats {
// Add notes to tracks
metronomeTrack?.add(noteNumber: 60, velocity: 100, position: AKDuration(beats: Double(midiSequenceIndex)), duration: AKDuration(beats: 0.5))
callbackTrack?.add(noteNumber: MIDINoteNumber(midiSequenceIndex), velocity: 100, position: AKDuration(beats: Double(midiSequenceIndex)), duration: AKDuration(beats: 0.5))
print("Adding beat number \(i+1) at position: \(midiSequenceIndex)")
midiSequenceIndex += 1
}
// Set the callback
callbackInst.callback = {status, noteNumber, velocity in
if status == .noteOn {
let currentTime = Date().timeIntervalSinceReferenceDate
let noteDelay = currentTime - ( self.startTime + ( 60.0 / self.tempo ) * Double(noteNumber) )
print("Beat number: \(noteNumber) delay: \(noteDelay)")
} else if ( noteNumber == midiSequenceIndex - 1 ) && ( status == .noteOff) {
print("Sequence ended.\n")
self.toggleMetronomePlayback()
} else {return}
}
// Preroll the sequencer
sequencer.preroll()
print("Setup ended.\n")
}
@IBAction func playButtonPressed(_ sender: UIButton) {
toggleMetronomePlayback()
}
func toggleMetronomePlayback() {
if sequencer.isPlaying == false {
print("Playback started.")
startTime = Date().timeIntervalSinceReferenceDate
sequencer.play()
} else {
sequencer.stop()
sequencer.rewind()
}
}
}
有人可以幫忙嗎? 謝謝。
正如Aure評論的那樣,啟動延遲是一個已知問題。 即使使用預卷,仍然會有明顯的延遲,尤其是在更高的節奏下。
但是如果你使用循環序列,我發現你有時可以通過將序列的“起始點”設置到最終MIDI事件之后的位置,但在循環長度內來減輕延遲的顯着程度。 如果您能找到一個好的位置,您可以在它循環回到您的內容之前獲得延遲效果。
確保在需要之前調用setTime()
(例如,在停止序列之后,而不是在准備好播放時),因為setTime()
調用本身可以引入大約200ms的難度。
編輯:作為事后的想法,你可以通過啟用循環和使用任意長的序列長度在非循環序列上做同樣的事情。 如果您需要在MIDI內容結束時停止播放,可以使用由最后音符后面的MIDI事件觸發的AKCallbackInstrument來完成此操作。
經過一些測試后,我發現它不是第一個播放的音符,而是隨后播放的音符。 此外,啟動音序器時准確播放的音符量取決於設定的速度。
有趣的是,如果節奏<400會有一個音符按時播放,其他音符會提前播放,如果是400 <= bpm <800則會有兩個音符正確播放而其他音符提前播放等等,每增加400 bpm,你可以正確播放一個音符。
所以...因為筆記是提前播放而不是遲到,所以解決它的解決方案是:
1)使用未直接連接到軌道midi輸出但在回調內調用其.play()
方法的采樣器。
2)跟蹤音序器何時開始
3)在每次回調時計算音符應該與開始時間相關的時間並存儲實際的時間,這樣你就可以計算出偏移量。
4)在偏移你的.play()
方法之后使用計算的偏移量來調度.play()
。
就是這樣,我在多台設備上進行了測試,現在所有筆記都按時完美播放。
我有同樣的問題,preroll沒有幫助,但我設法用第一個筆記的專用采樣器解決它。 我在另一個采樣器上使用延遲,大約0.06秒,就像一個魅力。 有點愚蠢的解決方案,但它完成了工作,我可以繼續項目:)
//This is for fixing AK bug that plays the first playback not in delay
let fixDelay = AKDelay()
fixDelay.dryWetMix = 1
fixDelay.feedback = 0
fixDelay.lowPassCutoff = 22000
fixDelay.time = 0.06
fixDelay.start()
let preDelayMixer = AKMixer()
let preFirstMixer = AKMixer()
[playbackSampler,vocalSampler] >>> preDelayMixer >>> fixDelay
[firstNoteVocalSampler, firstRoundPlaybackSampler] >>> preFirstMixer
[fixDelay,preFirstMixer] >>> endMixer
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.