iOS AudioKit中AKSequencer无法准确重启播放问题
Fixing AKSequencer Update & Playback Issues in Your AudioKit Project
Let's break down why your sequencer isn't updating correctly when you modify parameters after the first play, and fix it step by step.
Key Issues in Your Current Code
- You're creating new tracks every time
playBack()runs — old tracks remain in the sequencer and conflict with new ones, causing unexpected behavior on subsequent plays. - The callback is set inside a loop — this overwrites the callback multiple times unnecessarily, and it only needs to be configured once.
- You're not utilizing your
Linestruct's beat logic — your current code ignoresbeatsPerMeasure, so you're not generating the correct number of beats per measure. - No cleanup before reconfiguring the sequencer — when you rewind and adjust parameters, the sequencer still holds old track data.
Corrected Code
import UIKit import AudioKit class ViewController: UIViewController { let sequencer = AKSequencer() let click = AKSynthSnare() let callbackInst = AKCallbackInstrument() // Create the struct that defines each line struct Line { var name: String var measures: Int var beatsPerMeasure: Int func totalBeats() -> Int { return measures * beatsPerMeasure } } // Initialize intro line with default beats per measure (e.g., 4/4 time) var intro = Line(name: "Intro", measures: 1, beatsPerMeasure: 4) override func viewDidLoad() { super.viewDidLoad() // Set up AudioKit once AudioKit.output = click try! AudioKit.start() // Configure callback ONCE, not in a loop callbackInst.callback = { status, noteNumber, velocity in guard status == .noteOn else { return } print("beat number: \(noteNumber + 1)") } } // A function to create/update/playback the sequence on button press func playBack() { // Critical: Clear all existing tracks before creating new ones sequencer.stop() sequencer.deleteAllTracks() let metronomeTrack = sequencer.newTrack() metronomeTrack?.setMIDIOutput(click.midiIn) let callbackTrack = sequencer.newTrack() callbackTrack?.setMIDIOutput(callbackInst.midiIn) // Calculate total beats using your Line struct let totalBeats = intro.totalBeats() for beat in 0..<totalBeats { let beatPosition = AKDuration(beats: Double(beat)) // Trigger metronome on each beat metronomeTrack?.add(noteNumber: 60, velocity: 100, position: beatPosition, duration: AKDuration(beats: 0.5)) // Send beat number as MIDI note to callback callbackTrack?.add(noteNumber: MIDINoteNumber(beat), velocity: 100, position: beatPosition, duration: AKDuration(beats: 0.5)) } // Rewind to start after setting up tracks sequencer.rewind() } @IBOutlet weak var rowOneLocationOne: UIImageView! // Listener for UI display values var measuresRowOneValue: Int = 1 { didSet { intro.measures = measuresRowOneValue } } @IBAction func rowOnePlusButton(_ sender: UIButton) { measuresRowOneValue += 1 } @IBAction func rowOneMinusButton(_ sender: UIButton) { // Prevent negative measures guard measuresRowOneValue > 1 else { return } measuresRowOneValue -= 1 } @IBAction func playbackStart(_ sender: UIButton) { playBack() sequencer.play() } @IBAction func playbackStop(_ sender: UIButton) { sequencer.stop() } @IBAction func playbackRestart(_ sender: UIButton) { sequencer.stop() sequencer.rewind() } }
What Changed & Why
- Track Cleanup: Added
sequencer.deleteAllTracks()at the start ofplayBack()— this removes all old tracks so new configurations don't conflict with previous ones. - Callback Configuration: Moved the callback setup to
viewDidLoad()— callbacks only need to be defined once, not every time you rebuild the sequence. - Utilized
LineStruct: Now usesintro.totalBeats()to calculate the correct number of beats based on measures and beats per measure, which aligns with your original struct design. - Prevented Negative Measures: Added a guard clause in the minus button action to avoid invalid 0 or negative measure counts.
- Rewind After Setup: Added
sequencer.rewind()after building tracks to ensure playback starts from the first beat every time.
Additional Tips
- If you want to change
beatsPerMeasuredynamically (e.g., switch between 4/4 and 3/4), add a UI control to updateintro.beatsPerMeasurejust like you do for measures. - For more complex sequencing, consider storing track references as class properties instead of recreating them every time, but deleting tracks is the simplest fix for your use case.
内容的提问来源于stack exchange,提问作者moosgrn
相关产品推荐
相关产品推荐

