commit 4b30ac3054093bf44ed5d4c1774fc8f7e24289b4
parent 24e30e57b910ebe99c1444be65d55fac66092d0a
Author: 790 <790@users.noreply.github.com>
Date: Tue, 7 Dec 2021 20:33:42 +0000
native midi ports
Diffstat:
7 files changed, 239 insertions(+), 24 deletions(-)
diff --git a/source/jucePlugin/PluginEditor.cpp b/source/jucePlugin/PluginEditor.cpp
@@ -6,8 +6,8 @@
//==============================================================================
AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor(AudioPluginAudioProcessor &p) :
AudioProcessorEditor(&p), processorRef(p), m_btSingleMode("Single Mode"), m_btMultiMode("Multi Mode"),
- m_btLoadFile("Load bank"),
- m_tempEditor(p)
+ m_btLoadFile("Load bank"), m_cmbMidiInput("Midi Input"), m_cmbMidiOutput("Midi Output"),
+ deviceManager(), m_tempEditor(p)
{
ignoreUnused (processorRef);
@@ -31,11 +31,11 @@ AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor(AudioPluginAudi
m_btMultiMode.setToggleState(isMulti, juce::dontSendNotification);
m_btSingleMode.setClickingTogglesState(true);
m_btMultiMode.setClickingTogglesState(true);
- m_btMultiMode.setTopLeftPosition(m_btSingleMode.getPosition().x + m_btSingleMode.getWidth() + 10, 0);
+ m_btMultiMode.setTopLeftPosition(m_btSingleMode.getPosition().x + m_btSingleMode.getWidth() + 10, m_btSingleMode.getY());
m_btMultiMode.setSize(120,30);
addAndMakeVisible(m_btLoadFile);
- m_btLoadFile.setTopLeftPosition(m_btSingleMode.getPosition().x + m_btSingleMode.getWidth() + m_btMultiMode.getWidth() + 10, 0);
+ m_btLoadFile.setTopLeftPosition(m_btSingleMode.getPosition().x + m_btSingleMode.getWidth() + m_btMultiMode.getWidth() + 10, m_btSingleMode.getY());
m_btLoadFile.setSize(120, 30);
m_btLoadFile.onClick = [this]() {
loadFile();
@@ -64,10 +64,97 @@ AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor(AudioPluginAudi
addAndMakeVisible(m_partSelectors[pt]);
}
+ m_cmbMidiInput.setSize(160, 30);
+ m_cmbMidiInput.setTopLeftPosition(0, 400);
+ m_cmbMidiOutput.setSize(160, 30);
+ m_cmbMidiOutput.setTopLeftPosition(164, 400);
+ addAndMakeVisible(m_cmbMidiInput);
+ addAndMakeVisible(m_cmbMidiOutput);
+ m_cmbMidiInput.setTextWhenNoChoicesAvailable("No MIDI Inputs Enabled");
+ auto midiInputs = juce::MidiInput::getAvailableDevices();
+ juce::StringArray midiInputNames;
+ midiInputNames.add(" - Midi In - ");
+ auto inIndex = 0;
+ for (int i = 0; i < midiInputs.size(); i++)
+ {
+ const auto input = midiInputs[i];
+ if (processorRef.getMidiInput() != nullptr && input.identifier == processorRef.getMidiInput()->getIdentifier())
+ {
+ inIndex = i + 1;
+ }
+ midiInputNames.add(input.name);
+ }
+ m_cmbMidiInput.addItemList(midiInputNames, 1);
+ m_cmbMidiInput.setSelectedItemIndex(inIndex, juce::dontSendNotification);
+ m_cmbMidiOutput.setTextWhenNoChoicesAvailable("No MIDI Outputs Enabled");
+ auto midiOutputs = juce::MidiOutput::getAvailableDevices();
+ juce::StringArray midiOutputNames;
+ midiOutputNames.add(" - Midi Out - ");
+ auto outIndex = 0;
+ for (int i = 0; i < midiOutputs.size(); i++)
+ {
+ const auto output = midiOutputs[i];
+ if (processorRef.getMidiOutput() != nullptr && output.identifier == processorRef.getMidiOutput()->getIdentifier())
+ {
+ outIndex = i+1;
+ }
+ midiOutputNames.add(output.name);
+ }
+ m_cmbMidiOutput.addItemList(midiOutputNames, 1);
+ m_cmbMidiOutput.setSelectedItemIndex(outIndex, juce::dontSendNotification);
+ m_cmbMidiInput.onChange = [this]() { updateMidiInput(m_cmbMidiInput.getSelectedItemIndex()); };
+ m_cmbMidiOutput.onChange = [this]() { updateMidiOutput(m_cmbMidiOutput.getSelectedItemIndex()); };
+
addAndMakeVisible(m_tempEditor);
startTimerHz(5);
}
+void AudioPluginAudioProcessorEditor::updateMidiInput(int index)
+{
+ auto list = juce::MidiInput::getAvailableDevices();
+
+ if (index == 0)
+ {
+ m_lastInputIndex = index;
+ m_cmbMidiInput.setSelectedItemIndex(index, juce::dontSendNotification);
+ return;
+ }
+ index--;
+ auto newInput = list[index];
+
+ if (!deviceManager.isMidiInputDeviceEnabled(newInput.identifier))
+ deviceManager.setMidiInputDeviceEnabled(newInput.identifier, true);
+
+ processorRef.setMidiInput(newInput.identifier);
+
+ m_cmbMidiInput.setSelectedItemIndex(index+1, juce::dontSendNotification);
+
+ m_lastInputIndex = index;
+}
+void AudioPluginAudioProcessorEditor::updateMidiOutput(int index)
+{
+ auto list = juce::MidiOutput::getAvailableDevices();
+
+ if (index == 0)
+ {
+ m_cmbMidiOutput.setSelectedItemIndex(index, juce::dontSendNotification);
+ m_lastOutputIndex = index;
+ processorRef.setMidiOutput("");
+ return;
+ }
+ index--;
+ auto newOutput = list[index];
+ processorRef.setMidiOutput(newOutput.identifier);
+ if (processorRef.getMidiOutput() == nullptr)
+ {
+ m_cmbMidiOutput.setSelectedItemIndex(0, juce::dontSendNotification);
+ return;
+ }
+
+ m_cmbMidiOutput.setSelectedItemIndex(index+1, juce::dontSendNotification);
+
+ m_lastOutputIndex = index;
+}
AudioPluginAudioProcessorEditor::~AudioPluginAudioProcessorEditor()
{
diff --git a/source/jucePlugin/PluginEditor.h b/source/jucePlugin/PluginEditor.h
@@ -1,7 +1,7 @@
#pragma once
#include "PluginProcessor.h"
-
+#include <juce_audio_devices/juce_audio_devices.h>
//==============================================================================
class AudioPluginAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::Timer
{
@@ -10,24 +10,32 @@ public:
~AudioPluginAudioProcessorEditor() override;
//==============================================================================
+ //void handleIncomingMidiMessage(juce::MidiInput *source, const juce::MidiMessage &message) override;
void paint (juce::Graphics&) override;
void resized() override;
private:
+ void updateMidiInput(int index);
+ void updateMidiOutput(int index);
void timerCallback() override;
void loadFile();
+
// This reference is provided as a quick way for your editor to
// access the processor object that created it.
AudioPluginAudioProcessor& processorRef;
juce::GenericAudioProcessorEditor m_tempEditor;
-
juce::TextButton m_partSelectors[16];
juce::TextButton m_btSingleMode;
juce::TextButton m_btMultiMode;
juce::TextButton m_btLoadFile;
juce::String m_previousPath;
-
+ juce::ComboBox m_cmbMidiInput;
+ juce::ComboBox m_cmbMidiOutput;
+ juce::AudioDeviceManager deviceManager;
+ int m_lastInputIndex = 0;
+ int m_lastOutputIndex = 0;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginAudioProcessorEditor)
+
};
diff --git a/source/jucePlugin/PluginProcessor.cpp b/source/jucePlugin/PluginProcessor.cpp
@@ -1,6 +1,7 @@
#include "PluginProcessor.h"
#include "PluginEditor.h"
-
+#include <juce_audio_processors/juce_audio_processors.h>
+#include <juce_audio_devices/juce_audio_devices.h>
#include "../synthLib/os.h"
//==============================================================================
@@ -8,7 +9,7 @@ AudioPluginAudioProcessor::AudioPluginAudioProcessor() :
AudioProcessor(BusesProperties()
.withInput("Input", juce::AudioChannelSet::stereo(), true)
.withOutput("Output", juce::AudioChannelSet::stereo(), true)),
- m_device(synthLib::findROM()), m_plugin(&m_device)
+ m_device(synthLib::findROM()), m_plugin(&m_device), m_midiOutput(), m_midiInput(), juce::MidiInputCallback()
{
auto &ctrl = getController(); // init controller
}
@@ -199,8 +200,11 @@ void AudioPluginAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer,
m_midiOut.clear();
m_plugin.getMidiOut(m_midiOut);
+
if (!m_midiOut.empty())
- m_controller->dispatchVirusOut(m_midiOut);
+ {
+ m_controller->dispatchVirusOut(m_midiOut);
+ }
for (size_t i = 0; i < m_midiOut.size(); ++i)
{
@@ -209,10 +213,30 @@ void AudioPluginAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer,
if (e.source == synthLib::MidiEventSourceEditor)
continue;
- if(e.sysex.empty())
- midiMessages.addEvent(juce::MidiMessage (e.a, e.b, e.c, 0.0), 0);
+ if (e.sysex.empty())
+ {
+
+ midiMessages.addEvent(juce::MidiMessage(e.a, e.b, e.c, 0.0), 0);
+
+ // additionally send to the midi output we've selected in the editor
+ if (m_midiOutput != nullptr)
+ {
+ m_midiOutput.get()->sendMessageNow(juce::MidiMessage(e.a, e.b, e.c, 0.0));
+ }
+ }
else
- midiMessages.addEvent(juce::MidiMessage (&e.sysex[0], static_cast<int>(e.sysex.size()), 0.0), 0);
+ {
+
+ // additionally send to the midi output we've selected in the editor
+ if (m_midiOutput != nullptr)
+ {
+ m_midiOutput.get()->sendMessageNow(
+ juce::MidiMessage(&e.sysex[0], static_cast<int>(e.sysex.size()), 0.0));
+ }
+ midiMessages.addEvent(juce::MidiMessage(&e.sysex[0], static_cast<int>(e.sysex.size()), 0.0), 0);
+ }
+
+
}
}
@@ -270,6 +294,88 @@ void AudioPluginAudioProcessor::addMidiEvent(const synthLib::SMidiEvent& ev)
m_plugin.addMidiEvent(ev);
}
+juce::MidiOutput *AudioPluginAudioProcessor::getMidiOutput() { return m_midiOutput.get(); }
+juce::MidiInput *AudioPluginAudioProcessor::getMidiInput() { return m_midiInput.get(); }
+
+void AudioPluginAudioProcessor::setMidiOutput(juce::String _out) {
+ if (m_midiOutput != nullptr && m_midiOutput->isBackgroundThreadRunning())
+ {
+ m_midiOutput->stopBackgroundThread();
+ }
+ m_midiOutput.swap(juce::MidiOutput::openDevice(_out));
+ m_midiOutput->startBackgroundThread();
+}
+
+void AudioPluginAudioProcessor::setMidiInput(juce::String _in)
+{
+ if (m_midiInput != nullptr)
+ {
+ m_midiInput->stop();
+ }
+ m_midiInput.swap(juce::MidiInput::openDevice(_in, this));
+ m_midiInput->start();
+}
+
+void AudioPluginAudioProcessor::handleIncomingMidiMessage(juce::MidiInput *source, const juce::MidiMessage &message)
+{
+ auto raw = message.getSysExData();
+ if (raw != 0)
+ {
+ auto count = message.getSysExDataSize();
+ auto syx = Virus::SysEx();
+ syx.push_back((uint8_t)0xf0);
+ for (int i = 0; i < count; i++)
+ {
+ syx.push_back((uint8_t)raw[i]);
+ }
+ syx.push_back((uint8_t)0xf7);
+ synthLib::SMidiEvent sm;
+ sm.source = synthLib::MidiEventSourcePlugin;
+ sm.sysex = syx;
+ getController().parseMessage(syx);
+
+
+ addMidiEvent(sm);
+ if (m_midiOutput != nullptr && m_midiOutput.get() != 0)
+ {
+ std::vector<synthLib::SMidiEvent> data;
+ getLastMidiOut(data);
+ if (data.size() > 0)
+ {
+ auto msg = juce::MidiMessage::createSysExMessage(data.data(), data.size());
+
+ m_midiOutput->sendMessageNow(msg);
+ }
+ }
+ }
+ else
+ {
+ auto count = message.getRawDataSize();
+ auto raw = message.getRawData();
+ if (count >= 1 && count <= 3)
+ {
+ synthLib::SMidiEvent sm;
+ sm.source = synthLib::MidiEventSourcePlugin;
+ sm.a = raw[0];
+ sm.b = count > 1 ? raw[1] : 0;
+ sm.c = count > 2 ? raw[2] : 0;
+ addMidiEvent(sm);
+ }
+ else
+ {
+ synthLib::SMidiEvent sm;
+ sm.source = synthLib::MidiEventSourcePlugin;
+ auto syx = Virus::SysEx();
+ for (int i = 0; i < count; i++)
+ {
+ syx.push_back((uint8_t)raw[i]);
+ }
+ sm.sysex = syx;
+ addMidiEvent(sm);
+ }
+ }
+}
+
Virus::Controller &AudioPluginAudioProcessor::getController()
{
if (m_controller == nullptr)
diff --git a/source/jucePlugin/PluginProcessor.h b/source/jucePlugin/PluginProcessor.h
@@ -1,13 +1,13 @@
#pragma once
#include <juce_audio_processors/juce_audio_processors.h>
-
+#include <juce_audio_devices/juce_audio_devices.h>
#include "../synthLib/plugin.h"
#include "../virusLib/device.h"
#include "VirusController.h"
//==============================================================================
-class AudioPluginAudioProcessor : public juce::AudioProcessor
+class AudioPluginAudioProcessor : public juce::AudioProcessor, juce::MidiInputCallback
{
public:
//==============================================================================
@@ -54,12 +54,18 @@ public:
bool isPluginValid() const { return m_plugin.isValid(); }
void getLastMidiOut(std::vector<synthLib::SMidiEvent>& dst);
void addMidiEvent(const synthLib::SMidiEvent& ev);
-
+ void setMidiOutput(juce::String _out);
+ juce::MidiOutput* AudioPluginAudioProcessor::getMidiOutput();
+ void setMidiInput(juce::String _in);
+ juce::MidiInput* AudioPluginAudioProcessor::getMidiInput();
+ void handleIncomingMidiMessage(juce::MidiInput *source, const juce::MidiMessage &message) override;
+
// _____________
//
private:
std::unique_ptr<Virus::Controller> m_controller;
-
+ std::unique_ptr<juce::MidiOutput> m_midiOutput;
+ std::unique_ptr<juce::MidiInput> m_midiInput;
void setState(const void *_data, size_t _sizeInBytes);
//==============================================================================
diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp
@@ -1,3 +1,4 @@
+#include <juce_audio_processors/juce_audio_processors.h>
#include "VirusController.h"
#include "PluginProcessor.h"
@@ -104,6 +105,12 @@ namespace Virus
case MessageType::PARAM_CHANGE_C:
parseParamChange(msg);
break;
+ case MessageType::REQUEST_SINGLE:
+ case MessageType::REQUEST_MULTI:
+ case MessageType::REQUEST_GLOBAL:
+ case MessageType::REQUEST_TOTAL:
+ sendSysEx(msg);
+ break;
default:
std::cout << "Controller: Begin Unhandled SysEx! --" << std::endl;
printMessage(msg);
@@ -1661,6 +1668,6 @@ namespace Virus
{
const juce::ScopedLock sl(m_eventQueueLock);
- m_virusOut.insert(m_virusOut.end(), newData.begin(), newData.end());
+ m_virusOut.insert(m_virusOut.end(), newData.begin(), newData.end());
}
}; // namespace Virus
diff --git a/source/jucePlugin/VirusController.h b/source/jucePlugin/VirusController.h
@@ -37,6 +37,7 @@ namespace Virus
juce::String getCurrentPartPresetName(uint8_t part);
uint32_t getBankCount() const { return static_cast<uint32_t>(m_singles.size()); }
void parseMessage(const SysEx &);
+ void sendSysEx(const SysEx &);
private:
void timerCallback() override;
@@ -93,8 +94,8 @@ namespace Virus
void parseSingle(const SysEx &);
void parseMulti(const SysEx &);
void parseParamChange(const SysEx &);
- void parseControllerDump(synthLib::SMidiEvent &);
- void sendSysEx(const SysEx &);
+ void parseControllerDump(synthLib::SMidiEvent &);
+
std::vector<uint8_t> constructMessage(SysEx msg);
AudioPluginAudioProcessor &m_processor;
diff --git a/source/jucePlugin/version.h b/source/jucePlugin/version.h
@@ -1,4 +1,4 @@
-#pragma once
-
-static constexpr const char* const g_pluginVersionString = "1.2.0";
-static constexpr uint32_t g_pluginVersion = 120;
+#pragma once
+
+static constexpr const char* const g_pluginVersionString = "1.2.0";
+static constexpr uint32_t g_pluginVersion = 120;