audio - AKSequencer 中播放的第一个音符已关闭

标签 audio timing audiokit aksequencer

我正在使用 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() 调用本身可以引入大约 200 毫秒的不稳定。

编辑: 事后想想,您可以通过启用循环并使用任意长的序列长度在非循环序列上做同样的事情。如果您需要播放以在 MIDI 内容结束时停止,您可以使用由紧接在最后一个音符之后的 MIDI 事件触发的 AKCallbackInstrument 来实现。

关于audio - AKSequencer 中播放的第一个音符已关闭,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52062316/

相关文章:

c# - C#使用SoundPlayer暂停和恢复

java - 这会降低帧率吗?

ios - 如何使用音序器控制振荡器的频率

swift - 在 OS X 中启用沙箱时 Audiokit 崩溃

audio - 如何使用 ffmpeg 中的 libavcodec 将 opus 文件解码为 pcm 文件?

javascript播放声音启动太慢

JavaScript 事件序列

ios - 使用 AudioKit 在 UIView 中绘制波形

ios - UNNotificationSound.default() 是什么声音?

jQuery 定时事件