簡體   English   中英

AudioKit:將新的 AKSequencer 與任何種類的回調樂器結合使用

[英]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播放,一切都很好。 除了沒有回調事件發生(使用打印語句確認)

思考過程

使用AKAppleSequencerAKMIDICallbackInstrument ,您可以使用globalMIDIOutputAKAppleSequencer的 midi 輸入設置AKMIDICallBackInstrument

現在新的AKSequencerAKCallbackInstrument沒有這些選項,新的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM