[英]AudioKit: Using the new AKSequencer with any variety of the callback instruments
這個話題已經被討論過無數次了,我在我以前的應用程序中成功地使用了AKMIDICallbackInstrument
和舊的AKAppleSequencer
。
我開始使用新的AKSequencer
,它絕對是非凡的:優雅的界面,並且易於使用。 但是,我終生無法弄清楚如何用它處理回調事件。 我需要使用回調來觸發基於音序器播放的 GUI 事件。
這是我的示例代碼:
private func setMetronome(bpm: BPM, beats:Int)
{
sequencer = AKSequencer(targetNode: metronomeSampler)
sequencer.tempo = bpm
sequencer.loopEnabled = false
sequencer.length = Double(beats)
metroCallback.callback = {status, noteNumber, velocity in
if let midiStatus = AKMIDIStatus(byte: status), midiStatus.type != .noteOn { return }
//Do callback stuff here
}
let metroCallbackTrack = sequencer.addTrack(for: metroCallback)
for i in 0..<beats
{
if i == 0
{
sequencer.add(noteNumber: MIDINoteNumber(67), position: Double(i), duration: 1.0)
metroCallbackTrack.add(noteNumber: MIDINoteNumber(67), position: Double(i), duration: 1.0)
}
else if (i % 4 == 0)
{
sequencer.add(noteNumber: MIDINoteNumber(67), position: Double(i), duration: 1.0)
metroCallbackTrack.add(noteNumber: MIDINoteNumber(60), position: Double(i), duration: 1.0)
}
else
{
sequencer.add(noteNumber: MIDINoteNumber(60), position: Double(i), duration: 1.0)
metroCallbackTrack.add(noteNumber: MIDINoteNumber(60), position: Double(i), duration: 1.0)
}
print("seq count:\(i)")
}
for track in sequencer.tracks
{
print("Adding track to mixer:\(track.length)")
track >>> mixer
}
}
這段代碼正確地創建了一個包含n
個節拍的序列,它通過我的AKSampler
播放,一切都很好。 除了沒有回調事件發生(使用打印語句確認)
思考過程
使用AKAppleSequencer
和AKMIDICallbackInstrument
,您可以使用globalMIDIOutput
和AKAppleSequencer
的 midi 輸入設置AKMIDICallBackInstrument
。
現在新的AKSequencer
和AKCallbackInstrument
沒有這些選項,新的AKSequencerTrack
也沒有(舊的AKAppleSequencer
將使用可以設置 midi 輸入/輸出的AKMusicTrack
對象)。 查看新AKSequencer
的實現,它由AKNode
對象驅動, AKCallbackInstrument
是一個AKNode
對象,應該能夠由具有正確 MIDI 數據的軌道驅動。
我向我的音序器添加了一條軌道,並從該軌道添加了必要的 MIDI 數據,這些數據完全復制了我想要回調的 MIDI 事件並執行我的 GUI 事件。 但是使用這種方法,它似乎並沒有調用回調。
有誰知道如何通過回調使用這些新組件? 我真的不想回到AKAppleSequencer
除非顯然沒有辦法用新的AKSequencer
驅動回調。
要讓AKCallbackInstrument
與新的AKSequencer
一起工作,請嘗試將您的回調儀器連接到您的 output,例如,
metroCallback >>> mixer
不明顯,但對我有用。
編輯:包括帶有AKCallbackInstrument
的新AKSequencer
的最小工作版本
class SequencerWrapper {
var seq: AKSequencer!
var cbInst: AKCallbackInstrument!
var mixer: AKMixer!
init() {
mixer = AKMixer()
AudioKit.output = mixer
seq = AKSequencer()
cbInst = AKCallbackInstrument()
// set up a track
let track = seq.addTrack(for: cbInst)
for i in 0 ..< 4 {
track.add(noteNumber: 60, position: Double(i), duration: 0.5)
}
track.length = 4.0
track.loopEnabled = true
track >>> mixer // must send track to mixer
// set up the callback instrument
cbInst.callback = { status, note, vel in
guard let status = AKMIDIStatus(byte: status),
let type = status.type,
type == .noteOn else { return }
print("note on: \(note)")
// trigger sampler etc from here
}
cbInst >>> mixer // must send callbackInst to mixer
}
func play() {
seq.playFromStart()
}
}
感謝@c_booth 的工作示例,只是想為像我這樣無法弄清楚上述示例為什么不起作用的傻瓜添加。 你仍然需要調用 AudioKit.start()。
Audiokit 5.3 的工作版本。 實際上,原生音序器現在是 AudioKitEx 的一部分,因此您必須導入這兩個包。 真遺憾,食譜中沒有可用的示例。 總而言之,適用於 iOS 和 Mac 的最精確、最可靠的計時器。 只需將其放入 XCode 應用程序模板中,然后通過 Github-Links 導入這兩個包。
// Created by c_booth,
// current version by Heiko Henrich on 23.12.22.
//
import SwiftUI
import AudioKit
import AudioKitEX
import AVFoundation
class SequencerWrapper: ObservableObject {
var seq: Sequencer!
var cbInst: CallbackInstrument!
var mixer: Mixer!
var engine: AudioEngine!
var playing: Bool = false {
didSet {
if playing {
seq.playFromStart()
}
else {
seq.stop()
}
}
}
init() {
engine = AudioEngine()
mixer = Mixer()
engine.output = mixer
seq = Sequencer()
// set up the callback instrument
var counter = 0
cbInst = CallbackInstrument { status, note, vel in
guard let status = MIDIStatus(byte: status),
let type = status.type,
type == .noteOn else { return }
print("\(counter): note on: \(note)")
counter += 1
// trigger sampler etc from here
}
// set up a track
let track = seq.addTrack(for: cbInst)
for i in 0 ..< 4 {
track.add(noteNumber: 60, position: Double(i), duration: 0.5)
}
track.length = 4.0
track.loopEnabled = true
mixer.addInput(cbInst) // must send callbackInst to mixer
try! engine.start()
}
}
struct ContentView: View {
@State var sw: SequencerWrapper?
var body: some View {
VStack {
Button("play") {
sw?.playing.toggle()
}
}
.onAppear{
sw = SequencerWrapper()
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.