MIDI output form Standalone MIDI Processor Demo App to DAW

I am trying to get MIDI output from the AU Host demo app using the recent MIDI processor example. The processor works correctly in Logic Pro, but I cannot send MIDI from the AUv3 extension in standalone mode using the default host app to another program (e.g., Ableton).

The MIDI manager, which is part of the standalone host app, works fine, and I can send MIDI using it directly—Ableton receives it without issues. I have already set the midiOutputNames in the extension, and the midiOutBlock is mapped. However, the MIDI data from the AUv3 extension does not reach Ableton in standalone mode. I suspect the issue is that midiOutBlock might never be called in the plugin, or perhaps an input to the plugin is missing, which prevents it from sending MIDI. I am currently using the default routing.

I have modified the MIDI manager such that it works well as described above. Here is a part of my code for SimplePlayEngine.swift and my MIDIManager.swift for reference:

@MainActor
@Observable
public class SimplePlayEngine {
    
    private let midiOutBlock: AUMIDIOutputEventBlock = { sampleTime, cable, length, data in return noErr }
    var scheduleMIDIEventListBlock: AUMIDIEventListBlock? = nil
    
    public init() {
        engine.attach(player)
        

        engine.prepare()
        setupMIDI()
    }
    
    private func setupMIDI() {
        if !MIDIManager.shared.setupPort(midiProtocol: MIDIProtocolID._2_0, receiveBlock: { [weak self] eventList, _ in
            if let scheduleMIDIEventListBlock = self?.scheduleMIDIEventListBlock {
                _ = scheduleMIDIEventListBlock(AUEventSampleTimeImmediate, 0, eventList)
            }
        }) {
            fatalError("Failed to setup Core MIDI")
        }
    }
    
    func initComponent(type: String, subType: String, manufacturer: String) async -> ViewController? {
        reset()
        
        guard let component = AVAudioUnit.findComponent(type: type, subType: subType, manufacturer: manufacturer) else {
            fatalError("Failed to find component with type: \(type), subtype: \(subType), manufacturer: \(manufacturer))" )
        }
        
        do {
            let audioUnit = try await AVAudioUnit.instantiate(
                with: component.audioComponentDescription, options: AudioComponentInstantiationOptions.loadOutOfProcess)
            
            self.avAudioUnit = audioUnit
            self.connect(avAudioUnit: audioUnit)
            return await audioUnit.loadAudioUnitViewController()
        } catch {
            return nil
        }
    }
    


    private func startPlayingInternal() {
        guard let avAudioUnit = self.avAudioUnit else { return }
        setSessionActive(true)
        
        if avAudioUnit.wantsAudioInput { scheduleEffectLoop() }
        
        let hardwareFormat = engine.outputNode.outputFormat(forBus: 0)
        engine.connect(engine.mainMixerNode, to: engine.outputNode, format: hardwareFormat)
        
        do { try engine.start() } catch {
            isPlaying = false
            fatalError("Could not start engine. error: \(error).")
        }
        
        if avAudioUnit.wantsAudioInput { player.play() }
        isPlaying = true
    }
    
    
    private func resetAudioLoop() {
        guard let avAudioUnit = self.avAudioUnit else { return }
        if avAudioUnit.wantsAudioInput {
            guard let format = file?.processingFormat else { fatalError("No AVAudioFile defined.") }
            engine.connect(player, to: engine.mainMixerNode, format: format)
        }
    }
    
    public func connect(avAudioUnit: AVAudioUnit?, completion: @escaping (() -> Void) = {}) {
        guard let avAudioUnit = self.avAudioUnit else { return }
        
        engine.disconnectNodeInput(engine.mainMixerNode)
        resetAudioLoop()
        engine.detach(avAudioUnit)
        
        func rewiringComplete() {
            scheduleMIDIEventListBlock = auAudioUnit.scheduleMIDIEventListBlock
            if isPlaying { player.play() }
            completion()
        }
        
        let hardwareFormat = engine.outputNode.outputFormat(forBus: 0)
        engine.connect(engine.mainMixerNode, to: engine.outputNode, format: hardwareFormat)
        if isPlaying { player.pause() }
        
        let auAudioUnit = avAudioUnit.auAudioUnit
        if !auAudioUnit.midiOutputNames.isEmpty { auAudioUnit.midiOutputEventBlock = midiOutBlock }
        engine.attach(avAudioUnit)
        
        if avAudioUnit.wantsAudioInput {
            engine.disconnectNodeInput(engine.mainMixerNode)
            if let format = file?.processingFormat {
                engine.connect(player, to: avAudioUnit, format: format)
                engine.connect(avAudioUnit, to: engine.mainMixerNode, format: format)
            }
        } else {
            let stereoFormat = AVAudioFormat(standardFormatWithSampleRate: hardwareFormat.sampleRate, channels: 2)
            engine.connect(avAudioUnit, to: engine.mainMixerNode, format: stereoFormat)
        }
        rewiringComplete()
    }
}

and my MIDI Manager



@MainActor
class MIDIManager: Identifiable, ObservableObject {
    
    func setupPort(midiProtocol: MIDIProtocolID,
                   receiveBlock: @escaping @Sendable MIDIReceiveBlock) -> Bool {
        guard setupClient() else { return false }
        
        if MIDIInputPortCreateWithProtocol(client, portName, midiProtocol, &port, receiveBlock) != noErr {
            return false
        }
        
        for source in self.sources {
            if MIDIPortConnectSource(port, source, nil) != noErr {
                print("Failed to connect to source \(source)")
                return false
            }
        }
        
        setupVirtualMIDIOutput()
        return true
    }
    
    private func setupVirtualMIDIOutput() {
        let virtualStatus = MIDISourceCreate(client, virtualSourceName, &virtualSource)
        if virtualStatus != noErr {
            print("❌ Failed to create virtual MIDI source: \(virtualStatus)")
        } else {
            print("✅ Created virtual MIDI source: \(virtualSourceName)")
        }
    }
    
    
    func sendMIDIData(_ data: [UInt8]) {
        print("hey")
        var packetList = MIDIPacketList()
        withUnsafeMutablePointer(to: &packetList) { ptr in
            let pkt = MIDIPacketListInit(ptr)
            _ = MIDIPacketListAdd(ptr, 1024, pkt, 0, data.count, data)
            
            if virtualSource != 0 {
                let status = MIDIReceived(virtualSource, ptr)
                if status != noErr {
                    print("❌ Failed to send MIDI data: \(status)")
                } else {
                    print("✅ Sent MIDI data: \(data)")
                }
            }
        }
    }
    

}
MIDI output form Standalone MIDI Processor Demo App to DAW
 
 
Q