AudioKit ios AKSequencer не возобновляет воспроизведение точно

Я пытаюсь использовать AudioKit для воспроизведения звука при каждом ударе такта. Хотя я реализовал код из этого аналогичного вопроса относительно обратных вызовов через AudioKit, я не могу заставить секвенсор корректно обновлять изменения и воспроизводить их. Он будет воспроизводиться точно один раз, однако после перемотки и изменения значений он будет использовать только начальные значения (или не воспроизводить их вообще).

Мое намерение состоит в том, чтобы создать структуру тактов со значениями ударов для каждого такта, а затем использовать MIDI и обратный вызов для воспроизведения разных звуков в зависимости от количества тактов / ударов. Спасибо!

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
var intro = Line(name: "Intro", measures: 0, beatsPerMeasure: 0)

// A function to create/update/playback the sequence on button press
func playBack() {

    let metronomeTrack = sequencer.newTrack()
    metronomeTrack?.setMIDIOutput(click.midiIn)
    let callbackTrack = sequencer.newTrack()
    callbackTrack?.setMIDIOutput(callbackInst.midiIn)

    for steps in 0 ... Int(measuresRowOneValue) {
        // this will trigger the sampler on the four down beats
        metronomeTrack?.add(noteNumber: 60, velocity: 100, position: AKDuration(beats: Double(steps)), duration: AKDuration(beats: 0.5))

        // set the midiNote number to the current beat number
        callbackTrack?.add(noteNumber: MIDINoteNumber(steps), velocity: 100, position: AKDuration(beats: Double(steps)), duration: AKDuration(beats: 0.5))

        // set the callback
        callbackInst.callback = {status, noteNumber, velocity in
            guard status == .noteOn else { return }
            print("beat number: \(noteNumber + 1)")

        }
    }
}


@IBOutlet weak var rowOneLocationOne: UIImageView!
// Listener for UI display values
var measuresRowOneValue: Int = 0 {
    didSet {
        intro.measures = measuresRowOneValue
    }
}

@IBAction func rowOnePlusButton(_ sender: UIButton) {
    measuresRowOneValue += 1
}

@IBAction func rowOneMinusButton(_ sender: UIButton) {
    measuresRowOneValue -= 1
}

@IBAction func playbackStart(_ sender: UIButton) {
    playBack()
    sequencer.play()
}

@IBAction func playbackStop(_ sender: UIButton) {
    sequencer.stop()
}

@IBAction func playbackRestart(_ sender: UIButton) {
    sequencer.rewind()
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    AudioKit.output = click
    try!AudioKit.start()
}
}

1 ответ

Решение

В вашем коде есть несколько запутанных вещей, поэтому я не уверен, что это ваша единственная проблема, но, как минимум, каждый раз, когда вы изменяете длину вашей последовательности, вам нужно будет вызвать setLength() с последующим enableLooping, По сути, по умолчанию (т. Е. Если вы явно не установите длину) длина последовательности будет длиной самой длинной дорожки в последовательности. В вашем методе "воспроизведения" вы добавляете дорожку поверх дорожки, не удаляя старые, поэтому у вас нет возможности узнать, как долго вы предполагаете эту последовательность.

Ваш метод воспроизведения выполняет две разные вещи (ни одна из которых не включает воспроизведение). Вы можете хотеть сломать это. Вы могли бы иметь setup() сделать то, что нужно сделать только один раз (создать дорожки, настроить их выходы, настроить обратный вызов) и rewriteSequence() методы, которые вызываются, когда вы хотите переписать треки. Таким образом, вы можете повторно использовать существующие треки, а не постоянно создавать новые.

var metronomeTrack: AKMusicTrack!
var callbackTrack: AKMusicTrack!

    // call this just once at the start
    func setup() {
        metronomeTrack = sequencer.newTrack()
        metronomeTrack?.setMIDIOutput(click.midiIn)
        callbackTrack = sequencer.newTrack()
        callbackTrack?.setMIDIOutput(callbackInst.midiIn)

        callbackInst.callback = {status, noteNumber, velocity in
            guard status == .noteOn else { return }
            print("beat number: \(noteNumber + 1)")

        }
    }

    // call this each time you want to change the sequence
    func rewriteSequence() {
        metronomeTrack?.clear()
        callbackTrack?.clear()
        for steps in 0 ... Int(measuresRowOneValue) {
            metronomeTrack?.add(noteNumber: 60, velocity: 100, position: AKDuration(beats: Double(steps)), duration: AKDuration(beats: 0.5))
            callbackTrack?.add(noteNumber: MIDINoteNumber(steps), velocity: 100, position: AKDuration(beats: Double(steps)), duration: AKDuration(beats: 0.5))
        }

        // This will make sure it loops correctly:
        sequencer.setLength(AKDuration(beats: Double(measuresRowOneValue)))
        sequencer.enableLooping()
    }

Надеюсь, это поможет.

Другие вопросы по тегам