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)
};