commit 8b3d0c0dca30f9a2119d809408b4d1ce15a88a74
parent 29a329f3b36a08180880b54b9a42119786277a25
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Thu, 8 Aug 2024 20:12:57 +0200
Merge branch 'oss/main' into priv/n2x
# Conflicts:
# doc/changelog.txt
Diffstat:
11 files changed, 241 insertions(+), 150 deletions(-)
diff --git a/doc/changelog.txt b/doc/changelog.txt
@@ -2,6 +2,12 @@ Release Notes
1.3.18
+Framework:
+
+- [Imp] Physical MIDI port selection is now stored per instance
+
+- [Fix] Physical MIDI ports were not opened before GUI was opened
+
NodalRed2x:
- [Imp] Implement Morph parameters / VM Map button
@@ -14,6 +20,7 @@ NodalRed2x:
Xenia:
- [Fix] Do not expose parameters for parts 9-16 as the machine only has 8
+- [Fix] Physical MIDI in/out port selectors didn't work
1.3.17 (2024.08.04)
diff --git a/source/jucePluginEditorLib/midiPorts.cpp b/source/jucePluginEditorLib/midiPorts.cpp
@@ -4,75 +4,53 @@
#include "juceUiLib/editor.h"
-namespace jucePluginEditorLib
+namespace
{
- MidiPorts::MidiPorts(const genericUI::Editor& _editor, Processor& _processor) : m_processor(_processor)
+ void initComboBox(juce::ComboBox* _combo, const juce::Array<juce::MidiDeviceInfo>& _entries, const juce::String& _selectedEntry)
{
- const auto& properties = m_processor.getConfig();
-
- const auto midiIn = properties.getValue("midi_input", "");
- const auto midiOut = properties.getValue("midi_output", "");
-
- if (!midiIn.isEmpty())
- m_processor.setMidiInput(midiIn);
-
- if (!midiOut.isEmpty())
- m_processor.setMidiOutput(midiOut);
-
- m_midiIn = _editor.findComponentT<juce::ComboBox>("MidiIn");
- m_midiOut = _editor.findComponentT<juce::ComboBox>("MidiOut");
-
- m_midiIn->setTextWhenNoChoicesAvailable("-");
-
- const auto midiInputs = juce::MidiInput::getAvailableDevices();
-
int inIndex = 0;
- m_midiIn->addItem("<none>", 1);
+ _combo->addItem("<none>", 1);
- for (int i = 0; i < midiInputs.size(); i++)
+ for (int i = 0; i < _entries.size(); i++)
{
- const auto input = midiInputs[i];
+ const auto& input = _entries[i];
- if (m_processor.getMidiInput() != nullptr && input.identifier == m_processor.getMidiInput()->getIdentifier())
+ if (input.identifier == _selectedEntry)
inIndex = i + 1;
- m_midiIn->addItem(input.name, i+2);
+ _combo->addItem(input.name, i+2);
}
- m_midiIn->setSelectedItemIndex(inIndex, juce::dontSendNotification);
-
- m_midiIn->onChange = [this]() { updateMidiInput(m_midiIn->getSelectedItemIndex()); };
-
- m_midiOut->setTextWhenNoChoicesAvailable("-");
-
- const auto midiOutputs = juce::MidiOutput::getAvailableDevices();
-
- auto outIndex = 0;
+ _combo->setSelectedItemIndex(inIndex, juce::dontSendNotification);
+ }
+}
- m_midiOut->addItem("<none>", 1);
+namespace jucePluginEditorLib
+{
+ MidiPorts::MidiPorts(const genericUI::Editor& _editor, Processor& _processor) : m_processor(_processor)
+ {
+ m_midiIn = _editor.findComponentT<juce::ComboBox>("MidiIn", false);
+ m_midiOut = _editor.findComponentT<juce::ComboBox>("MidiOut", false);
- for (int i = 0; i < midiOutputs.size(); i++)
+ if(m_midiIn)
{
- const auto output = midiOutputs[i];
- if (m_processor.getMidiOutput() != nullptr &&
- output.identifier == m_processor.getMidiOutput()->getIdentifier())
- {
- outIndex = i + 1;
- }
- m_midiOut->addItem(output.name, i+2);
+ const auto* in = getMidiPorts().getMidiInput();
+ initComboBox(m_midiIn, juce::MidiInput::getAvailableDevices(), in != nullptr ? in->getIdentifier() : juce::String());
+ m_midiIn->onChange = [this]{ updateMidiInput(m_midiIn->getSelectedItemIndex()); };
}
- m_midiOut->setSelectedItemIndex(outIndex, juce::dontSendNotification);
-
- m_midiOut->onChange = [this]() { updateMidiOutput(m_midiOut->getSelectedItemIndex()); };
-
- deviceManager = new juce::AudioDeviceManager();
+ if(m_midiOut)
+ {
+ const auto* out = getMidiPorts().getMidiOutput();
+ initComboBox(m_midiOut, juce::MidiOutput::getAvailableDevices(), out != nullptr ? out->getIdentifier() : juce::String());
+ m_midiOut->onChange = [this]{ updateMidiOutput(m_midiOut->getSelectedItemIndex()); };
+ }
}
- MidiPorts::~MidiPorts()
+ pluginLib::MidiPorts& MidiPorts::getMidiPorts() const
{
- delete deviceManager;
+ return m_processor.getMidiPorts();
}
void MidiPorts::showMidiPortFailedMessage(const char* _name) const
@@ -82,71 +60,49 @@ namespace jucePluginEditorLib
"Make sure that the device is not already in use by another program.", nullptr, juce::ModalCallbackFunction::create([](int){}));
}
- void MidiPorts::updateMidiInput(int index)
+ void MidiPorts::updateMidiInput(int _index) const
{
const auto list = juce::MidiInput::getAvailableDevices();
- auto& properties = m_processor.getConfig();
-
- if (index <= 0)
+ if (_index <= 0)
{
- properties.setValue("midi_input", "");
- properties.save();
- m_lastInputIndex = 0;
- m_midiIn->setSelectedItemIndex(index, juce::dontSendNotification);
+ m_midiIn->setSelectedItemIndex(_index, juce::dontSendNotification);
return;
}
- index--;
-
- const auto newInput = list[index];
+ _index--;
- if (!deviceManager->isMidiInputDeviceEnabled(newInput.identifier))
- deviceManager->setMidiInputDeviceEnabled(newInput.identifier, true);
+ const auto newInput = list[_index];
- if (!m_processor.setMidiInput(newInput.identifier))
+ if (!getMidiPorts().setMidiInput(newInput.identifier))
{
showMidiPortFailedMessage("Input");
m_midiIn->setSelectedItemIndex(0, juce::dontSendNotification);
- m_lastInputIndex = 0;
return;
}
- properties.setValue("midi_input", newInput.identifier);
- properties.save();
-
- m_midiIn->setSelectedItemIndex(index + 1, juce::dontSendNotification);
- m_lastInputIndex = index;
+ m_midiIn->setSelectedItemIndex(_index + 1, juce::dontSendNotification);
}
- void MidiPorts::updateMidiOutput(int index)
+ void MidiPorts::updateMidiOutput(int _index) const
{
const auto list = juce::MidiOutput::getAvailableDevices();
- auto& properties = m_processor.getConfig();
-
- if (index == 0)
+ if (_index == 0)
{
- properties.setValue("midi_output", "");
- properties.save();
- m_midiOut->setSelectedItemIndex(index, juce::dontSendNotification);
- m_lastOutputIndex = index;
- m_processor.setMidiOutput("");
+ m_midiOut->setSelectedItemIndex(_index, juce::dontSendNotification);
+ getMidiPorts().setMidiOutput({});
return;
}
- index--;
- const auto newOutput = list[index];
- if (!m_processor.setMidiOutput(newOutput.identifier))
+ _index--;
+ const auto newOutput = list[_index];
+ if (!getMidiPorts().setMidiOutput(newOutput.identifier))
{
showMidiPortFailedMessage("Output");
m_midiOut->setSelectedItemIndex(0, juce::dontSendNotification);
- m_lastOutputIndex = 0;
return;
}
- properties.setValue("midi_output", newOutput.identifier);
- properties.save();
-
- m_midiOut->setSelectedItemIndex(index + 1, juce::dontSendNotification);
- m_lastOutputIndex = index;
+
+ m_midiOut->setSelectedItemIndex(_index + 1, juce::dontSendNotification);
}
}
diff --git a/source/jucePluginEditorLib/midiPorts.h b/source/jucePluginEditorLib/midiPorts.h
@@ -1,5 +1,10 @@
#pragma once
+namespace pluginLib
+{
+ class MidiPorts;
+}
+
namespace genericUI
{
class Editor;
@@ -7,6 +12,8 @@ namespace genericUI
namespace juce
{
+ struct MidiDeviceInfo;
+ class String;
class AudioDeviceManager;
class ComboBox;
}
@@ -19,20 +26,17 @@ namespace jucePluginEditorLib
{
public:
explicit MidiPorts(const genericUI::Editor& _editor, Processor& _processor);
- ~MidiPorts();
private:
+ pluginLib::MidiPorts& getMidiPorts() const;
+
+ void showMidiPortFailedMessage(const char* _name) const;
+ void updateMidiInput(int _index) const;
+ void updateMidiOutput(int _index) const;
+
Processor& m_processor;
juce::ComboBox* m_midiIn = nullptr;
juce::ComboBox* m_midiOut = nullptr;
-
- juce::AudioDeviceManager* deviceManager = nullptr;
- int m_lastInputIndex = 0;
- int m_lastOutputIndex = 0;
-
- void showMidiPortFailedMessage(const char* _name) const;
- void updateMidiInput(int _index);
- void updateMidiOutput(int _index);
};
}
diff --git a/source/jucePluginLib/CMakeLists.txt b/source/jucePluginLib/CMakeLists.txt
@@ -14,6 +14,7 @@ set(SOURCES
dummydevice.cpp dummydevice.h
event.cpp event.h
midipacket.cpp midipacket.h
+ midiports.cpp midiports.h
parameter.cpp parameter.h
parameterbinding.cpp parameterbinding.h
parameterdescription.cpp parameterdescription.h
diff --git a/source/jucePluginLib/midiports.cpp b/source/jucePluginLib/midiports.cpp
@@ -0,0 +1,102 @@
+#include "midiports.h"
+
+#include "processor.h"
+#include "juce_audio_devices/juce_audio_devices.h"
+
+namespace pluginLib
+{
+ MidiPorts::MidiPorts(Processor& _processor) : m_processor(_processor)
+ {
+ }
+
+ MidiPorts::~MidiPorts()
+ {
+ setMidiInput({});
+ setMidiOutput({});
+
+ m_deviceManager.reset();
+ }
+
+ juce::MidiOutput *MidiPorts::getMidiOutput() const
+ {
+ return m_midiOutput.get();
+ }
+
+ juce::MidiInput *MidiPorts::getMidiInput() const
+ {
+ return m_midiInput.get();
+ }
+
+ void MidiPorts::saveChunkData(synthLib::BinaryStream& _binaryStream) const
+ {
+ synthLib::ChunkWriter cw(_binaryStream, "mpIO", 1);
+
+ if(m_midiInput)
+ _binaryStream.write(m_midiInput->getIdentifier().toStdString());
+ else
+ _binaryStream.write(std::string());
+ if(m_midiOutput)
+ _binaryStream.write(m_midiOutput->getIdentifier().toStdString());
+ else
+ _binaryStream.write(std::string());
+ }
+
+ void MidiPorts::loadChunkData(synthLib::ChunkReader& _cr)
+ {
+ _cr.add("mpIO", 1, [&](synthLib::BinaryStream& _data, uint32_t)
+ {
+ const auto input = _data.readString();
+ const auto output = _data.readString();
+
+ setMidiInput(input);
+ setMidiOutput(output);
+ });
+ }
+
+ bool MidiPorts::setMidiOutput(const juce::String& _out)
+ {
+ if (m_midiOutput != nullptr && m_midiOutput->isBackgroundThreadRunning())
+ {
+ m_midiOutput->stopBackgroundThread();
+ }
+ if(_out.isEmpty())
+ return false;
+ m_midiOutput = juce::MidiOutput::openDevice(_out);
+ if (m_midiOutput != nullptr)
+ {
+ m_midiOutput->startBackgroundThread();
+ return true;
+ }
+ return false;
+ }
+
+ bool MidiPorts::setMidiInput(const juce::String& _in)
+ {
+ if (m_midiInput != nullptr)
+ {
+ m_midiInput->stop();
+ }
+
+ if(_in.isEmpty())
+ return false;
+
+ if(!m_deviceManager)
+ m_deviceManager.reset(new juce::AudioDeviceManager());
+
+ if (!m_deviceManager->isMidiInputDeviceEnabled(_in))
+ m_deviceManager->setMidiInputDeviceEnabled(_in, true);
+
+ m_midiInput = juce::MidiInput::openDevice(_in, this);
+ if (m_midiInput != nullptr)
+ {
+ m_midiInput->start();
+ return true;
+ }
+ return false;
+ }
+
+ void MidiPorts::handleIncomingMidiMessage(juce::MidiInput* _source, const juce::MidiMessage& _message)
+ {
+ m_processor.handleIncomingMidiMessage(_source, _message);
+ }
+}
diff --git a/source/jucePluginLib/midiports.h b/source/jucePluginLib/midiports.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <memory>
+
+#include "juce_audio_devices/juce_audio_devices.h"
+#include "synthLib/binarystream.h"
+
+namespace juce
+{
+ class String;
+}
+
+namespace pluginLib
+{
+ class Processor;
+
+ class MidiPorts : juce::MidiInputCallback
+ {
+ public:
+ MidiPorts(Processor& _processor);
+ MidiPorts(MidiPorts&&) = delete;
+ MidiPorts(const MidiPorts&) = delete;
+
+ ~MidiPorts() override;
+
+ MidiPorts& operator = (const MidiPorts&) = delete;
+ MidiPorts& operator = (MidiPorts&&) = delete;
+
+ bool setMidiOutput(const juce::String& _out);
+ juce::MidiOutput* getMidiOutput() const;
+ bool setMidiInput(const juce::String& _in);
+ juce::MidiInput* getMidiInput() const;
+
+ void saveChunkData(synthLib::BinaryStream& _binaryStream) const;
+ void loadChunkData(synthLib::ChunkReader& _cr);
+
+ private:
+ void handleIncomingMidiMessage(juce::MidiInput* _source, const juce::MidiMessage& _message) override;
+
+ Processor& m_processor;
+
+ std::unique_ptr<juce::MidiOutput> m_midiOutput{};
+ std::unique_ptr<juce::MidiInput> m_midiInput{};
+ std::unique_ptr<juce::AudioDeviceManager> m_deviceManager;
+ };
+}
diff --git a/source/jucePluginLib/processor.cpp b/source/jucePluginLib/processor.cpp
@@ -22,7 +22,7 @@ namespace pluginLib
constexpr char g_saveMagic[] = "DSP56300";
constexpr uint32_t g_saveVersion = 2;
- Processor::Processor(const BusesProperties& _busesProperties, Properties _properties) : juce::AudioProcessor(_busesProperties), m_properties(std::move(_properties))
+ Processor::Processor(const BusesProperties& _busesProperties, Properties _properties) : juce::AudioProcessor(_busesProperties), m_properties(std::move(_properties)), m_midiPorts(*this)
{
}
@@ -38,47 +38,14 @@ namespace pluginLib
getPlugin().addMidiEvent(ev);
}
- juce::MidiOutput *Processor::getMidiOutput() const { return m_midiOutput.get(); }
- juce::MidiInput *Processor::getMidiInput() const { return m_midiInput.get(); }
-
- bool Processor::setMidiOutput(const juce::String& _out)
- {
- if (m_midiOutput != nullptr && m_midiOutput->isBackgroundThreadRunning())
- {
- m_midiOutput->stopBackgroundThread();
- }
- m_midiOutput = juce::MidiOutput::openDevice(_out);
- if (m_midiOutput != nullptr)
- {
- m_midiOutput->startBackgroundThread();
- return true;
- }
- return false;
- }
-
- bool Processor::setMidiInput(const juce::String& _in)
- {
- if (m_midiInput != nullptr)
- {
- m_midiInput->stop();
- }
- m_midiInput = juce::MidiInput::openDevice(_in, this);
- if (m_midiInput != nullptr)
- {
- m_midiInput->start();
- return true;
- }
- return false;
- }
-
- void Processor::handleIncomingMidiMessage(juce::MidiInput *source, const juce::MidiMessage &message)
+ void Processor::handleIncomingMidiMessage(juce::MidiInput *_source, const juce::MidiMessage &_message)
{
synthLib::SMidiEvent sm(synthLib::MidiEventSource::PhysicalInput);
- const auto* raw = message.getSysExData();
+ const auto* raw = _message.getSysExData();
if (raw)
{
- const auto count = message.getSysExDataSize();
+ const auto count = _message.getSysExDataSize();
auto syx = pluginLib::SysEx();
syx.push_back(0xf0);
for (int i = 0; i < count; i++)
@@ -93,8 +60,8 @@ namespace pluginLib
}
else
{
- const auto count = message.getRawDataSize();
- const auto* rawData = message.getRawData();
+ const auto count = _message.getRawDataSize();
+ const auto* rawData = _message.getRawData();
if (count >= 1 && count <= 3)
{
sm.a = rawData[0];
@@ -228,6 +195,8 @@ namespace pluginLib
baseLib::ChunkWriter cw(s, "DSSR", 1);
s.write(m_preferredDeviceSamplerate);
}
+
+ m_midiPorts.saveChunkData(s);
}
bool Processor::loadCustomData(const std::vector<uint8_t>& _sourceBuffer)
@@ -277,6 +246,8 @@ namespace pluginLib
const auto sr = _binaryStream.read<float>();
setPreferredDeviceSamplerate(sr);
});
+
+ m_midiPorts.loadChunkData(_cr);
}
void Processor::readGain(baseLib::BinaryStream& _s)
@@ -585,8 +556,8 @@ namespace pluginLib
midiMessages.addEvent(message, 0);
// additionally send to the midi output we've selected in the editor
- if (m_midiOutput)
- m_midiOutput->sendMessageNow(message);
+ if (auto* out = m_midiPorts.getMidiOutput())
+ out->sendMessageNow(message);
}
}
}
diff --git a/source/jucePluginLib/processor.h b/source/jucePluginLib/processor.h
@@ -4,6 +4,7 @@
#include <juce_audio_devices/juce_audio_devices.h>
#include "controller.h"
+#include "midiports.h"
#include "synthLib/plugin.h"
@@ -21,7 +22,7 @@ namespace synthLib
namespace pluginLib
{
- class Processor : public juce::AudioProcessor, juce::MidiInputCallback
+ class Processor : public juce::AudioProcessor
{
public:
struct Properties
@@ -38,12 +39,7 @@ namespace pluginLib
void addMidiEvent(const synthLib::SMidiEvent& ev);
- bool setMidiOutput(const juce::String& _out);
- juce::MidiOutput* getMidiOutput() const;
- bool setMidiInput(const juce::String& _in);
- juce::MidiInput* getMidiInput() const;
-
- void handleIncomingMidiMessage(juce::MidiInput *source, const juce::MidiMessage &message) override;
+ void handleIncomingMidiMessage(juce::MidiInput* _source, const juce::MidiMessage& _message);
Controller& getController();
bool isPluginValid() { return getPlugin().isValid(); }
@@ -110,6 +106,8 @@ namespace pluginLib
bool rebootDevice();
+ auto& getMidiPorts() { return m_midiPorts; }
+
protected:
void destroyController();
@@ -153,9 +151,7 @@ namespace pluginLib
synthLib::DeviceError m_deviceError = synthLib::DeviceError::None;
std::unique_ptr<synthLib::Device> m_device;
std::unique_ptr<synthLib::Plugin> m_plugin;
- std::unique_ptr<juce::MidiOutput> m_midiOutput{};
- std::unique_ptr<juce::MidiInput> m_midiInput{};
- std::vector<synthLib::SMidiEvent> m_midiOut{};
+ std::vector<synthLib::SMidiEvent> m_midiOut;
private:
const Properties m_properties;
@@ -164,5 +160,6 @@ namespace pluginLib
uint32_t m_dspClockPercent = 100;
float m_preferredDeviceSamplerate = 0.0f;
float m_hostSamplerate = 0.0f;
+ MidiPorts m_midiPorts;
};
}
diff --git a/source/xtJucePlugin/skins/xtDefault/xtDefault.json b/source/xtJucePlugin/skins/xtDefault/xtDefault.json
@@ -6298,7 +6298,7 @@
}
},
{
- "name" : "dropdown",
+ "name" : "MidiIn",
"combobox" : {
"offsetL" : "15",
"offsetT" : "1",
@@ -6317,7 +6317,7 @@
}
},
{
- "name" : "dropdown",
+ "name" : "MidiOut",
"combobox" : {
"offsetL" : "15",
"offsetT" : "1",
diff --git a/source/xtJucePlugin/xtEditor.cpp b/source/xtJucePlugin/xtEditor.cpp
@@ -10,6 +10,8 @@
#include "xtPatchManager.h"
#include "xtWaveEditor.h"
+#include "jucePluginEditorLib/midiPorts.h"
+
#include "jucePluginLib/parameterbinding.h"
namespace xtJucePlugin
@@ -119,6 +121,8 @@ namespace xtJucePlugin
auto* waveEditorButtonParent = findComponent("waveEditorButtonParent");
waveEditorButtonParent->setVisible(false);
#endif
+
+ m_midiPorts.reset(new jucePluginEditorLib::MidiPorts(*this, getProcessor()));
}
Editor::~Editor()
diff --git a/source/xtJucePlugin/xtEditor.h b/source/xtJucePlugin/xtEditor.h
@@ -11,6 +11,7 @@ class Controller;
namespace jucePluginEditorLib
{
+ class MidiPorts;
class FocusedParameter;
class Processor;
}
@@ -62,6 +63,8 @@ namespace xtJucePlugin
std::unique_ptr<FocusedParameter> m_focusedParameter;
std::unique_ptr<FrontPanel> m_frontPanel;
std::unique_ptr<Parts> m_parts;
+ std::unique_ptr<jucePluginEditorLib::MidiPorts> m_midiPorts;
+
WaveEditor* m_waveEditor = nullptr;
pluginLib::EventListener<bool> m_playModeChangeListener;