|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2017 - ROLI Ltd.
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 5 End-User License
- Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
- 27th April 2017).
-
- End User License Agreement: www.juce.com/juce-5-licence
- Privacy Policy: www.juce.com/juce-5-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- #include "../JuceDemoHeader.h"
-
- /** Simple list box that just displays a StringArray. */
- class MidiLogListBoxModel : public ListBoxModel
- {
- public:
- MidiLogListBoxModel (const Array<MidiMessage>& list)
- : midiMessageList (list)
- {
- }
-
- int getNumRows() override { return midiMessageList.size(); }
-
- void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) override
- {
- if (rowIsSelected)
- g.fillAll (Colours::blue.withAlpha (0.2f));
-
- if (isPositiveAndBelow (row, midiMessageList.size()))
- {
- g.setColour (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText,
- Colours::black));
-
- const MidiMessage& message = midiMessageList.getReference (row);
- double time = message.getTimeStamp();
-
- g.drawText (String::formatted ("%02d:%02d:%02d",
- ((int) (time / 3600.0)) % 24,
- ((int) (time / 60.0)) % 60,
- ((int) time) % 60)
- + " - " + message.getDescription(),
- Rectangle<int> (width, height).reduced (4, 0),
- Justification::centredLeft, true);
- }
- }
-
- private:
- const Array<MidiMessage>& midiMessageList;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiLogListBoxModel)
- };
-
- //==============================================================================
- class MidiDemo : public Component,
- private ComboBox::Listener,
- private MidiInputCallback,
- private MidiKeyboardStateListener,
- private AsyncUpdater
- {
- public:
- MidiDemo()
- : deviceManager (MainAppWindow::getSharedAudioDeviceManager()),
- lastInputIndex (0), isAddingFromMidiInput (false),
- keyboardComponent (keyboardState, MidiKeyboardComponent::horizontalKeyboard),
- midiLogListBoxModel (midiMessageList)
- {
- setOpaque (true);
-
- // MIDI Inputs
- addAndMakeVisible (midiInputListLabel);
- midiInputListLabel.setText ("MIDI Input:", dontSendNotification);
- midiInputListLabel.attachToComponent (&midiInputList, true);
-
- addAndMakeVisible (midiInputList);
- midiInputList.setTextWhenNoChoicesAvailable ("No MIDI Inputs Enabled");
- const StringArray midiInputs (MidiInput::getDevices());
- midiInputList.addItemList (midiInputs, 1);
- midiInputList.addListener (this);
-
- // find the first enabled device and use that by default
- for (int i = 0; i < midiInputs.size(); ++i)
- {
- if (deviceManager.isMidiInputEnabled (midiInputs[i]))
- {
- setMidiInput (i);
- break;
- }
- }
-
- // if no enabled devices were found just use the first one in the list
- if (midiInputList.getSelectedId() == 0)
- setMidiInput (0);
-
-
- // MIDI Outputs
- addAndMakeVisible (midiOutputListLabel);
- midiOutputListLabel.setText ("MIDI Output:", dontSendNotification);
- midiOutputListLabel.attachToComponent (&midiOutputList, true);
-
- addAndMakeVisible (midiOutputList);
- midiOutputList.setTextWhenNoChoicesAvailable ("No MIDI Output Enabled");
- midiOutputList.addItemList (MidiOutput::getDevices(), 1);
- midiOutputList.addListener (this);
-
- addAndMakeVisible (keyboardComponent);
- keyboardState.addListener (this);
-
- addAndMakeVisible (messageListBox);
- messageListBox.setModel (&midiLogListBoxModel);
- }
-
- ~MidiDemo()
- {
- keyboardState.removeListener (this);
- deviceManager.removeMidiInputCallback (MidiInput::getDevices()[midiInputList.getSelectedItemIndex()], this);
- midiInputList.removeListener (this);
- }
-
- void paint (Graphics& g) override
- {
- g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
- }
-
- void resized() override
- {
- Rectangle<int> area (getLocalBounds());
- midiInputList.setBounds (area.removeFromTop (36).removeFromRight (getWidth() - 150).reduced (8));
- midiOutputList.setBounds (area.removeFromTop (36).removeFromRight (getWidth() - 150).reduced (8));
- keyboardComponent.setBounds (area.removeFromTop (80).reduced(8));
- messageListBox.setBounds (area.reduced (8));
- }
-
- private:
- AudioDeviceManager& deviceManager;
- ComboBox midiInputList, midiOutputList;
- Label midiInputListLabel, midiOutputListLabel;
- int lastInputIndex;
- bool isAddingFromMidiInput;
-
- MidiKeyboardState keyboardState;
- MidiKeyboardComponent keyboardComponent;
-
- ListBox messageListBox;
- Array<MidiMessage> midiMessageList;
- MidiLogListBoxModel midiLogListBoxModel;
- ScopedPointer<MidiOutput> currentMidiOutput;
-
- //==============================================================================
- /** Starts listening to a MIDI input device, enabling it if necessary. */
- void setMidiInput (int index)
- {
- const StringArray list (MidiInput::getDevices());
-
- deviceManager.removeMidiInputCallback (list[lastInputIndex], this);
-
- const String newInput (list[index]);
-
- if (! deviceManager.isMidiInputEnabled (newInput))
- deviceManager.setMidiInputEnabled (newInput, true);
-
- deviceManager.addMidiInputCallback (newInput, this);
- midiInputList.setSelectedId (index + 1, dontSendNotification);
-
- lastInputIndex = index;
- }
-
- //==============================================================================
- void setMidiOutput (int index)
- {
- currentMidiOutput = nullptr;
-
- if (MidiOutput::getDevices() [index].isNotEmpty())
- {
- currentMidiOutput = MidiOutput::openDevice (index);
- jassert (currentMidiOutput);
- }
- }
-
- void comboBoxChanged (ComboBox* box) override
- {
- if (box == &midiInputList) setMidiInput (midiInputList.getSelectedItemIndex());
- if (box == &midiOutputList) setMidiOutput (midiOutputList.getSelectedItemIndex());
- }
-
- // These methods handle callbacks from the midi device + on-screen keyboard..
- void handleIncomingMidiMessage (MidiInput*, const MidiMessage& message) override
- {
- const ScopedValueSetter<bool> scopedInputFlag (isAddingFromMidiInput, true);
- keyboardState.processNextMidiEvent (message);
- postMessageToList (message);
- }
-
- void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override
- {
- if (! isAddingFromMidiInput)
- {
- MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity));
- m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
- postMessageToList (m);
- }
- }
-
- void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override
- {
- if (! isAddingFromMidiInput)
- {
- MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber, velocity));
- m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
- postMessageToList (m);
- }
- }
-
- // This is used to dispach an incoming message to the message thread
- struct IncomingMessageCallback : public CallbackMessage
- {
- IncomingMessageCallback (MidiDemo* d, const MidiMessage& m)
- : demo (d), message (m) {}
-
- void messageCallback() override
- {
- if (demo != nullptr)
- demo->addMessageToList (message);
- }
-
- Component::SafePointer<MidiDemo> demo;
- MidiMessage message;
- };
-
- void postMessageToList (const MidiMessage& message)
- {
- if (currentMidiOutput != nullptr)
- currentMidiOutput->sendMessageNow (message);
-
- (new IncomingMessageCallback (this, message))->post();
- }
-
- void addMessageToList (const MidiMessage& message)
- {
- midiMessageList.add (message);
- triggerAsyncUpdate();
- }
-
- void handleAsyncUpdate() override
- {
- messageListBox.updateContent();
- messageListBox.scrollToEnsureRowIsOnscreen (midiMessageList.size() - 1);
- messageListBox.repaint();
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiDemo)
- };
-
-
- // This static object will register this demo type in a global list of demos..
- static JuceDemoType<MidiDemo> demo ("32 Audio: MIDI i/o");
|