commit 1adff39743dfa6eb4fd37bd9c8d352ef400a554d
parent e0ed9111e1d2f628fee8624208f43c4b1e532a23
Author: dsp56300 <87139854+dsp56300@users.noreply.github.com>
Date: Mon, 27 Dec 2021 19:01:27 +0100
Merge pull request #29 from 790/midiports
native midi ports
Diffstat:
8 files changed, 281 insertions(+), 25 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -6,7 +6,7 @@ set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
-project(gearmulator VERSION 1.2.0)
+project(gearmulator VERSION 1.2.1)
include(base.cmake)
diff --git a/source/jucePlugin/PluginEditor.cpp b/source/jucePlugin/PluginEditor.cpp
@@ -6,11 +6,18 @@
//==============================================================================
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);
+ juce::PropertiesFile::Options opts;
+ opts.applicationName = "DSP56300 Emulator";
+ opts.filenameSuffix = ".settings";
+ opts.folderName = "DSP56300 Emulator";
+ opts.osxLibrarySubFolder = "Application Support/DSP56300 Emulator";
+ m_properties = new juce::PropertiesFile(opts);
+
// Make sure that before the constructor has finished, you've set the
// editor's size to whatever you need it to be.
setSize(800, 800);
@@ -31,11 +38,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();
@@ -63,11 +70,121 @@ AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor(AudioPluginAudi
};
addAndMakeVisible(m_partSelectors[pt]);
}
+
+ auto midiIn = m_properties->getValue("midi_input", "");
+ auto midiOut = m_properties->getValue("midi_output", "");
+ if (midiIn != "")
+ {
+ processorRef.setMidiInput(midiIn);
+ }
+ if (midiOut != "")
+ {
+ processorRef.setMidiOutput(midiOut);
+ }
+
+ 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_properties->setValue("midi_input", "");
+ m_properties->save();
+ 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);
+
+ if (!processorRef.setMidiInput(newInput.identifier))
+ {
+ m_cmbMidiInput.setSelectedItemIndex(0, juce::dontSendNotification);
+ m_lastInputIndex = 0;
+ return;
+ }
+
+ m_properties->setValue("midi_input", newInput.identifier);
+ m_properties->save();
+
+ m_cmbMidiInput.setSelectedItemIndex(index+1, juce::dontSendNotification);
+ m_lastInputIndex = index;
+}
+void AudioPluginAudioProcessorEditor::updateMidiOutput(int index)
+{
+ auto list = juce::MidiOutput::getAvailableDevices();
+
+ if (index == 0)
+ {
+ m_properties->setValue("midi_output", "");
+ m_properties->save();
+ m_cmbMidiOutput.setSelectedItemIndex(index, juce::dontSendNotification);
+ m_lastOutputIndex = index;
+ processorRef.setMidiOutput("");
+ return;
+ }
+ index--;
+ auto newOutput = list[index];
+ if(!processorRef.setMidiOutput(newOutput.identifier))
+ {
+ m_cmbMidiOutput.setSelectedItemIndex(0, juce::dontSendNotification);
+ m_lastOutputIndex = 0;
+ return;
+ }
+ m_properties->setValue("midi_output", newOutput.identifier);
+ m_properties->save();
+
+ 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,33 @@ 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;
+ juce::PropertiesFile *m_properties;
+ 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,98 @@ 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(); }
+
+bool AudioPluginAudioProcessor::setMidiOutput(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 AudioPluginAudioProcessor::setMidiInput(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 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);
-
+ bool setMidiOutput(juce::String _out);
+ juce::MidiOutput* getMidiOutput();
+ bool setMidiInput(juce::String _in);
+ juce::MidiInput* 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.1";
+static constexpr uint32_t g_pluginVersion = 121;