Listenning Midi devices, plug, unplug and plug devices from computer again

Hi there,
In the tutorial: Handling MIDI events, it could handing MIDI data from an external source. My win7 compuetr has not MIDI input port itself. I connect presonus 2|6 into my computer. It has one midi input port and one midi output port. The midi input port and midi output port is loopbacked. Before run the demo program, presonus 2|6 has been connect into my computer through USB cable.After runing the demo program, it could listenning the midi messages from midi input port.But it failed to listen to the midi messages from midi input port when the device is unplugged from and then plugged into my compuetr again. At this time, i must close the demo program and rerun it again. So it could listen the midi input port again. I modified the code to listen the midi input not the use of closing the demo program and rerun it again when the device is unplugged from and then plugged into my compuetr again. But it seems still not work!

class MainContentComponent  : public Component,
                              private MidiInputCallback,
                              private MidiKeyboardStateListener,
	                          private Timer
{
public:
    MainContentComponent()
      : keyboardComponent (keyboardState, MidiKeyboardComponent::horizontalKeyboard),
        startTime (Time::getMillisecondCounterHiRes() * 0.001)
    {
        setOpaque (true);

        addAndMakeVisible (midiInputListLabel);
        midiInputListLabel.setText ("MIDI Input:", dontSendNotification);
        midiInputListLabel.attachToComponent (&midiInputList, true);

        addAndMakeVisible (midiInputList);
        midiInputList.setTextWhenNoChoicesAvailable ("No MIDI Inputs Enabled");
        auto midiInputs = MidiInput::getDevices();
		pre_item_list = midiInputs;
        midiInputList.addItemList (midiInputs, 1);
        midiInputList.onChange = [this] { setMidiInput (midiInputList.getSelectedItemIndex()); };

        // find the first enabled device and use that by default
        for (auto midiInput : midiInputs)
        {
            if (deviceManager.isMidiInputEnabled (midiInput))
            {
                setMidiInput (midiInputs.indexOf (midiInput));
                break;
            }
        }

        // if no enabled devices were found just use the first one in the list
        if (midiInputList.getSelectedId() == 0)
            setMidiInput (0);

        addAndMakeVisible (keyboardComponent);
        keyboardState.addListener (this);

        addAndMakeVisible (midiMessagesBox);
        midiMessagesBox.setMultiLine (true);
        midiMessagesBox.setReturnKeyStartsNewLine (true);
        midiMessagesBox.setReadOnly (true);
        midiMessagesBox.setScrollbarsShown (true);
        midiMessagesBox.setCaretVisible (false);
        midiMessagesBox.setPopupMenuEnabled (true);
        midiMessagesBox.setColour (TextEditor::backgroundColourId, Colour (0x32ffffff));
        midiMessagesBox.setColour (TextEditor::outlineColourId, Colour (0x1c000000));
        midiMessagesBox.setColour (TextEditor::shadowColourId, Colour (0x16000000));

		startTimer(100);
        setSize (600, 400);
    }

    ~MainContentComponent() override
    {
        keyboardState.removeListener (this);
        deviceManager.removeMidiInputCallback (MidiInput::getDevices()[midiInputList.getSelectedItemIndex()], this);
    }

    void paint (Graphics& g) override
    {
        g.fillAll (Colours::black);
    }

    void resized() override
    {
        auto area = getLocalBounds();

        midiInputList    .setBounds (area.removeFromTop (36).removeFromRight (getWidth() - 150).reduced (8));
        keyboardComponent.setBounds (area.removeFromTop (80).reduced(8));
        midiMessagesBox  .setBounds (area.reduced (8));
    }

private:
	void timerCallback() override
	{
		now_item_list = MidiInput::getDevices();
		if (now_item_list != pre_item_list)
		{
			midiInputList.clear(dontSendNotification);
			midiInputList.addItemList(now_item_list, 1);
		    setMidiInput(0);

		}
		pre_item_list = now_item_list;
	}
	//============================================================
    static String getMidiMessageDescription (const MidiMessage& m)
    {
        if (m.isNoteOn())           return "Note on "          + MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) + "::" + String::toHexString(m.getRawData(), m.getRawDataSize());
        if (m.isNoteOff())          return "Note off "         + MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) + "::" + String::toHexString(m.getRawData(), m.getRawDataSize());
        if (m.isProgramChange())    return "Program change "   + String (m.getProgramChangeNumber()) + "::" + String::toHexString(m.getRawData(), m.getRawDataSize());
        if (m.isPitchWheel())       return "Pitch wheel "      + String (m.getPitchWheelValue()) + "::" + String::toHexString(m.getRawData(), m.getRawDataSize());
        if (m.isAftertouch())       return "After touch "      + MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) +  ": " + String (m.getAfterTouchValue()) + "::" + String::toHexString(m.getRawData(), m.getRawDataSize());
        if (m.isChannelPressure())  return "Channel pressure " + String (m.getChannelPressureValue()) + "::" + String::toHexString(m.getRawData(), m.getRawDataSize());
        if (m.isAllNotesOff())      return "All notes off" + String("::") + String::toHexString(m.getRawData(), m.getRawDataSize());
        if (m.isAllSoundOff())      return "All sound off" + String("::") + String::toHexString(m.getRawData(), m.getRawDataSize());
        if (m.isMetaEvent())        return "Meta event" + String("::") + String::toHexString(m.getRawData(), m.getRawDataSize());

        if (m.isController())
        {
            String name (MidiMessage::getControllerName (m.getControllerNumber()));

            if (name.isEmpty())
                name = "[" + String (m.getControllerNumber()) + "]";
			
            return "Controller " + name + ": " + String (m.getControllerValue()) + "::" + String::toHexString(m.getRawData(), m.getRawDataSize());
        }

        return String::toHexString (m.getRawData(), m.getRawDataSize());
    }

    void logMessage (const String& m)
    {
        midiMessagesBox.moveCaretToEnd();
        midiMessagesBox.insertTextAtCaret (m + newLine);
    }

    /** Starts listening to a MIDI input device, enabling it if necessary. */
    void setMidiInput (int index)
    {
        auto list = MidiInput::getDevices();

        deviceManager.removeMidiInputCallback (list[lastInputIndex], this);

        auto newInput = list[index];

        if (! deviceManager.isMidiInputEnabled (newInput))
            deviceManager.setMidiInputEnabled (newInput, true);

        deviceManager.addMidiInputCallback (newInput, this);
        midiInputList.setSelectedId (index + 1, dontSendNotification);

        lastInputIndex = index;
    }

    // These methods handle callbacks from the midi device + on-screen keyboard..
    void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) override
    {
        const ScopedValueSetter<bool> scopedInputFlag (isAddingFromMidiInput, true);
        keyboardState.processNextMidiEvent (message);
        postMessageToList (message, source->getName());
    }

    void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override
    {
        if (! isAddingFromMidiInput)
        {
            auto m = MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity);
            m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
            postMessageToList (m, "On-Screen Keyboard");
        }
    }

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

    // This is used to dispach an incoming message to the message thread
    class IncomingMessageCallback   : public CallbackMessage
    {
    public:
        IncomingMessageCallback (MainContentComponent* o, const MidiMessage& m, const String& s)
           : owner (o), message (m), source (s)
        {}

        void messageCallback() override
        {
            if (owner != nullptr)
                owner->addMessageToList (message, source);
        }

        Component::SafePointer<MainContentComponent> owner;
        MidiMessage message;
        String source;
    };

    void postMessageToList (const MidiMessage& message, const String& source)
    {
        (new IncomingMessageCallback (this, message, source))->post();
    }

    void addMessageToList (const MidiMessage& message, const String& source)
    {
        auto time = message.getTimeStamp() - startTime;

        auto hours   = ((int) (time / 3600.0)) % 24;
        auto minutes = ((int) (time / 60.0)) % 60;
        auto seconds = ((int) time) % 60;
        auto millis  = ((int) (time * 1000.0)) % 1000;

        auto timecode = String::formatted ("%02d:%02d:%02d.%03d",
                                           hours,
                                           minutes,
                                           seconds,
                                           millis);

        auto description = getMidiMessageDescription (message);

        String midiMessageString (timecode + "  -  " + description + " (" + source + ")"); // [7]
        logMessage (midiMessageString);
    }

    //==============================================================================
    AudioDeviceManager deviceManager;           // [1]
    ComboBox midiInputList;                     // [2]
    Label midiInputListLabel;
    int lastInputIndex = 0;                     // [3]
    bool isAddingFromMidiInput = false;         // [4]

    MidiKeyboardState keyboardState;            // [5]
    MidiKeyboardComponent keyboardComponent;    // [6]

    TextEditor midiMessagesBox;
    double startTime;
	//==============================================================================
	StringArray now_item_list = {};
	StringArray pre_item_list = {};
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

I mainly use the timer callback function. When the device is unplugged and plugged again, the device items list will change. Once the device items list change, it would update the device items list and reset the midi input port. Here only one midi input port.

Any one know how to do that?

Hello, Caoleiwe!I would like to ask if the problem of JUCE monitoring the plug and unplug device of MIDI device has been solved.I had the same problem!

@caoleiwe
Do you have a solution?