gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

commit 2fccfd62984d6bb416458de95074de15d4ad9ef1
parent e0d23b53fe12e463d846d2f75025cd03934e8830
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Fri,  9 Aug 2024 19:09:29 +0200

Merge branch 'oss/main' into priv/n2x

# Conflicts:
#	doc/changelog.txt
#	source/jucePluginLib/midiports.cpp
#	source/jucePluginLib/midiports.h

Diffstat:
Mdoc/changelog.txt | 4++++
Msource/jucePluginEditorLib/midiPorts.cpp | 67++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msource/jucePluginEditorLib/midiPorts.h | 8++++++++
Msource/jucePluginLib/midiports.cpp | 10++++++++++
Msource/jucePluginLib/midiports.h | 10++++++++--
Msource/mqJucePlugin/PluginEditorState.cpp | 4++++
6 files changed, 94 insertions(+), 9 deletions(-)

diff --git a/doc/changelog.txt b/doc/changelog.txt @@ -17,6 +17,10 @@ NodalRed2x: - [Fix] Do not attempt to boot a rom that is not for N2x +Vavra: + +- [Imp] Physical MIDI in/out can now be selected via global context menu + Xenia: - [Fix] Do not expose parameters for parts 9-16 as the machine only has 8 diff --git a/source/jucePluginEditorLib/midiPorts.cpp b/source/jucePluginEditorLib/midiPorts.cpp @@ -6,11 +6,13 @@ namespace { + constexpr const char* const g_none = "<none>"; + void initComboBox(juce::ComboBox* _combo, const juce::Array<juce::MidiDeviceInfo>& _entries, const juce::String& _selectedEntry) { int inIndex = 0; - _combo->addItem("<none>", 1); + _combo->addItem(g_none, 1); for (int i = 0; i < _entries.size(); i++) { @@ -24,30 +26,76 @@ namespace _combo->setSelectedItemIndex(inIndex, juce::dontSendNotification); } + + uint32_t createMenu(juce::PopupMenu& _menu, const juce::Array<juce::MidiDeviceInfo>& _devices, const juce::String& _current, const std::function<void(juce::String)>& _onSelect) + { + _menu.addItem(g_none, true, _current.isEmpty(), [_onSelect] + { + _onSelect({}); + }); + + for (const auto& device : _devices) + { + _menu.addItem(device.name, true, device.identifier == _current, [id = device.identifier, _onSelect] + { + _onSelect(id); + }); + } + return _devices.size(); + } } namespace jucePluginEditorLib { MidiPorts::MidiPorts(const genericUI::Editor& _editor, Processor& _processor) : m_processor(_processor) { - m_midiIn = _editor.findComponentT<juce::ComboBox>("MidiIn", false); + m_midiIn = _editor.findComponentT<juce::ComboBox>("MidiIn" , false); m_midiOut = _editor.findComponentT<juce::ComboBox>("MidiOut", false); if(m_midiIn) { - const auto* in = getMidiPorts().getMidiInput(); - initComboBox(m_midiIn, juce::MidiInput::getAvailableDevices(), in != nullptr ? in->getIdentifier() : juce::String()); + initComboBox(m_midiIn, juce::MidiInput::getAvailableDevices(), getMidiPorts().getInputId()); m_midiIn->onChange = [this]{ updateMidiInput(m_midiIn->getSelectedItemIndex()); }; } if(m_midiOut) { - const auto* out = getMidiPorts().getMidiOutput(); - initComboBox(m_midiOut, juce::MidiOutput::getAvailableDevices(), out != nullptr ? out->getIdentifier() : juce::String()); + initComboBox(m_midiOut, juce::MidiOutput::getAvailableDevices(), getMidiPorts().getOutputId()); m_midiOut->onChange = [this]{ updateMidiOutput(m_midiOut->getSelectedItemIndex()); }; } } + void MidiPorts::createMidiInputMenu(juce::PopupMenu& _menu, pluginLib::MidiPorts& _ports) + { + createMenu(_menu, juce::MidiInput::getAvailableDevices(), _ports.getInputId(), [&_ports](const juce::String& _id) + { + if(!_ports.setMidiInput(_id)) + showMidiPortFailedMessage(_ports.getProcessor(), "Input"); + }); + } + + void MidiPorts::createMidiOutputMenu(juce::PopupMenu& _menu, pluginLib::MidiPorts& _ports) + { + createMenu(_menu, juce::MidiOutput::getAvailableDevices(), _ports.getOutputId(), [&_ports](const juce::String& _id) + { + if(!_ports.setMidiOutput(_id)) + showMidiPortFailedMessage(_ports.getProcessor(), "Output"); + }); + } + + void MidiPorts::createMidiPortsMenu(juce::PopupMenu& _menu, pluginLib::MidiPorts& _ports) + { + juce::PopupMenu inputs, outputs, ports; + + createMidiInputMenu(inputs, _ports); + createMidiOutputMenu(outputs, _ports); + + ports.addSubMenu("Input", inputs); + ports.addSubMenu("Output", outputs); + + _menu.addSubMenu("MIDI Ports", ports); + } + pluginLib::MidiPorts& MidiPorts::getMidiPorts() const { return m_processor.getMidiPorts(); @@ -55,7 +103,12 @@ namespace jucePluginEditorLib void MidiPorts::showMidiPortFailedMessage(const char* _name) const { - juce::NativeMessageBox::showMessageBoxAsync(juce::MessageBoxIconType::WarningIcon, m_processor.getProperties().name, + showMidiPortFailedMessage(m_processor, _name); + } + + void MidiPorts::showMidiPortFailedMessage(const pluginLib::Processor& _processor, const char* _name) + { + juce::NativeMessageBox::showMessageBoxAsync(juce::MessageBoxIconType::WarningIcon, _processor.getProperties().name, std::string("Failed to open Midi ") + _name + ".\n\n" "Make sure that the device is not already in use by another program.", nullptr, juce::ModalCallbackFunction::create([](int){})); } diff --git a/source/jucePluginEditorLib/midiPorts.h b/source/jucePluginEditorLib/midiPorts.h @@ -2,6 +2,7 @@ namespace pluginLib { + class Processor; class MidiPorts; } @@ -12,6 +13,7 @@ namespace genericUI namespace juce { + class PopupMenu; struct MidiDeviceInfo; class String; class AudioDeviceManager; @@ -27,10 +29,16 @@ namespace jucePluginEditorLib public: explicit MidiPorts(const genericUI::Editor& _editor, Processor& _processor); + static void createMidiInputMenu(juce::PopupMenu& _menu, pluginLib::MidiPorts&); + static void createMidiOutputMenu(juce::PopupMenu& _menu, pluginLib::MidiPorts&); + static void createMidiPortsMenu(juce::PopupMenu& _menu, pluginLib::MidiPorts&); + private: pluginLib::MidiPorts& getMidiPorts() const; void showMidiPortFailedMessage(const char* _name) const; + static void showMidiPortFailedMessage(const pluginLib::Processor& _processor, const char* _name); + void updateMidiInput(int _index) const; void updateMidiOutput(int _index) const; diff --git a/source/jucePluginLib/midiports.cpp b/source/jucePluginLib/midiports.cpp @@ -28,6 +28,16 @@ namespace pluginLib return m_midiInput.get(); } + juce::String MidiPorts::getInputId() const + { + return getMidiInput() != nullptr ? getMidiInput()->getIdentifier() : juce::String(); + } + + juce::String MidiPorts::getOutputId() const + { + return getMidiOutput() != nullptr ? getMidiOutput()->getIdentifier() : juce::String(); + } + void MidiPorts::saveChunkData(baseLib::BinaryStream& _binaryStream) const { baseLib::ChunkWriter cw(_binaryStream, "mpIO", 1); diff --git a/source/jucePluginLib/midiports.h b/source/jucePluginLib/midiports.h @@ -31,11 +31,17 @@ namespace pluginLib MidiPorts& operator = (const MidiPorts&) = delete; MidiPorts& operator = (MidiPorts&&) = delete; - bool setMidiOutput(const juce::String& _out); + auto& getProcessor() const { return m_processor; } + juce::MidiOutput* getMidiOutput() const; - bool setMidiInput(const juce::String& _in); juce::MidiInput* getMidiInput() const; + bool setMidiOutput(const juce::String& _out); + bool setMidiInput(const juce::String& _in); + + juce::String getInputId() const; + juce::String getOutputId() const; + void saveChunkData(baseLib::BinaryStream& _binaryStream) const; void loadChunkData(baseLib::ChunkReader& _cr); diff --git a/source/mqJucePlugin/PluginEditorState.cpp b/source/mqJucePlugin/PluginEditorState.cpp @@ -3,6 +3,8 @@ #include "mqEditor.h" #include "PluginProcessor.h" +#include "jucePluginEditorLib/midiPorts.h" + #include "synthLib/os.h" const std::vector<PluginEditorState::Skin> g_includedSkins = @@ -31,6 +33,8 @@ void PluginEditorState::initContextMenu(juce::PopupMenu& _menu) gainMenu.addItem("+12 dB", true, gain == 4, [&p] { p.setOutputGain(4); }); _menu.addSubMenu("Output Gain", gainMenu); + + jucePluginEditorLib::MidiPorts::createMidiPortsMenu(_menu, p.getMidiPorts()); } bool PluginEditorState::initAdvancedContextMenu(juce::PopupMenu& _menu, bool _enabled)