You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在AudioKit Keyboard上显示MIDI键盘按下的按键?

解决AudioKit Keyboard同步MIDI键盘按键显示问题

问题说明

已实现MIDI键盘输入的监听,也能展示AudioKit的Keyboard组件,但无法将MIDI键盘按下的按键状态同步到AudioKit Keyboard上,希望实现MIDI按键时,对应的Keyboard琴键同步高亮显示。

解决方案

AudioKit的Keyboard组件支持通过pressedPitches参数绑定已按下的音高集合,只需在MIDI监听逻辑中维护这个集合,就能实现同步显示。具体修改如下:

1. 更新MIDI监听类,维护已按下音高集合

在MIDIMonitorConductor中添加一个@Published修饰的Set<Pitch>,用于存储当前按下的音高:

class MIDIMonitorConductor: ObservableObject, MIDIListener {
    let midi = MIDI()
    @Published var data = MIDIMonitorData()
    @Published var midiEventType: MIDIEventType = .none
    // 添加已按下音高集合
    @Published var pressedPitches: Set<Pitch> = []
    
    // ... 其他原有代码 ...
    
    func receivedMIDINoteOn(noteNumber: MIDINoteNumber,
                            velocity: MIDIVelocity,
                            channel: MIDIChannel,
                            portID _: MIDIUniqueID?,
                            timeStamp _: MIDITimeStamp?)
    {
        DispatchQueue.main.async {
            self.midiEventType = .noteOn
            self.data.noteOn = Int(noteNumber)
            self.data.velocity = Int(velocity)
            self.data.channel = Int(channel)
            
            // 将MIDI音符转换为Pitch并加入集合
            if let pitch = Pitch(midiNoteNumber: noteNumber) {
                self.pressedPitches.insert(pitch)
            }
            
            print(Int(noteNumber))
        }
    }
    
    func receivedMIDINoteOff(noteNumber: MIDINoteNumber,
                             velocity: MIDIVelocity,
                             channel: MIDIChannel,
                             portID _: MIDIUniqueID?,
                             timeStamp _: MIDITimeStamp?)
    {
        DispatchQueue.main.async {
            self.midiEventType = .noteOff
            self.data.noteOff = Int(noteNumber)
            self.data.velocity = Int(velocity)
            self.data.channel = Int(channel)
            
            // 将对应的Pitch从集合中移除
            if let pitch = Pitch(midiNoteNumber: noteNumber) {
                self.pressedPitches.remove(pitch)
            }
        }
    }
    
    // ... 其他原有代码 ...
}

2. 在ContentView中绑定Keyboard的pressedPitches参数

修改Keyboard的初始化代码,将pressedPitches绑定到conductor的对应属性:

struct ContentView: View {
    @StateObject private var conductor = MIDIMonitorConductor()
    
    var body: some View {
        HStack {
            VStack {
                Keyboard(
                    layout: .verticalPiano(pitchRange: Pitch(intValue: 21) ... Pitch(intValue: 108)),
                    // 绑定已按下音高集合
                    pressedPitches: $conductor.pressedPitches
                ).frame(width: 150)
            }
        } .onAppear {
            conductor.start()
        }
        .onDisappear {
            conductor.stop()
        }
    }
}

原理说明

AudioKit的Keyboard组件内部会监听pressedPitches的变化,当集合中的音高更新时,自动对应高亮或取消高亮相应的琴键。通过在MIDI的NoteOn/NoteOff事件中更新这个集合,就能实现MIDI键盘与UI键盘的状态同步。

内容的提问来源于stack exchange,提问作者sidneivl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 17:47:56