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()
}
Надеюсь, это поможет.