MIDI demo questions


#1

New to JUCE and relatively new to C++ (although I’ve been a C programmer for years). Having a bit of trouble understanding a couple things (and couldn’t find anything relevant with a search).

My biggest questions right now with the HandlingMidiEventsTutorial are:
1) Why are handleIncomingMidiMessage and handleNoteOn/handleNoteOff all handled separately?
It seems to be to handle the HW vs SW MIDI inputs separately, but the HW triggers the On/Off callbacks and there’s special logic to work around this. Also the comment mentions handleIncomingMidiMessage handling both HW and SW keyboard, but behavior doesn’t reflect that. My guess is it’s partially to show two different handlers for these events, but I may be off.
2) How are each of those subscribed to specific events?
I assume it’s deeper in the base classes (MidiInputCallback and MidiKeyboardStateListener, respectively), but does that mean MidiKeyboardStateListener is only meant for SW keyboards? Or was the HW just not subscribed to that callback?
3) Threads for JUCE?
Is there documentation for what classes spawn which threads when? Some of the class API mentions it, but not all (and I see a 8-ish threads generated for this demo). This may just require digging deeper into the base class headers (at least for the GUI stuff I’m mostly ignoring for now).

I had previously attempted to write a lot of this myself from scratch in C, but that was unreasonable. JUCE seems like the perfect fit for projects I want to work on, just need to brush up my C++ a bit more.

Thanks!


#2

Hello,

  1. Why are handleIncomingMidiMessage and handleNoteOn/handleNoteOff all handled separately?

You’re right that this distinction between HW and SW sources might not be needed in some cases, but in the tutorial this is used to identify MIDI notes coming from the “On-Screen Keyboard”. We still need to update the MidiKeyboardState from handleIncomingMidiMessage() in order for the keyboard component to update but we don’t want to respond to events twice. An alternative implementation for those three methods could be the following. Storing the source name in a string in handleIncomingMidiMessage() and clearing it before return. Then we can assume that an empty string is the on-screen keyboard.

        //...
    private:
        String midiSource;
        
    public:
        void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) override
        {
            midiSource = source->getName();
            keyboardState.processNextMidiEvent (message);
            midiSource = String();
        }

        void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override
        {
            MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity));
            m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
            postMessageToList (m, midiSource.isEmpty() ? "On-Screen Keyboard" : midiSource);
        }

        void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float /*velocity*/) override
        {
            MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber));
            m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
            postMessageToList (m, midiSource.isEmpty() ? "On-Screen Keyboard" : midiSource);
        }
        
        //...

Then you would be handling all the note ons/offs in handleNoteOn() and handleNoteOff().

does that mean MidiKeyboardStateListener is only meant for SW keyboards?

No, it is just the state of the keyboard, but one is required by the MidiKeyboardComponent class to know what to display. The MidiKeyboardComponent updates the MidiKeyboardState but it isn’t updated from HW automatically, you need to update it manually from your handleIncomingMidiMessage() callback.


#3

Thank you so much for the thorough response! Glad I was on the right track with that. The clarification with your code really helps me