commit 5d10c754fc61c9cb7909a3fbd18dacad56b9bcb7
parent 58a00a7078b046070f399edc51b76d330de4a631
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Thu, 29 Jun 2023 00:07:19 +0200
vavra initial commit
Diffstat:
134 files changed, 28599 insertions(+), 2 deletions(-)
diff --git a/.gitmodules b/.gitmodules
@@ -4,6 +4,12 @@
[submodule "source/JUCE"]
path = source/JUCE
url = https://github.com/dsp56300/JUCE
+[submodule "source/cpp-terminal"]
+ path = source/cpp-terminal
+ url = https://github.com/dsp56300/cpp-terminal
+[submodule "source/mc68k"]
+ path = source/mc68k
+ url = https://github.com/dsp56300/mc68k.git
[submodule "source/clap-juce-extensions"]
path = source/clap-juce-extensions
url = https://github.com/free-audio/clap-juce-extensions
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
@@ -3,7 +3,8 @@ cmake_minimum_required(VERSION 3.15)
option(${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN "Build Juce plugin" on)
option(${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN_CLAP "Build CLAP version of Juce plugin" on)
-option(${CMAKE_PROJECT_NAME}_SYNTH_OSIRUS "Build Osirus" on)
+option(${CMAKE_PROJECT_NAME}_SYNTH_OSIRUS "Build Osirus" off)
+option(${CMAKE_PROJECT_NAME}_SYNTH_VAVRA "Build Vavra" on)
# ----------------- DSP56300 emulator
@@ -49,3 +50,31 @@ if(${CMAKE_PROJECT_NAME}_SYNTH_OSIRUS)
add_subdirectory(jucePlugin)
endif()
endif()
+
+# ----------------- Synth Vavra
+
+if(${CMAKE_PROJECT_NAME}_SYNTH_VAVRA)
+ add_subdirectory(mc68k)
+ add_subdirectory(mqLib)
+
+ # needed for test console
+ set(CPPTERMINAL_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
+ set(CPPTERMINAL_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
+ set(CPPTERMINAL_ENABLE_TESING OFF CACHE BOOL "" FORCE)
+ add_subdirectory(cpp-terminal)
+
+ if(NOT ANDROID)
+ add_subdirectory(portmidi)
+ endif()
+
+ set(PA_USE_ASIO OFF CACHE BOOL "" FORCE)
+ add_subdirectory(portaudio)
+
+ add_subdirectory(mqConsoleLib)
+ add_subdirectory(mqTestConsole)
+ add_subdirectory(mqPerformanceTest)
+
+ if(${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN)
+ add_subdirectory(mqJucePlugin)
+ endif()
+endif()
diff --git a/source/cpp-terminal b/source/cpp-terminal
@@ -0,0 +1 @@
+Subproject commit bdcb6bf5170d0352913fc86e6c56f7ef767cdb57
diff --git a/source/mc68k b/source/mc68k
@@ -0,0 +1 @@
+Subproject commit efaf7ebf8dd7e1eb4da3333f66aa6acda226e896
diff --git a/source/mqConsoleLib/CMakeLists.txt b/source/mqConsoleLib/CMakeLists.txt
@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 3.10)
+project(mqConsoleLib)
+
+add_library(mqConsoleLib STATIC)
+
+set(SOURCES
+ audioOutput.h
+ audioOutput.cpp
+ audioOutputPA.cpp audioOutputPA.h
+ audioOutputWAV.cpp audioOutputWAV.h
+ device.cpp device.h
+ midiDevice.cpp midiDevice.h
+ midiInput.cpp midiInput.h
+ midiOutput.cpp midiOutput.h
+ mqGui.cpp mqGui.h
+ mqGuiBase.cpp mqGuiBase.h
+ mqKeyInput.cpp mqKeyInput.h
+ mqSettingsGui.cpp mqSettingsGui.h
+)
+
+target_sources(mqConsoleLib PRIVATE ${SOURCES})
+source_group("source" FILES ${SOURCES})
+
+target_link_libraries(mqConsoleLib PUBLIC mqLib cpp-terminal)
diff --git a/source/mqConsoleLib/audioOutput.cpp b/source/mqConsoleLib/audioOutput.cpp
@@ -0,0 +1 @@
+#include "audioOutput.h"
diff --git a/source/mqConsoleLib/audioOutput.h b/source/mqConsoleLib/audioOutput.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <functional>
+
+#include "../mqLib/mqtypes.h"
+
+namespace mqConsoleLib
+{
+ class AudioOutput
+ {
+ public:
+ using ProcessCallback = std::function<void(uint32_t, const mqLib::TAudioOutputs*&)>;
+
+ AudioOutput(const ProcessCallback& _callback) : m_processCallback(_callback)
+ {
+ }
+ virtual ~AudioOutput() = default;
+
+ virtual void process() {}
+
+ protected:
+ const ProcessCallback& m_processCallback;
+ };
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/audioOutputPA.cpp b/source/mqConsoleLib/audioOutputPA.cpp
@@ -0,0 +1,175 @@
+#include "audioOutputPA.h"
+
+#include <thread>
+
+#include "../portaudio/include/portaudio.h"
+
+#include "dsp56kEmu/audio.h"
+#include "dsp56kEmu/logging.h"
+
+constexpr uint32_t g_blocksize = 256;
+
+namespace mqConsoleLib
+{
+static int ourPortAudioCallback(const void*/* inputBuffer*/, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ const PaStreamCallbackTimeInfo*/* timeInfo*/,
+ PaStreamCallbackFlags/* statusFlags*/,
+ void *userData)
+{
+ const auto aa = static_cast<AudioOutputPA*>(userData);
+ return aa->portAudioCallback(outputBuffer, framesPerBuffer);
+}
+
+AudioOutputPA::AudioOutputPA(const ProcessCallback& _callback, const std::string& _deviceName) : AudioOutput(_callback), Device(_deviceName)
+{
+ PaError err = Pa_Initialize();
+
+ if (err != paNoError)
+ {
+ LOG("Failed to initialize PortAudio");
+ return;
+ }
+
+ if(!Device::openDevice())
+ return;
+
+ err = Pa_StartStream(m_stream);
+ if (err != paNoError)
+ LOG("Failed to start stream");
+
+ m_thread.reset(new std::thread([&]()
+ {
+ while(!m_exit)
+ {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ process();
+ }
+ }));
+}
+
+AudioOutputPA::~AudioOutputPA()
+{
+ m_exit = true;
+
+ while(!m_callbackExited)
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+
+ m_thread->join();
+ m_thread.reset();
+
+ Pa_StopStream(m_stream);
+ Pa_CloseStream(m_stream);
+ m_stream = nullptr;
+ m_deviceId = -1;
+}
+
+bool AudioOutputPA::openDevice(const int _devId)
+{
+ PaStreamParameters p{};
+
+ p.channelCount = 2;
+ p.device = _devId;
+ p.sampleFormat = paFloat32;
+ p.suggestedLatency = Pa_GetDeviceInfo(_devId)->defaultHighOutputLatency;
+// const auto err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32, 44100, g_blocksize, ourPortAudioCallback, this);
+ const auto err = Pa_OpenStream(&m_stream, nullptr, &p, 44100, g_blocksize, paNoFlag, ourPortAudioCallback, this);
+
+ if(err != paNoError)
+ LOG("Failed to open audio device named " << m_deviceName);
+ return err == paNoError;
+}
+
+int AudioOutputPA::getDefaultDeviceId() const
+{
+ return Pa_GetDefaultOutputDevice();
+}
+
+int AudioOutputPA::portAudioCallback(void* _dst, uint32_t _frames)
+{
+ auto out = static_cast<float*>(_dst);
+
+ while(_frames > 0)
+ {
+ auto f = _frames;//std::min(static_cast<uint32_t>(64), _frames);
+ _frames -= f;
+
+ const mqLib::TAudioOutputs* outputs = nullptr;
+ m_processCallback(f, outputs);
+
+ if(!outputs)
+ {
+ while(f > 0)
+ {
+ *out++ = 0.0f;
+ *out++ = 0.0f;
+ --f;
+ }
+ continue;
+ }
+
+ auto outs = *outputs;
+
+ auto* outA = &outs[0].front();
+ auto* outB = &outs[1].front();
+
+ while(f > 0)
+ {
+ *out++ = dsp56k::dsp2sample<float>(*outA++);
+ *out++ = dsp56k::dsp2sample<float>(*outB++);
+ --f;
+ }
+ }
+
+ if(m_exit)
+ {
+ m_callbackExited = true;
+ return paAbort;
+ }
+ return paContinue;
+}
+
+void AudioOutputPA::process()
+{
+ // WDKMS platform may behave weird, it gets a timeout and then ends the audio thread and the stream is no longer active. Restart it
+ if(!Pa_IsStreamActive(m_stream))
+ {
+ LOG("Audio Stream inactive, restarting");
+ Pa_AbortStream(m_stream);
+ Pa_StartStream(m_stream);
+ }
+}
+
+int AudioOutputPA::deviceIdFromName(const std::string& _devName) const
+{
+ const auto count = Pa_GetDeviceCount();
+
+ for(int i=0; i<count; ++i)
+ {
+ const auto name = deviceNameFromId(i);
+
+ if(name.empty())
+ continue;
+
+ if(name == _devName)
+ return i;
+ }
+ return -1;
+}
+
+std::string AudioOutputPA::deviceNameFromId(const int _devId) const
+{
+ return getDeviceNameFromId(_devId);
+}
+
+std::string AudioOutputPA::getDeviceNameFromId(int _devId)
+{
+ const auto* di = Pa_GetDeviceInfo(_devId);
+ if(!di)
+ return {};
+ const auto* api = Pa_GetHostApiInfo(di->hostApi);
+ if(!api)
+ return di->name;
+ return std::string("[") + api->name + "] " + di->name;
+}
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/audioOutputPA.h b/source/mqConsoleLib/audioOutputPA.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <thread>
+
+#include "audioOutput.h"
+#include "device.h"
+
+namespace mqConsoleLib
+{
+class AudioOutputPA : public AudioOutput, public Device
+{
+public:
+ AudioOutputPA(const ProcessCallback& _callback, const std::string& _deviceName);
+ ~AudioOutputPA() override;
+
+ int portAudioCallback(void* _dst, uint32_t _frames);
+
+ void process() override;
+
+ int deviceIdFromName(const std::string& _devName) const override;
+ std::string deviceNameFromId(int _devId) const override;
+
+ static std::string getDeviceNameFromId(int _devId);
+
+ bool openDevice(int _devId) override;
+
+ int getDefaultDeviceId() const override;
+
+private:
+ void* m_stream = nullptr;
+ const std::string m_deviceName;
+ bool m_exit = false;
+ bool m_callbackExited = false;
+ std::unique_ptr<std::thread> m_thread;
+};
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/audioOutputWAV.cpp b/source/mqConsoleLib/audioOutputWAV.cpp
@@ -0,0 +1,63 @@
+#include "audioOutputWAV.h"
+
+#include <array>
+
+namespace mqConsoleLib
+{
+constexpr uint32_t g_blockSize = 64;
+
+const std::string g_filename = "mq_output_" + std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()) + ".wav";
+
+AudioOutputWAV::AudioOutputWAV(const ProcessCallback& _callback): AudioOutput(_callback), wavWriter(g_filename, 44100)
+{
+ m_stereoOutput.resize(g_blockSize<<1);
+
+ m_thread.reset(new std::thread([&]()
+ {
+ while(!m_terminate)
+ threadFunc();
+ }));
+}
+
+AudioOutputWAV::~AudioOutputWAV()
+{
+ m_terminate = true;
+ m_thread->join();
+ m_thread.reset();
+}
+
+void AudioOutputWAV::threadFunc()
+{
+ const mqLib::TAudioOutputs* outputs = nullptr;
+ m_processCallback(g_blockSize, outputs);
+
+ if(!outputs)
+ {
+ std::this_thread::yield();
+ return;
+ }
+
+ const auto& outs = *outputs;
+
+ for(size_t i=0; i<g_blockSize; ++i)
+ {
+ m_stereoOutput[i<<1] = outs[0][i];
+ m_stereoOutput[(i<<1) + 1] = outs[1][i];
+
+ if(silence && (outs[0][i] || outs[1][i]))
+ silence = false;
+ }
+
+ if(!silence)
+ {
+ // continously write to disk if not silent anymore
+ wavWriter.append([&](auto& _dst)
+ {
+ _dst.reserve(_dst.size() + m_stereoOutput.size());
+ for (auto& d : m_stereoOutput)
+ _dst.push_back(d);
+ }
+ );
+ }
+}
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/audioOutputWAV.h b/source/mqConsoleLib/audioOutputWAV.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "audioOutput.h"
+
+#include "../synthLib/wavWriter.h"
+
+namespace mqConsoleLib
+{
+ class AudioOutputWAV : AudioOutput
+ {
+ public:
+ explicit AudioOutputWAV(const ProcessCallback& _callback);
+ ~AudioOutputWAV() override;
+
+ void threadFunc();
+
+ private:
+ synthLib::AsyncWriter wavWriter;
+
+ std::vector<dsp56k::TWord> m_stereoOutput;
+ std::unique_ptr<std::thread> m_thread;
+ bool silence = true;
+ bool m_terminate = false;
+ };
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/device.cpp b/source/mqConsoleLib/device.cpp
@@ -0,0 +1,40 @@
+#include "device.h"
+
+namespace mqConsoleLib
+{
+std::string Device::getDeviceName() const
+{
+ if(m_deviceName.empty())
+ return deviceNameFromId(m_deviceId);
+ return m_deviceName;
+}
+
+bool Device::openDevice()
+{
+ if(!getDeviceName().empty())
+ {
+ const auto devId = deviceIdFromName(getDeviceName());
+
+ if(devId >= 0)
+ {
+ if(openDevice(devId))
+ {
+ m_deviceId = devId;
+ return true;
+ }
+
+ return openDefaultDevice();
+ }
+ }
+
+ return openDefaultDevice();
+}
+
+bool Device::openDefaultDevice()
+{
+ if(!openDevice(getDefaultDeviceId()))
+ return false;
+ m_deviceId = getDefaultDeviceId();
+ return true;
+}
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/device.h b/source/mqConsoleLib/device.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <string>
+
+namespace mqConsoleLib
+{
+class Device
+{
+public:
+ explicit Device(std::string _deviceName) : m_deviceName(std::move(_deviceName)) {}
+ virtual ~Device() = default;
+
+ virtual std::string getDeviceName() const;
+ virtual int getDeviceId() const { return m_deviceId; }
+ virtual int getDefaultDeviceId() const = 0;
+
+ virtual std::string deviceNameFromId(int _devId) const = 0;
+ virtual int deviceIdFromName(const std::string& _devName) const = 0;
+
+ bool openDevice();
+ virtual bool openDevice(int devId) = 0;
+
+private:
+ bool openDefaultDevice();
+
+ std::string m_deviceName;
+
+protected:
+ int m_deviceId = -1;
+};
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/midiDevice.cpp b/source/mqConsoleLib/midiDevice.cpp
@@ -0,0 +1,107 @@
+#include "midiDevice.h"
+
+#include "../portmidi/pm_common/portmidi.h"
+
+namespace mqConsoleLib
+{
+
+#ifdef ANDROID
+extern "C"
+{
+ // nop implementation of PortMidi
+
+ PmError Pm_Initialize( void )
+ {
+ return pmHostError;
+ }
+ const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id)
+ {
+ return nullptr;
+ }
+ int Pm_CountDevices( void )
+ {
+ return 0;
+ }
+ PmDeviceID Pm_GetDefaultInputDeviceID( void )
+ {
+ return 0;
+ }
+ PmDeviceID Pm_GetDefaultOutputDeviceID( void )
+ {
+ return 0;
+ }
+ PmError Pm_Close( PortMidiStream* stream )
+ {
+ return pmNoError;
+ }
+ PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when, unsigned char *msg)
+ {
+ return pmHostError;
+ }
+ const char *Pm_GetErrorText( PmError errnum )
+ {
+ return "Platform not supported";
+ }
+ PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t length)
+ {
+ return pmHostError;
+ }
+ PmError Pm_OpenOutput(PortMidiStream** stream, PmDeviceID outputDevice, void *outputDriverInfo,int32_t bufferSize, PmTimeProcPtr time_proc, void *time_info, int32_t latency )
+ {
+ return pmHostError;
+ }
+ PmError Pm_Poll( PortMidiStream *stream )
+ {
+ return pmNoData;
+ }
+ int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length)
+ {
+ return 0;
+ }
+ PmError Pm_OpenInput(PortMidiStream** stream, PmDeviceID inputDevice, void *inputDriverInfo, int32_t bufferSize, PmTimeProcPtr time_proc, void *time_info)
+ {
+ return pmHostError;
+ }
+}
+#endif
+
+MidiDevice::MidiDevice(const std::string& _deviceName, bool _output): Device(_deviceName), m_output(_output)
+{
+ Pm_Initialize();
+}
+
+std::string MidiDevice::getDeviceNameFromId(const int _devId)
+{
+ const auto* di = Pm_GetDeviceInfo(_devId);
+ if(!di)
+ return {};
+ return di->name;
+}
+
+std::string MidiDevice::deviceNameFromId(const int _devId) const
+{
+ return getDeviceNameFromId(_devId);
+}
+
+int MidiDevice::deviceIdFromName(const std::string& _devName) const
+{
+ const auto count = Pm_CountDevices();
+
+ for(int i=0; i<count; ++i)
+ {
+ const auto* di = Pm_GetDeviceInfo(i);
+
+ if(m_output && !di->output)
+ continue;
+ if(!m_output && !di->input)
+ continue;
+
+ const auto name = deviceNameFromId(i);
+ if(name.empty())
+ continue;;
+ if(_devName == name)
+ return i;
+ }
+ return -1;
+}
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/midiDevice.h b/source/mqConsoleLib/midiDevice.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "device.h"
+
+namespace mqConsoleLib
+{
+class MidiDevice : public Device
+{
+public:
+ MidiDevice(const std::string& _deviceName, bool _output);
+ ~MidiDevice() override = default;
+
+ static std::string getDeviceNameFromId(int _devId);
+
+ std::string deviceNameFromId(int _devId) const override;
+ int deviceIdFromName(const std::string& _devName) const override;
+
+private:
+ const bool m_output;
+};
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/midiInput.cpp b/source/mqConsoleLib/midiInput.cpp
@@ -0,0 +1,128 @@
+#include "midiInput.h"
+
+#include <array>
+#include <cassert>
+
+#include "../portmidi/pm_common/portmidi.h"
+
+#include <chrono>
+
+#include "dsp56kEmu/logging.h"
+
+#ifndef Pm_MessageData3
+#define Pm_MessageData3(msg) (((msg) >> 24) & 0xFF)
+#endif
+
+PmTimestamp returnTimeProc(void*)
+{
+ const auto now = std::chrono::system_clock::now().time_since_epoch();
+ return static_cast<PmTimestamp>(std::chrono::duration_cast<std::chrono::milliseconds>(now).count());
+}
+
+namespace mqConsoleLib
+{
+MidiInput::MidiInput(const std::string& _deviceName) : MidiDevice(_deviceName, false)
+{
+ Device::openDevice();
+}
+
+MidiInput::~MidiInput()
+{
+ Pm_Close(m_stream);
+ m_stream = nullptr;
+ m_deviceId = -1;
+}
+
+int MidiInput::getDefaultDeviceId() const
+{
+ return Pm_GetDefaultInputDeviceID();
+}
+
+bool MidiInput::openDevice(int _devId)
+{
+ const auto err = Pm_OpenInput(&m_stream, _devId, nullptr, 1024, returnTimeProc, this);
+
+ if(err != pmNoError)
+ {
+ LOG("Failed to open MIDI input device " << deviceNameFromId(_devId));
+ m_stream = nullptr;
+ }
+
+ return err == pmNoError;
+}
+
+bool MidiInput::process(std::vector<synthLib::SMidiEvent>& _events)
+{
+ if(!m_stream)
+ return false;
+
+ if(Pm_Poll(m_stream) == pmNoData)
+ return false;
+
+ while(true)
+ {
+ PmEvent e;
+ const auto count = Pm_Read(m_stream, &e, 1);
+
+ if(!count)
+ return false;
+
+ if(count != 1)
+ continue;
+
+ process(_events, e.message);
+ }
+}
+
+void MidiInput::process(std::vector<synthLib::SMidiEvent>& _events, uint32_t _message)
+{
+ const uint8_t bytes[] = {
+ static_cast<uint8_t>(Pm_MessageStatus(_message)),
+ static_cast<uint8_t>(Pm_MessageData1(_message)),
+ static_cast<uint8_t>(Pm_MessageData2(_message)),
+ static_cast<uint8_t>(Pm_MessageData3(_message))
+ };
+
+ for (const auto byte : bytes)
+ {
+ if(byte == synthLib::M_STARTOFSYSEX)
+ {
+ assert(m_sysexBuffer.empty());
+ m_readSysex = true;
+ m_sysexBuffer.push_back(byte);
+ }
+ else if(m_readSysex)
+ {
+ if(byte >= 0x80)
+ {
+ if(byte == synthLib::M_ENDOFSYSEX)
+ {
+ m_sysexBuffer.push_back(byte);
+ std::stringstream ss;
+ ss << HEXN(m_sysexBuffer.front(), 2);
+ for(size_t i=1; i<m_sysexBuffer.size(); ++i)
+ ss << ',' << HEXN(m_sysexBuffer[i], 2);
+ const std::string s(ss.str());
+ LOG("Received sysex of size " << m_sysexBuffer.size() << ": " << s);
+ }
+ else
+ {
+ LOG("Received ABORTED sysex of size " << m_sysexBuffer.size());
+ }
+
+ m_readSysex = false;
+ synthLib::SMidiEvent ev;
+ std::swap(m_sysexBuffer, ev.sysex);
+ m_sysexBuffer.clear();
+ _events.emplace_back(ev);
+ return;
+ }
+
+ m_sysexBuffer.push_back(byte);
+ }
+ }
+
+ if(!m_readSysex)
+ _events.emplace_back(bytes[0], bytes[1], bytes[2]);
+}
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/midiInput.h b/source/mqConsoleLib/midiInput.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "midiDevice.h"
+
+#include "../synthLib/midiTypes.h"
+
+namespace mqConsoleLib
+{
+class MidiInput : public MidiDevice
+{
+public:
+ MidiInput(const std::string& _deviceName);
+ ~MidiInput() override;
+
+ bool process(std::vector<synthLib::SMidiEvent>& _events);
+
+ int getDefaultDeviceId() const override;
+
+ bool openDevice(int _devId) override;
+private:
+ void process(std::vector<synthLib::SMidiEvent>& _events, uint32_t _message);
+
+ void* m_stream = nullptr;
+ std::vector<uint8_t> m_sysexBuffer;
+ bool m_readSysex = false;
+};
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/midiOutput.cpp b/source/mqConsoleLib/midiOutput.cpp
@@ -0,0 +1,73 @@
+#include "midiOutput.h"
+
+#include "midiInput.h"
+#include "../portmidi/pm_common/portmidi.h"
+#include "../synthLib/midiTypes.h"
+#include "dsp56kEmu/logging.h"
+
+extern PmTimestamp returnTimeProc(void*);
+
+namespace mqConsoleLib
+{
+MidiOutput::MidiOutput(const std::string& _deviceName) : MidiDevice(_deviceName, true)
+{
+ Device::openDevice();
+}
+
+int MidiOutput::getDefaultDeviceId() const
+{
+ return Pm_GetDefaultOutputDeviceID();
+}
+
+bool MidiOutput::openDevice(int devId)
+{
+ const auto err = Pm_OpenOutput(&m_stream, devId, nullptr, 1024, returnTimeProc, nullptr, 0);
+ if(err != pmNoError)
+ {
+ LOG("Failed to open Midi output " << devId);
+ m_stream = nullptr;
+ }
+ return err == pmNoError;
+}
+
+MidiOutput::~MidiOutput()
+{
+ Pm_Close(m_stream);
+ m_stream = nullptr;
+ m_deviceId = -1;
+}
+
+void MidiOutput::write(const std::vector<uint8_t>& _data)
+{
+ m_parser.write(_data);
+ std::vector<synthLib::SMidiEvent> events;
+ m_parser.getEvents(events);
+ write(events);
+}
+
+void MidiOutput::write(const std::vector<synthLib::SMidiEvent>& _events) const
+{
+ if(!m_stream)
+ return;
+
+ for (const auto& e : _events)
+ {
+ if(!e.sysex.empty())
+ {
+ LOG("MIDI Out Write Sysex of length " << e.sysex.size());
+ const auto err = Pm_WriteSysEx(m_stream, 0, const_cast<unsigned char*>(&e.sysex.front()));
+ if(err != pmNoError)
+ {
+ LOG("Failed to send sysex, err " << err << " => " << Pm_GetErrorText(err));
+ }
+ }
+ else
+ {
+ PmEvent ev;
+ ev.message = Pm_Message(e.a, e.b, e.c);
+ LOG("MIDI Out Write " << HEXN(e.a, 2) << " " << HEXN(e.b, 2) << ' ' << HEXN(e.c, 2));
+ Pm_Write(m_stream, &ev, 1);
+ }
+ }
+}
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/midiOutput.h b/source/mqConsoleLib/midiOutput.h
@@ -0,0 +1,31 @@
+#pragma once
+#include <vector>
+
+#include "../synthLib/midiBufferParser.h"
+
+#include "midiDevice.h"
+
+namespace synthLib
+{
+ struct SMidiEvent;
+}
+
+namespace mqConsoleLib
+{
+class MidiOutput : public MidiDevice
+{
+public:
+ MidiOutput(const std::string& _deviceName);
+ ~MidiOutput() override;
+
+ void write(const std::vector<uint8_t>& _data);
+ void write(const std::vector<synthLib::SMidiEvent>& _events) const;
+
+ int getDefaultDeviceId() const override;
+
+ bool openDevice(int devId) override;
+private:
+ void* m_stream = nullptr;
+ synthLib::MidiBufferParser m_parser;
+};
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/mqGui.cpp b/source/mqConsoleLib/mqGui.cpp
@@ -0,0 +1,336 @@
+#include "mqGui.h"
+
+#include <iostream>
+
+#include "../mqLib/microq.h"
+#include "../mqLib/mqhardware.h"
+
+namespace mqConsoleLib
+{
+ constexpr int g_deviceW = 140;
+ constexpr int g_deviceH = 16;
+
+ Gui::Gui(mqLib::MicroQ& _mq)
+ : m_mq(_mq)
+ , m_win(g_deviceW, g_deviceH + 6)
+ {
+ }
+
+ void Gui::render()
+ {
+ handleTerminalSize();
+
+ m_win.fill_fg(1,1,g_deviceW, 1, Term::fg::bright_yellow);
+ m_win.fill_fg(1,g_deviceH,g_deviceW, g_deviceH, Term::fg::bright_yellow);
+
+ for(int i=2; i<g_deviceH; ++i)
+ {
+ m_win.fill_fg(1,i,1,i, Term::fg::bright_yellow);
+ m_win.fill_fg(g_deviceW,i,g_deviceW,i, Term::fg::bright_yellow);
+ }
+
+ m_win.print_rect(1,1,g_deviceW, g_deviceH);
+
+ m_win.fill_fg(3, g_deviceH>>1, 9, g_deviceH>>1, Term::fg::bright_yellow);
+ m_win.print_str(3, g_deviceH >> 1, "microQ");
+ m_win.fill_fg((g_deviceW>>1) - 3, g_deviceH - 1, (g_deviceW>>1) + 7 , g_deviceH - 1, Term::fg::bright_blue);
+ m_win.print_str((g_deviceW>>1) - 3, g_deviceH - 1, "waldorf");
+
+ constexpr int lcdX = 10;
+
+ renderAboveLCD(lcdX, 3);
+ renderLCD(lcdX,6);
+ renderBelowLCD(lcdX, 12);
+
+ renderAlphaAndPlay(lcdX + 28, 7);
+
+ renderMultimodePeek(lcdX + 23, g_deviceH - 2);
+ renderVerticalLedsAndButtons(lcdX + 46, 3);
+ renderCursorBlock(lcdX + 59, 6);
+
+ constexpr int rightBase = 81;
+ renderMatrixLEDs(rightBase, 3);
+ renderMatrixText(rightBase + 18, 3);
+ renderRightEncoders(rightBase + 20, g_deviceH - 4);
+
+ renderButton(mqLib::Buttons::ButtonType::Power, g_deviceW - 6, g_deviceH - 2);
+ renderLED(m_mq.getLedState(mqLib::Leds::Led::Power), g_deviceW - 4, g_deviceH - 3);
+ renderLabel(g_deviceW - 2, g_deviceH - 1, "Power", true);
+
+ renderHelp(2, g_deviceH + 2);
+ renderDebug(2, g_deviceH + 5);
+
+ std::cout << m_win.render(1,1,true) << std::flush;
+ }
+
+ void Gui::renderAboveLCD(int xStart, int yStart)
+ {
+ constexpr int stepX = 5;
+ constexpr int stepY = 2;
+ int x = xStart + 3;
+ renderLED(mqLib::Leds::Led::Inst1, x, yStart); x += stepX;
+ renderLED(mqLib::Leds::Led::Inst2, x, yStart); x += stepX;
+ renderLED(mqLib::Leds::Led::Inst3, x, yStart); x += stepX;
+ renderLED(mqLib::Leds::Led::Inst4, x, yStart);
+
+ x = xStart + 3;
+ auto y = yStart + stepY;
+ renderButton(mqLib::Buttons::ButtonType::Inst1, x, y); x += stepX;
+ renderButton(mqLib::Buttons::ButtonType::Inst2, x, y); x += stepX;
+ renderButton(mqLib::Buttons::ButtonType::Inst3, x, y); x += stepX;
+ renderButton(mqLib::Buttons::ButtonType::Inst4, x, y);
+
+ x = xStart + 4;
+ y = yStart + 1;
+ renderLabel(x, y, "1", false); x += stepX;
+ renderLabel(x, y, "2", false); x += stepX;
+ renderLabel(x, y, "3", false); x += stepX;
+ renderLabel(x, y, "4", false);
+ }
+
+ void Gui::renderLCD(int x, int y)
+ {
+ m_win.print_rect(x+1, y+1, x+22, y+4,true);
+
+ m_win.fill_bg(x+2, y+2, x+21, y+3, Term::bg::green);
+ m_win.fill_fg(x+2, y+2, x+21, y+3, Term::fg::black);
+
+ std::array<char, 40> text{};
+ m_mq.readLCD(text);
+
+ int cx = x + 2;
+ int cy = y + 2;
+
+ for(size_t i=0; i<text.size(); ++i)
+ {
+ // https://en.wikipedia.org/wiki/List_of_Unicode_characters
+
+ char32_t c = static_cast<uint8_t>(text[i]);
+ switch (c)
+ {
+ case 0: c = 0x24EA; break;
+ case 1: c = 0x2460; break;
+ case 2: c = 0x2461; break;
+ case 3: c = 0x2462; break;
+ case 4: c = 0x2463; break;
+ case 5: c = 0x2464; break;
+ case 6: c = 0x2465; break;
+ case 7: c = 0x2466; break;
+ case 0xdf: c = 0x2598; break; // Quadrant upper left
+ case 0xff: c = 0x2588; break; // Full block
+ default:
+ if(c < 32 || c >= 0x0010FFFF)
+ c = '?';
+ }
+ m_win.set_char(cx, cy, c);
+ ++cx;
+ if(i == 19)
+ {
+ cx -= 20;
+ ++cy;
+ }
+ }
+ }
+
+ void Gui::renderBelowLCD(int x, int y)
+ {
+ renderEncoder(mqLib::Buttons::Encoders::LcdLeft, x + 4, y);
+ renderEncoder(mqLib::Buttons::Encoders::LcdRight, x + 14, y);
+ }
+
+ void Gui::renderAlphaAndPlay(int x, int y)
+ {
+ renderEncoder(mqLib::Buttons::Encoders::Master, x, y);
+ renderLED(mqLib::Leds::Led::Play, x + 8, y + 2);
+ renderButton(mqLib::Buttons::ButtonType::Play, x + 6, y + 3);
+ renderLabel(x+5, y+4, "Play", false);
+ }
+
+ void Gui::renderMultimodePeek(int x, int y)
+ {
+ const auto xStart = x;
+ renderLED(mqLib::Leds::Led::Multimode, x, y);
+ x += 4;
+ renderButton(mqLib::Buttons::ButtonType::Multimode, x, y);
+ x += 6;
+ renderLED(mqLib::Leds::Led::Peek, x, y);
+ x += 4;
+ renderButton(mqLib::Buttons::ButtonType::Peek, x, y);
+ renderLabel(xStart - 2, y+1, "Multimode Peek", false);
+ }
+
+ void Gui::renderCursorBlock(int x, int y)
+ {
+ renderButton(mqLib::Buttons::ButtonType::Left, x, y + 2);
+ renderButton(mqLib::Buttons::ButtonType::Right, x + 6, y + 2);
+ renderButton(mqLib::Buttons::ButtonType::Up, x + 3, y);
+ renderButton(mqLib::Buttons::ButtonType::Down, x + 3, y + 4);
+ }
+
+ void Gui::renderVerticalLedsAndButtons(int xStart, int y)
+ {
+ constexpr int spreadX = 4;
+ constexpr int stepY = 2;
+
+ int x = xStart;
+ renderLED(mqLib::Leds::Led::Global, x, y); x += spreadX;
+ renderButton(mqLib::Buttons::ButtonType::Global, x, y);
+ x = xStart;
+ renderLabel(x + 3, y + 1, "Global", true);
+ renderLabel(x + 4, y + 1, "Util", false);
+ y += stepY;
+ renderLED(mqLib::Leds::Led::Multi, x, y); x += spreadX;
+ renderButton(mqLib::Buttons::ButtonType::Multi, x, y);
+ x = xStart;
+ renderLabel(x + 3, y + 1, "Multi", true);
+ renderLabel(x + 4, y + 1, "Compare", false);
+ y += stepY;
+ renderLED(mqLib::Leds::Led::Edit, x, y); x += spreadX;
+ renderButton(mqLib::Buttons::ButtonType::Edit, x, y);
+ x = xStart;
+ renderLabel(x + 3, y + 1, "Edit", true);
+ renderLabel(x + 4, y + 1, "Recall", false);
+ y += stepY;
+ renderLED(mqLib::Leds::Led::Sound, x, y); x += spreadX;
+ renderButton(mqLib::Buttons::ButtonType::Sound, x, y);
+ x = xStart;
+ renderLabel(x + 3, y + 1, "Sound", true);
+ renderLabel(x + 4, y + 1, "Store", false);
+ y += stepY;
+ renderLED(mqLib::Leds::Led::Shift, x, y); x += spreadX;
+ renderButton(mqLib::Buttons::ButtonType::Shift, x, y);
+ x = xStart;
+ renderLabel(x + 1, y + 1, "Shift", false);
+ }
+
+ void Gui::renderMatrixLEDs(int xStart, int yStart)
+ {
+ constexpr int stepX = 4;
+ constexpr int stepY = 1;
+
+ int x = xStart;
+ int y = yStart;
+
+ renderLED(mqLib::Leds::Led::Osc1, x, y); x += stepX;
+ renderLED(mqLib::Leds::Led::Osc2, x, y); x += stepX;
+ renderLED(mqLib::Leds::Led::Osc3, x, y);
+ x = xStart; y += stepY;
+ renderLED(mqLib::Leds::Led::MixerRouting, x, y);
+ x = xStart; y += stepY;
+ renderLED(mqLib::Leds::Led::Filters1, x, y); x += stepX;
+ renderLED(mqLib::Leds::Led::Filters2, x, y);
+ x = xStart; y += stepY;
+ renderLED(mqLib::Leds::Led::AmpFx, x, y);
+ x = xStart; y += stepY;
+ renderLED(mqLib::Leds::Led::Env1, x, y); x += stepX;
+ renderLED(mqLib::Leds::Led::Env2, x, y); x += stepX;
+ renderLED(mqLib::Leds::Led::Env3, x, y); x += stepX;
+ renderLED(mqLib::Leds::Led::Env4, x, y);
+ x = xStart; y += stepY;
+ renderLED(mqLib::Leds::Led::LFOs, x, y);
+ x = xStart; y += stepY;
+ renderLED(mqLib::Leds::Led::ModMatrix, x, y);
+ }
+
+ void Gui::renderMatrixText(int x, int y)
+ {
+ m_win.fill_fg(x,y, x + 36, y + 7, Term::fg::gray);
+
+ m_win.print_str(x, y, "Octave Detune Shape PWM "); ++y;
+ m_win.print_str(x, y, "Osc1 Osc2 Osc3 Filter "); ++y;
+ m_win.print_str(x, y, "Cutoff Res Env Type "); ++y;
+ m_win.print_str(x, y, "Volume FX Mix ArpMode Tempo "); ++y;
+ m_win.print_str(x, y, "Attack Decay Sustain Release "); ++y;
+ m_win.print_str(x, y, "LFO1Speed Shapee LFO2Speed LFO3Speed"); ++y;
+ m_win.print_str(x, y, "Select Source Amount Dest ");
+ }
+
+ void Gui::renderRightEncoders(int x, int y)
+ {
+ constexpr int xStep = 8;
+
+ renderEncoder(mqLib::Buttons::Encoders::Matrix1, x, y); x += xStep;
+ renderEncoder(mqLib::Buttons::Encoders::Matrix2, x, y); x += xStep;
+ renderEncoder(mqLib::Buttons::Encoders::Matrix3, x, y); x += xStep;
+ renderEncoder(mqLib::Buttons::Encoders::Matrix4, x, y);
+ }
+
+ void Gui::renderHelp(int x, int y)
+ {
+ renderLabel(x, y , "Buttons: 1-4=Inst | Arrows=Cursor | g=Global | m=Multi | e=Edit | s=Sound | S=Shift | M=Multimode | p=Play | P=Peek | q=Power");
+ renderLabel(x, y+1, "Encoders: F1/F2 & F3/F4=LCD Left/Right | 5/6=Alpha Dial | F5-F12=Matrix");
+ renderLabel(x, y+2, "MIDI: 7=Note ON | 8=Note OFF | 9=Modwheel Max | 0=Modwheel Min");
+ renderLabel(x, y+3, "Escape: Open Settings to select MIDI In/Out & Audio Out");
+ }
+
+ void Gui::renderDebug(int x, int y)
+ {
+ // renderLabel(g_deviceW - 1, y, std::string(" DSP ") + m_hw.getDspThread().getMipsString(), true, Term::fg::red);
+ }
+
+ void Gui::renderLED(mqLib::Leds::Led _led, int x, int y)
+ {
+ renderLED(m_mq.getLedState(_led), x, y);
+ }
+
+ void Gui::renderLED(const bool on, int x, int y)
+ {
+ if(on)
+ {
+ m_win.fill_fg(x, y, x+1, y, Term::fg::bright_white);
+ m_win.fill_fg(x+1, y, x+1, y, Term::fg::bright_yellow);
+ m_win.fill_fg(x+2, y, x+1, y, Term::fg::bright_white);
+ m_win.print_str(x, y, "(*)");
+ }
+ else
+ {
+ m_win.fill_fg(x, y, x+1, y, Term::fg::white);
+ m_win.fill_fg(x+1, y, x+1, y, Term::fg::white);
+ m_win.fill_fg(x+2, y, x+1, y, Term::fg::white);
+ m_win.print_str(x, y, "(-)");
+ }
+ }
+
+ void Gui::renderButton(mqLib::Buttons::ButtonType _button, int x, int y)
+ {
+ const auto pressed = m_mq.getButton(_button);
+
+ if(pressed)
+ {
+ m_win.print_str(x,y, "[#]");
+ }
+ else
+ {
+ m_win.print_str(x,y, "[ ]");
+ }
+ }
+
+ void Gui::renderEncoder(mqLib::Buttons::Encoders _encoder, int x, int y)
+ {
+ const auto state = m_mq.getEncoder(_encoder);
+ constexpr char32_t overline = 0x0000203E;
+
+ const char c0 = state & 2 ? '*' : '.';
+ const char c1 = state & 1 ? '*' : '.';
+
+ if(_encoder == mqLib::Buttons::Encoders::Master)
+ {
+ m_win.fill_fg(x, y, x + 5, y + 3, Term::fg::bright_red);
+ }
+ m_win.print_str(x, y, " / \\");
+ m_win.print_str(x, y+1, std::string("| ") + c0 + c1 + " |");
+ m_win.print_str(x, y+2, " \\__/");
+
+ m_win.set_char(x+2, y, overline);
+ m_win.set_char(x+3, y, overline);
+ }
+
+ void Gui::renderLabel(int x, int y, const std::string& _text, bool _rightAlign, Term::fg _color/* = Term::fg::gray*/)
+ {
+ const int len = static_cast<int>(_text.size());
+ if(_rightAlign)
+ x -= len;
+ m_win.fill_fg(x, y, x + len - 1, y, _color);
+ m_win.print_str(x,y,_text);
+ }
+}
diff --git a/source/mqConsoleLib/mqGui.h b/source/mqConsoleLib/mqGui.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <cpp-terminal/base.hpp>
+#include <cpp-terminal/window.hpp>
+
+#include "mqGuiBase.h"
+
+#include "../mqLib/buttons.h"
+#include "../mqLib/leds.h"
+
+namespace mqLib
+{
+ class MicroQ;
+}
+
+namespace mqConsoleLib
+{
+ class Gui : public GuiBase
+ {
+ public:
+ explicit Gui(mqLib::MicroQ& _mq);
+
+ void render();
+
+ private:
+ void renderAboveLCD(int x, int y);
+ void renderLCD(int x, int y);
+ void renderBelowLCD(int x, int y);
+ void renderAlphaAndPlay(int x, int y);
+ void renderMultimodePeek(int x, int y);
+ void renderCursorBlock(int x, int y);
+ void renderVerticalLedsAndButtons(int x, int y);
+ void renderMatrixLEDs(int x, int y);
+ void renderMatrixText(int x, int y);
+ void renderRightEncoders(int x, int y);
+ void renderHelp(int x, int y);
+ void renderDebug(int x, int y);
+
+ void renderLED(mqLib::Leds::Led _led, int x, int y);
+ void renderLED(bool _on, int x, int y);
+ void renderButton(mqLib::Buttons::ButtonType _button, int x, int y);
+ void renderEncoder(mqLib::Buttons::Encoders _encoder, int x, int y);
+ void renderLabel(int x, int y, const std::string& _text, bool _rightAlign = false, Term::fg _color = Term::fg::gray);
+
+ mqLib::MicroQ& m_mq;
+ Term::Window m_win;
+ };
+}
diff --git a/source/mqConsoleLib/mqGuiBase.cpp b/source/mqConsoleLib/mqGuiBase.cpp
@@ -0,0 +1,24 @@
+#include "mqGuiBase.h"
+
+#include <iostream>
+
+#include <cpp-terminal/base.hpp>
+
+namespace mqConsoleLib
+{
+ bool GuiBase::handleTerminalSize()
+ {
+ int w = -1, h = -1;
+ Term::get_term_size(w, h);
+
+ if(w == m_termSizeX && h == m_termSizeY)
+ return false;
+
+ std::cout << Term::clear_screen();
+
+ m_termSizeX = w;
+ m_termSizeY = h;
+
+ return true;
+ }
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/mqGuiBase.h b/source/mqConsoleLib/mqGuiBase.h
@@ -0,0 +1,16 @@
+#pragma once
+
+namespace mqConsoleLib
+{
+ class GuiBase
+ {
+ public:
+ virtual ~GuiBase() = default;
+ virtual void onOpen() {}
+ protected:
+ bool handleTerminalSize();
+ private:
+ int m_termSizeX = -1;
+ int m_termSizeY = -1;
+ };
+}
diff --git a/source/mqConsoleLib/mqKeyInput.cpp b/source/mqConsoleLib/mqKeyInput.cpp
@@ -0,0 +1,105 @@
+#include "mqKeyInput.h"
+
+#include <cpp-terminal/input.hpp>
+
+#include "../mqLib/buttons.h"
+#include "../mqLib/microq.h"
+#include "../mqLib/mqhardware.h"
+
+#include "../synthLib/midiTypes.h"
+
+using Key = Term::Key;
+using EncoderType = mqLib::Buttons::Encoders;
+using ButtonType = mqLib::Buttons::ButtonType;
+
+namespace mqConsoleLib
+{
+ void KeyInput::processKey(int ch)
+ {
+ auto& mq = m_mq;
+ auto* hw = mq.getHardware();
+
+ auto toggleButton = [&](const mqLib::Buttons::ButtonType _type)
+ {
+ mq.setButton(_type, mq.getButton(_type) ? false : true);
+ };
+
+ auto encRotate = [&](mqLib::Buttons::Encoders _encoder, const int _amount)
+ {
+ mq.rotateEncoder(_encoder, _amount);
+ };
+
+ {
+ switch (ch)
+ {
+ case '1': toggleButton(ButtonType::Inst1); break;
+ case '2': toggleButton(ButtonType::Inst2); break;
+ case '3': toggleButton(ButtonType::Inst3); break;
+ case '4': toggleButton(ButtonType::Inst4); break;
+ case Key::ARROW_DOWN: toggleButton(ButtonType::Down); break;
+ case Key::ARROW_LEFT: toggleButton(ButtonType::Left); break;
+ case Key::ARROW_RIGHT: toggleButton(ButtonType::Right); break;
+ case Key::ARROW_UP: toggleButton(ButtonType::Up); break;
+ case 'g': toggleButton(ButtonType::Global); break;
+ case 'm': toggleButton(ButtonType::Multi); break;
+ case 'e': toggleButton(ButtonType::Edit); break;
+ case 's': toggleButton(ButtonType::Sound); break;
+ case 'S': toggleButton(ButtonType::Shift); break;
+ case 'q': toggleButton(ButtonType::Power); break;
+ case 'M': toggleButton(ButtonType::Multimode); break;
+ case 'P': toggleButton(ButtonType::Peek); break;
+ case 'p': toggleButton(ButtonType::Play); break;
+
+ case Key::F1: encRotate(EncoderType::LcdLeft, -1); break;
+ case Key::F2: encRotate(EncoderType::LcdLeft, 1); break;
+ case Key::F3: encRotate(EncoderType::LcdRight, -1); break;
+ case Key::F4: encRotate(EncoderType::LcdRight, 1); break;
+ case '5': encRotate(EncoderType::Master, -1); break;
+ case '6': encRotate(EncoderType::Master, 1); break;
+ case Key::F5: encRotate(EncoderType::Matrix1, -1); break;
+ case Key::F6: encRotate(EncoderType::Matrix1, 1); break;
+ case Key::F7: encRotate(EncoderType::Matrix2, -1); break;
+ case Key::F8: encRotate(EncoderType::Matrix2, 1); break;
+ case Key::F9: encRotate(EncoderType::Matrix3, -1); break;
+ case Key::F10: encRotate(EncoderType::Matrix3, 1); break;
+ case Key::F11: encRotate(EncoderType::Matrix4, -1); break;
+ case Key::F12: encRotate(EncoderType::Matrix4, 1); break;
+ case '7':
+ // Midi Note On
+ mq.sendMidiEvent({synthLib::M_NOTEON, synthLib::Note_C3, 0x7f});
+ break;
+ case '8':
+ // Midi Note Off
+ mq.sendMidiEvent({synthLib::M_NOTEOFF, synthLib::Note_C3, 0x7f});
+ break;
+ case '9':
+ // Modwheel Max
+ mq.sendMidiEvent({synthLib::M_CONTROLCHANGE, synthLib::MC_MODULATION, 0x7f});
+ break;
+ case '0':
+ // Modwheel Min
+ mq.sendMidiEvent({synthLib::M_CONTROLCHANGE, synthLib::MC_MODULATION, 0x0});
+ break;
+ case '!':
+ hw->getDSP(0).dumpPMem("dspA_dump_P_" + std::to_string(hw->getUcCycles()));
+ if constexpr (mqLib::g_useVoiceExpansion)
+ {
+ hw->getDSP(1).dumpPMem("dspB_dump_P_" + std::to_string(hw->getUcCycles()));
+ hw->getDSP(2).dumpPMem("dspC_dump_P_" + std::to_string(hw->getUcCycles()));
+ }
+ break;
+ case '&':
+ hw->getDSP().dumpXYMem("dsp_dump_mem_" + std::to_string(hw->getUcCycles()) + "_");
+ break;
+ case '"':
+ hw->getUC().dumpMemory("mc_dump_mem");
+ break;
+ case '$':
+ hw->getUC().dumpROM("rom_runtime");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/source/mqConsoleLib/mqKeyInput.h b/source/mqConsoleLib/mqKeyInput.h
@@ -0,0 +1,22 @@
+#pragma once
+
+namespace mqLib
+{
+ class MicroQ;
+}
+
+namespace mqConsoleLib
+{
+ class KeyInput
+ {
+ public:
+ KeyInput(mqLib::MicroQ& _mQ) : m_mq(_mQ)
+ {
+ }
+
+ void processKey(int ch);
+
+ private:
+ mqLib::MicroQ& m_mq;
+ };
+}
diff --git a/source/mqConsoleLib/mqSettingsGui.cpp b/source/mqConsoleLib/mqSettingsGui.cpp
@@ -0,0 +1,298 @@
+#include "mqSettingsGui.h"
+
+#include <iostream>
+#include <cpp-terminal/input.hpp>
+
+#include "audioOutputPA.h"
+#include "midiDevice.h"
+#include "../portmidi/pm_common/portmidi.h"
+#include "../portaudio/include/portaudio.h"
+
+#include "dsp56kEmu/logging.h"
+
+using Key = Term::Key;
+
+constexpr auto g_itemActive = Term::fg::bright_yellow;
+constexpr auto g_itemDefault = Term::fg::white;
+constexpr auto g_itemHovered = Term::bg::gray;
+constexpr auto g_itemNotHovered = Term::bg::black;
+
+constexpr int32_t g_maxEntries = 17;
+
+namespace mqConsoleLib
+{
+SettingsGui::SettingsGui()
+ : m_win(140, 21)
+{
+}
+
+void SettingsGui::render(int _midiInput, int _midiOutput, int _audioOutput)
+{
+ if(!handleTerminalSize())
+ m_win.clear();
+
+ m_midiInput.clear();
+ m_midiOutput.clear();
+ m_audioOutput.clear();
+
+ int x = 1;
+ int y = 1;
+
+ m_win.fill_bg(1,1, static_cast<int>(m_win.get_w()), 1, Term::bg::gray);
+ m_win.fill_fg(1,1, static_cast<int>(m_win.get_w()), 1, Term::fg::black);
+
+ x = renderSettings(x, y, _midiInput, 0, "MIDI Input"); x += 3;
+ x = renderSettings(x, y, _midiOutput, 1, "MIDI Output"); x += 3;
+ renderSettings(x, y, _audioOutput, 2, "Audio Output");
+
+ m_win.fill_fg(1,21, 90, 21, Term::fg::gray);
+ m_win.print_str(1, 21, "Use Cursor Keys to navigate, Return to select a device. Escape to go back.");
+
+ std::cout << m_win.render(1,1,true) << std::flush;
+}
+
+void SettingsGui::onOpen()
+{
+ GuiBase::onOpen();
+ findSettings();
+}
+
+void SettingsGui::onEnter()
+{
+ m_enter = true;
+}
+
+void SettingsGui::onDown()
+{
+ moveCursor(0,1);
+}
+
+void SettingsGui::onLeft()
+{
+ moveCursor(-1,0);
+}
+
+void SettingsGui::onRight()
+{
+ moveCursor(1,0);
+}
+
+void SettingsGui::onUp()
+{
+ moveCursor(0,-1);
+}
+
+bool SettingsGui::processKey(int _ch)
+{
+ switch (_ch)
+ {
+ case Key::ESC:
+ case Key::ENTER:
+ onEnter();
+ return true;
+ case Key::ARROW_DOWN:
+ onDown();
+ return true;
+ case Key::ARROW_LEFT:
+ onLeft();
+ return true;
+ case Key::ARROW_RIGHT:
+ onRight();
+ return true;
+ case Key::ARROW_UP:
+ onUp();
+ return true;
+ default:
+ return false;
+ }
+}
+
+void SettingsGui::findSettings()
+{
+ for (auto& setting : m_settings)
+ setting.clear();
+
+ // MIDI
+ const auto devCount = Pm_CountDevices();
+
+ for(int i=0; i<devCount; ++i)
+ {
+ const auto* di = Pm_GetDeviceInfo(i);
+ if(!di)
+ continue;
+
+ const auto name = MidiDevice::getDeviceNameFromId(i);
+ if(di->input)
+ m_settings[0].push_back({i, name});
+ if(di->output)
+ m_settings[1].push_back({i, name});
+ }
+
+ const auto count = Pa_GetDeviceCount();
+
+ for(int i=0; i<count; ++i)
+ {
+ const auto* di = Pa_GetDeviceInfo(i);
+ if(!di)
+ continue;
+
+ if(di->maxOutputChannels < 2)
+ continue;
+
+ PaStreamParameters p{};
+
+ p.channelCount = 2;
+ p.sampleFormat = paFloat32;
+ p.device = i;
+ p.suggestedLatency = di->defaultLowOutputLatency;
+
+ const auto supportResult = Pa_IsFormatSupported(nullptr, &p, 44100.0);
+ if(supportResult != paFormatIsSupported)
+ {
+ std::stringstream ss;
+
+ ss << "Audio Device not supported: " << AudioOutputPA::getDeviceNameFromId(i) << ", result " << Pa_GetErrorText(supportResult);
+
+ const auto* hostError = Pa_GetLastHostErrorInfo();
+ if(hostError)
+ ss << ", host error info: " << hostError->errorCode << ", msg: " << (hostError->errorText ? hostError->errorText : "null");
+
+ const auto msg(ss.str());
+ LOG( msg);
+ continue;
+ }
+
+ const std::string name = AudioOutputPA::getDeviceNameFromId(i);
+
+ m_settings[2].push_back({i, name});
+ }
+}
+
+void SettingsGui::changeDevice(const Setting& _value, int column)
+{
+ switch (column)
+ {
+ case 0:
+ m_midiInput = _value.name;
+ break;
+ case 1:
+ m_midiOutput = _value.name;
+ break;
+ case 2:
+ m_audioOutput = _value.name;
+ break;
+ default:
+ break;
+ }
+}
+
+int SettingsGui::renderSettings(int x, int y, int _selectedId, const int _column, const std::string& _headline)
+{
+ m_win.print_str(x, y, _headline);
+ y += 2;
+
+ int maxX = x + static_cast<int>(_headline.size());
+
+ const auto& settings = m_settings[_column];
+
+ const bool scroll = settings.size() > g_maxEntries;
+
+ auto& scrollPos = m_scrollPos[_column];
+
+ if(scroll)
+ {
+ int focusPos = 0;
+
+ if(m_cursorX == _column)
+ {
+ focusPos = m_cursorY;
+ }
+ else
+ {
+ for(size_t i=0; i<settings.size(); ++i)
+ {
+ if(_selectedId == settings[i].devId)
+ {
+ focusPos = static_cast<int>(i);
+ break;
+ }
+ }
+ }
+
+ while((focusPos - scrollPos) < 3 && scrollPos > 0)
+ --scrollPos;
+ while((focusPos - scrollPos) > g_maxEntries - 3 && (scrollPos + g_maxEntries) < static_cast<int>(settings.size()))
+ ++scrollPos;
+
+ if(scrollPos > 0)
+ {
+ constexpr char32_t arrowUp = 0x2191;
+ m_win.set_char(x, y-1, arrowUp);
+ m_win.set_char(x + 3, y-1, arrowUp);
+ m_win.set_char(x + 6, y-1, arrowUp);
+ }
+
+ if((scrollPos + g_maxEntries) < static_cast<int>(settings.size()))
+ {
+ constexpr char32_t arrowDown = 0x2193;
+ m_win.set_char(x, y + g_maxEntries, arrowDown);
+ m_win.set_char(x + 3, y + g_maxEntries, arrowDown);
+ m_win.set_char(x + 6, y + g_maxEntries, arrowDown);
+ }
+ }
+ else
+ {
+ scrollPos = 0;
+ }
+ for(size_t i=0; i<std::min(static_cast<int>(settings.size()), g_maxEntries); ++i)
+ {
+ const int idx = static_cast<int>(i) + m_scrollPos[_column];
+
+ if(idx < 0)
+ continue;
+
+ if(idx >= settings.size())
+ break;
+
+ const auto& s = settings[idx];
+
+ const auto len = static_cast<int>(s.name.size());
+
+ if(x + len > maxX)
+ maxX = x + len;
+
+ if(m_cursorY == idx && m_cursorX == _column)
+ {
+ m_win.fill_bg(x,y, x + len - 1, y, g_itemHovered);
+ if(m_enter)
+ {
+ m_enter = false;
+ changeDevice(s, _column);
+ }
+ }
+ else
+ {
+ m_win.fill_bg(x,y, x + len - 1, y, g_itemNotHovered);
+ }
+ m_win.fill_fg(x,y, x + len, y, s.devId == _selectedId ? g_itemActive : g_itemDefault);
+ m_win.print_str(x, y, s.name);
+ ++y;
+ }
+ return maxX;
+}
+
+void SettingsGui::moveCursor(int x, int y)
+{
+ m_cursorX += x;
+ m_cursorY += y;
+
+ if(m_cursorX >= static_cast<int>(m_settings.size()))
+ m_cursorX = 0;
+ if(m_cursorX < 0)
+ m_cursorX = static_cast<int>(m_settings.size() - 1);
+ if(m_cursorY < 0)
+ m_cursorY = m_settings[m_cursorX].empty() ? 0 : static_cast<int>(m_settings[m_cursorX].size()) - 1;
+ if(m_cursorY >= static_cast<int>(m_settings[m_cursorX].size()))
+ m_cursorY = 0;
+}
+}
+\ No newline at end of file
diff --git a/source/mqConsoleLib/mqSettingsGui.h b/source/mqConsoleLib/mqSettingsGui.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <array>
+
+#include <cpp-terminal/window.hpp>
+
+#include "../mqConsoleLib/mqGuiBase.h"
+
+namespace mqConsoleLib
+{
+class SettingsGui : public GuiBase
+{
+public:
+ SettingsGui();
+
+ void render(int _midiInput, int _midiOutput, int _audioOutput);
+ void onOpen() override;
+
+ void onEnter();
+ void onDown();
+ void onLeft();
+ void onRight();
+ void onUp();
+
+ bool processKey(int _ch);
+
+ const std::string& getMidiInput() const { return m_midiInput; }
+ const std::string& getMidiOutput() const { return m_midiOutput; }
+ const std::string& getAudioOutput() const { return m_audioOutput; }
+
+private:
+ struct Setting
+ {
+ int devId;
+ std::string name;
+ };
+
+ void findSettings();
+ void changeDevice(const Setting& _value, int column);
+ int renderSettings(int x, int y, int _selectedId, int _column, const std::string& _headline);
+ void moveCursor(int x, int y);
+ Term::Window m_win;
+
+ int m_cursorX = 0;
+ int m_cursorY = 0;
+ bool m_enter = false;
+
+ std::array<std::vector<Setting>, 3> m_settings;
+ std::array<int, 3> m_scrollPos{0};
+
+ std::string m_midiInput;
+ std::string m_midiOutput;
+ std::string m_audioOutput;
+};
+}
+\ No newline at end of file
diff --git a/source/mqJucePlugin/.gitignore b/source/mqJucePlugin/.gitignore
@@ -0,0 +1 @@
+/version.h
diff --git a/source/mqJucePlugin/CMakeLists.txt b/source/mqJucePlugin/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.15)
+project(jucePlugin VERSION ${CMAKE_PROJECT_VERSION})
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/version.h)
+
+set(SOURCES
+ parameterDescriptions_mq.json
+ PluginEditorState.cpp PluginEditorState.h
+ PluginProcessor.cpp PluginProcessor.h
+ mqController.cpp mqController.h
+ mqEditor.cpp mqEditor.h
+ mqFrontPanel.cpp mqFrontPanel.h
+ mqLcd.cpp mqLcd.h
+ mqLcdBase.cpp mqLcdBase.h
+ mqLcdText.cpp mqLcdText.h
+ mqPartSelect.cpp mqPartSelect.h
+ mqPatchBrowser.cpp mqPatchBrowser.h
+ version.h
+)
+
+# https://forum.juce.com/t/help-needed-using-binarydata-with-cmake-juce-6/40486
+# "This might be because the BinaryData files are generated during the build, so the IDE may not be able to find them until the build has been run once (and even then, some IDEs might need a bit of a nudge to re-index the binary directory…)"
+SET(ASSETS "parameterDescriptions_mq.json")
+
+include(skins/mqFrontPanel/assets.cmake)
+include(skins/mqDefault/assets.cmake)
+
+juce_add_binary_data(mqJucePlugin_BinaryData SOURCES ${ASSETS} ${ASSETS_mqFrontPanel} ${ASSETS_mqDefault})
+
+createJucePluginWithFX(mqJucePlugin "Vavra" "Tmqs" "Tmqf" mqJucePlugin_BinaryData mqLib)
diff --git a/source/mqJucePlugin/PluginEditor.cpp b/source/mqJucePlugin/PluginEditor.cpp
@@ -0,0 +1,85 @@
+#include "PluginEditor.h"
+
+#include "PluginEditorState.h"
+#include "PluginProcessor.h"
+
+#include "mqController.h"
+
+//==============================================================================
+AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor(AudioPluginAudioProcessor &p, PluginEditorState& s) :
+ AudioProcessorEditor(&p), processorRef(p), m_state(s)
+{
+ addMouseListener(this, true);
+
+ m_state.evSkinLoaded = [&](juce::Component* _component)
+ {
+ setUiRoot(_component);
+ };
+
+ m_state.evSetGuiScale = [&](const int _scale)
+ {
+ if(getNumChildComponents() > 0)
+ setGuiScale(getChildComponent(0), _scale);
+ };
+
+ m_state.enableBindings();
+
+ setUiRoot(m_state.getUiRoot());
+}
+
+AudioPluginAudioProcessorEditor::~AudioPluginAudioProcessorEditor()
+{
+ m_state.evSetGuiScale = [&](int){};
+ m_state.evSkinLoaded = [&](juce::Component*){};
+
+ m_state.disableBindings();
+
+ setUiRoot(nullptr);
+}
+
+void AudioPluginAudioProcessorEditor::setGuiScale(juce::Component* _comp, int percent)
+{
+ if(!_comp)
+ return;
+
+ const auto s = static_cast<float>(percent)/100.0f * m_state.getRootScale();
+ _comp->setTransform(juce::AffineTransform::scale(s,s));
+
+ setSize(static_cast<int>(m_state.getWidth() * s), static_cast<int>(m_state.getHeight() * s));
+
+// auto* config = processorRef.getController().getConfig();
+// config->setValue("scale", percent);
+// config->saveIfNeeded();
+}
+
+void AudioPluginAudioProcessorEditor::setUiRoot(juce::Component* _component)
+{
+ removeAllChildren();
+
+ if(!_component)
+ return;
+
+// const auto& config = processorRef.getController().getConfig();
+// const auto scale = config->getIntValue("scale", 100);
+
+// setGuiScale(_component, scale);
+ addAndMakeVisible(_component);
+}
+
+void AudioPluginAudioProcessorEditor::mouseDown(const juce::MouseEvent& event)
+{
+ if(!event.mods.isPopupMenu())
+ {
+ AudioProcessorEditor::mouseDown(event);
+ return;
+ }
+
+ // file browsers have their own menu, do not display two menus at once
+ if(event.eventComponent && event.eventComponent->findParentComponentOfClass<juce::FileBrowserComponent>())
+ return;
+
+ if(dynamic_cast<juce::TextEditor*>(event.eventComponent))
+ return;
+
+ m_state.openMenu();
+}
diff --git a/source/mqJucePlugin/PluginEditor.h b/source/mqJucePlugin/PluginEditor.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <juce_audio_processors/juce_audio_processors.h>
+
+class AudioPluginAudioProcessor;
+class PluginEditorState;
+
+//==============================================================================
+class AudioPluginAudioProcessorEditor : public juce::AudioProcessorEditor
+{
+public:
+ explicit AudioPluginAudioProcessorEditor (AudioPluginAudioProcessor&, PluginEditorState&);
+ ~AudioPluginAudioProcessorEditor() override;
+
+ void mouseDown(const juce::MouseEvent& event) override;
+
+ void paint(juce::Graphics& g) override {}
+
+private:
+ void setGuiScale(juce::Component* _component, int percent);
+ void setUiRoot(juce::Component* _component);
+
+ // This reference is provided as a quick way for your editor to
+ // access the processor object that created it.
+ AudioPluginAudioProcessor& processorRef;
+
+ PluginEditorState& m_state;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioPluginAudioProcessorEditor)
+};
diff --git a/source/mqJucePlugin/PluginEditorState.cpp b/source/mqJucePlugin/PluginEditorState.cpp
@@ -0,0 +1,39 @@
+#include "PluginEditorState.h"
+
+#include "mqEditor.h"
+#include "PluginProcessor.h"
+
+#include "../synthLib/os.h"
+
+const std::vector<PluginEditorState::Skin> g_includedSkins =
+{
+ {"Editor", "mqDefault.json", ""},
+ {"Device", "mqFrontPanel.json", ""}
+};
+
+PluginEditorState::PluginEditorState(AudioPluginAudioProcessor& _processor) : jucePluginEditorLib::PluginEditorState(_processor, _processor.getController(), g_includedSkins)
+{
+ loadDefaultSkin();
+}
+
+void PluginEditorState::initContextMenu(juce::PopupMenu& _menu)
+{
+ jucePluginEditorLib::PluginEditorState::initContextMenu(_menu);
+
+ auto& p = static_cast<AudioPluginAudioProcessor&>(m_processor);
+
+ const auto gain = static_cast<int>(std::roundf(p.getOutputGain()));
+
+ juce::PopupMenu gainMenu;
+
+ gainMenu.addItem("0 db (default)", true, gain == 1, [&p] { p.setOutputGain(1); });
+ gainMenu.addItem("+6 db", true, gain == 2, [&p] { p.setOutputGain(2); });
+ gainMenu.addItem("+12 db", true, gain == 4, [&p] { p.setOutputGain(4); });
+
+ _menu.addSubMenu("Output Gain", gainMenu);
+}
+
+genericUI::Editor* PluginEditorState::createEditor(const Skin& _skin, std::function<void()> _openMenuCallback)
+{
+ return new mqJucePlugin::Editor(m_processor, m_parameterBinding, _skin.folder, _skin.jsonFilename);
+}
diff --git a/source/mqJucePlugin/PluginEditorState.h b/source/mqJucePlugin/PluginEditorState.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <functional>
+
+#include "../jucePluginEditorLib/pluginEditorState.h"
+
+namespace juce
+{
+ class Component;
+}
+
+class AudioPluginAudioProcessor;
+
+class PluginEditorState : public jucePluginEditorLib::PluginEditorState
+{
+public:
+ explicit PluginEditorState(AudioPluginAudioProcessor& _processor);
+ void initContextMenu(juce::PopupMenu& _menu) override;
+private:
+ genericUI::Editor* createEditor(const Skin& _skin, std::function<void()> _openMenuCallback) override;
+};
diff --git a/source/mqJucePlugin/PluginProcessor.cpp b/source/mqJucePlugin/PluginProcessor.cpp
@@ -0,0 +1,290 @@
+#include "PluginProcessor.h"
+#include "PluginEditorState.h"
+
+#include <juce_audio_processors/juce_audio_processors.h>
+#include <juce_audio_devices/juce_audio_devices.h>
+
+#include "../jucePluginEditorLib/pluginEditorWindow.h"
+
+#include "mqController.h"
+
+#include "../synthLib/os.h"
+#include "../synthLib/binarystream.h"
+
+class Controller;
+
+static juce::PropertiesFile::Options getConfigOptions()
+{
+ juce::PropertiesFile::Options opts;
+ opts.applicationName = "DSP56300EmulatorVavra";
+ opts.filenameSuffix = ".settings";
+ opts.folderName = "DSP56300EmulatorVavra";
+ opts.osxLibrarySubFolder = "Application Support/DSP56300EmulatorVavra";
+ return opts;
+}
+
+//==============================================================================
+AudioPluginAudioProcessor::AudioPluginAudioProcessor() :
+ Processor(BusesProperties()
+ .withInput("Input", juce::AudioChannelSet::stereo(), true)
+ .withOutput("Output", juce::AudioChannelSet::stereo(), true)
+#if JucePlugin_IsSynth
+ .withOutput("Out 2", juce::AudioChannelSet::stereo(), true)
+ .withOutput("Out 3", juce::AudioChannelSet::stereo(), true)
+#endif
+ , getConfigOptions())
+{
+ getController();
+ const auto latencyBlocks = getConfig().getIntValue("latencyBlocks", static_cast<int>(getPlugin().getLatencyBlocks()));
+ setLatencyBlocks(latencyBlocks);
+}
+
+AudioPluginAudioProcessor::~AudioPluginAudioProcessor() = default;
+
+//==============================================================================
+const juce::String AudioPluginAudioProcessor::getName() const
+{
+ return JucePlugin_Name;
+}
+
+bool AudioPluginAudioProcessor::acceptsMidi() const
+{
+ #if JucePlugin_WantsMidiInput
+ return true;
+ #else
+ return false;
+ #endif
+}
+
+bool AudioPluginAudioProcessor::producesMidi() const
+{
+ #if JucePlugin_ProducesMidiOutput
+ return true;
+ #else
+ return false;
+ #endif
+}
+
+bool AudioPluginAudioProcessor::isMidiEffect() const
+{
+ #if JucePlugin_IsMidiEffect
+ return true;
+ #else
+ return false;
+ #endif
+}
+
+bool AudioPluginAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
+{
+ // This is the place where you check if the layout is supported.
+ // In this template code we only support mono or stereo.
+ // Some plugin hosts, such as certain GarageBand versions, will only
+ // load plugins that support stereo bus layouts.
+ if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
+ return false;
+
+ // This checks if the input is stereo
+ if (layouts.getMainInputChannelSet() != juce::AudioChannelSet::stereo())
+ return false;
+
+ return true;
+}
+
+void AudioPluginAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer,
+ juce::MidiBuffer& midiMessages)
+{
+ juce::ignoreUnused (midiMessages);
+
+ juce::ScopedNoDenormals noDenormals;
+ const auto totalNumInputChannels = getTotalNumInputChannels();
+ const auto totalNumOutputChannels = getTotalNumOutputChannels();
+
+ const int numSamples = buffer.getNumSamples();
+
+ // In case we have more outputs than inputs, this code clears any output
+ // channels that didn't contain input data, (because these aren't
+ // guaranteed to be empty - they may contain garbage).
+ // This is here to avoid people getting screaming feedback
+ // when they first compile a plugin, but obviously you don't need to keep
+ // this code if your algorithm always overwrites all the output channels.
+ for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
+ buffer.clear (i, 0, numSamples);
+
+ // This is the place where you'd normally do the guts of your plugin's
+ // audio processing...
+ // Make sure to reset the state if your inner loop is processing
+ // the samples and the outer loop is handling the channels.
+ // Alternatively, you can process the samples with the channels
+ // interleaved by keeping the same state.
+
+ synthLib::TAudioInputs inputs{};
+ synthLib::TAudioOutputs outputs{};
+
+ for (int channel = 0; channel < totalNumInputChannels; ++channel)
+ inputs[channel] = buffer.getReadPointer(channel);
+
+ for (int channel = 0; channel < totalNumOutputChannels; ++channel)
+ outputs[channel] = buffer.getWritePointer(channel);
+
+ for(const auto metadata : midiMessages)
+ {
+ const auto message = metadata.getMessage();
+
+ synthLib::SMidiEvent ev{};
+
+ if(message.isSysEx() || message.getRawDataSize() > 3)
+ {
+ ev.sysex.resize(message.getRawDataSize());
+ memcpy( &ev.sysex[0], message.getRawData(), ev.sysex.size());
+
+ // Juce bug? Or VSTHost bug? Juce inserts f0/f7 when converting VST3 midi packet to Juce packet, but its already there
+ if(ev.sysex.size() > 1)
+ {
+ if(ev.sysex.front() == 0xf0 && ev.sysex[1] == 0xf0)
+ ev.sysex.erase(ev.sysex.begin());
+
+ if(ev.sysex.size() > 1)
+ {
+ if(ev.sysex[ev.sysex.size()-1] == 0xf7 && ev.sysex[ev.sysex.size()-2] == 0xf7)
+ ev.sysex.erase(ev.sysex.begin());
+ }
+ }
+ }
+ else
+ {
+ ev.a = message.getRawData()[0];
+ ev.b = message.getRawDataSize() > 0 ? message.getRawData()[1] : 0;
+ ev.c = message.getRawDataSize() > 1 ? message.getRawData()[2] : 0;
+
+ const auto status = ev.a & 0xf0;
+
+ if(status == synthLib::M_CONTROLCHANGE || status == synthLib::M_POLYPRESSURE)
+ {
+ // forward to UI to react to control input changes that should move knobs
+ getController().addPluginMidiOut({ev});
+ }
+ }
+
+ ev.offset = metadata.samplePosition;
+
+ getPlugin().addMidiEvent(ev);
+ }
+
+ midiMessages.clear();
+
+ juce::AudioPlayHead::CurrentPositionInfo pos{};
+
+ auto* playHead = getPlayHead();
+ if(playHead) {
+ playHead->getCurrentPosition(pos);
+/*
+ if(pos.bpm > 0) { // sync interal clock to host
+ const uint8_t bpmValue = juce::jmin(127, juce::jmax(0, (int)pos.bpm-63)); // clamp to virus range, 63-190
+ const auto clockParam = getController().getParameter(m_clockTempoParam, 0);
+ if (clockParam != nullptr && (int)clockParam->getValueObject().getValue() != bpmValue) {
+ clockParam->getValueObject().setValue(bpmValue);
+ }
+ }
+*/ }
+
+ getPlugin().process(inputs, outputs, numSamples, static_cast<float>(pos.bpm),
+ static_cast<float>(pos.ppqPosition), pos.isPlaying);
+
+ for (float* output : outputs)
+ {
+ if (output)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ output[i] *= m_outputGain;
+ }
+ }
+
+ m_midiOut.clear();
+ getPlugin().getMidiOut(m_midiOut);
+
+ if (!m_midiOut.empty())
+ {
+ getController().addPluginMidiOut(m_midiOut);
+ }
+
+ for (auto& e : m_midiOut)
+ {
+ if (e.source == synthLib::MidiEventSourceEditor)
+ continue;
+
+ auto toJuceMidiMessage = [&e]()
+ {
+ if(!e.sysex.empty())
+ return juce::MidiMessage(&e.sysex[0], static_cast<int>(e.sysex.size()), 0.0);
+ const auto len = synthLib::MidiBufferParser::lengthFromStatusByte(e.a);
+ if(len == 1)
+ return juce::MidiMessage(e.a, 0.0);
+ if(len == 2)
+ return juce::MidiMessage(e.a, e.b, 0.0);
+ return juce::MidiMessage(e.a, e.b, e.c, 0.0);
+ };
+
+ const juce::MidiMessage message = toJuceMidiMessage();
+ midiMessages.addEvent(message, 0);
+
+ // additionally send to the midi output we've selected in the editor
+ if (m_midiOutput)
+ m_midiOutput->sendMessageNow(message);
+ }
+}
+
+juce::AudioProcessorEditor* AudioPluginAudioProcessor::createEditor()
+{
+ if(!m_editorState)
+ m_editorState.reset(new PluginEditorState(*this));
+ return new jucePluginEditorLib::EditorWindow(*this, *m_editorState, getConfig());
+}
+
+synthLib::Device* AudioPluginAudioProcessor::createDevice()
+{
+ return new mqLib::Device();
+}
+
+void AudioPluginAudioProcessor::updateLatencySamples()
+{
+ if constexpr(JucePlugin_IsSynth)
+ setLatencySamples(getPlugin().getLatencyMidiToOutput());
+ else
+ setLatencySamples(getPlugin().getLatencyInputToOutput());
+}
+
+pluginLib::Controller* AudioPluginAudioProcessor::createController()
+{
+ return new Controller(*this);
+}
+
+void AudioPluginAudioProcessor::loadCustomData(const std::vector<uint8_t>& _sourceBuffer)
+{
+ Processor::loadCustomData(_sourceBuffer);
+
+ synthLib::BinaryStream<uint32_t> ss(_sourceBuffer);
+ const auto version = ss.read<uint32_t>();
+ if (version != 1)
+ return;
+ m_inputGain = ss.read<float>();
+ m_outputGain = ss.read<float>();
+}
+
+void AudioPluginAudioProcessor::saveCustomData(std::vector<uint8_t>& _targetBuffer)
+{
+ Processor::saveCustomData(_targetBuffer);
+
+ synthLib::BinaryStream<uint32_t> ss;
+ ss.write<uint32_t>(1);
+ ss.write(m_inputGain);
+ ss.write(m_outputGain);
+
+ ss.toVector(_targetBuffer);
+}
+
+//==============================================================================
+// This creates new instances of the plugin..
+juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
+{
+ return new AudioPluginAudioProcessor();
+}
diff --git a/source/mqJucePlugin/PluginProcessor.h b/source/mqJucePlugin/PluginProcessor.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <juce_audio_processors/juce_audio_processors.h>
+#include <juce_audio_devices/juce_audio_devices.h>
+
+#include "../synthLib/plugin.h"
+#include "../jucePluginEditorLib/pluginProcessor.h"
+
+#include "../mqLib/device.h"
+
+class PluginEditorState;
+
+//==============================================================================
+class AudioPluginAudioProcessor : public jucePluginEditorLib::Processor
+{
+public:
+ AudioPluginAudioProcessor();
+ ~AudioPluginAudioProcessor() override;
+
+ bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
+ void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
+ using AudioProcessor::processBlock;
+ juce::AudioProcessorEditor* createEditor() override;
+
+ const juce::String getName() const override;
+ bool acceptsMidi() const override;
+ bool producesMidi() const override;
+ bool isMidiEffect() const override;
+
+ // _____________
+ //
+ synthLib::Device* createDevice() override;
+
+ void updateLatencySamples() override;
+
+ pluginLib::Controller* createController() override;
+
+ void loadCustomData(const std::vector<uint8_t>& _sourceBuffer) override;
+ void saveCustomData(std::vector<uint8_t>& _targetBuffer) override;
+
+ float getOutputGain() const { return m_outputGain; }
+ void setOutputGain(const float _gain) { m_outputGain = _gain; }
+
+private:
+
+ //==============================================================================
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioPluginAudioProcessor)
+
+ std::unique_ptr<PluginEditorState> m_editorState;
+
+ float m_inputGain = 1.0f;
+ float m_outputGain = 1.0f;
+};
diff --git a/source/mqJucePlugin/mqController.cpp b/source/mqJucePlugin/mqController.cpp
@@ -0,0 +1,485 @@
+#include "mqController.h"
+
+#include <fstream>
+
+#include "mqEditor.h"
+#include "PluginProcessor.h"
+
+#include "../synthLib/os.h"
+
+#include "dsp56kEmu/logging.h"
+
+constexpr const char* g_midiPacketNames[] =
+{
+ "requestsingle",
+ "requestmulti",
+ "requestdrum",
+ "requestsinglebank",
+ "requestmultibank",
+ "requestdrumbank",
+ "requestglobal",
+ "requestallsingles",
+ "singleparameterchange",
+ "multiparameterchange",
+ "globalparameterchange",
+ "singledump",
+ "singledump_Q",
+ "multidump",
+ "globaldump",
+ "emuRequestLcd",
+ "emuRequestLeds",
+ "emuSendButton",
+ "emuSendRotary"
+};
+
+static_assert(std::size(g_midiPacketNames) == static_cast<size_t>(Controller::MidiPacketType::Count));
+
+static const char* midiPacketName(Controller::MidiPacketType _type)
+{
+ return g_midiPacketNames[static_cast<uint32_t>(_type)];
+}
+
+Controller::Controller(AudioPluginAudioProcessor& p, unsigned char _deviceId) : pluginLib::Controller(p, loadParameterDescriptions()), m_deviceId(_deviceId)
+{
+ registerParams(p);
+
+// sendSysEx(RequestAllSingles);
+ sendSysEx(RequestGlobal);
+// sendGlobalParameterChange(mqLib::GlobalParameter::SingleMultiMode, 1);
+
+ startTimer(50);
+
+ onPlayModeChanged.addListener(0, [this]()
+ {
+ requestAllPatches();
+ });
+}
+
+Controller::~Controller() = default;
+
+void Controller::setFrontPanel(mqJucePlugin::FrontPanel* _frontPanel)
+{
+ m_frontPanel = _frontPanel;
+}
+
+void Controller::sendSingle(const std::vector<uint8_t>& _sysex)
+{
+ auto data = _sysex;
+
+ data[mqLib::IdxBuffer] = static_cast<uint8_t>(isMultiMode() ? mqLib::MidiBufferNum::SingleEditBufferMultiMode : mqLib::MidiBufferNum::SingleEditBufferSingleMode);
+ data[mqLib::IdxLocation] = isMultiMode() ? getCurrentPart() : 0;
+ data[mqLib::IdxDeviceId] = m_deviceId;
+
+ const auto* p = getMidiPacket(g_midiPacketNames[SingleDump]);
+
+ if (!p->updateChecksums(data))
+ {
+ p = getMidiPacket(g_midiPacketNames[SingleDumpQ]);
+
+ if(!p->updateChecksums(data))
+ return;
+ }
+
+ pluginLib::Controller::sendSysEx(data);
+ parseSysexMessage(data);
+}
+
+std::string Controller::loadParameterDescriptions()
+{
+ const auto name = "parameterDescriptions_mq.json";
+ const auto path = synthLib::getModulePath() + name;
+
+ const std::ifstream f(path.c_str(), std::ios::in);
+ if(f.is_open())
+ {
+ std::stringstream buf;
+ buf << f.rdbuf();
+ return buf.str();
+ }
+
+ uint32_t size;
+ const auto res = mqJucePlugin::Editor::findEmbeddedResource(name, size);
+ if(res)
+ return {res, size};
+ return {};
+}
+
+void Controller::timerCallback()
+{
+ std::vector<synthLib::SMidiEvent> events;
+ getPluginMidiOut(events);
+
+ for (const auto& e : events)
+ {
+ if(!e.sysex.empty())
+ parseSysexMessage(e.sysex);
+ }
+}
+
+void Controller::onStateLoaded()
+{
+}
+
+std::string Controller::getSingleName(const pluginLib::MidiPacket::ParamValues& _values) const
+{
+ std::string name;
+ for(uint32_t i=0; i<16; ++i)
+ {
+ char paramName[16];
+ sprintf(paramName, "Name%02u", i);
+ const auto idx = getParameterIndexByName(paramName);
+ if(idx == InvalidParameterIndex)
+ break;
+
+ const auto it = _values.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idx));
+ if(it == _values.end())
+ break;
+
+ name += static_cast<char>(it->second);
+ }
+ return name;
+}
+
+void Controller::applyPatchParameters(const pluginLib::MidiPacket::ParamValues& _params, const uint8_t _part)
+{
+ for (const auto& it : _params)
+ {
+ auto* p = getParameter(it.first.second, _part);
+ p->setValueFromSynth(it.second, true, pluginLib::Parameter::ChangedBy::PresetChange);
+
+ for (const auto& derivedParam : p->getDerivedParameters())
+ derivedParam->setValueFromSynth(it.second, true, pluginLib::Parameter::ChangedBy::PresetChange);
+ }
+}
+
+void Controller::parseSingle(const pluginLib::SysEx& _msg, const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _params)
+{
+ Patch patch;
+ patch.data = _msg;
+ patch.name = getSingleName(_params);
+
+ const auto bank = _data.at(pluginLib::MidiDataType::Bank);
+ const auto prog = _data.at(pluginLib::MidiDataType::Program);
+
+ if(bank == static_cast<uint8_t>(mqLib::MidiBufferNum::SingleEditBufferSingleMode) && prog == static_cast<uint8_t>(mqLib::MidiSoundLocation::EditBufferCurrentSingle))
+ {
+ m_singleEditBuffer = patch;
+
+ if(!isMultiMode())
+ applyPatchParameters(_params, 0);
+ }
+ else if(bank == static_cast<uint8_t>(mqLib::MidiBufferNum::SingleEditBufferMultiMode))
+ {
+ m_singleEditBuffers[prog] = patch;
+
+ if (isMultiMode())
+ applyPatchParameters(_params, prog);
+
+ // if we switched to multi, all singles have to be requested. However, we cannot send all requests at once (device will miss some)
+ // so we chain them one after the other
+ if(prog + 1 < m_singleEditBuffers.size())
+ requestSingle(mqLib::MidiBufferNum::SingleEditBufferMultiMode, mqLib::MidiSoundLocation::EditBufferFirstMultiSingle, prog + 1);
+ }
+}
+
+void Controller::parseMulti(const pluginLib::SysEx& _msg, const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _params)
+{
+ Patch patch;
+ patch.data = _msg;
+ patch.name = getSingleName(_params);
+
+ const auto bank = _data.at(pluginLib::MidiDataType::Bank);
+// const auto prog = _data.at(pluginLib::MidiDataType::Program);
+
+ if(bank == static_cast<uint8_t>(mqLib::MidiBufferNum::MultiEditBuffer))
+ {
+ applyPatchParameters(_params, 0);
+ }
+}
+
+void Controller::parseSysexMessage(const pluginLib::SysEx& _msg)
+{
+ if(_msg.size() >= 5)
+ {
+ const auto cmd = static_cast<mqLib::SysexCommand>(_msg[4]);
+ switch (cmd)
+ {
+ case mqLib::SysexCommand::EmuRotaries:
+ case mqLib::SysexCommand::EmuButtons:
+ case mqLib::SysexCommand::EmuLCD:
+ case mqLib::SysexCommand::EmuLCDCGRata:
+ case mqLib::SysexCommand::EmuLEDs:
+ if(m_frontPanel)
+ m_frontPanel->processSysex(_msg);
+ return;
+ default:
+ break;
+ }
+ }
+
+ LOG("Got sysex of size " << _msg.size())
+
+ std::string name;
+ pluginLib::MidiPacket::Data data;
+ pluginLib::MidiPacket::ParamValues parameterValues;
+
+ if(parseMidiPacket(name, data, parameterValues, _msg))
+ {
+ if(name == midiPacketName(SingleDump))
+ {
+ parseSingle(_msg, data, parameterValues);
+ }
+ else if (name == midiPacketName(MultiDump))
+ {
+ parseMulti(_msg, data, parameterValues);
+ }
+ else if(name == midiPacketName(GlobalDump))
+ {
+ const auto lastPlayMode = isMultiMode();
+ memcpy(m_globalData.data(), &_msg[5], sizeof(m_globalData));
+ const auto newPlayMode = isMultiMode();
+
+ if(lastPlayMode != newPlayMode)
+ onPlayModeChanged();
+ else
+ requestAllPatches();
+ }
+ else if(name == midiPacketName(SingleParameterChange))
+ {
+ const auto page = data[pluginLib::MidiDataType::Page];
+ const auto index = data[pluginLib::MidiDataType::ParameterIndex];
+ const auto part = data[pluginLib::MidiDataType::Part];
+ const auto value = data[pluginLib::MidiDataType::ParameterValue];
+
+ auto& params = findSynthParam(part, page, index);
+
+ for (auto& param : params)
+ param->setValueFromSynth(value, true, pluginLib::Parameter::ChangedBy::ControlChange);
+
+ LOG("Single parameter " << static_cast<int>(index) << ", page " << static_cast<int>(page) << " for part " << static_cast<int>(part) << " changed to value " << static_cast<int>(value));
+ }
+ else if(name == midiPacketName(GlobalParameterChange))
+ {
+ const auto index = (static_cast<uint32_t>(data[pluginLib::MidiDataType::Page]) << 7) + static_cast<uint32_t>(data[pluginLib::MidiDataType::ParameterIndex]);
+ const auto value = data[pluginLib::MidiDataType::ParameterValue];
+
+ if(m_globalData[index] != value)
+ {
+ LOG("Global parameter " << index << " changed to value " << static_cast<int>(value));
+ m_globalData[index] = value;
+
+ if (index == static_cast<uint32_t>(mqLib::GlobalParameter::SingleMultiMode))
+ requestAllPatches();
+ }
+ }
+ else
+ {
+ LOG("Received unknown sysex of size " << _msg.size());
+ }
+ }
+}
+
+bool Controller::sendSysEx(MidiPacketType _type) const
+{
+ std::map<pluginLib::MidiDataType, uint8_t> params;
+ return sendSysEx(_type, params);
+}
+
+bool Controller::sendSysEx(const MidiPacketType _type, std::map<pluginLib::MidiDataType, uint8_t>& _params) const
+{
+ _params.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId));
+ return pluginLib::Controller::sendSysEx(midiPacketName(_type), _params);
+}
+
+bool Controller::isMultiMode() const
+{
+ return m_globalData[static_cast<uint32_t>(mqLib::GlobalParameter::SingleMultiMode)] != 0;
+}
+
+void Controller::setPlayMode(const bool _multiMode)
+{
+ const uint8_t playMode = _multiMode ? 1 : 0;
+
+ if(m_globalData[static_cast<uint32_t>(mqLib::GlobalParameter::SingleMultiMode)] == playMode)
+ return;
+
+ sendGlobalParameterChange(mqLib::GlobalParameter::SingleMultiMode, playMode);
+
+ onPlayModeChanged();
+}
+
+void Controller::selectNextPreset()
+{
+ selectPreset(+1);
+}
+
+void Controller::selectPrevPreset()
+{
+ selectPreset(-1);
+}
+
+std::vector<uint8_t> Controller::createSingleDump(const mqLib::MidiBufferNum _buffer, const mqLib::MidiSoundLocation _location, const uint8_t _locationOffset, const uint8_t _part) const
+{
+ pluginLib::MidiPacket::Data data;
+
+ data.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId));
+ data.insert(std::make_pair(pluginLib::MidiDataType::Bank, static_cast<uint8_t>(_buffer)));
+ data.insert(std::make_pair(pluginLib::MidiDataType::Program, static_cast<uint8_t>(_location) + _locationOffset));
+
+ std::vector<uint8_t> dst;
+
+ if (!createMidiDataFromPacket(dst, midiPacketName(SingleDump), data, _part))
+ return {};
+
+ return dst;
+}
+
+void Controller::selectPreset(int _offset)
+{
+ auto& current = isMultiMode() ? m_currentSingles[getCurrentPart()] : m_currentSingle;
+
+ int index = static_cast<int>(current) + _offset;
+
+ if (index < 0)
+ index += 300;
+
+ if (index >= 300)
+ index -= 300;
+
+ current = static_cast<uint32_t>(index);
+
+ const int single = index % 100;
+ const int bank = index / 100;
+
+ if (isMultiMode())
+ {
+ // TODO: modify multi
+ }
+ else
+ {
+ sendMidiEvent(synthLib::M_CONTROLCHANGE, synthLib::MC_BANKSELECTMSB, m_deviceId);
+ sendMidiEvent(synthLib::M_CONTROLCHANGE, synthLib::MC_BANKSELECTLSB, static_cast<uint8_t>(mqLib::MidiBufferNum::SingleBankA) + bank);
+ sendMidiEvent(synthLib::M_PROGRAMCHANGE, static_cast<uint8_t>(single), 0);
+/*
+ sendGlobalParameterChange(mqLib::GlobalParameter::InstrumentABankNumber, static_cast<uint8_t>(bank));
+ sendGlobalParameterChange(mqLib::GlobalParameter::InstrumentASingleNumber, static_cast<uint8_t>(single));
+*/ }
+}
+
+void Controller::sendParameterChange(const pluginLib::Parameter& _parameter, const uint8_t _value)
+{
+ const auto &desc = _parameter.getDescription();
+
+ std::map<pluginLib::MidiDataType, uint8_t> data;
+
+ if (desc.page >= 100)
+ {
+ uint8_t v;
+
+ if (!combineParameterChange(v, g_midiPacketNames[MultiDump], _parameter, _value))
+ return;
+
+ const auto& dump = mqLib::State::g_dumps[static_cast<int>(mqLib::State::DumpType::Multi)];
+
+ uint32_t idx = desc.index;
+
+ if(desc.page > 100)
+ idx += (static_cast<uint32_t>(mqLib::MultiParameter::Inst1) - static_cast<uint32_t>(mqLib::MultiParameter::Inst0)) * (desc.page - 101);
+
+ data.insert(std::make_pair(pluginLib::MidiDataType::Part, _parameter.getPart()));
+ data.insert(std::make_pair(pluginLib::MidiDataType::Page, idx >> 7));
+ data.insert(std::make_pair(pluginLib::MidiDataType::ParameterIndex, idx & 0x7f));
+ data.insert(std::make_pair(pluginLib::MidiDataType::ParameterValue, v));
+
+ sendSysEx(MultiParameterChange, data);
+ return;
+ }
+
+ uint8_t v;
+ if (!combineParameterChange(v, g_midiPacketNames[SingleDump], _parameter, _value))
+ return;
+
+ data.insert(std::make_pair(pluginLib::MidiDataType::Part, _parameter.getPart()));
+ data.insert(std::make_pair(pluginLib::MidiDataType::Page, desc.page));
+ data.insert(std::make_pair(pluginLib::MidiDataType::ParameterIndex, desc.index));
+ data.insert(std::make_pair(pluginLib::MidiDataType::ParameterValue, v));
+
+ sendSysEx(SingleParameterChange, data);
+}
+
+bool Controller::sendGlobalParameterChange(mqLib::GlobalParameter _param, uint8_t _value)
+{
+ const auto index = static_cast<uint32_t>(_param);
+
+ if(m_globalData[index] == _value)
+ return true;
+
+ std::map<pluginLib::MidiDataType, uint8_t> data;
+
+ data.insert(std::make_pair(pluginLib::MidiDataType::Page, index >> 7 ));
+ data.insert(std::make_pair(pluginLib::MidiDataType::ParameterIndex, index & 0x7f ));
+ data.insert(std::make_pair(pluginLib::MidiDataType::ParameterValue, _value));
+
+ m_globalData[index] = _value;
+
+ return sendSysEx(GlobalParameterChange, data);
+}
+
+void Controller::requestSingle(mqLib::MidiBufferNum _buf, mqLib::MidiSoundLocation _location, uint8_t _locationOffset/* = 0*/) const
+{
+ std::map<pluginLib::MidiDataType, uint8_t> params;
+ params[pluginLib::MidiDataType::Bank] = static_cast<uint8_t>(_buf);
+ params[pluginLib::MidiDataType::Program] = static_cast<uint8_t>(_location) + _locationOffset;
+ sendSysEx(RequestSingle, params);
+}
+
+void Controller::requestMulti(mqLib::MidiBufferNum _buf, mqLib::MidiSoundLocation _location, uint8_t _locationOffset) const
+{
+ std::map<pluginLib::MidiDataType, uint8_t> params;
+ params[pluginLib::MidiDataType::Bank] = static_cast<uint8_t>(_buf);
+ params[pluginLib::MidiDataType::Program] = static_cast<uint8_t>(_location) + _locationOffset;
+ sendSysEx(RequestMulti, params);
+}
+
+uint8_t Controller::getGlobalParam(mqLib::GlobalParameter _type) const
+{
+ return m_globalData[static_cast<uint32_t>(_type)];
+}
+
+bool Controller::isDerivedParameter(pluginLib::Parameter& _derived, pluginLib::Parameter& _base) const
+{
+ if(_derived.getDescription().page >= 100)
+ return false;
+
+ const auto& packetName = g_midiPacketNames[SingleDump];
+ const auto* packet = getMidiPacket(packetName);
+
+ if (!packet)
+ {
+ LOG("Failed to find midi packet " << packetName);
+ return true;
+ }
+
+ const auto* defA = packet->getDefinitionByParameterName(_derived.getDescription().name);
+ const auto* defB = packet->getDefinitionByParameterName(_base.getDescription().name);
+
+ if (!defA || !defB)
+ return true;
+
+ return defA->doMasksOverlap(*defB);
+}
+
+void Controller::requestAllPatches() const
+{
+ if (isMultiMode())
+ {
+ requestMulti(mqLib::MidiBufferNum::MultiEditBuffer, mqLib::MidiSoundLocation::EditBufferFirstMultiSingle);
+
+ // the other singles 1-15 are requested one after the other after a single has been received
+ requestSingle(mqLib::MidiBufferNum::SingleEditBufferMultiMode, mqLib::MidiSoundLocation::EditBufferFirstMultiSingle, 0);
+ }
+ else
+ {
+ requestSingle(mqLib::MidiBufferNum::SingleEditBufferSingleMode, mqLib::MidiSoundLocation::EditBufferCurrentSingle);
+ }
+}
diff --git a/source/mqJucePlugin/mqController.h b/source/mqJucePlugin/mqController.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#include "../jucePluginLib/controller.h"
+
+#include "../mqLib/mqmiditypes.h"
+#include "../jucePluginLib/event.h"
+
+namespace mqJucePlugin
+{
+ class FrontPanel;
+}
+
+class AudioPluginAudioProcessor;
+
+class Controller : public pluginLib::Controller, juce::Timer
+{
+public:
+ enum MidiPacketType
+ {
+ RequestSingle,
+ RequestMulti,
+ RequestDrum,
+ RequestSingleBank,
+ RequestMultiBank,
+ RequestDrumBank,
+ RequestGlobal,
+ RequestAllSingles,
+ SingleParameterChange,
+ MultiParameterChange,
+ GlobalParameterChange,
+ SingleDump,
+ SingleDumpQ,
+ MultiDump,
+ GlobalDump,
+ EmuRequestLcd,
+ EmuRequestLeds,
+ EmuSendButton,
+ EmuSendRotary,
+
+ Count
+ };
+
+ struct Patch
+ {
+ std::string name;
+ std::vector<uint8_t> data;
+ };
+
+ pluginLib::Event onPlayModeChanged;
+
+ Controller(AudioPluginAudioProcessor &, unsigned char _deviceId = 0);
+ ~Controller() override;
+
+ void setFrontPanel(mqJucePlugin::FrontPanel* _frontPanel);
+ void sendSingle(const std::vector<uint8_t>& _sysex);
+
+ bool sendSysEx(MidiPacketType _type) const;
+ bool sendSysEx(MidiPacketType _type, std::map<pluginLib::MidiDataType, uint8_t>& _params) const;
+
+ bool isMultiMode() const;
+ void setPlayMode(bool _multiMode);
+
+ void selectNextPreset();
+ void selectPrevPreset();
+
+ std::vector<uint8_t> createSingleDump(mqLib::MidiBufferNum _buffer, mqLib::MidiSoundLocation _location, uint8_t _locationOffset, uint8_t _part) const;
+
+private:
+ void selectPreset(int _offset);
+
+ static std::string loadParameterDescriptions();
+
+ void timerCallback() override;
+ void onStateLoaded() override;
+
+ std::string getSingleName(const pluginLib::MidiPacket::ParamValues& _values) const;
+ void applyPatchParameters(const pluginLib::MidiPacket::ParamValues& _params, uint8_t _part);
+ void parseSingle(const pluginLib::SysEx& _msg, const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _params);
+ void parseMulti(const pluginLib::SysEx& _msg, const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _params);
+ void parseSysexMessage(const pluginLib::SysEx&) override;
+
+ void sendParameterChange(const pluginLib::Parameter& _parameter, uint8_t _value) override;
+ bool sendGlobalParameterChange(mqLib::GlobalParameter _param, uint8_t _value);
+ void requestSingle(mqLib::MidiBufferNum _buf, mqLib::MidiSoundLocation _location, uint8_t _locationOffset = 0) const;
+ void requestMulti(mqLib::MidiBufferNum _buf, mqLib::MidiSoundLocation _location, uint8_t _locationOffset = 0) const;
+
+ uint8_t getGlobalParam(mqLib::GlobalParameter _type) const;
+
+ bool isDerivedParameter(pluginLib::Parameter& _derived, pluginLib::Parameter& _base) const override;
+
+ void requestAllPatches() const;
+
+ const uint8_t m_deviceId;
+
+ Patch m_singleEditBuffer;
+ std::array<Patch,16> m_singleEditBuffers;
+ std::array<uint8_t, 200> m_globalData{};
+ mqJucePlugin::FrontPanel* m_frontPanel = nullptr;
+ std::array<uint32_t, 16> m_currentSingles{0};
+ uint32_t m_currentSingle = 0;
+};
diff --git a/source/mqJucePlugin/mqEditor.cpp b/source/mqJucePlugin/mqEditor.cpp
@@ -0,0 +1,188 @@
+#include "mqEditor.h"
+
+#include "BinaryData.h"
+#include "PluginProcessor.h"
+
+#include "mqController.h"
+
+#include "../jucePluginLib/parameterbinding.h"
+
+#include "../mqLib/mqbuildconfig.h"
+
+namespace mqJucePlugin
+{
+ static constexpr uint32_t PlayModeListenerId = 1;
+
+ Editor::Editor(jucePluginEditorLib::Processor& _processor, pluginLib::ParameterBinding& _binding, std::string _skinFolder, const std::string& _jsonFilename)
+ : jucePluginEditorLib::Editor(_processor, _binding, std::move(_skinFolder))
+ , m_controller(dynamic_cast<Controller&>(_processor.getController()))
+ {
+ create(_jsonFilename);
+
+ m_frontPanel.reset(new FrontPanel(*this, m_controller));
+
+ if(findComponent("ContainerPatchList", false))
+ m_patchBrowser.reset(new PatchBrowser(*this, _processor.getController(), _processor.getConfig()));
+
+ auto disableButton = [](juce::Component* _comp)
+ {
+ _comp->setAlpha(0.5f);
+ _comp->setEnabled(false);
+ };
+
+ auto disableByName = [this, &disableButton](const std::string& _button)
+ {
+ if (auto* bt = findComponentT<juce::Button>(_button, false))
+ disableButton(bt);
+ };
+
+ m_btPlayModeMulti = findComponentT<juce::Button>("btPlayModeMulti", false);
+ if(m_btPlayModeMulti)
+ {
+ if constexpr(mqLib::g_pluginDemo)
+ {
+ disableButton(m_btPlayModeMulti);
+ }
+ else
+ {
+ m_btPlayModeMulti->onClick = [this]()
+ {
+ m_controller.setPlayMode(m_btPlayModeMulti->getToggleState());
+ };
+ }
+ }
+
+ m_btSave = findComponentT<juce::Button>("btSave", false);
+ if (m_btSave)
+ {
+ if constexpr (mqLib::g_pluginDemo)
+ {
+ disableButton(m_btSave);
+ }
+ else
+ {
+ m_btSave->onClick = [this]()
+ {
+ onBtSave();
+ };
+ }
+ }
+
+ if constexpr(mqLib::g_pluginDemo)
+ {
+ disableByName("btPageMulti");
+ disableByName("btPageDrum");
+ }
+
+ m_btPresetPrev = findComponentT<juce::Button>("btPresetPrev", false);
+ m_btPresetNext = findComponentT<juce::Button>("btPresetNext", m_btPresetPrev != nullptr);
+
+ if (m_btPresetPrev)
+ {
+ m_btPresetPrev->onClick = [this] { onBtPresetPrev(); };
+ m_btPresetNext->onClick = [this] { onBtPresetNext(); };
+ }
+
+ m_focusedParameter.reset(new jucePluginEditorLib::FocusedParameter(m_controller, _binding, *this));
+
+ if(!findComponents("partSelectButton", false).empty())
+ m_partSelect.reset(new mqPartSelect(*this, m_controller, _binding));
+
+ addMouseListener(this, true);
+
+ m_controller.onPlayModeChanged.addListener(PlayModeListenerId, [this]()
+ {
+ if(m_btPlayModeMulti)
+ m_btPlayModeMulti->setToggleState(m_controller.isMultiMode(), juce::dontSendNotification);
+ if(m_partSelect)
+ m_partSelect->onPlayModeChanged();
+ });
+ }
+
+ Editor::~Editor()
+ {
+ m_controller.onPlayModeChanged.removeListener(PlayModeListenerId);
+
+ m_partSelect.reset();
+ m_focusedParameter.reset();
+ m_frontPanel.reset();
+ }
+
+ const char* Editor::findEmbeddedResource(const std::string& _filename, uint32_t& _size)
+ {
+ for(size_t i=0; i<BinaryData::namedResourceListSize; ++i)
+ {
+ if (BinaryData::originalFilenames[i] != _filename)
+ continue;
+
+ int size = 0;
+ const auto res = BinaryData::getNamedResource(BinaryData::namedResourceList[i], size);
+ _size = static_cast<uint32_t>(size);
+ return res;
+ }
+ return nullptr;
+ }
+
+ const char* Editor::findResourceByFilename(const std::string& _filename, uint32_t& _size)
+ {
+ return findEmbeddedResource(_filename, _size);
+ }
+
+ void Editor::mouseEnter(const juce::MouseEvent& _event)
+ {
+ m_focusedParameter->onMouseEnter(_event);
+ }
+
+ void Editor::savePreset(const FileType _type)
+ {
+ jucePluginEditorLib::Editor::savePreset([&](const juce::File& _file)
+ {
+ FileType type = _type;
+ const auto file = createValidFilename(type, _file);
+
+ const auto part = m_controller.getCurrentPart();
+ const auto p = m_controller.createSingleDump(mqLib::MidiBufferNum::SingleBankA, static_cast<mqLib::MidiSoundLocation>(0), part, part);
+ if(!p.empty())
+ {
+ const std::vector<std::vector<uint8_t>> presets{p};
+ jucePluginEditorLib::Editor::savePresets(_type, file, presets);
+ }
+ });
+ }
+
+ void Editor::onBtSave()
+ {
+ juce::PopupMenu menu;
+
+ auto addEntry = [&](juce::PopupMenu& _menu, const std::string& _name, const std::function<void(FileType)>& _callback)
+ {
+ juce::PopupMenu subMenu;
+
+ subMenu.addItem(".syx", [_callback]() {_callback(FileType::Syx); });
+ subMenu.addItem(".mid", [_callback]() {_callback(FileType::Mid); });
+
+ _menu.addSubMenu(_name, subMenu);
+ };
+
+ addEntry(menu, "Current Single (Edit Buffer)", [this](FileType _type)
+ {
+ savePreset(_type);
+ });
+
+ menu.showMenuAsync(juce::PopupMenu::Options());
+ }
+
+ void Editor::onBtPresetPrev()
+ {
+ if (m_patchBrowser->selectPrevPreset())
+ return;
+// m_controller.selectPrevPreset();
+ }
+
+ void Editor::onBtPresetNext()
+ {
+ if (m_patchBrowser->selectNextPreset())
+ return;
+// m_controller.selectNextPreset();
+ }
+}
diff --git a/source/mqJucePlugin/mqEditor.h b/source/mqJucePlugin/mqEditor.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "../jucePluginEditorLib/pluginEditor.h"
+#include "../jucePluginEditorLib/focusedParameter.h"
+
+#include "mqFrontPanel.h"
+#include "mqPatchBrowser.h"
+#include "mqPartSelect.h"
+
+namespace jucePluginEditorLib
+{
+ class FocusedParameter;
+ class Processor;
+}
+
+namespace pluginLib
+{
+ class ParameterBinding;
+}
+
+namespace mqJucePlugin
+{
+ class Editor final : public jucePluginEditorLib::Editor
+ {
+ public:
+ Editor(jucePluginEditorLib::Processor& _processor, pluginLib::ParameterBinding& _binding, std::string _skinFolder, const std::string& _jsonFilename);
+ ~Editor() override;
+ static const char* findEmbeddedResource(const std::string& _filename, uint32_t& _size);
+ const char* findResourceByFilename(const std::string& _filename, uint32_t& _size) override;
+
+ private:
+ void mouseEnter(const juce::MouseEvent& _event) override;
+
+ void savePreset(FileType _type);
+
+ void onBtSave();
+ void onBtPresetPrev();
+ void onBtPresetNext();
+
+ Controller& m_controller;
+
+ std::unique_ptr<FrontPanel> m_frontPanel;
+ std::unique_ptr<PatchBrowser> m_patchBrowser;
+ std::unique_ptr<mqPartSelect> m_partSelect;
+ juce::Button* m_btPlayModeMulti = nullptr;
+ juce::Button* m_btSave = nullptr;
+ juce::Button* m_btPresetPrev = nullptr;
+ juce::Button* m_btPresetNext = nullptr;
+
+ std::unique_ptr<jucePluginEditorLib::FocusedParameter> m_focusedParameter;
+ };
+}
diff --git a/source/mqJucePlugin/mqFrontPanel.cpp b/source/mqJucePlugin/mqFrontPanel.cpp
@@ -0,0 +1,229 @@
+#include "mqFrontPanel.h"
+
+#include "mqController.h"
+#include "mqEditor.h"
+#include "mqLcd.h"
+#include "mqLcdText.h"
+
+#include "../mqLib/device.h"
+#include "../mqLib/mqmiditypes.h"
+
+#include "dsp56kEmu/fastmath.h"
+
+namespace mqLib
+{
+ enum class SysexCommand : uint8_t;
+}
+
+namespace mqJucePlugin
+{
+ constexpr float g_encoderSpeed = 2.0f;
+
+ constexpr const char* g_ledNames[] =
+ {
+ "ledOsc1", "ledFilter2", "ledInst1", "ledGlobal", "ledPlay",
+ "ledOsc2", "ledAmpFxArp", "ledInst2", "ledMulti", "ledPeek",
+ "ledOsc3", "ledEnv1", "ledInst3", "ledEdit", "ledMultimode",
+ "ledMixerRouting", "ledEnv2", "ledInst4", "ledSound", "ledShift",
+ "ledFilter1", "ledEnv3", "ledModMatrix", "ledLFOs", "ledEnv4",
+ "ledPower"
+ };
+
+ constexpr const char* g_buttonNames[] =
+ {
+ "buttonInst1", "buttonInst2", "buttonInst3", "buttonInst4",
+ "buttonD", "buttonL", "buttonR", "buttonU", "buttonGlobal",
+ "buttonMulti", "buttonEdit", "buttonSound", "buttonShift",
+ "buttonMultimode", "buttonPeek", "buttonPlay", "buttonPower"
+ };
+
+ constexpr const char* g_encoderNames[] =
+ {
+ "encoderMatrix4", "encoderMatrix3", "encoderMatrix2", "encoderMatrix1",
+ "encoderLCDRight", "encoderLCDLeft",
+ "encoderAlphaDial"
+ };
+
+ FrontPanel::FrontPanel(const mqJucePlugin::Editor& _editor, Controller& _controller) : m_controller(_controller)
+ {
+ _controller.setFrontPanel(this);
+
+ for(size_t i=0; i<std::size(g_ledNames); ++i)
+ m_leds[i] = _editor.findComponentT<juce::Button>(g_ledNames[i], false);
+
+ for(size_t i=0; i<std::size(g_buttonNames); ++i)
+ {
+ auto* b = _editor.findComponentT<juce::Button>(g_buttonNames[i], false);
+ m_buttons[i] = b;
+ if(!b)
+ continue;
+
+ const auto index = static_cast<uint32_t>(i);
+ b->onStateChange = [this, index]
+ {
+ onButtonStateChanged(index);
+ };
+ }
+
+ for(size_t i=0; i<std::size(g_encoderNames); ++i)
+ {
+ auto* e = _editor.findComponentT<juce::Slider>(g_encoderNames[i], false);
+ if(!e)
+ continue;
+
+ m_encoders[i] = e;
+
+ e->setRotaryParameters(0.0f, juce::MathConstants<float>::twoPi, false);
+ const auto index = static_cast<uint32_t>(i);
+ e->onValueChange = [this, index]
+ {
+ onEncoderValueChanged(index);
+ };
+ }
+
+ auto *lcdArea = _editor.findComponentT<juce::Component>("lcdArea", false);
+
+ if (lcdArea)
+ m_lcd.reset(new MqLcd(*lcdArea));
+
+ std::array<juce::Label*, 2> lcdLines{};
+
+ lcdLines[0] = _editor.findComponentT<juce::Label>("lcdLineA", false);
+ lcdLines[1] = _editor.findComponentT<juce::Label>("lcdLineB", lcdLines[0] != nullptr);
+
+ if (lcdLines[0])
+ {
+ if (m_lcd)
+ {
+ lcdLines[0]->setVisible(false);
+ lcdLines[1]->setVisible(false);
+ }
+ else
+ {
+ m_lcd.reset(new MqLcdText(*lcdLines[0], *lcdLines[1]));
+ }
+ }
+
+ auto* shadow = _editor.findComponent("lcdshadow", false);
+
+ if(shadow)
+ shadow->setInterceptsMouseClicks(false, false);
+
+ _controller.sendSysEx(Controller::EmuRequestLcd);
+ _controller.sendSysEx(Controller::EmuRequestLeds);
+ }
+
+ FrontPanel::~FrontPanel()
+ {
+ m_controller.setFrontPanel(nullptr);
+ m_lcd.reset();
+ }
+
+ void FrontPanel::processSysex(const std::vector<uint8_t>& _msg) const
+ {
+ if(_msg.size() < 5)
+ return;
+
+ const auto cmd = static_cast<mqLib::SysexCommand>(_msg[4]);
+
+ switch (cmd)
+ {
+ case mqLib::SysexCommand::EmuLCD:
+ processLCDUpdate(_msg);
+ break;
+ case mqLib::SysexCommand::EmuLCDCGRata:
+ processLCDCGRamUpdate(_msg);
+ break;
+ case mqLib::SysexCommand::EmuLEDs:
+ processLedUpdate(_msg);
+ break;
+ case mqLib::SysexCommand::EmuRotaries:
+ case mqLib::SysexCommand::EmuButtons:
+ default:
+ break;
+ }
+ }
+
+ void FrontPanel::processLCDUpdate(const std::vector<uint8_t>& _msg) const
+ {
+ const auto* data = &_msg[5];
+
+ std::array<uint8_t, 40> d{};
+
+ for(size_t i=0; i<d.size(); ++i)
+ d[i] = data[i];
+
+ m_lcd->setText(d);
+ }
+
+ void FrontPanel::processLCDCGRamUpdate(const std::vector<uint8_t>& _msg) const
+ {
+ const auto *data = &_msg[5];
+
+ std::array<uint8_t, 64> d{};
+ for (size_t i = 0; i < d.size(); ++i)
+ d[i] = data[i];
+
+ m_lcd->setCgRam(d);
+ }
+
+ void FrontPanel::processLedUpdate(const std::vector<uint8_t>& _msg) const
+ {
+ const uint32_t leds =
+ (static_cast<uint32_t>(_msg[5]) << 24) |
+ (static_cast<uint32_t>(_msg[6]) << 16) |
+ (static_cast<uint32_t>(_msg[7]) << 8) |
+ static_cast<uint32_t>(_msg[8]);
+
+ for(size_t i=0; i<static_cast<uint32_t>(mqLib::Leds::Led::Count); ++i)
+ {
+ if(m_leds[i])
+ m_leds[i]->setToggleState((leds & (1<<i)) != 0, juce::dontSendNotification);
+ }
+ }
+
+ void FrontPanel::onButtonStateChanged(uint32_t _index) const
+ {
+ const auto* b = m_buttons[_index];
+
+ std::map<pluginLib::MidiDataType, uint8_t> params;
+
+ params[pluginLib::MidiDataType::ParameterIndex] = static_cast<uint8_t>(_index);
+
+ if(b->getClickingTogglesState())
+ params[pluginLib::MidiDataType::ParameterValue] = b->getToggleState() ? 1 : 0;
+ else
+ params[pluginLib::MidiDataType::ParameterValue] = b->getState() == juce::Button::buttonDown ? 1 : 0;
+
+ m_controller.sendSysEx(Controller::EmuSendButton, params);
+ }
+
+ void FrontPanel::onEncoderValueChanged(uint32_t _index)
+ {
+ const auto* e = m_encoders[_index];
+
+ float& vOld = m_encoderValues[_index];
+ const auto v = static_cast<float>(e->getValue());
+
+ auto delta = v - vOld;
+
+ if(v >= 9.0f && vOld <= 1.0f)
+ delta = v - (vOld + 10.0f);
+ if(v <= 1.0f && vOld >= 9.0f)
+ delta = v - (vOld - 10.0f);
+
+ const int deltaInt = dsp56k::clamp(juce::roundToInt(g_encoderSpeed * delta) + 64, 0, 127);
+
+ if(deltaInt == 64)
+ return;
+
+ vOld = v;
+
+ std::map<pluginLib::MidiDataType, uint8_t> params;
+
+ params[pluginLib::MidiDataType::ParameterIndex] = static_cast<uint8_t>(_index);
+ params[pluginLib::MidiDataType::ParameterValue] = static_cast<uint8_t>(deltaInt);
+
+ m_controller.sendSysEx(Controller::EmuSendRotary, params);
+ }
+}
+\ No newline at end of file
diff --git a/source/mqJucePlugin/mqFrontPanel.h b/source/mqJucePlugin/mqFrontPanel.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "../mqLib/buttons.h"
+#include "../mqLib/leds.h"
+
+#include "mqLcdBase.h"
+
+#include <juce_audio_processors/juce_audio_processors.h>
+
+class Controller;
+
+namespace mqJucePlugin
+{
+ class Editor;
+
+ class FrontPanel
+ {
+ public:
+ explicit FrontPanel(const Editor& _editor, Controller& _controller);
+ ~FrontPanel();
+
+ void processSysex(const std::vector<uint8_t>& _msg) const;
+
+ private:
+ void processLCDUpdate(const std::vector<uint8_t>& _msg) const;
+ void processLCDCGRamUpdate(const std::vector<uint8_t>& _msg) const;
+ void processLedUpdate(const std::vector<uint8_t>& _msg) const;
+
+ void onButtonStateChanged(uint32_t _index) const;
+ void onEncoderValueChanged(uint32_t _index);
+
+ std::array<juce::Button*, static_cast<uint32_t>(mqLib::Leds::Led::Count)> m_leds{};
+ std::array<juce::Button*, static_cast<uint32_t>(mqLib::Buttons::ButtonType::Count)> m_buttons{};
+ std::array<juce::Slider*, static_cast<uint32_t>(mqLib::Buttons::Encoders::Count)> m_encoders{};
+ std::array<float, static_cast<uint32_t>(mqLib::Buttons::Encoders::Count)> m_encoderValues{};
+
+ Controller& m_controller;
+ std::unique_ptr<MqLcdBase> m_lcd;
+ };
+}
diff --git a/source/mqJucePlugin/mqLcd.cpp b/source/mqJucePlugin/mqLcd.cpp
@@ -0,0 +1,179 @@
+#include "mqLcd.h"
+
+#include "version.h"
+#include "../mqLib/lcdfonts.h"
+
+// EW20290GLW display simulation
+
+constexpr int g_pixelsPerCharW = 5;
+constexpr int g_pixelsPerCharH = 8;
+
+constexpr int g_numCharsW = 20;
+constexpr int g_numCharsH = 2;
+
+constexpr float g_pixelSpacingAdjust = 3.0f; // 1.0f = 100% as on the hardware, but it looks better on screen if its a bit more
+
+constexpr float g_pixelSpacingW = 0.05f * g_pixelSpacingAdjust;
+constexpr float g_pixelSizeW = 0.6f;
+constexpr float g_charSpacingW = 0.4f;
+
+constexpr float g_charSizeW = g_pixelsPerCharW * g_pixelSizeW + g_pixelSpacingW * (g_pixelsPerCharW - 1);
+constexpr float g_sizeW = g_numCharsW * g_charSizeW + g_charSpacingW * (g_numCharsW - 1);
+constexpr float g_pixelStrideW = g_pixelSizeW + g_pixelSpacingW;
+constexpr float g_charStrideW = g_charSizeW + g_charSpacingW;
+
+constexpr float g_pixelSpacingH = 0.05f * g_pixelSpacingAdjust;
+constexpr float g_pixelSizeH = 0.65f;
+constexpr float g_charSpacingH = 0.4f;
+
+constexpr float g_charSizeH = g_pixelsPerCharH * g_pixelSizeH + g_pixelSpacingH * (g_pixelsPerCharH - 1);
+constexpr float g_sizeH = g_numCharsH * g_charSizeH + g_charSpacingH * (g_numCharsH - 1);
+constexpr float g_pixelStrideH = g_pixelSizeH + g_pixelSpacingH;
+constexpr float g_charStrideH = g_charSizeH + g_charSpacingH;
+
+constexpr int g_numPixelsW = g_numCharsW * g_pixelsPerCharW + g_numCharsW - 1;
+constexpr int g_numPixelsH = g_numCharsH * g_pixelsPerCharH + g_numCharsH - 1;
+
+constexpr float g_pixelBorder = 0.1f;
+
+MqLcd::MqLcd(Component& _parent) : juce::Button("mqLCDButton"), m_scaleW(static_cast<float>(_parent.getWidth()) / g_sizeW), m_scaleH(static_cast<float>(_parent.getHeight()) / g_sizeH)
+{
+ setSize(_parent.getWidth(), _parent.getHeight());
+
+ _parent.addAndMakeVisible(this);
+
+ for (uint32_t i=16; i<256; ++i)
+ {
+ m_characterPaths[i] = createPath(static_cast<uint8_t>(i));
+ }
+
+ const std::string color = _parent.getProperties().getWithDefault("lcdBackgroundColor", juce::String()).toString().toStdString();
+
+ if (color.size() == 6)
+ {
+ m_charBgColor = strtol(color.c_str(), nullptr, 16) | 0xff000000;
+ }
+
+ m_text.fill(255); // block character
+ m_cgData.fill({0});
+
+ onClick = [&]()
+ {
+ onClicked();
+ };
+
+ setEnabled(true);
+}
+
+MqLcd::~MqLcd() = default;
+
+void MqLcd::setText(const std::array<uint8_t, 40>& _text)
+{
+ if (m_text == _text)
+ return;
+
+ m_text = _text;
+
+ repaint();
+}
+
+void MqLcd::setCgRam(std::array<uint8_t, 64>& _data)
+{
+ for (auto i=0; i<m_cgData.size(); ++i)
+ {
+ std::array<uint8_t, 8> c{};
+ memcpy(c.data(), &_data[i*8], 8);
+
+ if (c != m_cgData[i])
+ {
+ m_cgData[i] = c;
+ m_characterPaths[i] = createPath(i);
+ }
+ }
+
+ repaint();
+}
+
+void MqLcd::paint(juce::Graphics& _g)
+{
+ const auto& text = m_overrideText[0] ? m_overrideText : m_text;
+
+ uint32_t charIdx = 0;
+
+ for (auto y=0; y<2; ++y)
+ {
+ const auto ty = static_cast<float>(y) * g_charStrideH * m_scaleH;
+
+ for (auto x = 0; x < 20; ++x, ++charIdx)
+ {
+ const auto tx = static_cast<float>(x) * g_charStrideW * m_scaleW;
+
+ const auto t = juce::AffineTransform::translation(tx, ty);
+
+ const auto c = text[charIdx];
+ const auto& p = m_characterPaths[c];
+
+ if (m_charBgColor)
+ {
+ _g.setColour(juce::Colour(m_charBgColor));
+ _g.fillPath(m_characterPaths[255], t);
+ }
+ _g.setColour(juce::Colour(0xff000000));
+ _g.fillPath(p, t);
+ }
+ }
+}
+
+juce::Path MqLcd::createPath(const uint8_t _character) const
+{
+ const auto* data = _character < m_cgData.size() ? m_cgData[_character].data() : mqLib::getCharacterData(_character);
+
+ juce::Path path;
+
+ const auto h = g_pixelSizeH * m_scaleH;
+ const auto w = g_pixelSizeW * m_scaleW;
+
+ for (auto y=0; y<8; ++y)
+ {
+ const auto y0 = static_cast<float>(y) * g_pixelStrideH * m_scaleH;
+
+ for (auto x=0; x<=4; ++x)
+ {
+ const auto bit = 4-x;
+
+ const auto set = data[y] & (1<<bit);
+
+ if(!set)
+ continue;
+
+ const auto x0 = static_cast<float>(x) * g_pixelStrideW * m_scaleW;
+
+ path.addRectangle(x0, y0, w, h);
+ }
+ }
+
+ return path;
+}
+
+void MqLcd::onClicked()
+{
+ if(isTimerRunning())
+ return;
+
+ const std::string lineA(std::string("Vavra v") + g_pluginVersionString);
+ const std::string lineB = __DATE__ " " __TIME__;
+
+ m_overrideText.fill(' ');
+
+ memcpy(m_overrideText.data(), lineA.c_str(), std::min(std::size(lineA), static_cast<size_t>(20)));
+ memcpy(&m_overrideText[20], lineB.c_str(), std::min(std::size(lineB), static_cast<size_t>(20)));
+
+ startTimer(3000);
+}
+
+void MqLcd::timerCallback()
+{
+ stopTimer();
+ m_overrideText[0] = 0;
+ repaint();
+}
diff --git a/source/mqJucePlugin/mqLcd.h b/source/mqJucePlugin/mqLcd.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <juce_audio_processors/juce_audio_processors.h>
+
+#include "mqLcdBase.h"
+
+class MqLcd final : juce::Button, public MqLcdBase, juce::Timer
+{
+public:
+ explicit MqLcd(juce::Component& _parent);
+ ~MqLcd() override;
+
+ void setText(const std::array<uint8_t, 40> &_text) override;
+ void setCgRam(std::array<uint8_t, 64> &_data) override;
+
+private:
+ void paintButton(juce::Graphics& g, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override {}
+ void paint(juce::Graphics& _g) override;
+ juce::Path createPath(uint8_t _character) const;
+ void onClicked();
+ void timerCallback() override;
+
+private:
+ std::array<juce::Path, 256> m_characterPaths;
+
+ const float m_scaleW;
+ const float m_scaleH;
+
+ std::array<uint8_t, 40> m_overrideText{0};
+ std::array<uint8_t, 40> m_text{' '};
+ std::array<std::array<uint8_t, 8>, 8> m_cgData{0};
+ uint32_t m_charBgColor = 0;
+};
diff --git a/source/mqJucePlugin/mqLcdBase.cpp b/source/mqJucePlugin/mqLcdBase.cpp
diff --git a/source/mqJucePlugin/mqLcdBase.h b/source/mqJucePlugin/mqLcdBase.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <juce_audio_processors/juce_audio_processors.h>
+
+class MqLcdBase
+{
+public:
+ virtual ~MqLcdBase() = default;
+
+ virtual void setText(const std::array<uint8_t, 40>& _text) = 0;
+ virtual void setCgRam(std::array<uint8_t, 64>& _data) = 0;
+};
diff --git a/source/mqJucePlugin/mqLcdText.cpp b/source/mqJucePlugin/mqLcdText.cpp
@@ -0,0 +1,29 @@
+#include "mqLcdText.h"
+
+MqLcdText::MqLcdText(juce::Label &_lineA, juce::Label &_lineB) : m_lineA(_lineA), m_lineB(_lineB)
+{
+ m_lineA.setJustificationType(juce::Justification::centredLeft);
+ m_lineB.setJustificationType(juce::Justification::centredLeft);
+}
+
+void MqLcdText::setText(const std::array<uint8_t, 40>& _text)
+{
+ std::array<char, 40> d;
+
+ for (size_t i = 0; i < 40; ++i)
+ {
+ if (_text[i] < 32)
+ d[i] = '?';
+ else
+ d[i] = static_cast<char>(_text[i]);
+ }
+
+ const std::string lineA(d.data(), 20);
+ const std::string lineB(&d[20], 20);
+
+// LOG("LCD CONTENT:\n'" << lineA << "'\n'" << lineB << "'");
+
+ // nasty but works: prefix/postfix with byte 1 to prevent juce trimming the text
+ m_lineA.setText("\1" + lineA + "\1", juce::dontSendNotification);
+ m_lineB.setText("\1" + lineB + "\1", juce::dontSendNotification);
+}
diff --git a/source/mqJucePlugin/mqLcdText.h b/source/mqJucePlugin/mqLcdText.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "mqLcdBase.h"
+
+namespace juce
+{
+ class Label;
+}
+
+class MqLcdText final : public MqLcdBase
+{
+public:
+ MqLcdText(juce::Label& _lineA, juce::Label& _lineB);
+
+ void setText(const std::array<uint8_t,40>& _text) override;
+ void setCgRam(std::array<uint8_t, 64>& _data) override {}
+
+private:
+ juce::Label& m_lineA;
+ juce::Label& m_lineB;
+};
diff --git a/source/mqJucePlugin/mqPartSelect.cpp b/source/mqJucePlugin/mqPartSelect.cpp
@@ -0,0 +1,72 @@
+#include "mqPartSelect.h"
+
+#include "mqController.h"
+#include "mqEditor.h"
+
+mqPartSelect::mqPartSelect(mqJucePlugin::Editor& _editor, Controller& _controller, pluginLib::ParameterBinding& _parameterBinding)
+ : m_editor(_editor)
+ , m_controller(_controller)
+ , m_parameterBinding(_parameterBinding)
+{
+ std::vector<juce::Button*> buttons;
+ std::vector<juce::Button*> leds;
+
+ _editor.findComponents(buttons, "partSelectButton", 16);
+ _editor.findComponents(leds, "partSelectLED", 16);
+
+ for(size_t i=0; i<m_parts.size(); ++i)
+ {
+ auto& part = m_parts[i];
+
+ part.button = buttons[i];
+ part.led = leds[i];
+
+ auto index = static_cast<uint8_t>(i);
+
+ part.button->onClick = [this,index] { selectPart(index); };
+ part.led->onClick = [this, index] { selectPart(index); };
+ }
+
+ updateUiState();
+}
+
+void mqPartSelect::onPlayModeChanged() const
+{
+ if(m_controller.getCurrentPart() > 0)
+ selectPart(0);
+ else
+ updateUiState();
+}
+
+void mqPartSelect::updateUiState() const
+{
+ const auto current = m_controller.isMultiMode() ? m_controller.getCurrentPart() : static_cast<uint8_t>(0);
+
+ for(size_t i=0; i<m_parts.size(); ++i)
+ {
+ const auto& part = m_parts[i];
+
+ part.button->setToggleState(i == current, juce::dontSendNotification);
+ part.led->setToggleState(i == current, juce::dontSendNotification);
+
+ if(i > 0)
+ {
+ part.button->setVisible(m_controller.isMultiMode());
+ part.led->setVisible(m_controller.isMultiMode());
+ /*
+ part.button->setEnabled(m_controller.isMultiMode());
+ part.led->setEnabled(m_controller.isMultiMode());
+
+ part.button->setAlpha(1.0f);
+ part.led->setAlpha(1.0f);
+ */
+ }
+ }
+}
+
+void mqPartSelect::selectPart(const uint8_t _index) const
+{
+ m_parameterBinding.setPart(_index);
+ m_editor.setCurrentPart(_index);
+ updateUiState();
+}
diff --git a/source/mqJucePlugin/mqPartSelect.h b/source/mqJucePlugin/mqPartSelect.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "../jucePluginLib/parameterbinding.h"
+
+class Controller;
+
+namespace mqJucePlugin
+{
+ class Editor;
+}
+
+class mqPartSelect
+{
+public:
+ explicit mqPartSelect(mqJucePlugin::Editor& _editor, Controller& _controller, pluginLib::ParameterBinding& _parameterBinding);
+
+ void onPlayModeChanged() const;
+
+private:
+ void updateUiState() const;
+ void selectPart(uint8_t _index) const;
+
+ struct Part
+ {
+ juce::Button* button = nullptr;
+ juce::Button* led = nullptr;
+ };
+
+ mqJucePlugin::Editor& m_editor;
+ Controller& m_controller;
+ pluginLib::ParameterBinding& m_parameterBinding;
+ std::array<Part, 16> m_parts{};
+};
+
diff --git a/source/mqJucePlugin/mqPatchBrowser.cpp b/source/mqJucePlugin/mqPatchBrowser.cpp
@@ -0,0 +1,101 @@
+#include "mqPatchBrowser.h"
+
+#include "mqController.h"
+
+#include "../mqLib/mqstate.h"
+
+namespace mqJucePlugin
+{
+ enum Columns
+ {
+ INDEX = 1,
+ NAME = 2,
+ CAT = 3,
+ };
+
+ constexpr std::initializer_list<jucePluginEditorLib::PatchBrowser::ColumnDefinition> g_columns =
+ {
+ {"#", INDEX, 32},
+ {"Name", NAME, 150},
+ {"Category", CAT, 70}
+ };
+
+ PatchBrowser::PatchBrowser(const jucePluginEditorLib::Editor& _editor, pluginLib::Controller& _controller, juce::PropertiesFile& _config) : jucePluginEditorLib::PatchBrowser(_editor, _controller, _config, g_columns)
+ {
+ }
+
+ jucePluginEditorLib::Patch* PatchBrowser::createPatch()
+ {
+ return new Patch();
+ }
+
+ bool PatchBrowser::initializePatch(jucePluginEditorLib::Patch& _patch)
+ {
+ if(_patch.sysex.size() < 5 || _patch.sysex[4] != 0x10)
+ return false;
+
+ auto& patch = static_cast<Patch&>(_patch);
+
+ const auto& dumpMq = mqLib::State::g_dumps[static_cast<uint32_t>(mqLib::State::DumpType::Single)];
+
+ if (patch.sysex.size() == dumpMq.dumpSize)
+ {
+ patch.name = std::string(reinterpret_cast<const char *>(&patch.sysex[dumpMq.firstParamIndex + 363]), 16);
+ patch.category = std::string(reinterpret_cast<const char *>(&patch.sysex[dumpMq.firstParamIndex + 379]), 4);
+
+ return true;
+ }
+
+ const auto &dumpQ = mqLib::State::g_dumps[static_cast<uint32_t>(mqLib::State::DumpType::SingleQ)];
+
+ if (patch.sysex.size() == dumpQ.dumpSize)
+ {
+ patch.name = std::string(reinterpret_cast<const char *>(&patch.sysex[dumpQ.firstParamIndex + 364]), 16);
+ patch.category = std::string(reinterpret_cast<const char *>(&patch.sysex[dumpQ.firstParamIndex + 380]), 4);
+
+ return true;
+ }
+ return false;
+ }
+
+ juce::MD5 PatchBrowser::getChecksum(jucePluginEditorLib::Patch& _patch)
+ {
+ const auto& dump = mqLib::State::g_dumps[static_cast<uint32_t>(mqLib::State::DumpType::Single)];
+
+ return {&_patch.sysex[dump.firstParamIndex], dump.dumpSize - 2 - dump.firstParamIndex};
+ }
+
+ bool PatchBrowser::activatePatch(jucePluginEditorLib::Patch& _patch)
+ {
+ auto& c = static_cast<Controller&>(m_controller);
+
+ c.sendSingle(_patch.sysex);
+
+ return true;
+ }
+
+ int PatchBrowser::comparePatches(int _columnId, const jucePluginEditorLib::Patch& _a, const jucePluginEditorLib::Patch& _b) const
+ {
+ auto& a = static_cast<const Patch&>(_a);
+ auto& b = static_cast<const Patch&>(_b);
+
+ switch(_columnId)
+ {
+ case INDEX: return a.progNumber - b.progNumber;
+ case NAME: return a.name.compare(b.name);
+ case CAT: return a.category.compare(b.category);
+ default: return 0;
+ }
+ }
+
+ std::string PatchBrowser::getCellText(const jucePluginEditorLib::Patch& _patch, int _columnId)
+ {
+ switch (_columnId)
+ {
+ case INDEX: return std::to_string(_patch.progNumber);
+ case NAME: return _patch.name;
+ case CAT: return static_cast<const Patch&>(_patch).category;
+ default: return "?";
+ }
+ }
+}
diff --git a/source/mqJucePlugin/mqPatchBrowser.h b/source/mqJucePlugin/mqPatchBrowser.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "../jucePluginEditorLib/patchbrowser.h"
+
+namespace mqJucePlugin
+{
+ struct Patch : public jucePluginEditorLib::Patch
+ {
+ std::string category;
+ };
+
+ class PatchBrowser : public jucePluginEditorLib::PatchBrowser
+ {
+ public:
+ PatchBrowser(const jucePluginEditorLib::Editor& _editor, pluginLib::Controller& _controller, juce::PropertiesFile& _config);
+ protected:
+ jucePluginEditorLib::Patch* createPatch() override;
+ bool initializePatch(jucePluginEditorLib::Patch& _patch) override;
+ juce::MD5 getChecksum(jucePluginEditorLib::Patch& _patch) override;
+ bool activatePatch(jucePluginEditorLib::Patch& _patch) override;
+ public:
+ int comparePatches(int _columnId, const jucePluginEditorLib::Patch& _a, const jucePluginEditorLib::Patch& _b) const override;
+ protected:
+ std::string getCellText(const jucePluginEditorLib::Patch& _patch, int _columnId) override;
+ };
+}
diff --git a/source/mqJucePlugin/parameterDescriptions_mq.json b/source/mqJucePlugin/parameterDescriptions_mq.json
@@ -0,0 +1,2819 @@
+{
+ "parameterdescriptiondefaults":
+ {
+ "isPublic":true,
+ "isBipolar":false,
+ "toText":"unsignedZero",
+ "name":"",
+ "class":"",
+ "min":0,
+ "max":127,
+ "isBool":false,
+ "isDiscrete":false,
+ "page":0,
+ "step":0
+ },
+ "parameterdescriptions":
+ [
+ {"page":0, "index":0, "name":"Version", "min":1, "max":1, "isPublic":false, "isDiscrete":true},
+
+ // Osc 1
+ {"index":1 , "name":"O1Octave" , "min":16, "max":112, "step":12},
+ {"index":2 , "name":"O1Semi" , "min":52, "max":76, "isDiscrete":true, "isBipolar":true, "toText":"signed"},
+ {"index":3 , "name":"O1Detune"},
+ {"index":4 , "name":"O1BendRange" , "min":40, "max":88},
+ {"index":5 , "name":"O1KeyTrack", "default":96, "toText":"keytrack"},
+ {"index":6 , "name":"O1FmSource" , "min":0 , "max":11, "isDiscrete":true, "toText":"fmSource"},
+ {"index":7 , "name":"O1FmAmount"},
+ {"index":8 , "name":"O1Shape" , "min":0 , "max":6, "isDiscrete":true},
+ {"index":9 , "name":"O1PulseWidth"},
+ {"index":10, "name":"O1PwmSource" , "min":0 , "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":11, "name":"O1Pwm", "isBipolar":true, "toText":"signed"},
+ {"index":12, "name":"O1SubFreqDiv" , "min":0 , "max":31, "isDiscrete":true},
+ {"index":13, "name":"O1SubVolume" , "min":0 , "max":31},
+
+ // Osc 2
+ {"index":17, "name":"O2Octave" , "min":16, "max":112, "step":12},
+ {"index":18, "name":"O2Semi" , "min":52, "max":76, "isDiscrete":true, "isBipolar":true, "toText":"signed"},
+ {"index":19, "name":"O2Detune"},
+ {"index":20, "name":"O2BendRange" , "min":40, "max":88},
+ {"index":21, "name":"O2KeyTrack", "default":96, "toText":"keytrack"},
+ {"index":22, "name":"O2FmSource" , "min":0 , "max":11, "isDiscrete":true, "toText":"fmSource"},
+ {"index":23, "name":"O2FmAmount"},
+ {"index":24, "name":"O2Shape" , "min":0 , "max":6, "isDiscrete":true},
+ {"index":25, "name":"O2PulseWidth"},
+ {"index":26, "name":"O2PwmSource" , "min":0 , "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":27, "name":"O2Pwm", "isBipolar":true, "toText":"signed"},
+ {"index":28, "name":"O2SubFreqDiv" , "min":0 , "max":31, "isDiscrete":true},
+ {"index":29, "name":"O2SubVolume" , "min":0 , "max":31},
+
+ // Osc 3
+ {"index":33, "name":"O3Octave" , "min":16, "max":112, "step":12},
+ {"index":34, "name":"O3Semi" , "min":52, "max":76, "isDiscrete":true, "isBipolar":true, "toText":"signed"},
+ {"index":35, "name":"O3Detune"},
+ {"index":36, "name":"O3BendRange" , "min":40, "max":88},
+ {"index":37, "name":"O3KeyTrack", "default":96, "toText":"keytrack"},
+ {"index":38, "name":"O3FmSource" , "min":0 , "max":11, "isDiscrete":true, "toText":"fmSource"},
+ {"index":39, "name":"O3FmAmount"},
+ {"index":40, "name":"O3Shape" , "min":0 , "max":4, "isDiscrete":true},
+ {"index":41, "name":"O3PulseWidth"},
+ {"index":42, "name":"O3PwmSource" , "min":0 , "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":43, "name":"O3Pwm", "isBipolar":true, "toText":"signed"},
+
+ // Sync
+ {"index":49, "name":"Sync", "min":0 , "max":1, "isBool":true},
+
+ // Pitch Mod
+ {"index":50, "name":"PitchModSrc", "min":0 , "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":51, "name":"PitchModAmount", "isBipolar":true, "toText":"signed"},
+
+ // Glide
+ {"index":53, "name":"GlideEnable", "min":0 , "max":1, "isBool":true},
+ {"index":56, "name":"GlideMode", "min":0 , "max":4, "isDiscrete":true, "toText":"glideMode"},
+ {"index":57, "name":"GlideRate", "min":0 , "max":127},
+
+ // Sound
+ {"index":58, "name":"VoiceMode", "min":0 , "max":1, "isBool":true},
+ {"index":58, "name":"UnisonoCount", "min":0 , "max":5, "isDiscrete":true, "toText":"unisonoMode"},
+ {"index":59, "name":"UnisonoDetune"},
+
+ // Mixer
+ {"index":61, "name":"O1Level"},
+ {"index":62, "name":"O1Balance", "toText":"filterBalance", "isBipolar":true},
+ {"index":63, "name":"O2Level"},
+ {"index":64, "name":"O2Balance", "toText":"filterBalance", "isBipolar":true},
+ {"index":65, "name":"O3Level"},
+ {"index":66, "name":"O3Balance", "toText":"filterBalance", "isBipolar":true},
+ {"index":67, "name":"NoiseLevel"},
+ {"index":68, "name":"NoiseBalance", "toText":"filterBalance", "isBipolar":true},
+ {"index":71, "name":"RingModLevel"},
+ {"index":72, "name":"RingModBalance", "toText":"filterBalance", "isBipolar":true},
+ {"index":75, "name":"NoiseModeF1", "min":0 , "max":3, "isDiscrete":true, "toText":"noiseMode"},
+ {"index":76, "name":"NoiseModeF2", "min":0 , "max":3, "isDiscrete":true, "toText":"noiseMode"},
+
+ // Filter 1
+ {"index":77, "name":"F1Type", "min":0 , "max":10, "isDiscrete":true, "toText":"filterType"},
+ {"index":78, "name":"F1Cutoff"},
+ {"index":80, "name":"F1Resonance"},
+ {"index":81, "name":"F1Drive"},
+ {"index":86, "name":"F1KeyTrack", "toText":"keytrack"},
+ {"index":87, "name":"F1EnvMod", "isBipolar":true, "toText":"signed"},
+ {"index":88, "name":"F1VelMod", "isBipolar":true, "toText":"signed"},
+ {"index":89, "name":"F1ModSource", "min":0 , "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":90, "name":"F1CutoffMod"},
+ {"index":91, "name":"F1FmSource", "min":0 , "max":11, "isDiscrete":true, "toText":"fmSource"},
+ {"index":92, "name":"F1FmAmount"},
+ {"index":93, "name":"F1Pan", "toText":"pan", "isBipolar":true},
+ {"index":94, "name":"F1PanModSource", "min":0 , "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":95, "name":"F1PanMod", "isBipolar":true, "toText":"signed"},
+
+ // Filter 2
+ {"index":97, "name":"F2Type", "min":0 , "max":10, "isDiscrete":true, "toText":"filterType"},
+ {"index":98, "name":"F2Cutoff"},
+ {"index":100, "name":"F2Resonance"},
+ {"index":101, "name":"F2Drive"},
+ {"index":106, "name":"F2KeyTrack", "toText":"keytrack"},
+ {"index":107, "name":"F2EnvMod", "isBipolar":true, "toText":"signed"},
+ {"index":108, "name":"F2VelMod", "isBipolar":true, "toText":"signed"},
+ {"index":109, "name":"F2ModSource", "min":0 , "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":110, "name":"F2CutoffMod"},
+ {"index":111, "name":"F2FmSource", "min":0 , "max":11, "isDiscrete":true, "toText":"fmSource"},
+ {"index":112, "name":"F2FmAmount"},
+ {"index":113, "name":"F2Pan", "toText":"pan", "isBipolar":true},
+ {"index":114, "name":"F2PanModSource", "min":0 , "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":115, "name":"F2PanMod", "isBipolar":true, "toText":"signed"},
+
+ {"index":117, "name":"FilterRouting", "min":0 , "max":1, "isBool":true, "toText":"filterRouting"},
+
+ // Amp
+ {"index":121, "name":"AmpVolume"},
+ {"index":122, "name":"AmpVelocity", "isBipolar":true, "toText":"signed"},
+ {"index":123, "name":"AmpModSource", "min":0 , "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":124, "name":"AmpModAmount"},
+
+ // Effects
+ {"index":128, "name":"FX1Type", "min":0 , "max":6, "isDiscrete":true, "toText":"fx1Type"},
+ {"index":144, "name":"FX2Type", "min":0 , "max":10, "isDiscrete":true, "toText":"fx2Type"},
+ {"index":129, "name":"FX1Mix"},
+ {"index":145, "name":"FX2Mix"},
+
+ {"index":130, "name":"Fx1ChorusSpeed"},
+ {"index":131, "name":"Fx1ChorusDepth"},
+ {"index":133, "name":"Fx1ChorusDelay"},
+
+ {"index":146, "name":"Fx2ChorusSpeed"},
+ {"index":147, "name":"Fx2ChorusDepth"},
+ {"index":149, "name":"Fx2ChorusDelay"},
+
+ {"index":130, "name":"Fx1FlangerSpeed"},
+ {"index":131, "name":"Fx1FlangerDepth"},
+ {"index":134, "name":"Fx1FlangerFeedback"},
+ {"index":138, "name":"Fx1FlangerPolarity", "min":0 , "max":1, "isBool":true},
+
+ {"index":146, "name":"Fx2FlangerSpeed"},
+ {"index":147, "name":"Fx2FlangerDepth"},
+ {"index":150, "name":"Fx2FlangerFeedback"},
+ {"index":154, "name":"Fx2FlangerPolarity", "min":0 , "max":1, "isBool":true},
+
+ {"index":130, "name":"Fx1PhaserSpeed"},
+ {"index":131, "name":"Fx1PhaserDepth"},
+ {"index":134, "name":"Fx1PhaserFeedback"},
+ {"index":135, "name":"Fx1PhaserCenter"},
+ {"index":136, "name":"Fx1PhaserSpacing"},
+ {"index":138, "name":"Fx1PhaserPolarity", "min":0 , "max":1, "isBool":true},
+
+ {"index":146, "name":"Fx2PhaserSpeed"},
+ {"index":147, "name":"Fx2PhaserDepth"},
+ {"index":150, "name":"Fx2PhaserFeedback"},
+ {"index":151, "name":"Fx2PhaserCenter"},
+ {"index":152, "name":"Fx2PhaserSpacing"},
+ {"index":154, "name":"Fx2PhaserPolarity", "min":0 , "max":1, "isBool":true},
+
+ {"index":150, "name":"Fx2DelayFeedback"},
+ {"index":151, "name":"Fx2DelayCutoff"},
+ {"index":154, "name":"Fx2DelayPolarity", "min":0 , "max":1, "isBool":true},
+ {"index":155, "name":"Fx2DelayAutopan", "min":0 , "max":1, "isBool":true},
+
+ {"index":131, "name":"Fx1OverdriveDrive"},
+ {"index":132, "name":"Fx1OverdrivePostGain"},
+ {"index":135, "name":"Fx1OverdriveCutoff"},
+
+ {"index":147, "name":"Fx2OverdriveDrive"},
+ {"index":148, "name":"Fx2OverdrivePostGain"},
+ {"index":151, "name":"Fx2OverdriveCutoff"},
+
+ {"index":130, "name":"FiveFX1ChorusSpeed", "min":1},
+ {"index":131, "name":"FiveFX1ChorusDepth"},
+ {"index":132, "name":"FiveFX1Delay"},
+ {"index":133, "name":"FiveFX1ChorusDelayL"},
+ {"index":134, "name":"FiveFX1SampleAndHold"},
+ {"index":135, "name":"FiveFX1Overdrive"},
+ {"index":136, "name":"FiveFX1RingModSource", "min":0 , "max":8},
+ {"index":137, "name":"FiveFX1RingModLevel"},
+
+ {"index":146, "name":"FiveFX2ChorusSpeed", "min":1},
+ {"index":147, "name":"FiveFX2ChorusDepth"},
+ {"index":148, "name":"FiveFX2Delay"},
+ {"index":149, "name":"FiveFX2ChorusDelayL"},
+ {"index":150, "name":"FiveFX2SampleAndHold"},
+ {"index":151, "name":"FiveFX2Overdrive"},
+ {"index":152, "name":"FiveFX2RingModSource", "min":0 , "max":8},
+ {"index":153, "name":"FiveFX2RingModLevel"},
+
+ {"index":130, "name":"Fx1VocoderBands", "min":2 , "max":25},
+ {"index":131, "name":"Fx1VocoderAnalysisSignal", "min":0 , "max":8},
+ {"index":132, "name":"Fx1VocoderAnalysisFreqLow"},
+ {"index":133, "name":"Fx1VocoderAnalysisFreqHigh"},
+ {"index":134, "name":"Fx1VocoderOffsetS"},
+ {"index":135, "name":"Fx1VocoderOffsetHi"},
+ {"index":136, "name":"Fx1VocoderBandwidth"},
+ {"index":137, "name":"Fx1VocoderResonance"},
+ {"index":138, "name":"Fx1VocoderAttack"},
+ {"index":139, "name":"Fx1VocoderDecay"},
+ {"index":140, "name":"Fx1VocoderEQLevelLow"},
+ {"index":141, "name":"Fx1VocoderEQBandMid", "min":0 , "max":24},
+ {"index":142, "name":"Fx1VocoderEQLevelMid"},
+ {"index":143, "name":"Fx1VocoderEQLevelHigh"},
+
+ {"index":146, "name":"Fx2VocoderBands", "min":2 , "max":25},
+ {"index":147, "name":"Fx2VocoderAnalysisSignal", "min":0 , "max":8},
+ {"index":148, "name":"Fx2VocoderAnalysisFreqLow"},
+ {"index":149, "name":"Fx2VocoderAnalysisFreqHigh"},
+ {"index":150, "name":"Fx2VocoderOffsetS"},
+ {"index":151, "name":"Fx2VocoderOffsetHi"},
+ {"index":152, "name":"Fx2VocoderBandwidth"},
+ {"index":153, "name":"Fx2VocoderResonance"},
+ {"index":154, "name":"Fx2VocoderAttack"},
+ {"index":155, "name":"Fx2VocoderDecay"},
+ {"index":156, "name":"Fx2VocoderEQLevelLow"},
+ {"index":157, "name":"Fx2VocoderEQBandMid", "min":0 , "max":24},
+ {"index":158, "name":"Fx2VocoderEQLevelMid"},
+ {"index":159, "name":"Fx2VocoderEQLevelHigh"},
+
+ {"index":130, "name":"Fx1ReverbSize"},
+ {"index":131, "name":"Fx1ReverbShape"},
+ {"index":132, "name":"Fx1ReverbDecay"},
+ {"index":133, "name":"Fx1ReverbPreDelay"},
+ {"index":135, "name":"Fx1ReverbLowpass"},
+ {"index":136, "name":"Fx1ReverbHighpass"},
+ {"index":137, "name":"Fx1ReverbDiffusion"},
+ {"index":138, "name":"Fx1ReverbDamping"},
+
+ {"index":146, "name":"Fx2ReverbSize"},
+ {"index":147, "name":"Fx2ReverbShape"},
+ {"index":148, "name":"Fx2ReverbDecay"},
+ {"index":149, "name":"Fx2ReverbPreDelay"},
+ {"index":151, "name":"Fx2ReverbLowpass"},
+ {"index":152, "name":"Fx2ReverbHighpass"},
+ {"index":153, "name":"Fx2ReverbDiffusion"},
+ {"index":154, "name":"Fx2ReverbDamping"},
+
+ {"index":146, "name":"Fx251DelayDelay"},
+ {"index":146, "name":"Fx251ClockDelayDelay", "min":0, "max":29},
+ {"index":147, "name":"Fx251DelayFeedback"},
+ {"index":148, "name":"Fx251DelayLfeLP"},
+ {"index":149, "name":"Fx251DelayInputHP"},
+ {"index":150, "name":"Fx251DelayDelayML"},
+ {"index":151, "name":"Fx251DelayFSLVolume"},
+ {"index":152, "name":"Fx251DelayDelayMR"},
+ {"index":153, "name":"Fx251DelayFSRVolume"},
+ {"index":154, "name":"Fx251DelayDelayS2L"},
+ {"index":155, "name":"Fx251DelayCenterSVolume"},
+ {"index":156, "name":"Fx251DelayDelayS1L"},
+ {"index":157, "name":"Fx251DelayRearSLVolume"},
+ {"index":158, "name":"Fx251DelayDelayS1R"},
+ {"index":159, "name":"Fx251DelayRearSRVolume"},
+
+ // LFO 1
+ {"index":160, "name":"Lfo1Shape", "min":0, "max":5, "isDiscrete":true},
+ {"index":161, "name":"Lfo1Speed"},
+ {"index":161, "name":"Lfo1SpeedClocked"},
+ {"index":163, "name":"Lfo1Sync", "min":0 , "max":1, "isBool":true},
+ {"index":164, "name":"Lfo1Clocked", "min":0 , "max":1, "isBool":true},
+ {"index":165, "name":"Lfo1StartPhase"},
+ {"index":166, "name":"Lfo1Delay"},
+ {"index":167, "name":"Lfo1Fade"},
+ {"index":170, "name":"Lfo1KeyTrack", "toText":"keytrack"},
+
+ // LFO 2
+ {"index":172, "name":"Lfo2Shape", "min":0, "max":5, "isDiscrete":true},
+ {"index":173, "name":"Lfo2Speed"},
+ {"index":173, "name":"Lfo2SpeedClocked"},
+ {"index":175, "name":"Lfo2Sync", "min":0 , "max":1, "isBool":true},
+ {"index":176, "name":"Lfo2Clocked", "min":0 , "max":1, "isBool":true},
+ {"index":177, "name":"Lfo2StartPhase"},
+ {"index":178, "name":"Lfo2Delay"},
+ {"index":179, "name":"Lfo2Fade"},
+ {"index":182, "name":"Lfo2KeyTrack", "toText":"keytrack"},
+
+ // LFO 3
+ {"index":184, "name":"Lfo3Shape", "min":0, "max":5, "isDiscrete":true},
+ {"index":185, "name":"Lfo3Speed"},
+ {"index":185, "name":"Lfo3SpeedClocked"},
+ {"index":187, "name":"Lfo3Sync", "min":0 , "max":1, "isBool":true},
+ {"index":188, "name":"Lfo3Clocked", "min":0 , "max":1, "isBool":true},
+ {"index":189, "name":"Lfo3StartPhase"},
+ {"index":190, "name":"Lfo3Delay"},
+ {"index":191, "name":"Lfo3Fade"},
+ {"index":194, "name":"Lfo3KeyTrack", "toText":"keytrack"},
+
+ // Filter Env
+ {"index":196, "name":"FilterEnvMode", "min":0, "max":4, "isDiscrete":true, "toText":"envMode"},
+ {"index":196, "name":"FilterEnvTriggerMode", "min":0, "max":2, "isBool":true},
+ {"index":199, "name":"FilterEnvAttack"},
+ {"index":200, "name":"FilterEnvAttackLevel"},
+ {"index":201, "name":"FilterEnvDecay"},
+ {"index":202, "name":"FilterEnvSustain"},
+ {"index":203, "name":"FilterEnvDecay2"},
+ {"index":204, "name":"FilterEnvSustain2"},
+ {"index":205, "name":"FilterEnvRelease"},
+
+ // Amp Env
+ {"index":208, "name":"AmpEnvMode", "min":0, "max":4, "isDiscrete":true, "toText":"envMode"},
+ {"index":208, "name":"AmpEnvTriggerMode", "min":0, "max":2, "isBool":true},
+ {"index":211, "name":"AmpEnvAttack"},
+ {"index":212, "name":"AmpEnvAttackLevel"},
+ {"index":213, "name":"AmpEnvDecay"},
+ {"index":214, "name":"AmpEnvSustain"},
+ {"index":215, "name":"AmpEnvDecay2"},
+ {"index":216, "name":"AmpEnvSustain2"},
+ {"index":217, "name":"AmpEnvRelease"},
+
+ // Env 3
+ {"index":220, "name":"Env3Mode", "min":0, "max":4, "isDiscrete":true, "toText":"envMode"},
+ {"index":220, "name":"Env3TriggerMode", "min":0, "max":2, "isBool":true},
+ {"index":223, "name":"Env3Attack"},
+ {"index":224, "name":"Env3AttackLevel"},
+ {"index":225, "name":"Env3Decay"},
+ {"index":226, "name":"Env3Sustain"},
+ {"index":227, "name":"Env3Decay2"},
+ {"index":228, "name":"Env3Sustain2"},
+ {"index":229, "name":"Env3Release"},
+
+ // Env 4
+ {"index":232, "name":"Env4Mode", "min":0, "max":4, "isDiscrete":true, "toText":"envMode"},
+ {"index":232, "name":"Env4TriggerMode", "min":0, "max":2, "isBool":true},
+ {"index":235, "name":"Env4Attack"},
+ {"index":236, "name":"Env4AttackLevel"},
+ {"index":237, "name":"Env4Decay"},
+ {"index":238, "name":"Env4Sustain"},
+ {"index":239, "name":"Env4Decay2"},
+ {"index":240, "name":"Env4Sustain2"},
+ {"index":241, "name":"Env4Release"},
+
+ // Modifiers
+ {"index":245, "name":"Mod1Source1", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":246, "name":"Mod1Source2", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":247, "name":"Mod1Operator", "min":0, "max":7, "isDiscrete":true, "toText":"operator"},
+ {"index":248, "name":"Mod1Constant"},
+ {"index":249, "name":"Mod2Source1", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":250, "name":"Mod2Source2", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":251, "name":"Mod2Operator", "min":0, "max":7, "isDiscrete":true, "toText":"operator"},
+ {"index":252, "name":"Mod2Constant"},
+ {"index":253, "name":"Mod3Source1", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":254, "name":"Mod3Source2", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":255, "name":"Mod3Operator", "min":0, "max":7, "isDiscrete":true, "toText":"operator"},
+ {"index":256, "name":"Mod3Constant"},
+ {"index":257, "name":"Mod4Source1", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":258, "name":"Mod4Source2", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":259, "name":"Mod4Operator", "min":0, "max":7, "isDiscrete":true, "toText":"operator"},
+ {"index":260, "name":"Mod4Constant"},
+
+ // Fast Mod Matrix
+ {"index":261, "name":"Slot1FSource", "min":0, "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":262, "name":"Slot1FDestination", "min":0, "max":30, "isDiscrete":true, "toText":"fastModDest"},
+ {"index":263, "name":"Slot1FAmount"},
+ {"index":264, "name":"Slot2FSource", "min":0, "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":265, "name":"Slot2FDestination", "min":0, "max":30, "isDiscrete":true, "toText":"fastModDest"},
+ {"index":266, "name":"Slot2FAmount"},
+ {"index":267, "name":"Slot3FSource", "min":0, "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":268, "name":"Slot3FDestination", "min":0, "max":30, "isDiscrete":true, "toText":"fastModDest"},
+ {"index":269, "name":"Slot3FAmount"},
+ {"index":270, "name":"Slot4FSource", "min":0, "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":271, "name":"Slot4FDestination", "min":0, "max":30, "isDiscrete":true, "toText":"fastModDest"},
+ {"index":272, "name":"Slot4FAmount"},
+ {"index":273, "name":"Slot5FSource", "min":0, "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":274, "name":"Slot5FDestination", "min":0, "max":30, "isDiscrete":true, "toText":"fastModDest"},
+ {"index":275, "name":"Slot5FAmount"},
+ {"index":276, "name":"Slot6FSource", "min":0, "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":277, "name":"Slot6FDestination", "min":0, "max":30, "isDiscrete":true, "toText":"fastModDest"},
+ {"index":278, "name":"Slot6FAmount"},
+ {"index":279, "name":"Slot7FSource", "min":0, "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":280, "name":"Slot7FDestination", "min":0, "max":30, "isDiscrete":true, "toText":"fastModDest"},
+ {"index":281, "name":"Slot7FAmount"},
+ {"index":282, "name":"Slot8FSource", "min":0, "max":13, "isDiscrete":true, "toText":"fastModSource"},
+ {"index":283, "name":"Slot8FDestination", "min":0, "max":30, "isDiscrete":true, "toText":"fastModDest"},
+ {"index":284, "name":"Slot8FAmount"},
+
+ // Standard Mod Matrix
+ {"index":285, "name":"Slot1SSource", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":286, "name":"Slot1SDestination", "min":0, "max":57, "isDiscrete":true, "toText":"modDest"},
+ {"index":287, "name":"Slot1SAmount"},
+ {"index":288, "name":"Slot2SSource", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":289, "name":"Slot2SDestination", "min":0, "max":57, "isDiscrete":true, "toText":"modDest"},
+ {"index":290, "name":"Slot2SAmount"},
+ {"index":291, "name":"Slot3SSource", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":292, "name":"Slot3SDestination", "min":0, "max":57, "isDiscrete":true, "toText":"modDest"},
+ {"index":293, "name":"Slot3SAmount"},
+ {"index":294, "name":"Slot4SSource", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":295, "name":"Slot4SDestination", "min":0, "max":57, "isDiscrete":true, "toText":"modDest"},
+ {"index":296, "name":"Slot4SAmount"},
+ {"index":297, "name":"Slot5SSource", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":298, "name":"Slot5SDestination", "min":0, "max":57, "isDiscrete":true, "toText":"modDest"},
+ {"index":299, "name":"Slot5SAmount"},
+ {"index":300, "name":"Slot6SSource", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":301, "name":"Slot6SDestination", "min":0, "max":57, "isDiscrete":true, "toText":"modDest"},
+ {"index":302, "name":"Slot6SAmount"},
+ {"index":303, "name":"Slot7SSource", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":304, "name":"Slot7SDestination", "min":0, "max":57, "isDiscrete":true, "toText":"modDest"},
+ {"index":305, "name":"Slot7SAmount"},
+ {"index":306, "name":"Slot8SSource", "min":0, "max":39, "isDiscrete":true, "toText":"modSource"},
+ {"index":307, "name":"Slot8SDestination", "min":0, "max":57, "isDiscrete":true, "toText":"modDest"},
+ {"index":308, "name":"Slot8SAmount"},
+
+ // Controller Delay ("not implemented")
+ {"index":309, "name":"ControllerDelaySource", "min":0, "max":39},
+ {"index":310, "name":"ControllerDelay"},
+
+ // Arp
+ {"index":311, "name":"ArpMode", "min":0, "max":3, "toText":"arpMode", "isDiscrete":true},
+ {"index":312, "name":"ArpPattern", "min":0, "max":16, "toText":"arpPattern", "isDiscrete":true},
+ {"index":313, "name":"ArpMaxNotes", "min":0, "max":15, "toText":"unsignedOne"},
+ {"index":314, "name":"ArpClock", "toText":"arpClock", "isDiscrete":true},
+ {"index":315, "name":"ArpLength", "toText":"arpLength", "isDiscrete":true},
+ {"index":316, "name":"ArpOctaveRange", "min":0, "max":9, "toText":"unsignedOne", "isDiscrete":true},
+ {"index":317, "name":"ArpDirection", "min":0, "max":3, "toText":"arpDirection", "isDiscrete":true},
+ {"index":318, "name":"ArpSortOrder", "min":0, "max":5, "toText":"arpSortOrder", "isDiscrete":true},
+ {"index":319, "name":"ArpVeloMode", "min":0, "max":2, "toText":"arpVeloMode", "isDiscrete":true},
+ {"index":320, "name":"ArpTFactor"},
+ {"index":321, "name":"ArpSameNoteOverlap", "min":0, "max":1, "isBool":true},
+ {"index":322, "name":"ArpPatternReset", "min":0, "max":1, "isBool":true},
+ {"index":323, "name":"ArpPatternLength", "min":0, "max":15, "toText":"unsignedOne", "isDiscrete":true},
+
+ {"index":326, "name":"Tempo", "toText":"tempo", "isDiscrete":true},
+
+ // Arp Step / Glide / Accent
+ {"index":327, "name":"Arp00Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":327, "name":"Arp00Glide", "min":0, "max":1, "isBool":true},
+ {"index":327, "name":"Arp00Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":328, "name":"Arp01Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":328, "name":"Arp01Glide", "min":0, "max":1, "isBool":true},
+ {"index":328, "name":"Arp01Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":329, "name":"Arp02Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":329, "name":"Arp02Glide", "min":0, "max":1, "isBool":true},
+ {"index":329, "name":"Arp02Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":330, "name":"Arp03Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":330, "name":"Arp03Glide", "min":0, "max":1, "isBool":true},
+ {"index":330, "name":"Arp03Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":331, "name":"Arp04Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":331, "name":"Arp04Glide", "min":0, "max":1, "isBool":true},
+ {"index":331, "name":"Arp04Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":332, "name":"Arp05Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":332, "name":"Arp05Glide", "min":0, "max":1, "isBool":true},
+ {"index":332, "name":"Arp05Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":333, "name":"Arp06Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":333, "name":"Arp06Glide", "min":0, "max":1, "isBool":true},
+ {"index":333, "name":"Arp06Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":334, "name":"Arp07Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":334, "name":"Arp07Glide", "min":0, "max":1, "isBool":true},
+ {"index":334, "name":"Arp07Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":335, "name":"Arp08Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":335, "name":"Arp08Glide", "min":0, "max":1, "isBool":true},
+ {"index":335, "name":"Arp08Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":336, "name":"Arp09Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":336, "name":"Arp09Glide", "min":0, "max":1, "isBool":true},
+ {"index":336, "name":"Arp09Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":337, "name":"Arp10Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":337, "name":"Arp10Glide", "min":0, "max":1, "isBool":true},
+ {"index":337, "name":"Arp10Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":338, "name":"Arp11Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":338, "name":"Arp11Glide", "min":0, "max":1, "isBool":true},
+ {"index":338, "name":"Arp11Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":339, "name":"Arp12Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":339, "name":"Arp12Glide", "min":0, "max":1, "isBool":true},
+ {"index":339, "name":"Arp12Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":340, "name":"Arp13Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":340, "name":"Arp13Glide", "min":0, "max":1, "isBool":true},
+ {"index":340, "name":"Arp13Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":341, "name":"Arp14Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":341, "name":"Arp14Glide", "min":0, "max":1, "isBool":true},
+ {"index":341, "name":"Arp14Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+ {"index":342, "name":"Arp15Step", "min":0, "max":7, "toText":"arpStep", "isDiscrete":true},
+ {"index":342, "name":"Arp15Glide", "min":0, "max":1, "isBool":true},
+ {"index":342, "name":"Arp15Accent", "min":0, "max":7, "toText":"arpAccent", "isDiscrete":true},
+
+ // Arp Step Length / Timing
+ {"index":343, "name":"Arp00Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":343, "name":"Arp00Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":344, "name":"Arp01Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":344, "name":"Arp01Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":345, "name":"Arp02Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":345, "name":"Arp02Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":346, "name":"Arp03Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":346, "name":"Arp03Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":347, "name":"Arp04Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":347, "name":"Arp04Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":348, "name":"Arp05Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":348, "name":"Arp05Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":349, "name":"Arp06Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":349, "name":"Arp06Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":350, "name":"Arp07Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":350, "name":"Arp07Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":351, "name":"Arp08Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":351, "name":"Arp08Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":352, "name":"Arp09Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":352, "name":"Arp09Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":353, "name":"Arp10Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":353, "name":"Arp10Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":354, "name":"Arp11Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":354, "name":"Arp11Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":355, "name":"Arp12Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":355, "name":"Arp12Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":356, "name":"Arp13Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":356, "name":"Arp13Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":357, "name":"Arp14Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":357, "name":"Arp14Timing", "min":0, "max":7, "isDiscrete":true},
+ {"index":358, "name":"Arp15Length", "min":0, "max":7, "isDiscrete":true},
+ {"index":358, "name":"Arp15Timing", "min":0, "max":7, "isDiscrete":true},
+
+ // Sound Name
+ {"index":363, "name":"Name00", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":364, "name":"Name01", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":365, "name":"Name02", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":366, "name":"Name03", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":367, "name":"Name04", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":368, "name":"Name05", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":369, "name":"Name06", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":370, "name":"Name07", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":371, "name":"Name08", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":372, "name":"Name09", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":373, "name":"Name10", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":374, "name":"Name11", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":375, "name":"Name12", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":376, "name":"Name13", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":377, "name":"Name14", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":378, "name":"Name15", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+
+ // Sound Category
+ {"index":379, "name":"Category00", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":380, "name":"Category01", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":381, "name":"Category02", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"index":382, "name":"Category03", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+
+ // Multi
+ {"class":"NonPartSensitive", "page":100, "index":0, "name":"MVolume"},
+ {"class":"NonPartSensitive", "page":100, "index":1, "name":"MControlW", "min":0, "max":121, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":100, "index":2, "name":"MControlX", "min":0, "max":121, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":100, "index":3, "name":"MControlY", "min":0, "max":121, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":100, "index":4, "name":"MControlZ", "min":0, "max":121, "isDiscrete":true},
+
+ {"class":"NonPartSensitive", "page":100, "index":16, "name":"MName00", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":17, "name":"MName01", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":18, "name":"MName02", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":19, "name":"MName03", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":20, "name":"MName04", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":21, "name":"MName05", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":22, "name":"MName06", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":23, "name":"MName07", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":24, "name":"MName08", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":25, "name":"MName09", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":26, "name":"MName10", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":27, "name":"MName11", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":28, "name":"MName12", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":29, "name":"MName13", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":30, "name":"MName14", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+ {"class":"NonPartSensitive", "page":100, "index":31, "name":"MName15", "min":32, "max":127, "isDiscrete":true, "toText":"ascii"},
+
+ // Multi Instruments
+ {"class":"NonPartSensitive", "page":101, "index":32, "name":"MI0SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":101, "index":33, "name":"MI0SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":101, "index":34, "name":"MI0MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":101, "index":35, "name":"MI0Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":101, "index":36, "name":"MI0Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":101, "index":37, "name":"MI0Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":101, "index":38, "name":"MI0Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":101, "index":39, "name":"MI0RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":101, "index":39, "name":"MI0TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":101, "index":39, "name":"MI0PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":101, "index":40, "name":"MI0Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":101, "index":43, "name":"MI0PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":101, "index":44, "name":"MI0VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":101, "index":45, "name":"MI0VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":101, "index":46, "name":"MI0KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":101, "index":47, "name":"MI0KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":101, "index":48, "name":"MI0Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":101, "index":48, "name":"MI0Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":101, "index":48, "name":"MI0Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":101, "index":48, "name":"MI0Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":101, "index":48, "name":"MI0Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":101, "index":48, "name":"MI0ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":102, "index":32, "name":"MI1SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":102, "index":33, "name":"MI1SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":102, "index":34, "name":"MI1MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":102, "index":35, "name":"MI1Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":102, "index":36, "name":"MI1Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":102, "index":37, "name":"MI1Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":102, "index":38, "name":"MI1Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":102, "index":39, "name":"MI1RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":102, "index":39, "name":"MI1TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":102, "index":39, "name":"MI1PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":102, "index":40, "name":"MI1Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":102, "index":43, "name":"MI1PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":102, "index":44, "name":"MI1VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":102, "index":45, "name":"MI1VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":102, "index":46, "name":"MI1KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":102, "index":47, "name":"MI1KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":102, "index":48, "name":"MI1Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":102, "index":48, "name":"MI1Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":102, "index":48, "name":"MI1Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":102, "index":48, "name":"MI1Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":102, "index":48, "name":"MI1Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":102, "index":48, "name":"MI1ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":103, "index":32, "name":"MI2SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":103, "index":33, "name":"MI2SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":103, "index":34, "name":"MI2MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":103, "index":35, "name":"MI2Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":103, "index":36, "name":"MI2Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":103, "index":37, "name":"MI2Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":103, "index":38, "name":"MI2Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":103, "index":39, "name":"MI2RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":103, "index":39, "name":"MI2TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":103, "index":39, "name":"MI2PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":103, "index":40, "name":"MI2Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":103, "index":43, "name":"MI2PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":103, "index":44, "name":"MI2VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":103, "index":45, "name":"MI2VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":103, "index":46, "name":"MI2KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":103, "index":47, "name":"MI2KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":103, "index":48, "name":"MI2Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":103, "index":48, "name":"MI2Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":103, "index":48, "name":"MI2Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":103, "index":48, "name":"MI2Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":103, "index":48, "name":"MI2Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":103, "index":48, "name":"MI2ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":104, "index":32, "name":"MI3SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":104, "index":33, "name":"MI3SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":104, "index":34, "name":"MI3MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":104, "index":35, "name":"MI3Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":104, "index":36, "name":"MI3Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":104, "index":37, "name":"MI3Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":104, "index":38, "name":"MI3Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":104, "index":39, "name":"MI3RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":104, "index":39, "name":"MI3TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":104, "index":39, "name":"MI3PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":104, "index":40, "name":"MI3Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":104, "index":43, "name":"MI3PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":104, "index":44, "name":"MI3VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":104, "index":45, "name":"MI3VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":104, "index":46, "name":"MI3KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":104, "index":47, "name":"MI3KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":104, "index":48, "name":"MI3Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":104, "index":48, "name":"MI3Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":104, "index":48, "name":"MI3Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":104, "index":48, "name":"MI3Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":104, "index":48, "name":"MI3Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":104, "index":48, "name":"MI3ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":105, "index":32, "name":"MI4SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":105, "index":33, "name":"MI4SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":105, "index":34, "name":"MI4MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":105, "index":35, "name":"MI4Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":105, "index":36, "name":"MI4Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":105, "index":37, "name":"MI4Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":105, "index":38, "name":"MI4Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":105, "index":39, "name":"MI4RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":105, "index":39, "name":"MI4TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":105, "index":39, "name":"MI4PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":105, "index":40, "name":"MI4Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":105, "index":43, "name":"MI4PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":105, "index":44, "name":"MI4VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":105, "index":45, "name":"MI4VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":105, "index":46, "name":"MI4KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":105, "index":47, "name":"MI4KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":105, "index":48, "name":"MI4Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":105, "index":48, "name":"MI4Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":105, "index":48, "name":"MI4Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":105, "index":48, "name":"MI4Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":105, "index":48, "name":"MI4Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":105, "index":48, "name":"MI4ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":106, "index":32, "name":"MI5SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":106, "index":33, "name":"MI5SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":106, "index":34, "name":"MI5MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":106, "index":35, "name":"MI5Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":106, "index":36, "name":"MI5Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":106, "index":37, "name":"MI5Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":106, "index":38, "name":"MI5Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":106, "index":39, "name":"MI5RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":106, "index":39, "name":"MI5TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":106, "index":39, "name":"MI5PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":106, "index":40, "name":"MI5Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":106, "index":43, "name":"MI5PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":106, "index":44, "name":"MI5VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":106, "index":45, "name":"MI5VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":106, "index":46, "name":"MI5KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":106, "index":47, "name":"MI5KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":106, "index":48, "name":"MI5Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":106, "index":48, "name":"MI5Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":106, "index":48, "name":"MI5Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":106, "index":48, "name":"MI5Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":106, "index":48, "name":"MI5Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":106, "index":48, "name":"MI5ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":107, "index":32, "name":"MI6SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":107, "index":33, "name":"MI6SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":107, "index":34, "name":"MI6MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":107, "index":35, "name":"MI6Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":107, "index":36, "name":"MI6Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":107, "index":37, "name":"MI6Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":107, "index":38, "name":"MI6Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":107, "index":39, "name":"MI6RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":107, "index":39, "name":"MI6TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":107, "index":39, "name":"MI6PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":107, "index":40, "name":"MI6Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":107, "index":43, "name":"MI6PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":107, "index":44, "name":"MI6VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":107, "index":45, "name":"MI6VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":107, "index":46, "name":"MI6KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":107, "index":47, "name":"MI6KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":107, "index":48, "name":"MI6Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":107, "index":48, "name":"MI6Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":107, "index":48, "name":"MI6Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":107, "index":48, "name":"MI6Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":107, "index":48, "name":"MI6Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":107, "index":48, "name":"MI6ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":108, "index":32, "name":"MI7SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":108, "index":33, "name":"MI7SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":108, "index":34, "name":"MI7MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":108, "index":35, "name":"MI7Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":108, "index":36, "name":"MI7Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":108, "index":37, "name":"MI7Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":108, "index":38, "name":"MI7Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":108, "index":39, "name":"MI7RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":108, "index":39, "name":"MI7TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":108, "index":39, "name":"MI7PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":108, "index":40, "name":"MI7Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":108, "index":43, "name":"MI7PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":108, "index":44, "name":"MI7VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":108, "index":45, "name":"MI7VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":108, "index":46, "name":"MI7KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":108, "index":47, "name":"MI7KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":108, "index":48, "name":"MI7Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":108, "index":48, "name":"MI7Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":108, "index":48, "name":"MI7Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":108, "index":48, "name":"MI7Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":108, "index":48, "name":"MI7Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":108, "index":48, "name":"MI7ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":109, "index":32, "name":"MI8SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":109, "index":33, "name":"MI8SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":109, "index":34, "name":"MI8MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":109, "index":35, "name":"MI8Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":109, "index":36, "name":"MI8Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":109, "index":37, "name":"MI8Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":109, "index":38, "name":"MI8Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":109, "index":39, "name":"MI8RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":109, "index":39, "name":"MI8TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":109, "index":39, "name":"MI8PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":109, "index":40, "name":"MI8Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":109, "index":43, "name":"MI8PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":109, "index":44, "name":"MI8VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":109, "index":45, "name":"MI8VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":109, "index":46, "name":"MI8KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":109, "index":47, "name":"MI8KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":109, "index":48, "name":"MI8Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":109, "index":48, "name":"MI8Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":109, "index":48, "name":"MI8Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":109, "index":48, "name":"MI8Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":109, "index":48, "name":"MI8Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":109, "index":48, "name":"MI8ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":110, "index":32, "name":"MI9SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":110, "index":33, "name":"MI9SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":110, "index":34, "name":"MI9MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":110, "index":35, "name":"MI9Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":110, "index":36, "name":"MI9Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":110, "index":37, "name":"MI9Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":110, "index":38, "name":"MI9Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":110, "index":39, "name":"MI9RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":110, "index":39, "name":"MI9TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":110, "index":39, "name":"MI9PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":110, "index":40, "name":"MI9Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":110, "index":43, "name":"MI9PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":110, "index":44, "name":"MI9VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":110, "index":45, "name":"MI9VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":110, "index":46, "name":"MI9KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":110, "index":47, "name":"MI9KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":110, "index":48, "name":"MI9Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":110, "index":48, "name":"MI9Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":110, "index":48, "name":"MI9Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":110, "index":48, "name":"MI9Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":110, "index":48, "name":"MI9Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":110, "index":48, "name":"MI9ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":111, "index":32, "name":"MI10SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":111, "index":33, "name":"MI10SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":111, "index":34, "name":"MI10MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":111, "index":35, "name":"MI10Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":111, "index":36, "name":"MI10Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":111, "index":37, "name":"MI10Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":111, "index":38, "name":"MI10Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":111, "index":39, "name":"MI10RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":111, "index":39, "name":"MI10TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":111, "index":39, "name":"MI10PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":111, "index":40, "name":"MI10Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":111, "index":43, "name":"MI10PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":111, "index":44, "name":"MI10VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":111, "index":45, "name":"MI10VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":111, "index":46, "name":"MI10KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":111, "index":47, "name":"MI10KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":111, "index":48, "name":"MI10Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":111, "index":48, "name":"MI10Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":111, "index":48, "name":"MI10Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":111, "index":48, "name":"MI10Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":111, "index":48, "name":"MI10Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":111, "index":48, "name":"MI10ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":112, "index":32, "name":"MI11SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":112, "index":33, "name":"MI11SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":112, "index":34, "name":"MI11MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":112, "index":35, "name":"MI11Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":112, "index":36, "name":"MI11Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":112, "index":37, "name":"MI11Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":112, "index":38, "name":"MI11Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":112, "index":39, "name":"MI11RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":112, "index":39, "name":"MI11TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":112, "index":39, "name":"MI11PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":112, "index":40, "name":"MI11Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":112, "index":43, "name":"MI11PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":112, "index":44, "name":"MI11VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":112, "index":45, "name":"MI11VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":112, "index":46, "name":"MI11KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":112, "index":47, "name":"MI11KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":112, "index":48, "name":"MI11Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":112, "index":48, "name":"MI11Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":112, "index":48, "name":"MI11Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":112, "index":48, "name":"MI11Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":112, "index":48, "name":"MI11Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":112, "index":48, "name":"MI11ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":113, "index":32, "name":"MI12SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":113, "index":33, "name":"MI12SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":113, "index":34, "name":"MI12MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":113, "index":35, "name":"MI12Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":113, "index":36, "name":"MI12Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":113, "index":37, "name":"MI12Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":113, "index":38, "name":"MI12Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":113, "index":39, "name":"MI12RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":113, "index":39, "name":"MI12TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":113, "index":39, "name":"MI12PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":113, "index":40, "name":"MI12Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":113, "index":43, "name":"MI12PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":113, "index":44, "name":"MI12VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":113, "index":45, "name":"MI12VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":113, "index":46, "name":"MI12KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":113, "index":47, "name":"MI12KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":113, "index":48, "name":"MI12Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":113, "index":48, "name":"MI12Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":113, "index":48, "name":"MI12Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":113, "index":48, "name":"MI12Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":113, "index":48, "name":"MI12Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":113, "index":48, "name":"MI12ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":114, "index":32, "name":"MI13SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":114, "index":33, "name":"MI13SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":114, "index":34, "name":"MI13MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":114, "index":35, "name":"MI13Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":114, "index":36, "name":"MI13Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":114, "index":37, "name":"MI13Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":114, "index":38, "name":"MI13Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":114, "index":39, "name":"MI13RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":114, "index":39, "name":"MI13TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":114, "index":39, "name":"MI13PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":114, "index":40, "name":"MI13Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":114, "index":43, "name":"MI13PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":114, "index":44, "name":"MI13VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":114, "index":45, "name":"MI13VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":114, "index":46, "name":"MI13KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":114, "index":47, "name":"MI13KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":114, "index":48, "name":"MI13Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":114, "index":48, "name":"MI13Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":114, "index":48, "name":"MI13Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":114, "index":48, "name":"MI13Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":114, "index":48, "name":"MI13Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":114, "index":48, "name":"MI13ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":115, "index":32, "name":"MI14SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":115, "index":33, "name":"MI14SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":115, "index":34, "name":"MI14MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":115, "index":35, "name":"MI14Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":115, "index":36, "name":"MI14Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":115, "index":37, "name":"MI14Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":115, "index":38, "name":"MI14Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":115, "index":39, "name":"MI14RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":115, "index":39, "name":"MI14TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":115, "index":39, "name":"MI14PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":115, "index":40, "name":"MI14Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":115, "index":43, "name":"MI14PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":115, "index":44, "name":"MI14VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":115, "index":45, "name":"MI14VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":115, "index":46, "name":"MI14KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":115, "index":47, "name":"MI14KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":115, "index":48, "name":"MI14Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":115, "index":48, "name":"MI14Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":115, "index":48, "name":"MI14Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":115, "index":48, "name":"MI14Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":115, "index":48, "name":"MI14Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":115, "index":48, "name":"MI14ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5},
+
+ {"class":"NonPartSensitive", "page":116, "index":32, "name":"MI15SoundBank", "min":0, "max":5, "isDiscrete":true, "toText":"soundBank"},
+ {"class":"NonPartSensitive", "page":116, "index":33, "name":"MI15SoundNumber", "min":0, "max":99, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":116, "index":34, "name":"MI15MidiChannel", "min":0, "max":17, "isDiscrete":true, "toText":"midiChannel"},
+ {"class":"NonPartSensitive", "page":116, "index":35, "name":"MI15Volume", "min":0, "max":127},
+ {"class":"NonPartSensitive", "page":116, "index":36, "name":"MI15Transpose", "min":16, "max":112, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":116, "index":37, "name":"MI15Detune", "min":0, "max":127, "toText":"signed", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":116, "index":38, "name":"MI15Output", "min":0, "max":7, "isDiscrete":true, "toText":"output"},
+ {"class":"NonPartSensitive", "page":116, "index":39, "name":"MI15RX", "min":0, "max":3, "isDiscrete":true, "toText":"multiRX", "mask":3, "shift":0},
+ {"class":"NonPartSensitive", "page":116, "index":39, "name":"MI15TX", "min":0, "max":3, "isDiscrete":true, "toText":"multiTX", "mask":3, "shift":2},
+ {"class":"NonPartSensitive", "page":116, "index":39, "name":"MI15PlayMode", "min":0, "max":2, "isDiscrete":true, "toText":"multiPartMode", "mask":3, "shift":4},
+ {"class":"NonPartSensitive", "page":116, "index":40, "name":"MI15Pan", "min":0, "max":127, "isDiscrete":true, "toText":"pan", "isBipolar":true},
+ {"class":"NonPartSensitive", "page":116, "index":43, "name":"MI15PatternNum", "min":0, "max":100, "isDiscrete":true, "toText":"patternNum"},
+ {"class":"NonPartSensitive", "page":116, "index":44, "name":"MI15VeloLow", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":116, "index":45, "name":"MI15VeloHigh", "min":0, "max":127, "isDiscrete":true},
+ {"class":"NonPartSensitive", "page":116, "index":46, "name":"MI15KeyLow", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":116, "index":47, "name":"MI15KeyHigh", "min":0, "max":127, "isDiscrete":true, "toText":"midiNote"},
+ {"class":"NonPartSensitive", "page":116, "index":48, "name":"MI15Pitchbend", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":0},
+ {"class":"NonPartSensitive", "page":116, "index":48, "name":"MI15Modwheel", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":1},
+ {"class":"NonPartSensitive", "page":116, "index":48, "name":"MI15Aftertouch", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":2},
+ {"class":"NonPartSensitive", "page":116, "index":48, "name":"MI15Sustain", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":3},
+ {"class":"NonPartSensitive", "page":116, "index":48, "name":"MI15Button12", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":4},
+ {"class":"NonPartSensitive", "page":116, "index":48, "name":"MI15ProgChange", "min":0, "max":1, "isDiscrete":true, "mask":1, "shift":5}
+ ],
+ "valuelists":
+ {
+ "unsignedZero":
+ [
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
+ "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
+ "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
+ "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
+ "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
+ "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
+ "120", "121", "122", "123", "124", "125", "126", "127"
+ ],
+ "unsignedOne":
+ [
+ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
+ "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
+ "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
+ "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
+ "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
+ "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
+ "120", "121", "122", "123", "124", "125", "126", "127", "128"
+ ],
+ "signed":
+ [
+ "-64", "-63", "-62", "-61",
+ "-60", "-59", "-58", "-57", "-56", "-55", "-54", "-53", "-52", "-51", "-50", "-49", "-48", "-47", "-46", "-45", "-44", "-43", "-42", "-41",
+ "-40", "-39", "-38", "-37", "-36", "-35", "-34", "-33", "-32", "-31", "-30", "-29", "-28", "-27", "-26", "-25", "-24", "-23", "-22", "-21",
+ "-20", "-19", "-18", "-17", "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9", "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
+ "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15", "+16", "+17", "+18", "+19",
+ "+20", "+21", "+22", "+23", "+24", "+25", "+26", "+27", "+28", "+29", "+30", "+31", "+32", "+33", "+34", "+35", "+36", "+37", "+38", "+39",
+ "+40", "+41", "+42", "+43", "+44", "+45", "+46", "+47", "+48", "+49", "+50", "+51", "+52", "+53", "+54", "+55", "+56", "+57", "+58", "+59",
+ "+60", "+61", "+62", "+63"
+ ],
+ "pan":
+ [
+ "Left", "-63", "-62", "-61",
+ "-60", "-59", "-58", "-57", "-56", "-55", "-54", "-53", "-52", "-51", "-50", "-49", "-48", "-47", "-46", "-45", "-44", "-43", "-42", "-41",
+ "-40", "-39", "-38", "-37", "-36", "-35", "-34", "-33", "-32", "-31", "-30", "-29", "-28", "-27", "-26", "-25", "-24", "-23", "-22", "-21",
+ "-20", "-19", "-18", "-17", "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9", "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
+ "Center","+1", "+2", "+3", "+4", "+5", "+6", "+7", "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15", "+16", "+17", "+18", "+19",
+ "+20", "+21", "+22", "+23", "+24", "+25", "+26", "+27", "+28", "+29", "+30", "+31", "+32", "+33", "+34", "+35", "+36", "+37", "+38", "+39",
+ "+40", "+41", "+42", "+43", "+44", "+45", "+46", "+47", "+48", "+49", "+50", "+51", "+52", "+53", "+54", "+55", "+56", "+57", "+58", "+59",
+ "+60", "+61", "+62", "Right"
+ ],
+ "filterBalance":
+ [
+ "F1", "-63", "-62", "-61",
+ "-60", "-59", "-58", "-57", "-56", "-55", "-54", "-53", "-52", "-51", "-50", "-49", "-48", "-47", "-46", "-45", "-44", "-43", "-42", "-41",
+ "-40", "-39", "-38", "-37", "-36", "-35", "-34", "-33", "-32", "-31", "-30", "-29", "-28", "-27", "-26", "-25", "-24", "-23", "-22", "-21",
+ "-20", "-19", "-18", "-17", "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9", "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
+ "Center","+1", "+2", "+3", "+4", "+5", "+6", "+7", "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15", "+16", "+17", "+18", "+19",
+ "+20", "+21", "+22", "+23", "+24", "+25", "+26", "+27", "+28", "+29", "+30", "+31", "+32", "+33", "+34", "+35", "+36", "+37", "+38", "+39",
+ "+40", "+41", "+42", "+43", "+44", "+45", "+46", "+47", "+48", "+49", "+50", "+51", "+52", "+53", "+54", "+55", "+56", "+57", "+58", "+59",
+ "+60", "+61", "+62", "F2"
+ ],
+ "negPos":
+ [
+ "Negative", "Positive"
+ ],
+ "ascii":
+ [
+ "NUL","SOH","STX","ETX","EOT","ENQ","ACK","BEL","BS","HT","LF","VT","FF","CR","SO","SI","DLE","DC1","DC2","DC3","DC4","NAK","SYN","ETB","CAN","EM","SUB","ESC","FS","GS","RS","US",
+ ", ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/",
+ "0","1","2","3","4","5","6","7","8","9",
+ ":",";","<","=",">","?","@",
+ "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
+ "[","\\","]","^","_","`",
+ "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
+ "{","|","}","~","DEL"
+ ],
+ "noiseMode":
+ [
+ "Noise", "Ext Left", "Ext Right", "Ext L+R"
+ ],
+ "unisonoMode":
+ [
+ "Off", "Dual", "3", "4", "5", "6"
+ ],
+ "glideMode":
+ [
+ "Portamento", "Fingered Porta", "Glissando", "?", "Fingered Gliss"
+ ],
+ "fastModSource":
+ [
+ "Off", "LFO 1", "LFO 1 * ModW", "LFO 2", "LFO 2 * Prs", "LFO 3", "Filter Env", "Amp Env", "Env 3", "Env 4", "Velocity", "Mod Wheel", "Pitchbend", "Pressure"
+ ],
+ "fastModDest":
+ [
+ "Pitch", "O1 Pitch", "O1 FM", "O1 PW", "O2 Pitch", "O2 FM", "O2 PW", "O3 Pitch", "O3 FM", "O3 PW", "O1 Level", "O1 Bal", "O2 Level", "O2 Bal", "O3 Level",
+ "O3 Bal", "Ring Level", "Ring Bal", "N/E Level", "N/E Bal", "F1 Cutoff", "F1 Res", "F1 FM", "F1 Drive", "F1 Pan", "F2 Cutoff", "F2 Res", "F2 FM", "F2 Drive", "F2 Pan", "Volume"
+ ],
+ "modSource":
+ [
+ "Off", "LFO1", "LFO1*MW", "LFO2", "LFO2*Prs", "LFO3", "FilterEnv", "AmpEnv", "Env3", "Env4", "Keytrack", "Velocity", "Rel Velocity", "Pressure", "Poly Pressure", "PitchBend",
+ "Modwheel", "Sust. Controller", "Foot Controller", "Breath Controller", "Control W", "Control X", "Control Y", "Control Z", "Ctr Delay", "Mod1", "Mod2", "Mod3", "Mod4", "min",
+ "MAX", "Voice Num", "Voice %16", "Voice %8", "Voice %4", "Voice %2", "Unisono Voice", "U.Detune", "U.De-Pan", "U.De-Oct"
+ ],
+ "modDest":
+ [
+ "Pitch", "O1 Pitch", "O1 FM", "O1 PW", "O2 Pitch", "O2 FM", "O2 PW", "O3 Pitch", "O3 FM", "O3 PW", "O1 Level", "O1 Bal", "O2 Level", "O2 Bal", "O3 Level", "O3 Bal",
+ "Ring Level", "Ring Bal", "N/E Level", "N/E Bal", "F1 Cutoff", "F1 Res", "F1 FM", "F1 Drive", "F1 Pan", "F2 Cutoff", "F2 Res", "F2 FM", "F2 Drive", "F2 Pan", "Volume",
+ "LFO1 Speed", "LFO2 Speed", "LFO3 Speed", "FE Attack", "FE Decay", "FE Sustain", "FE Release", "AE Attack", "AE Decay", "AE Sustain", "AE Release", "Env3 Attack",
+ "Env3 Decay", "Env3 Sustain", "Env3 Release", "Env4 Attack", "Env4 Decay", "Env4 Sustain", "Env4 Release", "M1F Amount", "M2F Amount", "M1S Amount", "M2S Amount",
+ "O1 Sub Div", "O1 Sub Volume", "O2 Sub Div", "O2 Sub Volume"
+ ],
+ "operator":
+ [
+ "+", "-", "*", "AND", "OR", "XOR", "MAX", "min"
+ ],
+ "fmSource":
+ [
+ "Off", "Osc 1", "Osc 2", "Osc 3", "Noise", "LFO 1", "LFO 2", "LFO 3", "Filter Env", "Amp Env", "Env 3", "Env 4"
+ ],
+ "filterType":
+ [
+ "Off", "24dB LP", "12dB LP", "24dB BP", "12dB BP", "24dB HP", "12dB HP", "24dB Notch", "12dB Notch", "Comb+", "Comb-"
+ ],
+ "filterRouting":
+ [
+ "Parallel", "Serial"
+ ],
+ "soundBank":
+ [
+ "A", "B", "C", "X", "D", "E"
+ ],
+ "midiChannel":
+ [
+ "Global", "Omni", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"
+ ],
+ "output":
+ [
+ "Main", "Sub 1", "Sub 2", "FX1", "FX2", "FX3", "FX4", "Aux"
+ ],
+ "multiRX":
+ [
+ "Off", "Local", "MIDI", "Local+MIDI"
+ ],
+ "multiTX":
+ [
+ "Off", "Direct", "Seq", "Seq+Arp"
+ ],
+ "multiPartMode":
+ [
+ "Play", "Mute", "Solo"
+ ],
+ "patternNum":
+ [
+ "Off",
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
+ "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
+ "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
+ "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
+ "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
+ "100"
+ ],
+ "midiNote":
+ [
+ "C-2","C#-2","D-2","D#-2","E-2","F-2","F#-2","G-2","G#-2","A-2","A#-2","B-2",
+ "C-1","C#-1","D-1","D#-1","E-1","F-1","F#-1","G-1","G#-1","A-1","A#-1","B-1",
+ "C0", "C#0", "D0", "D#0", "E0", "F0", "F#0", "G0", "G#0", "A0", "A#0", "B0",
+ "C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1", "G#1", "A1", "A#1", "B1",
+ "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2",
+ "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3",
+ "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4",
+ "C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5",
+ "C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6",
+ "C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7", "A7", "A#7", "B7",
+ "C8", "C#8", "D8", "D#8", "E8", "F8", "F#8", "G8"
+ ],
+ "envMode":
+ [
+ "ADSR", "ADS1DS2R", "One Shot", "Loop S1S2", "Loop All"
+ ],
+ "fx1Type":
+ [
+ "Bypass", "Chorus", "Flanger", "Phaser", "Overdrive", "Five FX", "Vocoder"
+ ],
+ "fx2Type":
+ [
+ "Bypass", "Chorus", "Flanger", "Phaser", "Overdrive", "Five FX", "Vocoder", "Delay", "Reverb", "5.1 Delay", "5.1 Delay Clk"
+ ],
+ "arpMode":
+ [
+ "Off", "On", "One Shot", "Hold"
+ ],
+ "arpPattern":
+ [
+ "Off", "User", "ROM 1", "ROM 2", "ROM 3", "ROM 4", "ROM 5", "ROM 6", "ROM 7", "ROM 8", "ROM 9", "ROM 10", "ROM 11", "ROM 12", "ROM 13", "ROM 14", "ROM 15"
+ ],
+ "arpClock":
+ [
+ "3/192", "4/192", "5/192", "6/192", "7/192", "8/192", "9/192",
+ "10/192", "11/192", "12/192", "13/192", "14/192", "15/192", "16/192", "17/192", "18/192", "19/192",
+ "20/192", "21/192", "22/192", "23/192", "24/192", "25/192", "26/192", "27/192", "28/192", "29/192",
+ "30/192", "31/192", "32/192", "33/192", "34/192", "35/192", "36/192", "37/192", "38/192", "39/192",
+ "40/192", "41/192", "42/192", "43/192", "44/192", "45/192", "46/192", "47/192", "48/192", "49/192",
+ "50/192", "51/192", "52/192", "53/192", "54/192", "55/192", "56/192", "57/192", "58/192", "59/192",
+ "60/192", "61/192", "62/192", "63/192", "64/192", "65/192", "66/192", "67/192", "68/192", "69/192",
+ "70/192", "71/192", "72/192", "73/192", "74/192", "75/192", "76/192", "77/192", "78/192", "79/192",
+ "80/192", "81/192", "82/192", "83/192", "84/192", "85/192", "86/192", "87/192", "88/192", "89/192",
+ "90/192", "91/192", "92/192", "93/192", "94/192", "95/192", "96/192", "97/192", "98/192", "99/192",
+ "100/192", "101/192", "102/192", "103/192", "104/192", "105/192", "106/192", "107/192", "108/192", "109/192",
+ "110/192", "111/192", "112/192", "113/192", "114/192", "115/192", "116/192", "117/192", "118/192", "119/192",
+ "120/192", "121/192", "122/192", "123/192", "124/192", "125/192", "126/192", "127/192", "128/192", "129/192",
+ "130/192"
+ ],
+ "arpLength":
+ [
+ "Legato", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
+ "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
+ "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
+ "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
+ "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
+ "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
+ "120", "121", "122", "123", "124", "125", "126", "127"
+ ],
+ "arpDirection":
+ [
+ "Up", "Down", "Alt Up", "Alt Down"
+ ],
+ "arpSortOrder":
+ [
+ "As played", "Reversed", "Note Low>High", "Note Hi>Low", "Vel Low>Hi", "Vel Hi>Low"
+ ],
+ "arpVeloMode":
+ [
+ "Each Note", "First Note", "Last Note"
+ ],
+ "arpStep":
+ [
+ "*", " ", "-", "<", ">", "<>", "Chord", "?"
+ ],
+ "arpAccent":
+ [
+ "Off", "-3", "-2", "-1", "0", "+1", "+2", "+3"
+ ],
+ "tempo":
+ [
+ "40", "42", "44", "46", "48",
+ "50", "52", "54", "56", "58",
+ "60", "62", "64", "66", "68",
+ "70", "72", "74", "76", "78",
+ "80", "82", "84", "86", "88",
+ "90",
+ "91", "92", "93", "94", "95", "96", "97", "98", "99",
+ "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
+ "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
+ "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
+ "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
+ "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
+ "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
+ "160", "161", "162", "163", "164",
+ "165", "170", "175", "180", "185", "190", "195",
+ "200", "205", "210", "215", "220", "225", "230", "235", "240", "245",
+ "250", "255", "260", "265", "270", "275", "280", "285", "290", "295",
+ "300"
+ ],
+ "keytrack":
+ [
+ "-200%", "-197%", "-194%", "-191%", "-188%", "-185%", "-182%", "-179%",
+ "-175%", "-172%", "-169%", "-166%", "-163%", "-160%", "-157%", "-154%",
+ "-150%", "-147%", "-144%", "-141%", "-138%", "-135%", "-132%", "-129%",
+ "-125%", "-122%", "-119%", "-116%", "-113%", "-110%", "-107%", "-104%",
+ "-100%", "-97%", "-94%", "-91%", "-88%", "-85%", "-82%", "-79%",
+ "-75%", "-72%", "-69%", "-66%", "-63%", "-60%", "-57%", "-54%",
+ "-50%", "-47%", "-44%", "-41%", "-38%", "-35%", "-32%", "-29%",
+ "-25%", "-22%", "-19%", "-16%", "-13%", "-10%", "-7%", "-4%",
+ "0%", "+3%", "+6%", "+9%", "+12%", "+15%", "+18%", "+21%",
+ "+25%", "+28%", "+31%", "+34%", "+37%", "+40%", "+43%", "+46%",
+ "+50%", "+53%", "+56%", "+59%", "+62%", "+65%", "+68%", "+71%",
+ "+75%", "+78%", "+81%", "+84%", "+87%", "+90%", "+93%", "+96%",
+ "+100%", "+103%", "+106%", "+109%", "+112%", "+115%", "+118%", "+121%",
+ "+125%", "+128%", "+131%", "+134%", "+137%", "+140%", "+143%", "+146%",
+ "+150%", "+153%", "+156%", "+159%", "+162%", "+165%", "+168%", "+171%",
+ "+175%", "+178%", "+181%", "+184%", "+187%", "+190%", "+193%", "+196%"
+ ]
+ },
+ "midipackets":
+ {
+ "requestsingle": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "00"},
+ {"type": "bank"},
+ {"type": "program"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "requestmulti": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "01"},
+ {"type": "bank"},
+ {"type": "program"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "requestdrum": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "02"},
+ {"type": "bank"},
+ {"type": "program"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "requestsinglebank": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "00"},
+ {"type": "byte", "value": "10"},
+ {"type": "bank"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "requestmultibank": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "01"},
+ {"type": "byte", "value": "10"},
+ {"type": "bank"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "requestdrumbank": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "02"},
+ {"type": "byte", "value": "10"},
+ {"type": "bank"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "requestglobal": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "04"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "requestallsingles": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "00"},
+ {"type": "byte", "value": "10"},
+ {"type": "byte", "value": "00"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "singleparameterchange": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "20"},
+ {"type": "part"},
+ {"type": "page"},
+ {"type": "paramindex"},
+ {"type": "paramvalue"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "multiparameterchange": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "21"},
+ {"type": "page"},
+ {"type": "paramindex"},
+ {"type": "paramvalue"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "globalparameterchange": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "24"},
+ {"type": "page"},
+ {"type": "paramindex"},
+ {"type": "paramvalue"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "singledump": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "10"},
+ {"type": "bank"},
+ {"type": "program"},
+
+ {"type": "param", "name": "Version"}, // 0
+ {"type": "param", "name": "O1Octave"}, // 1
+ {"type": "param", "name": "O1Semi"}, // 2
+ {"type": "param", "name": "O1Detune"}, // 3
+ {"type": "param", "name": "O1BendRange"}, // 4
+ {"type": "param", "name": "O1KeyTrack"}, // 5
+ {"type": "param", "name": "O1FmSource"}, // 6
+ {"type": "param", "name": "O1FmAmount"}, // 7
+ {"type": "param", "name": "O1Shape"}, // 8
+ {"type": "param", "name": "O1PulseWidth"}, // 9
+ {"type": "param", "name": "O1PwmSource"}, // 10
+ {"type": "param", "name": "O1Pwm"}, // 11
+ {"type": "param", "name": "O1SubFreqDiv"}, // 12
+ {"type": "param", "name": "O1SubVolume"}, // 13
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "O2Octave"}, // 17
+ {"type": "param", "name": "O2Semi"}, // 18
+ {"type": "param", "name": "O2Detune"}, // 19
+ {"type": "param", "name": "O2BendRange"}, // 20
+ {"type": "param", "name": "O2KeyTrack"}, // 21
+ {"type": "param", "name": "O2FmSource"}, // 22
+ {"type": "param", "name": "O2FmAmount"}, // 23
+ {"type": "param", "name": "O2Shape"}, // 24
+ {"type": "param", "name": "O2PulseWidth"}, // 25
+ {"type": "param", "name": "O2PwmSource"}, // 26
+ {"type": "param", "name": "O2Pwm"}, // 27
+ {"type": "param", "name": "O2SubFreqDiv"}, // 28
+ {"type": "param", "name": "O2SubVolume"}, // 29
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "O3Octave"}, // 33
+ {"type": "param", "name": "O3Semi"}, // 34
+ {"type": "param", "name": "O3Detune"}, // 35
+ {"type": "param", "name": "O3BendRange"}, // 36
+ {"type": "param", "name": "O3KeyTrack"}, // 37
+ {"type": "param", "name": "O3FmSource"}, // 38
+ {"type": "param", "name": "O3FmAmount"}, // 39
+ {"type": "param", "name": "O3Shape"}, // 40
+ {"type": "param", "name": "O3PulseWidth"}, // 41
+ {"type": "param", "name": "O3PwmSource"}, // 42
+ {"type": "param", "name": "O3Pwm"}, // 43
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Sync"}, // 49
+ {"type": "param", "name":"PitchModSrc"}, // 50
+ {"type": "param", "name":"PitchModAmount"}, // 51
+ {"type": "null"},
+ {"type": "param", "name":"GlideEnable"}, // 53
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"GlideMode"}, // 56
+ {"type": "param", "name":"GlideRate"}, // 57
+ {"type": "param", "name":"VoiceMode", "mask":"7", "shift":"0"}, // 58
+ {"type": "param", "name":"UnisonoCount", "mask":"7", "shift":"4"}, // 58
+ {"type": "param", "name":"UnisonoDetune"}, // 59
+ {"type": "null"},
+ {"type": "param", "name": "O1Level"}, // 61
+ {"type": "param", "name": "O1Balance"}, // 62
+ {"type": "param", "name": "O2Level"}, // 63
+ {"type": "param", "name": "O2Balance"}, // 64
+ {"type": "param", "name": "O3Level"}, // 65
+ {"type": "param", "name": "O3Balance"}, // 66
+ {"type": "param", "name": "NoiseLevel"}, // 67
+ {"type": "param", "name": "NoiseBalance"}, // 68
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "RingModLevel"}, // 71
+ {"type": "param", "name": "RingModBalance"}, // 72
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "NoiseModeF1"}, // 75
+ {"type": "param", "name": "NoiseModeF2"}, // 76
+ {"type": "param", "name": "F1Type"}, // 77
+ {"type": "param", "name": "F1Cutoff"}, // 78
+ {"type": "null"},
+ {"type": "param", "name": "F1Resonance"}, // 80
+ {"type": "param", "name": "F1Drive"}, // 81
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "F1KeyTrack"}, // 86
+ {"type": "param", "name": "F1EnvMod"}, // 87
+ {"type": "param", "name": "F1VelMod"}, // 88
+ {"type": "param", "name": "F1ModSource"}, // 89
+ {"type": "param", "name": "F1CutoffMod"}, // 90
+ {"type": "param", "name": "F1FmSource"}, // 91
+ {"type": "param", "name": "F1FmAmount"}, // 92
+ {"type": "param", "name": "F1Pan"}, // 93
+ {"type": "param", "name": "F1PanModSource"}, // 94
+ {"type": "param", "name": "F1PanMod"}, // 95
+ {"type": "null"},
+ {"type": "param", "name": "F2Type"}, // 97
+ {"type": "param", "name": "F2Cutoff"}, // 98
+ {"type": "null"},
+ {"type": "param", "name": "F2Resonance"}, // 100
+ {"type": "param", "name": "F2Drive"}, // 101
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "F2KeyTrack"}, // 106
+ {"type": "param", "name": "F2EnvMod"}, // 107
+ {"type": "param", "name": "F2VelMod"}, // 108
+ {"type": "param", "name": "F2ModSource"}, // 109
+ {"type": "param", "name": "F2CutoffMod"}, // 110
+ {"type": "param", "name": "F2FmSource"}, // 111
+ {"type": "param", "name": "F2FmAmount"}, // 112
+ {"type": "param", "name": "F2Pan"}, // 113
+ {"type": "param", "name": "F2PanModSource"}, // 114
+ {"type": "param", "name": "F2PanMod"}, // 115
+ {"type": "null"},
+ {"type": "param", "name": "FilterRouting"}, // 117
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "AmpVolume"}, // 121
+ {"type": "param", "name": "AmpVelocity"}, // 122
+ {"type": "param", "name": "AmpModSource"}, // 123
+ {"type": "param", "name": "AmpModAmount"}, // 124
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "FX1Type"}, // 128
+ {"type": "param", "name": "FX1Mix"}, // 129
+ {"type": "param", "name": "Fx1VocoderBands"}, // 130
+ {"type": "param", "name": "Fx1VocoderAnalysisSignal"}, // 131
+ {"type": "param", "name": "Fx1VocoderAnalysisFreqLow"}, // 132
+ {"type": "param", "name": "Fx1VocoderAnalysisFreqHigh"}, // 133
+ {"type": "param", "name": "Fx1VocoderOffsetS"}, // 134
+ {"type": "param", "name": "Fx1VocoderOffsetHi"}, // 135
+ {"type": "param", "name": "Fx1VocoderBandwidth"}, // 136
+ {"type": "param", "name": "Fx1VocoderResonance"}, // 137
+ {"type": "param", "name": "Fx1VocoderAttack"}, // 138
+ {"type": "param", "name": "Fx1VocoderDecay"}, // 139
+ {"type": "param", "name": "Fx1VocoderEQLevelLow"}, // 140
+ {"type": "param", "name": "Fx1VocoderEQBandMid"}, // 141
+ {"type": "param", "name": "Fx1VocoderEQLevelMid"}, // 142
+ {"type": "param", "name": "Fx1VocoderEQLevelHigh"}, // 143
+ {"type": "param", "name": "FX2Type"}, // 144
+ {"type": "param", "name": "FX2Mix"}, // 145
+ {"type": "param", "name":"Fx2VocoderBands"}, // 146
+ {"type": "param", "name":"Fx2VocoderAnalysisSignal"}, // 147
+ {"type": "param", "name":"Fx2VocoderAnalysisFreqLow"}, // 148
+ {"type": "param", "name":"Fx2VocoderAnalysisFreqHigh"}, // 149
+ {"type": "param", "name":"Fx2VocoderOffsetS"}, // 150
+ {"type": "param", "name":"Fx2VocoderOffsetHi"}, // 151
+ {"type": "param", "name":"Fx2VocoderBandwidth"}, // 152
+ {"type": "param", "name":"Fx2VocoderResonance"}, // 153
+ {"type": "param", "name":"Fx2VocoderAttack"}, // 154
+ {"type": "param", "name":"Fx2VocoderDecay"}, // 155
+ {"type": "param", "name":"Fx2VocoderEQLevelLow"}, // 156
+ {"type": "param", "name":"Fx2VocoderEQBandMid"}, // 157
+ {"type": "param", "name":"Fx2VocoderEQLevelMid"}, // 158
+ {"type": "param", "name":"Fx2VocoderEQLevelHigh"}, // 159
+ {"type": "param", "name":"Lfo1Shape"}, // 160
+ {"type": "param", "name":"Lfo1Speed"}, // 161
+ {"type": "null"},
+ {"type": "param", "name":"Lfo1Sync"}, // 163
+ {"type": "param", "name":"Lfo1Clocked"}, // 164
+ {"type": "param", "name":"Lfo1StartPhase"}, // 165
+ {"type": "param", "name":"Lfo1Delay"}, // 166
+ {"type": "param", "name":"Lfo1Fade"}, // 167
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Lfo1KeyTrack"}, // 170
+ {"type": "null"},
+ {"type": "param", "name":"Lfo2Shape"}, // 172
+ {"type": "param", "name":"Lfo2Speed"}, // 173
+ {"type": "null"},
+ {"type": "param", "name":"Lfo2Sync"}, // 175
+ {"type": "param", "name":"Lfo2Clocked"}, // 176
+ {"type": "param", "name":"Lfo2StartPhase"}, // 177
+ {"type": "param", "name":"Lfo2Delay"}, // 178
+ {"type": "param", "name":"Lfo2Fade"}, // 179
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Lfo2KeyTrack"}, // 182
+ {"type": "null"},
+ {"type": "param", "name":"Lfo3Shape"}, // 184
+ {"type": "param", "name":"Lfo3Speed"}, // 185
+ {"type": "null"},
+ {"type": "param", "name":"Lfo3Sync"}, // 187
+ {"type": "param", "name":"Lfo3Clocked"}, // 188
+ {"type": "param", "name":"Lfo3StartPhase"}, // 189
+ {"type": "param", "name":"Lfo3Delay"}, // 190
+ {"type": "param", "name":"Lfo3Fade"}, // 191
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Lfo3KeyTrack"}, // 194
+ {"type": "null"},
+ {"type": "param", "name":"FilterEnvMode", "mask":7}, // 196
+ {"type": "param", "name":"FilterEnvTriggerMode", "mask":3, "shift":3}, // 196
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"FilterEnvAttack"}, // 199
+ {"type": "param", "name":"FilterEnvAttackLevel"}, // 200
+ {"type": "param", "name":"FilterEnvDecay"}, // 201
+ {"type": "param", "name":"FilterEnvSustain"}, // 202
+ {"type": "param", "name":"FilterEnvDecay2"}, // 203
+ {"type": "param", "name":"FilterEnvSustain2"}, // 204
+ {"type": "param", "name":"FilterEnvRelease"}, // 205
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"AmpEnvMode", "mask":7}, // 208
+ {"type": "param", "name":"AmpEnvTriggerMode", "mask":3, "shift":3}, // 208
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"AmpEnvAttack"}, // 211
+ {"type": "param", "name":"AmpEnvAttackLevel"}, // 212
+ {"type": "param", "name":"AmpEnvDecay"}, // 213
+ {"type": "param", "name":"AmpEnvSustain"}, // 214
+ {"type": "param", "name":"AmpEnvDecay2"}, // 215
+ {"type": "param", "name":"AmpEnvSustain2"}, // 216
+ {"type": "param", "name":"AmpEnvRelease"}, // 217
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Env3Mode", "mask":7}, // 220
+ {"type": "param", "name":"Env3TriggerMode", "mask":3, "shift":3}, // 220
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Env3Attack"}, // 223
+ {"type": "param", "name":"Env3AttackLevel"}, // 224
+ {"type": "param", "name":"Env3Decay"}, // 225
+ {"type": "param", "name":"Env3Sustain"}, // 226
+ {"type": "param", "name":"Env3Decay2"}, // 227
+ {"type": "param", "name":"Env3Sustain2"}, // 228
+ {"type": "param", "name":"Env3Release"}, // 229
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Env4Mode", "mask":7}, // 232
+ {"type": "param", "name":"Env4TriggerMode", "mask":3, "shift":3}, // 232
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Env4Attack"}, // 235
+ {"type": "param", "name":"Env4AttackLevel"}, // 236
+ {"type": "param", "name":"Env4Decay"}, // 237
+ {"type": "param", "name":"Env4Sustain"}, // 238
+ {"type": "param", "name":"Env4Decay2"}, // 239
+ {"type": "param", "name":"Env4Sustain2"}, // 240
+ {"type": "param", "name":"Env4Release"}, // 241
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Mod1Source1"}, // 245
+ {"type": "param", "name":"Mod1Source2"}, // 246
+ {"type": "param", "name":"Mod1Operator"}, // 247
+ {"type": "param", "name":"Mod1Constant"}, // 248
+ {"type": "param", "name":"Mod2Source1"}, // 249
+ {"type": "param", "name":"Mod2Source2"}, // 250
+ {"type": "param", "name":"Mod2Operator"}, // 251
+ {"type": "param", "name":"Mod2Constant"}, // 252
+ {"type": "param", "name":"Mod3Source1"}, // 253
+ {"type": "param", "name":"Mod3Source2"}, // 254
+ {"type": "param", "name":"Mod3Operator"}, // 255
+ {"type": "param", "name":"Mod3Constant"}, // 256
+ {"type": "param", "name":"Mod4Source1"}, // 257
+ {"type": "param", "name":"Mod4Source2"}, // 258
+ {"type": "param", "name":"Mod4Operator"}, // 259
+ {"type": "param", "name":"Mod4Constant"}, // 260
+ {"type": "param", "name":"Slot1FSource"}, // 261
+ {"type": "param", "name":"Slot1FDestination"}, // 262
+ {"type": "param", "name":"Slot1FAmount"}, // 263
+ {"type": "param", "name":"Slot2FSource"}, // 264
+ {"type": "param", "name":"Slot2FDestination"}, // 265
+ {"type": "param", "name":"Slot2FAmount"}, // 266
+ {"type": "param", "name":"Slot3FSource"}, // 267
+ {"type": "param", "name":"Slot3FDestination"}, // 268
+ {"type": "param", "name":"Slot3FAmount"}, // 269
+ {"type": "param", "name":"Slot4FSource"}, // 270
+ {"type": "param", "name":"Slot4FDestination"}, // 271
+ {"type": "param", "name":"Slot4FAmount"}, // 272
+ {"type": "param", "name":"Slot5FSource"}, // 273
+ {"type": "param", "name":"Slot5FDestination"}, // 274
+ {"type": "param", "name":"Slot5FAmount"}, // 275
+ {"type": "param", "name":"Slot6FSource"}, // 276
+ {"type": "param", "name":"Slot6FDestination"}, // 277
+ {"type": "param", "name":"Slot6FAmount"}, // 278
+ {"type": "param", "name":"Slot7FSource"}, // 279
+ {"type": "param", "name":"Slot7FDestination"}, // 280
+ {"type": "param", "name":"Slot7FAmount"}, // 281
+ {"type": "param", "name":"Slot8FSource"}, // 282
+ {"type": "param", "name":"Slot8FDestination"}, // 283
+ {"type": "param", "name":"Slot8FAmount"}, // 284
+ {"type": "param", "name":"Slot1SSource"}, // 285
+ {"type": "param", "name":"Slot1SDestination"}, // 286
+ {"type": "param", "name":"Slot1SAmount"}, // 287
+ {"type": "param", "name":"Slot2SSource"}, // 288
+ {"type": "param", "name":"Slot2SDestination"}, // 289
+ {"type": "param", "name":"Slot2SAmount"}, // 290
+ {"type": "param", "name":"Slot3SSource"}, // 291
+ {"type": "param", "name":"Slot3SDestination"}, // 292
+ {"type": "param", "name":"Slot3SAmount"}, // 293
+ {"type": "param", "name":"Slot4SSource"}, // 294
+ {"type": "param", "name":"Slot4SDestination"}, // 295
+ {"type": "param", "name":"Slot4SAmount"}, // 296
+ {"type": "param", "name":"Slot5SSource"}, // 297
+ {"type": "param", "name":"Slot5SDestination"}, // 298
+ {"type": "param", "name":"Slot5SAmount"}, // 299
+ {"type": "param", "name":"Slot6SSource"}, // 300
+ {"type": "param", "name":"Slot6SDestination"}, // 301
+ {"type": "param", "name":"Slot6SAmount"}, // 302
+ {"type": "param", "name":"Slot7SSource"}, // 303
+ {"type": "param", "name":"Slot7SDestination"}, // 304
+ {"type": "param", "name":"Slot7SAmount"}, // 305
+ {"type": "param", "name":"Slot8SSource"}, // 306
+ {"type": "param", "name":"Slot8SDestination"}, // 307
+ {"type": "param", "name":"Slot8SAmount"}, // 308
+ {"type": "param", "name":"ControllerDelaySource"}, // 309
+ {"type": "param", "name":"ControllerDelay"}, // 310
+ {"type": "param", "name":"ArpMode"}, // 311
+ {"type": "param", "name":"ArpPattern"}, // 312
+ {"type": "param", "name":"ArpMaxNotes"}, // 313
+ {"type": "param", "name":"ArpClock"}, // 314
+ {"type": "param", "name":"ArpLength"}, // 315
+ {"type": "param", "name":"ArpOctaveRange"}, // 316
+ {"type": "param", "name":"ArpDirection"}, // 317
+ {"type": "param", "name":"ArpSortOrder"}, // 318
+ {"type": "param", "name":"ArpVeloMode"}, // 319
+ {"type": "param", "name":"ArpTFactor"}, // 320
+ {"type": "param", "name":"ArpSameNoteOverlap"}, // 321
+ {"type": "param", "name":"ArpPatternReset"}, // 322
+ {"type": "param", "name":"ArpPatternLength"}, // 323
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Tempo"}, // 326
+ {"type": "param", "name":"Arp00Step", "mask":"7", "shift":4}, // 327
+ {"type": "param", "name":"Arp00Glide", "mask":"1", "shift":3}, // 327
+ {"type": "param", "name":"Arp00Accent", "mask":"7"}, // 327
+ {"type": "param", "name":"Arp01Step", "mask":"7", "shift":4}, // 328
+ {"type": "param", "name":"Arp01Glide", "mask":"1", "shift":3}, // 328
+ {"type": "param", "name":"Arp01Accent", "mask":"7"}, // 328
+ {"type": "param", "name":"Arp02Step", "mask":"7", "shift":4}, // 329
+ {"type": "param", "name":"Arp02Glide", "mask":"1", "shift":3}, // 329
+ {"type": "param", "name":"Arp02Accent", "mask":"7"}, // 329
+ {"type": "param", "name":"Arp03Step", "mask":"7", "shift":4}, // 330
+ {"type": "param", "name":"Arp03Glide", "mask":"1", "shift":3}, // 330
+ {"type": "param", "name":"Arp03Accent", "mask":"7"}, // 330
+ {"type": "param", "name":"Arp04Step", "mask":"7", "shift":4}, // 331
+ {"type": "param", "name":"Arp04Glide", "mask":"1", "shift":3}, // 331
+ {"type": "param", "name":"Arp04Accent", "mask":"7"}, // 331
+ {"type": "param", "name":"Arp05Step", "mask":"7", "shift":4}, // 332
+ {"type": "param", "name":"Arp05Glide", "mask":"1", "shift":3}, // 332
+ {"type": "param", "name":"Arp05Accent", "mask":"7"}, // 332
+ {"type": "param", "name":"Arp06Step", "mask":"7", "shift":4}, // 333
+ {"type": "param", "name":"Arp06Glide", "mask":"1", "shift":3}, // 333
+ {"type": "param", "name":"Arp06Accent", "mask":"7"}, // 333
+ {"type": "param", "name":"Arp07Step", "mask":"7", "shift":4}, // 334
+ {"type": "param", "name":"Arp07Glide", "mask":"1", "shift":3}, // 334
+ {"type": "param", "name":"Arp07Accent", "mask":"7"}, // 334
+ {"type": "param", "name":"Arp08Step", "mask":"7", "shift":4}, // 335
+ {"type": "param", "name":"Arp08Glide", "mask":"1", "shift":3}, // 335
+ {"type": "param", "name":"Arp08Accent", "mask":"7"}, // 335
+ {"type": "param", "name":"Arp09Step", "mask":"7", "shift":4}, // 336
+ {"type": "param", "name":"Arp09Glide", "mask":"1", "shift":3}, // 336
+ {"type": "param", "name":"Arp09Accent", "mask":"7"}, // 336
+ {"type": "param", "name":"Arp10Step", "mask":"7", "shift":4}, // 337
+ {"type": "param", "name":"Arp10Glide", "mask":"1", "shift":3}, // 337
+ {"type": "param", "name":"Arp10Accent", "mask":"7"}, // 337
+ {"type": "param", "name":"Arp11Step", "mask":"7", "shift":4}, // 338
+ {"type": "param", "name":"Arp11Glide", "mask":"1", "shift":3}, // 338
+ {"type": "param", "name":"Arp11Accent", "mask":"7"}, // 338
+ {"type": "param", "name":"Arp12Step", "mask":"7", "shift":4}, // 339
+ {"type": "param", "name":"Arp12Glide", "mask":"1", "shift":3}, // 339
+ {"type": "param", "name":"Arp12Accent", "mask":"7"}, // 339
+ {"type": "param", "name":"Arp13Step", "mask":"7", "shift":4}, // 340
+ {"type": "param", "name":"Arp13Glide", "mask":"1", "shift":3}, // 340
+ {"type": "param", "name":"Arp13Accent", "mask":"7"}, // 340
+ {"type": "param", "name":"Arp14Step", "mask":"7", "shift":4}, // 341
+ {"type": "param", "name":"Arp14Glide", "mask":"1", "shift":3}, // 341
+ {"type": "param", "name":"Arp14Accent", "mask":"7"}, // 341
+ {"type": "param", "name":"Arp15Step", "mask":"7", "shift":4}, // 342
+ {"type": "param", "name":"Arp15Glide", "mask":"1", "shift":3}, // 342
+ {"type": "param", "name":"Arp15Accent", "mask":"7"}, // 342
+ {"type": "param", "name":"Arp00Length", "mask":"7", "shift":4}, // 343
+ {"type": "param", "name":"Arp00Timing", "mask":"7"}, // 343
+ {"type": "param", "name":"Arp01Length", "mask":"7", "shift":4}, // 344
+ {"type": "param", "name":"Arp01Timing", "mask":"7"}, // 344
+ {"type": "param", "name":"Arp02Length", "mask":"7", "shift":4}, // 345
+ {"type": "param", "name":"Arp02Timing", "mask":"7"}, // 345
+ {"type": "param", "name":"Arp03Length", "mask":"7", "shift":4}, // 346
+ {"type": "param", "name":"Arp03Timing", "mask":"7"}, // 346
+ {"type": "param", "name":"Arp04Length", "mask":"7", "shift":4}, // 347
+ {"type": "param", "name":"Arp04Timing", "mask":"7"}, // 347
+ {"type": "param", "name":"Arp05Length", "mask":"7", "shift":4}, // 348
+ {"type": "param", "name":"Arp05Timing", "mask":"7"}, // 348
+ {"type": "param", "name":"Arp06Length", "mask":"7", "shift":4}, // 349
+ {"type": "param", "name":"Arp06Timing", "mask":"7"}, // 349
+ {"type": "param", "name":"Arp07Length", "mask":"7", "shift":4}, // 350
+ {"type": "param", "name":"Arp07Timing", "mask":"7"}, // 350
+ {"type": "param", "name":"Arp08Length", "mask":"7", "shift":4}, // 351
+ {"type": "param", "name":"Arp08Timing", "mask":"7"}, // 351
+ {"type": "param", "name":"Arp09Length", "mask":"7", "shift":4}, // 352
+ {"type": "param", "name":"Arp09Timing", "mask":"7"}, // 352
+ {"type": "param", "name":"Arp10Length", "mask":"7", "shift":4}, // 353
+ {"type": "param", "name":"Arp10Timing", "mask":"7"}, // 353
+ {"type": "param", "name":"Arp11Length", "mask":"7", "shift":4}, // 354
+ {"type": "param", "name":"Arp11Timing", "mask":"7"}, // 354
+ {"type": "param", "name":"Arp12Length", "mask":"7", "shift":4}, // 355
+ {"type": "param", "name":"Arp12Timing", "mask":"7"}, // 355
+ {"type": "param", "name":"Arp13Length", "mask":"7", "shift":4}, // 356
+ {"type": "param", "name":"Arp13Timing", "mask":"7"}, // 356
+ {"type": "param", "name":"Arp14Length", "mask":"7", "shift":4}, // 357
+ {"type": "param", "name":"Arp14Timing", "mask":"7"}, // 357
+ {"type": "param", "name":"Arp15Length", "mask":"7", "shift":4}, // 358
+ {"type": "param", "name":"Arp15Timing", "mask":"7"}, // 358
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Name00"}, // 363
+ {"type": "param", "name":"Name01"}, // 364
+ {"type": "param", "name":"Name02"}, // 365
+ {"type": "param", "name":"Name03"}, // 366
+ {"type": "param", "name":"Name04"}, // 367
+ {"type": "param", "name":"Name05"}, // 368
+ {"type": "param", "name":"Name06"}, // 369
+ {"type": "param", "name":"Name07"}, // 370
+ {"type": "param", "name":"Name08"}, // 371
+ {"type": "param", "name":"Name09"}, // 372
+ {"type": "param", "name":"Name10"}, // 373
+ {"type": "param", "name":"Name11"}, // 374
+ {"type": "param", "name":"Name12"}, // 375
+ {"type": "param", "name":"Name13"}, // 376
+ {"type": "param", "name":"Name14"}, // 377
+ {"type": "param", "name":"Name15"}, // 378
+ {"type": "param", "name":"Category00"}, // 379
+ {"type": "param", "name":"Category01"}, // 380
+ {"type": "param", "name":"Category02"}, // 381
+ {"type": "param", "name":"Category03"}, // 382
+
+ {"type": "checksum", "first": 4, "last": 389},
+ {"type": "byte", "value": "f7"}
+ ],
+ "singledump_Q": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "0f"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "10"},
+ {"type": "bank"},
+ {"type": "program"},
+
+ {"type": "param", "name": "Version"}, // 0
+ {"type": "param", "name": "O1Octave"}, // 1
+ {"type": "param", "name": "O1Semi"}, // 2
+ {"type": "param", "name": "O1Detune"}, // 3
+ {"type": "param", "name": "O1BendRange"}, // 4
+ {"type": "param", "name": "O1KeyTrack"}, // 5
+ {"type": "param", "name": "O1FmSource"}, // 6
+ {"type": "param", "name": "O1FmAmount"}, // 7
+ {"type": "param", "name": "O1Shape"}, // 8
+ {"type": "param", "name": "O1PulseWidth"}, // 9
+ {"type": "param", "name": "O1PwmSource"}, // 10
+ {"type": "param", "name": "O1Pwm"}, // 11
+ {"type": "param", "name": "O1SubFreqDiv"}, // 12
+ {"type": "param", "name": "O1SubVolume"}, // 13
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "O2Octave"}, // 17
+ {"type": "param", "name": "O2Semi"}, // 18
+ {"type": "param", "name": "O2Detune"}, // 19
+ {"type": "param", "name": "O2BendRange"}, // 20
+ {"type": "param", "name": "O2KeyTrack"}, // 21
+ {"type": "param", "name": "O2FmSource"}, // 22
+ {"type": "param", "name": "O2FmAmount"}, // 23
+ {"type": "param", "name": "O2Shape"}, // 24
+ {"type": "param", "name": "O2PulseWidth"}, // 25
+ {"type": "param", "name": "O2PwmSource"}, // 26
+ {"type": "param", "name": "O2Pwm"}, // 27
+ {"type": "param", "name": "O2SubFreqDiv"}, // 28
+ {"type": "param", "name": "O2SubVolume"}, // 29
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "O3Octave"}, // 33
+ {"type": "param", "name": "O3Semi"}, // 34
+ {"type": "param", "name": "O3Detune"}, // 35
+ {"type": "param", "name": "O3BendRange"}, // 36
+ {"type": "param", "name": "O3KeyTrack"}, // 37
+ {"type": "param", "name": "O3FmSource"}, // 38
+ {"type": "param", "name": "O3FmAmount"}, // 39
+ {"type": "param", "name": "O3Shape"}, // 40
+ {"type": "param", "name": "O3PulseWidth"}, // 41
+ {"type": "param", "name": "O3PwmSource"}, // 42
+ {"type": "param", "name": "O3Pwm"}, // 43
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Sync"}, // 49
+ {"type": "param", "name":"PitchModSrc"}, // 50
+ {"type": "param", "name":"PitchModAmount"}, // 51
+ {"type": "null"},
+ {"type": "param", "name":"GlideEnable"}, // 53
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"GlideMode"}, // 56
+ {"type": "param", "name":"GlideRate"}, // 57
+ {"type": "param", "name":"VoiceMode", "mask":"7", "shift":"0"}, // 58
+ {"type": "param", "name":"UnisonoCount", "mask":"7", "shift":"4"}, // 58
+ {"type": "param", "name":"UnisonoDetune"}, // 59
+ {"type": "null"},
+ {"type": "param", "name": "O1Level"}, // 61
+ {"type": "param", "name": "O1Balance"}, // 62
+ {"type": "param", "name": "O2Level"}, // 63
+ {"type": "param", "name": "O2Balance"}, // 64
+ {"type": "param", "name": "O3Level"}, // 65
+ {"type": "param", "name": "O3Balance"}, // 66
+ {"type": "param", "name": "NoiseLevel"}, // 67
+ {"type": "param", "name": "NoiseBalance"}, // 68
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "RingModLevel"}, // 71
+ {"type": "param", "name": "RingModBalance"}, // 72
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "NoiseModeF1"}, // 75
+ {"type": "param", "name": "NoiseModeF2"}, // 76
+ {"type": "param", "name": "F1Type"}, // 77
+ {"type": "param", "name": "F1Cutoff"}, // 78
+ {"type": "null"},
+ {"type": "param", "name": "F1Resonance"}, // 80
+ {"type": "param", "name": "F1Drive"}, // 81
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "F1KeyTrack"}, // 86
+ {"type": "param", "name": "F1EnvMod"}, // 87
+ {"type": "param", "name": "F1VelMod"}, // 88
+ {"type": "param", "name": "F1ModSource"}, // 89
+ {"type": "param", "name": "F1CutoffMod"}, // 90
+ {"type": "param", "name": "F1FmSource"}, // 91
+ {"type": "param", "name": "F1FmAmount"}, // 92
+ {"type": "param", "name": "F1Pan"}, // 93
+ {"type": "param", "name": "F1PanModSource"}, // 94
+ {"type": "param", "name": "F1PanMod"}, // 95
+ {"type": "null"},
+ {"type": "param", "name": "F2Type"}, // 97
+ {"type": "param", "name": "F2Cutoff"}, // 98
+ {"type": "null"},
+ {"type": "param", "name": "F2Resonance"}, // 100
+ {"type": "param", "name": "F2Drive"}, // 101
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "F2KeyTrack"}, // 106
+ {"type": "param", "name": "F2EnvMod"}, // 107
+ {"type": "param", "name": "F2VelMod"}, // 108
+ {"type": "param", "name": "F2ModSource"}, // 109
+ {"type": "param", "name": "F2CutoffMod"}, // 110
+ {"type": "param", "name": "F2FmSource"}, // 111
+ {"type": "param", "name": "F2FmAmount"}, // 112
+ {"type": "param", "name": "F2Pan"}, // 113
+ {"type": "param", "name": "F2PanModSource"}, // 114
+ {"type": "param", "name": "F2PanMod"}, // 115
+ {"type": "null"},
+ {"type": "param", "name": "FilterRouting"}, // 117
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "AmpVolume"}, // 121
+ {"type": "param", "name": "AmpVelocity"}, // 122
+ {"type": "param", "name": "AmpModSource"}, // 123
+ {"type": "param", "name": "AmpModAmount"}, // 124
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "FX1Type"}, // 128
+ {"type": "param", "name": "FX1Mix"}, // 129
+ {"type": "param", "name": "Fx1VocoderBands"}, // 130
+ {"type": "param", "name": "Fx1VocoderAnalysisSignal"}, // 131
+ {"type": "param", "name": "Fx1VocoderAnalysisFreqLow"}, // 132
+ {"type": "param", "name": "Fx1VocoderAnalysisFreqHigh"}, // 133
+ {"type": "param", "name": "Fx1VocoderOffsetS"}, // 134
+ {"type": "param", "name": "Fx1VocoderOffsetHi"}, // 135
+ {"type": "param", "name": "Fx1VocoderBandwidth"}, // 136
+ {"type": "param", "name": "Fx1VocoderResonance"}, // 137
+ {"type": "param", "name": "Fx1VocoderAttack"}, // 138
+ {"type": "param", "name": "Fx1VocoderDecay"}, // 139
+ {"type": "param", "name": "Fx1VocoderEQLevelLow"}, // 140
+ {"type": "param", "name": "Fx1VocoderEQBandMid"}, // 141
+ {"type": "param", "name": "Fx1VocoderEQLevelMid"}, // 142
+ {"type": "param", "name": "Fx1VocoderEQLevelHigh"}, // 143
+ {"type": "param", "name": "FX2Type"}, // 144
+ {"type": "param", "name": "FX2Mix"}, // 145
+ {"type": "param", "name":"Fx2VocoderBands"}, // 146
+ {"type": "param", "name":"Fx2VocoderAnalysisSignal"}, // 147
+ {"type": "param", "name":"Fx2VocoderAnalysisFreqLow"}, // 148
+ {"type": "param", "name":"Fx2VocoderAnalysisFreqHigh"}, // 149
+ {"type": "param", "name":"Fx2VocoderOffsetS"}, // 150
+ {"type": "param", "name":"Fx2VocoderOffsetHi"}, // 151
+ {"type": "param", "name":"Fx2VocoderBandwidth"}, // 152
+ {"type": "param", "name":"Fx2VocoderResonance"}, // 153
+ {"type": "param", "name":"Fx2VocoderAttack"}, // 154
+ {"type": "param", "name":"Fx2VocoderDecay"}, // 155
+ {"type": "param", "name":"Fx2VocoderEQLevelLow"}, // 156
+ {"type": "param", "name":"Fx2VocoderEQBandMid"}, // 157
+ {"type": "param", "name":"Fx2VocoderEQLevelMid"}, // 158
+ {"type": "param", "name":"Fx2VocoderEQLevelHigh"}, // 159
+ {"type": "param", "name":"Lfo1Shape"}, // 160
+ {"type": "param", "name":"Lfo1Speed"}, // 161
+ {"type": "null"},
+ {"type": "param", "name":"Lfo1Sync"}, // 163
+ {"type": "param", "name":"Lfo1Clocked"}, // 164
+ {"type": "param", "name":"Lfo1StartPhase"}, // 165
+ {"type": "param", "name":"Lfo1Delay"}, // 166
+ {"type": "param", "name":"Lfo1Fade"}, // 167
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Lfo1KeyTrack"}, // 170
+ {"type": "null"},
+ {"type": "param", "name":"Lfo2Shape"}, // 172
+ {"type": "param", "name":"Lfo2Speed"}, // 173
+ {"type": "null"},
+ {"type": "param", "name":"Lfo2Sync"}, // 175
+ {"type": "param", "name":"Lfo2Clocked"}, // 176
+ {"type": "param", "name":"Lfo2StartPhase"}, // 177
+ {"type": "param", "name":"Lfo2Delay"}, // 178
+ {"type": "param", "name":"Lfo2Fade"}, // 179
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Lfo2KeyTrack"}, // 182
+ {"type": "null"},
+ {"type": "param", "name":"Lfo3Shape"}, // 184
+ {"type": "param", "name":"Lfo3Speed"}, // 185
+ {"type": "null"},
+ {"type": "param", "name":"Lfo3Sync"}, // 187
+ {"type": "param", "name":"Lfo3Clocked"}, // 188
+ {"type": "param", "name":"Lfo3StartPhase"}, // 189
+ {"type": "param", "name":"Lfo3Delay"}, // 190
+ {"type": "param", "name":"Lfo3Fade"}, // 191
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Lfo3KeyTrack"}, // 194
+ {"type": "null"},
+ {"type": "param", "name":"FilterEnvMode", "mask":7}, // 196
+ {"type": "param", "name":"FilterEnvTriggerMode", "mask":3, "shift":3}, // 196
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"FilterEnvAttack"}, // 199
+ {"type": "param", "name":"FilterEnvAttackLevel"}, // 200
+ {"type": "param", "name":"FilterEnvDecay"}, // 201
+ {"type": "param", "name":"FilterEnvSustain"}, // 202
+ {"type": "param", "name":"FilterEnvDecay2"}, // 203
+ {"type": "param", "name":"FilterEnvSustain2"}, // 204
+ {"type": "param", "name":"FilterEnvRelease"}, // 205
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"AmpEnvMode", "mask":7}, // 208
+ {"type": "param", "name":"AmpEnvTriggerMode", "mask":3, "shift":3}, // 208
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"AmpEnvAttack"}, // 211
+ {"type": "param", "name":"AmpEnvAttackLevel"}, // 212
+ {"type": "param", "name":"AmpEnvDecay"}, // 213
+ {"type": "param", "name":"AmpEnvSustain"}, // 214
+ {"type": "param", "name":"AmpEnvDecay2"}, // 215
+ {"type": "param", "name":"AmpEnvSustain2"}, // 216
+ {"type": "param", "name":"AmpEnvRelease"}, // 217
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Env3Mode", "mask":7}, // 220
+ {"type": "param", "name":"Env3TriggerMode", "mask":3, "shift":3}, // 220
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Env3Attack"}, // 223
+ {"type": "param", "name":"Env3AttackLevel"}, // 224
+ {"type": "param", "name":"Env3Decay"}, // 225
+ {"type": "param", "name":"Env3Sustain"}, // 226
+ {"type": "param", "name":"Env3Decay2"}, // 227
+ {"type": "param", "name":"Env3Sustain2"}, // 228
+ {"type": "param", "name":"Env3Release"}, // 229
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Env4Mode", "mask":7}, // 232
+ {"type": "param", "name":"Env4TriggerMode", "mask":3, "shift":3}, // 232
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Env4Attack"}, // 235
+ {"type": "param", "name":"Env4AttackLevel"}, // 236
+ {"type": "param", "name":"Env4Decay"}, // 237
+ {"type": "param", "name":"Env4Sustain"}, // 238
+ {"type": "param", "name":"Env4Decay2"}, // 239
+ {"type": "param", "name":"Env4Sustain2"}, // 240
+ {"type": "param", "name":"Env4Release"}, // 241
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"}, // 244, Q "Env Mode" Filter/Amp=0, Env3/4=2. All parameters below moved down one position, dump is one byte longer
+ {"type": "null"},
+ {"type": "param", "name":"Mod1Source1"}, // 245
+ {"type": "param", "name":"Mod1Source2"}, // 246
+ {"type": "param", "name":"Mod1Operator"}, // 247
+ {"type": "param", "name":"Mod1Constant"}, // 248
+ {"type": "param", "name":"Mod2Source1"}, // 249
+ {"type": "param", "name":"Mod2Source2"}, // 250
+ {"type": "param", "name":"Mod2Operator"}, // 251
+ {"type": "param", "name":"Mod2Constant"}, // 252
+ {"type": "param", "name":"Mod3Source1"}, // 253
+ {"type": "param", "name":"Mod3Source2"}, // 254
+ {"type": "param", "name":"Mod3Operator"}, // 255
+ {"type": "param", "name":"Mod3Constant"}, // 256
+ {"type": "param", "name":"Mod4Source1"}, // 257
+ {"type": "param", "name":"Mod4Source2"}, // 258
+ {"type": "param", "name":"Mod4Operator"}, // 259
+ {"type": "param", "name":"Mod4Constant"}, // 260
+ {"type": "param", "name":"Slot1FSource"}, // 261
+ {"type": "param", "name":"Slot1FDestination"}, // 262
+ {"type": "param", "name":"Slot1FAmount"}, // 263
+ {"type": "param", "name":"Slot2FSource"}, // 264
+ {"type": "param", "name":"Slot2FDestination"}, // 265
+ {"type": "param", "name":"Slot2FAmount"}, // 266
+ {"type": "param", "name":"Slot3FSource"}, // 267
+ {"type": "param", "name":"Slot3FDestination"}, // 268
+ {"type": "param", "name":"Slot3FAmount"}, // 269
+ {"type": "param", "name":"Slot4FSource"}, // 270
+ {"type": "param", "name":"Slot4FDestination"}, // 271
+ {"type": "param", "name":"Slot4FAmount"}, // 272
+ {"type": "param", "name":"Slot5FSource"}, // 273
+ {"type": "param", "name":"Slot5FDestination"}, // 274
+ {"type": "param", "name":"Slot5FAmount"}, // 275
+ {"type": "param", "name":"Slot6FSource"}, // 276
+ {"type": "param", "name":"Slot6FDestination"}, // 277
+ {"type": "param", "name":"Slot6FAmount"}, // 278
+ {"type": "param", "name":"Slot7FSource"}, // 279
+ {"type": "param", "name":"Slot7FDestination"}, // 280
+ {"type": "param", "name":"Slot7FAmount"}, // 281
+ {"type": "param", "name":"Slot8FSource"}, // 282
+ {"type": "param", "name":"Slot8FDestination"}, // 283
+ {"type": "param", "name":"Slot8FAmount"}, // 284
+ {"type": "param", "name":"Slot1SSource"}, // 285
+ {"type": "param", "name":"Slot1SDestination"}, // 286
+ {"type": "param", "name":"Slot1SAmount"}, // 287
+ {"type": "param", "name":"Slot2SSource"}, // 288
+ {"type": "param", "name":"Slot2SDestination"}, // 289
+ {"type": "param", "name":"Slot2SAmount"}, // 290
+ {"type": "param", "name":"Slot3SSource"}, // 291
+ {"type": "param", "name":"Slot3SDestination"}, // 292
+ {"type": "param", "name":"Slot3SAmount"}, // 293
+ {"type": "param", "name":"Slot4SSource"}, // 294
+ {"type": "param", "name":"Slot4SDestination"}, // 295
+ {"type": "param", "name":"Slot4SAmount"}, // 296
+ {"type": "param", "name":"Slot5SSource"}, // 297
+ {"type": "param", "name":"Slot5SDestination"}, // 298
+ {"type": "param", "name":"Slot5SAmount"}, // 299
+ {"type": "param", "name":"Slot6SSource"}, // 300
+ {"type": "param", "name":"Slot6SDestination"}, // 301
+ {"type": "param", "name":"Slot6SAmount"}, // 302
+ {"type": "param", "name":"Slot7SSource"}, // 303
+ {"type": "param", "name":"Slot7SDestination"}, // 304
+ {"type": "param", "name":"Slot7SAmount"}, // 305
+ {"type": "param", "name":"Slot8SSource"}, // 306
+ {"type": "param", "name":"Slot8SDestination"}, // 307
+ {"type": "param", "name":"Slot8SAmount"}, // 308
+ {"type": "param", "name":"ControllerDelaySource"}, // 309
+ {"type": "param", "name":"ControllerDelay"}, // 310
+ {"type": "param", "name":"ArpMode"}, // 311
+ {"type": "param", "name":"ArpPattern"}, // 312
+ {"type": "param", "name":"ArpMaxNotes"}, // 313
+ {"type": "param", "name":"ArpClock"}, // 314
+ {"type": "param", "name":"ArpLength"}, // 315
+ {"type": "param", "name":"ArpOctaveRange"}, // 316
+ {"type": "param", "name":"ArpDirection"}, // 317
+ {"type": "param", "name":"ArpSortOrder"}, // 318
+ {"type": "param", "name":"ArpVeloMode"}, // 319
+ {"type": "param", "name":"ArpTFactor"}, // 320
+ {"type": "param", "name":"ArpSameNoteOverlap"}, // 321
+ {"type": "param", "name":"ArpPatternReset"}, // 322
+ {"type": "param", "name":"ArpPatternLength"}, // 323
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Tempo"}, // 326
+ {"type": "param", "name":"Arp00Step", "mask":"7", "shift":4}, // 327
+ {"type": "param", "name":"Arp00Glide", "mask":"1", "shift":3}, // 327
+ {"type": "param", "name":"Arp00Accent", "mask":"7"}, // 327
+ {"type": "param", "name":"Arp01Step", "mask":"7", "shift":4}, // 328
+ {"type": "param", "name":"Arp01Glide", "mask":"1", "shift":3}, // 328
+ {"type": "param", "name":"Arp01Accent", "mask":"7"}, // 328
+ {"type": "param", "name":"Arp02Step", "mask":"7", "shift":4}, // 329
+ {"type": "param", "name":"Arp02Glide", "mask":"1", "shift":3}, // 329
+ {"type": "param", "name":"Arp02Accent", "mask":"7"}, // 329
+ {"type": "param", "name":"Arp03Step", "mask":"7", "shift":4}, // 330
+ {"type": "param", "name":"Arp03Glide", "mask":"1", "shift":3}, // 330
+ {"type": "param", "name":"Arp03Accent", "mask":"7"}, // 330
+ {"type": "param", "name":"Arp04Step", "mask":"7", "shift":4}, // 331
+ {"type": "param", "name":"Arp04Glide", "mask":"1", "shift":3}, // 331
+ {"type": "param", "name":"Arp04Accent", "mask":"7"}, // 331
+ {"type": "param", "name":"Arp05Step", "mask":"7", "shift":4}, // 332
+ {"type": "param", "name":"Arp05Glide", "mask":"1", "shift":3}, // 332
+ {"type": "param", "name":"Arp05Accent", "mask":"7"}, // 332
+ {"type": "param", "name":"Arp06Step", "mask":"7", "shift":4}, // 333
+ {"type": "param", "name":"Arp06Glide", "mask":"1", "shift":3}, // 333
+ {"type": "param", "name":"Arp06Accent", "mask":"7"}, // 333
+ {"type": "param", "name":"Arp07Step", "mask":"7", "shift":4}, // 334
+ {"type": "param", "name":"Arp07Glide", "mask":"1", "shift":3}, // 334
+ {"type": "param", "name":"Arp07Accent", "mask":"7"}, // 334
+ {"type": "param", "name":"Arp08Step", "mask":"7", "shift":4}, // 335
+ {"type": "param", "name":"Arp08Glide", "mask":"1", "shift":3}, // 335
+ {"type": "param", "name":"Arp08Accent", "mask":"7"}, // 335
+ {"type": "param", "name":"Arp09Step", "mask":"7", "shift":4}, // 336
+ {"type": "param", "name":"Arp09Glide", "mask":"1", "shift":3}, // 336
+ {"type": "param", "name":"Arp09Accent", "mask":"7"}, // 336
+ {"type": "param", "name":"Arp10Step", "mask":"7", "shift":4}, // 337
+ {"type": "param", "name":"Arp10Glide", "mask":"1", "shift":3}, // 337
+ {"type": "param", "name":"Arp10Accent", "mask":"7"}, // 337
+ {"type": "param", "name":"Arp11Step", "mask":"7", "shift":4}, // 338
+ {"type": "param", "name":"Arp11Glide", "mask":"1", "shift":3}, // 338
+ {"type": "param", "name":"Arp11Accent", "mask":"7"}, // 338
+ {"type": "param", "name":"Arp12Step", "mask":"7", "shift":4}, // 339
+ {"type": "param", "name":"Arp12Glide", "mask":"1", "shift":3}, // 339
+ {"type": "param", "name":"Arp12Accent", "mask":"7"}, // 339
+ {"type": "param", "name":"Arp13Step", "mask":"7", "shift":4}, // 340
+ {"type": "param", "name":"Arp13Glide", "mask":"1", "shift":3}, // 340
+ {"type": "param", "name":"Arp13Accent", "mask":"7"}, // 340
+ {"type": "param", "name":"Arp14Step", "mask":"7", "shift":4}, // 341
+ {"type": "param", "name":"Arp14Glide", "mask":"1", "shift":3}, // 341
+ {"type": "param", "name":"Arp14Accent", "mask":"7"}, // 341
+ {"type": "param", "name":"Arp15Step", "mask":"7", "shift":4}, // 342
+ {"type": "param", "name":"Arp15Glide", "mask":"1", "shift":3}, // 342
+ {"type": "param", "name":"Arp15Accent", "mask":"7"}, // 342
+ {"type": "param", "name":"Arp00Length", "mask":"7", "shift":4}, // 343
+ {"type": "param", "name":"Arp00Timing", "mask":"7"}, // 343
+ {"type": "param", "name":"Arp01Length", "mask":"7", "shift":4}, // 344
+ {"type": "param", "name":"Arp01Timing", "mask":"7"}, // 344
+ {"type": "param", "name":"Arp02Length", "mask":"7", "shift":4}, // 345
+ {"type": "param", "name":"Arp02Timing", "mask":"7"}, // 345
+ {"type": "param", "name":"Arp03Length", "mask":"7", "shift":4}, // 346
+ {"type": "param", "name":"Arp03Timing", "mask":"7"}, // 346
+ {"type": "param", "name":"Arp04Length", "mask":"7", "shift":4}, // 347
+ {"type": "param", "name":"Arp04Timing", "mask":"7"}, // 347
+ {"type": "param", "name":"Arp05Length", "mask":"7", "shift":4}, // 348
+ {"type": "param", "name":"Arp05Timing", "mask":"7"}, // 348
+ {"type": "param", "name":"Arp06Length", "mask":"7", "shift":4}, // 349
+ {"type": "param", "name":"Arp06Timing", "mask":"7"}, // 349
+ {"type": "param", "name":"Arp07Length", "mask":"7", "shift":4}, // 350
+ {"type": "param", "name":"Arp07Timing", "mask":"7"}, // 350
+ {"type": "param", "name":"Arp08Length", "mask":"7", "shift":4}, // 351
+ {"type": "param", "name":"Arp08Timing", "mask":"7"}, // 351
+ {"type": "param", "name":"Arp09Length", "mask":"7", "shift":4}, // 352
+ {"type": "param", "name":"Arp09Timing", "mask":"7"}, // 352
+ {"type": "param", "name":"Arp10Length", "mask":"7", "shift":4}, // 353
+ {"type": "param", "name":"Arp10Timing", "mask":"7"}, // 353
+ {"type": "param", "name":"Arp11Length", "mask":"7", "shift":4}, // 354
+ {"type": "param", "name":"Arp11Timing", "mask":"7"}, // 354
+ {"type": "param", "name":"Arp12Length", "mask":"7", "shift":4}, // 355
+ {"type": "param", "name":"Arp12Timing", "mask":"7"}, // 355
+ {"type": "param", "name":"Arp13Length", "mask":"7", "shift":4}, // 356
+ {"type": "param", "name":"Arp13Timing", "mask":"7"}, // 356
+ {"type": "param", "name":"Arp14Length", "mask":"7", "shift":4}, // 357
+ {"type": "param", "name":"Arp14Timing", "mask":"7"}, // 357
+ {"type": "param", "name":"Arp15Length", "mask":"7", "shift":4}, // 358
+ {"type": "param", "name":"Arp15Timing", "mask":"7"}, // 358
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"Name00"}, // 363
+ {"type": "param", "name":"Name01"}, // 364
+ {"type": "param", "name":"Name02"}, // 365
+ {"type": "param", "name":"Name03"}, // 366
+ {"type": "param", "name":"Name04"}, // 367
+ {"type": "param", "name":"Name05"}, // 368
+ {"type": "param", "name":"Name06"}, // 369
+ {"type": "param", "name":"Name07"}, // 370
+ {"type": "param", "name":"Name08"}, // 371
+ {"type": "param", "name":"Name09"}, // 372
+ {"type": "param", "name":"Name10"}, // 373
+ {"type": "param", "name":"Name11"}, // 374
+ {"type": "param", "name":"Name12"}, // 375
+ {"type": "param", "name":"Name13"}, // 376
+ {"type": "param", "name":"Name14"}, // 377
+ {"type": "param", "name":"Name15"}, // 378
+ {"type": "param", "name":"Category00"}, // 379
+ {"type": "param", "name":"Category01"}, // 380
+ {"type": "param", "name":"Category02"}, // 381
+ {"type": "param", "name":"Category03"}, // 382
+
+ {"type": "checksum", "first": 4, "last": 390},
+ {"type": "byte", "value": "f7"}
+ ],
+ "multidump": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "11"},
+ {"type": "bank"},
+ {"type": "program"},
+
+ {"type": "param", "name": "MVolume"}, // 0
+ {"type": "param", "name": "MControlW"}, // 1
+ {"type": "param", "name": "MControlX"}, // 2
+ {"type": "param", "name": "MControlY"}, // 3
+ {"type": "param", "name": "MControlZ"}, // 4
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name": "MName00"}, // 16
+ {"type": "param", "name": "MName01"},
+ {"type": "param", "name": "MName02"},
+ {"type": "param", "name": "MName03"},
+ {"type": "param", "name": "MName04"},
+ {"type": "param", "name": "MName05"},
+ {"type": "param", "name": "MName06"},
+ {"type": "param", "name": "MName07"},
+ {"type": "param", "name": "MName08"},
+ {"type": "param", "name": "MName09"},
+ {"type": "param", "name": "MName10"},
+ {"type": "param", "name": "MName11"},
+ {"type": "param", "name": "MName12"},
+ {"type": "param", "name": "MName13"},
+ {"type": "param", "name": "MName14"},
+ {"type": "param", "name": "MName15"},
+
+ {"type": "param", "name":"MI0SoundBank"}, // 32
+ {"type": "param", "name":"MI0SoundNumber"},
+ {"type": "param", "name":"MI0MidiChannel"},
+ {"type": "param", "name":"MI0Volume"},
+ {"type": "param", "name":"MI0Transpose"},
+ {"type": "param", "name":"MI0Detune"},
+ {"type": "param", "name":"MI0Output"},
+ {"type": "param", "name":"MI0RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI0TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI0PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI0Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI0PatternNum"},
+ {"type": "param", "name":"MI0VeloLow"},
+ {"type": "param", "name":"MI0VeloHigh"},
+ {"type": "param", "name":"MI0KeyLow"},
+ {"type": "param", "name":"MI0KeyHigh"},
+ {"type": "param", "name":"MI0Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI0Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI0Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI0Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI0Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI0ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI1SoundBank"},
+ {"type": "param", "name":"MI1SoundNumber"},
+ {"type": "param", "name":"MI1MidiChannel"},
+ {"type": "param", "name":"MI1Volume"},
+ {"type": "param", "name":"MI1Transpose"},
+ {"type": "param", "name":"MI1Detune"},
+ {"type": "param", "name":"MI1Output"},
+ {"type": "param", "name":"MI1RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI1TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI1PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI1Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI1PatternNum"},
+ {"type": "param", "name":"MI1VeloLow"},
+ {"type": "param", "name":"MI1VeloHigh"},
+ {"type": "param", "name":"MI1KeyLow"},
+ {"type": "param", "name":"MI1KeyHigh"},
+ {"type": "param", "name":"MI1Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI1Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI1Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI1Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI1Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI1ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI2SoundBank"},
+ {"type": "param", "name":"MI2SoundNumber"},
+ {"type": "param", "name":"MI2MidiChannel"},
+ {"type": "param", "name":"MI2Volume"},
+ {"type": "param", "name":"MI2Transpose"},
+ {"type": "param", "name":"MI2Detune"},
+ {"type": "param", "name":"MI2Output"},
+ {"type": "param", "name":"MI2RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI2TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI2PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI2Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI2PatternNum"},
+ {"type": "param", "name":"MI2VeloLow"},
+ {"type": "param", "name":"MI2VeloHigh"},
+ {"type": "param", "name":"MI2KeyLow"},
+ {"type": "param", "name":"MI2KeyHigh"},
+ {"type": "param", "name":"MI2Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI2Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI2Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI2Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI2Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI2ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI3SoundBank"},
+ {"type": "param", "name":"MI3SoundNumber"},
+ {"type": "param", "name":"MI3MidiChannel"},
+ {"type": "param", "name":"MI3Volume"},
+ {"type": "param", "name":"MI3Transpose"},
+ {"type": "param", "name":"MI3Detune"},
+ {"type": "param", "name":"MI3Output"},
+ {"type": "param", "name":"MI3RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI3TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI3PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI3Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI3PatternNum"},
+ {"type": "param", "name":"MI3VeloLow"},
+ {"type": "param", "name":"MI3VeloHigh"},
+ {"type": "param", "name":"MI3KeyLow"},
+ {"type": "param", "name":"MI3KeyHigh"},
+ {"type": "param", "name":"MI3Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI3Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI3Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI3Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI3Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI3ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI4SoundBank"},
+ {"type": "param", "name":"MI4SoundNumber"},
+ {"type": "param", "name":"MI4MidiChannel"},
+ {"type": "param", "name":"MI4Volume"},
+ {"type": "param", "name":"MI4Transpose"},
+ {"type": "param", "name":"MI4Detune"},
+ {"type": "param", "name":"MI4Output"},
+ {"type": "param", "name":"MI4RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI4TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI4PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI4Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI4PatternNum"},
+ {"type": "param", "name":"MI4VeloLow"},
+ {"type": "param", "name":"MI4VeloHigh"},
+ {"type": "param", "name":"MI4KeyLow"},
+ {"type": "param", "name":"MI4KeyHigh"},
+ {"type": "param", "name":"MI4Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI4Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI4Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI4Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI4Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI4ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI5SoundBank"},
+ {"type": "param", "name":"MI5SoundNumber"},
+ {"type": "param", "name":"MI5MidiChannel"},
+ {"type": "param", "name":"MI5Volume"},
+ {"type": "param", "name":"MI5Transpose"},
+ {"type": "param", "name":"MI5Detune"},
+ {"type": "param", "name":"MI5Output"},
+ {"type": "param", "name":"MI5RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI5TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI5PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI5Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI5PatternNum"},
+ {"type": "param", "name":"MI5VeloLow"},
+ {"type": "param", "name":"MI5VeloHigh"},
+ {"type": "param", "name":"MI5KeyLow"},
+ {"type": "param", "name":"MI5KeyHigh"},
+ {"type": "param", "name":"MI5Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI5Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI5Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI5Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI5Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI5ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI6SoundBank"},
+ {"type": "param", "name":"MI6SoundNumber"},
+ {"type": "param", "name":"MI6MidiChannel"},
+ {"type": "param", "name":"MI6Volume"},
+ {"type": "param", "name":"MI6Transpose"},
+ {"type": "param", "name":"MI6Detune"},
+ {"type": "param", "name":"MI6Output"},
+ {"type": "param", "name":"MI6RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI6TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI6PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI6Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI6PatternNum"},
+ {"type": "param", "name":"MI6VeloLow"},
+ {"type": "param", "name":"MI6VeloHigh"},
+ {"type": "param", "name":"MI6KeyLow"},
+ {"type": "param", "name":"MI6KeyHigh"},
+ {"type": "param", "name":"MI6Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI6Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI6Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI6Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI6Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI6ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI7SoundBank"},
+ {"type": "param", "name":"MI7SoundNumber"},
+ {"type": "param", "name":"MI7MidiChannel"},
+ {"type": "param", "name":"MI7Volume"},
+ {"type": "param", "name":"MI7Transpose"},
+ {"type": "param", "name":"MI7Detune"},
+ {"type": "param", "name":"MI7Output"},
+ {"type": "param", "name":"MI7RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI7TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI7PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI7Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI7PatternNum"},
+ {"type": "param", "name":"MI7VeloLow"},
+ {"type": "param", "name":"MI7VeloHigh"},
+ {"type": "param", "name":"MI7KeyLow"},
+ {"type": "param", "name":"MI7KeyHigh"},
+ {"type": "param", "name":"MI7Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI7Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI7Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI7Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI7Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI7ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI8SoundBank"},
+ {"type": "param", "name":"MI8SoundNumber"},
+ {"type": "param", "name":"MI8MidiChannel"},
+ {"type": "param", "name":"MI8Volume"},
+ {"type": "param", "name":"MI8Transpose"},
+ {"type": "param", "name":"MI8Detune"},
+ {"type": "param", "name":"MI8Output"},
+ {"type": "param", "name":"MI8RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI8TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI8PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI8Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI8PatternNum"},
+ {"type": "param", "name":"MI8VeloLow"},
+ {"type": "param", "name":"MI8VeloHigh"},
+ {"type": "param", "name":"MI8KeyLow"},
+ {"type": "param", "name":"MI8KeyHigh"},
+ {"type": "param", "name":"MI8Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI8Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI8Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI8Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI8Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI8ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI9SoundBank"},
+ {"type": "param", "name":"MI9SoundNumber"},
+ {"type": "param", "name":"MI9MidiChannel"},
+ {"type": "param", "name":"MI9Volume"},
+ {"type": "param", "name":"MI9Transpose"},
+ {"type": "param", "name":"MI9Detune"},
+ {"type": "param", "name":"MI9Output"},
+ {"type": "param", "name":"MI9RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI9TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI9PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI9Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI9PatternNum"},
+ {"type": "param", "name":"MI9VeloLow"},
+ {"type": "param", "name":"MI9VeloHigh"},
+ {"type": "param", "name":"MI9KeyLow"},
+ {"type": "param", "name":"MI9KeyHigh"},
+ {"type": "param", "name":"MI9Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI9Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI9Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI9Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI9Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI9ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI10SoundBank"},
+ {"type": "param", "name":"MI10SoundNumber"},
+ {"type": "param", "name":"MI10MidiChannel"},
+ {"type": "param", "name":"MI10Volume"},
+ {"type": "param", "name":"MI10Transpose"},
+ {"type": "param", "name":"MI10Detune"},
+ {"type": "param", "name":"MI10Output"},
+ {"type": "param", "name":"MI10RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI10TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI10PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI10Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI10PatternNum"},
+ {"type": "param", "name":"MI10VeloLow"},
+ {"type": "param", "name":"MI10VeloHigh"},
+ {"type": "param", "name":"MI10KeyLow"},
+ {"type": "param", "name":"MI10KeyHigh"},
+ {"type": "param", "name":"MI10Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI10Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI10Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI10Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI10Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI10ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI11SoundBank"},
+ {"type": "param", "name":"MI11SoundNumber"},
+ {"type": "param", "name":"MI11MidiChannel"},
+ {"type": "param", "name":"MI11Volume"},
+ {"type": "param", "name":"MI11Transpose"},
+ {"type": "param", "name":"MI11Detune"},
+ {"type": "param", "name":"MI11Output"},
+ {"type": "param", "name":"MI11RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI11TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI11PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI11Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI11PatternNum"},
+ {"type": "param", "name":"MI11VeloLow"},
+ {"type": "param", "name":"MI11VeloHigh"},
+ {"type": "param", "name":"MI11KeyLow"},
+ {"type": "param", "name":"MI11KeyHigh"},
+ {"type": "param", "name":"MI11Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI11Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI11Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI11Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI11Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI11ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI12SoundBank"},
+ {"type": "param", "name":"MI12SoundNumber"},
+ {"type": "param", "name":"MI12MidiChannel"},
+ {"type": "param", "name":"MI12Volume"},
+ {"type": "param", "name":"MI12Transpose"},
+ {"type": "param", "name":"MI12Detune"},
+ {"type": "param", "name":"MI12Output"},
+ {"type": "param", "name":"MI12RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI12TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI12PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI12Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI12PatternNum"},
+ {"type": "param", "name":"MI12VeloLow"},
+ {"type": "param", "name":"MI12VeloHigh"},
+ {"type": "param", "name":"MI12KeyLow"},
+ {"type": "param", "name":"MI12KeyHigh"},
+ {"type": "param", "name":"MI12Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI12Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI12Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI12Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI12Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI12ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI13SoundBank"},
+ {"type": "param", "name":"MI13SoundNumber"},
+ {"type": "param", "name":"MI13MidiChannel"},
+ {"type": "param", "name":"MI13Volume"},
+ {"type": "param", "name":"MI13Transpose"},
+ {"type": "param", "name":"MI13Detune"},
+ {"type": "param", "name":"MI13Output"},
+ {"type": "param", "name":"MI13RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI13TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI13PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI13Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI13PatternNum"},
+ {"type": "param", "name":"MI13VeloLow"},
+ {"type": "param", "name":"MI13VeloHigh"},
+ {"type": "param", "name":"MI13KeyLow"},
+ {"type": "param", "name":"MI13KeyHigh"},
+ {"type": "param", "name":"MI13Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI13Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI13Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI13Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI13Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI13ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI14SoundBank"},
+ {"type": "param", "name":"MI14SoundNumber"},
+ {"type": "param", "name":"MI14MidiChannel"},
+ {"type": "param", "name":"MI14Volume"},
+ {"type": "param", "name":"MI14Transpose"},
+ {"type": "param", "name":"MI14Detune"},
+ {"type": "param", "name":"MI14Output"},
+ {"type": "param", "name":"MI14RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI14TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI14PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI14Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI14PatternNum"},
+ {"type": "param", "name":"MI14VeloLow"},
+ {"type": "param", "name":"MI14VeloHigh"},
+ {"type": "param", "name":"MI14KeyLow"},
+ {"type": "param", "name":"MI14KeyHigh"},
+ {"type": "param", "name":"MI14Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI14Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI14Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI14Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI14Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI14ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "param", "name":"MI15SoundBank"},
+ {"type": "param", "name":"MI15SoundNumber"},
+ {"type": "param", "name":"MI15MidiChannel"},
+ {"type": "param", "name":"MI15Volume"},
+ {"type": "param", "name":"MI15Transpose"},
+ {"type": "param", "name":"MI15Detune"},
+ {"type": "param", "name":"MI15Output"},
+ {"type": "param", "name":"MI15RX", "mask":3, "shift":0},
+ {"type": "param", "name":"MI15TX", "mask":3, "shift":2},
+ {"type": "param", "name":"MI15PlayMode", "mask":3, "shift":4},
+ {"type": "param", "name":"MI15Pan"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "param", "name":"MI15PatternNum"},
+ {"type": "param", "name":"MI15VeloLow"},
+ {"type": "param", "name":"MI15VeloHigh"},
+ {"type": "param", "name":"MI15KeyLow"},
+ {"type": "param", "name":"MI15KeyHigh"},
+ {"type": "param", "name":"MI15Pitchbend", "mask":1, "shift":0},
+ {"type": "param", "name":"MI15Modwheel", "mask":1, "shift":1},
+ {"type": "param", "name":"MI15Aftertouch", "mask":1, "shift":2},
+ {"type": "param", "name":"MI15Sustain", "mask":1, "shift":3},
+ {"type": "param", "name":"MI15Button12", "mask":1, "shift":4},
+ {"type": "param", "name":"MI15ProgChange", "mask":1, "shift":5},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+ {"type": "null"},
+
+ {"type": "checksum", "first": 4, "last": 390},
+ {"type": "byte", "value": "f7"}
+ ],
+ "globaldump": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "14"},
+
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+ {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"},
+
+ {"type": "checksum", "first": 4, "last": 204},
+ {"type": "byte", "value": "f7"}
+ ],
+ "emuRequestLcd": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "50"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "emuRequestLeds": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "51"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "emuSendButton": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "52"},
+ {"type": "paramindex"},
+ {"type": "paramvalue"},
+ {"type": "byte", "value": "f7"}
+ ],
+ "emuSendRotary": [
+ {"type": "byte", "value": "f0"},
+ {"type": "byte", "value": "3e"},
+ {"type": "byte", "value": "10"},
+ {"type": "deviceid"},
+ {"type": "byte", "value": "53"},
+ {"type": "paramindex"},
+ {"type": "paramvalue"},
+ {"type": "byte", "value": "f7"}
+ ]
+ }
+}
diff --git a/source/mqJucePlugin/skins/mqDefault/Digital.ttf b/source/mqJucePlugin/skins/mqDefault/Digital.ttf
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/assets.cmake b/source/mqJucePlugin/skins/mqDefault/assets.cmake
@@ -0,0 +1,34 @@
+set(ASSETS_mqDefault
+ ${CMAKE_CURRENT_LIST_DIR}/button.png
+ ${CMAKE_CURRENT_LIST_DIR}/Digital.ttf
+ ${CMAKE_CURRENT_LIST_DIR}/encoder_ranged.png
+ ${CMAKE_CURRENT_LIST_DIR}/encoder_ranged_red.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx1Chorus.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx1FiveFX.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx1Flanger.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx1Overdrive.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx1Phaser.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx1Vocoder.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx251Delay.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx2Delay.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx2DelayClocked.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx2DelayNotClocked.png
+ ${CMAKE_CURRENT_LIST_DIR}/fx2Reverb.png
+ ${CMAKE_CURRENT_LIST_DIR}/lcdshadow.png
+ ${CMAKE_CURRENT_LIST_DIR}/led.png
+ ${CMAKE_CURRENT_LIST_DIR}/ledcircle_11.png
+ ${CMAKE_CURRENT_LIST_DIR}/ledcircle_5.png
+ ${CMAKE_CURRENT_LIST_DIR}/ledcircle_6.png
+ ${CMAKE_CURRENT_LIST_DIR}/ledcircle_7.png
+ ${CMAKE_CURRENT_LIST_DIR}/ledcircle_9.png
+ ${CMAKE_CURRENT_LIST_DIR}/mqDefaultBG.png
+ ${CMAKE_CURRENT_LIST_DIR}/pageFilters.png
+ ${CMAKE_CURRENT_LIST_DIR}/pageFx.png
+ ${CMAKE_CURRENT_LIST_DIR}/pageLfo.png
+ ${CMAKE_CURRENT_LIST_DIR}/pageMulti.png
+ ${CMAKE_CURRENT_LIST_DIR}/pageOsc.png
+ ${CMAKE_CURRENT_LIST_DIR}/pagePatchBrowser.png
+ ${CMAKE_CURRENT_LIST_DIR}/toggleWithLED.png
+
+ ${CMAKE_CURRENT_LIST_DIR}/mqDefault.json
+)
diff --git a/source/mqJucePlugin/skins/mqDefault/button.png b/source/mqJucePlugin/skins/mqDefault/button.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/encoder_ranged.png b/source/mqJucePlugin/skins/mqDefault/encoder_ranged.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/encoder_ranged_red.png b/source/mqJucePlugin/skins/mqDefault/encoder_ranged_red.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx1Chorus.png b/source/mqJucePlugin/skins/mqDefault/fx1Chorus.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx1FiveFX.png b/source/mqJucePlugin/skins/mqDefault/fx1FiveFX.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx1Flanger.png b/source/mqJucePlugin/skins/mqDefault/fx1Flanger.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx1Overdrive.png b/source/mqJucePlugin/skins/mqDefault/fx1Overdrive.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx1Phaser.png b/source/mqJucePlugin/skins/mqDefault/fx1Phaser.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx1Vocoder.png b/source/mqJucePlugin/skins/mqDefault/fx1Vocoder.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx251Delay.png b/source/mqJucePlugin/skins/mqDefault/fx251Delay.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx2Delay.png b/source/mqJucePlugin/skins/mqDefault/fx2Delay.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx2DelayClocked.png b/source/mqJucePlugin/skins/mqDefault/fx2DelayClocked.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx2DelayNotClocked.png b/source/mqJucePlugin/skins/mqDefault/fx2DelayNotClocked.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/fx2Reverb.png b/source/mqJucePlugin/skins/mqDefault/fx2Reverb.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/lcdshadow.png b/source/mqJucePlugin/skins/mqDefault/lcdshadow.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/led.png b/source/mqJucePlugin/skins/mqDefault/led.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/ledcircle_11.png b/source/mqJucePlugin/skins/mqDefault/ledcircle_11.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/ledcircle_5.png b/source/mqJucePlugin/skins/mqDefault/ledcircle_5.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/ledcircle_6.png b/source/mqJucePlugin/skins/mqDefault/ledcircle_6.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/ledcircle_7.png b/source/mqJucePlugin/skins/mqDefault/ledcircle_7.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/ledcircle_9.png b/source/mqJucePlugin/skins/mqDefault/ledcircle_9.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/mqDefault.json b/source/mqJucePlugin/skins/mqDefault/mqDefault.json
@@ -0,0 +1,12926 @@
+{
+ "name" : "Root",
+ "root" : {
+ "x" : "0",
+ "y" : "0",
+ "width" : "3400",
+ "height" : "2000",
+ "scale" : "0.5"
+ },
+ "tabgroup" : {
+ "name" : "Root",
+ "buttons" : [
+ "btPageOsc",
+ "btPagePatchBrowser",
+ "btPageMulti",
+ "btPageFilter",
+ "btPageLfo",
+ "btPageFx"
+ ],
+ "pages" : [
+ "pageOsc",
+ "pagePatchBrowser",
+ "pageMulti",
+ "pageFilters",
+ "pageLfo",
+ "pageFx"
+ ]
+ },
+ "children" : [
+ {
+ "name" : "bakedBG",
+ "image" : {
+ "x" : "0",
+ "y" : "0",
+ "width" : "3400",
+ "height" : "2000",
+ "texture" : "mqDefaultBG"
+ }
+ },
+ {
+ "name" : "btPageOsc",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "224.6718",
+ "y" : "45.59766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "btPageFilter",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "470.9707",
+ "y" : "45.59766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "btPageLfo",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "717.2695",
+ "y" : "45.59766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "btPageFx",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "963.5685",
+ "y" : "45.59766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "btPlayModeMulti",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "224.6707",
+ "y" : "220.8999",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "btPageMulti",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "717.269",
+ "y" : "220.8999",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "btPresetPrev",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2301.228",
+ "y" : "223.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "btPresetNext",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2517.376",
+ "y" : "223.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "btPagePatchBrowser",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2301.228",
+ "y" : "50.46399",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "btSave",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2517.376",
+ "y" : "50.46399",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "lcdArea",
+ "component" : {
+ "x" : "1390",
+ "y" : "161.8429",
+ "width" : "620",
+ "height" : "108"
+ },
+ "componentProperties" : {
+ "lcdBackgroundColor" : "248727"
+ }
+ },
+ {
+ "name" : "lcdLineA",
+ "label" : {
+ "text" : "01234567890123456789",
+ "textHeight" : "46",
+ "color" : "000000FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "fontFile" : "Digital",
+ "fontName" : "Register",
+ "x" : "1390",
+ "y" : "151.8429",
+ "width" : "630",
+ "height" : "64"
+ }
+ },
+ {
+ "name" : "lcdLineB",
+ "label" : {
+ "text" : "01234567890123456789",
+ "textHeight" : "46",
+ "color" : "000000FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "fontFile" : "Digital",
+ "fontName" : "Register",
+ "x" : "1390",
+ "y" : "215.8429",
+ "width" : "630",
+ "height" : "64"
+ }
+ },
+ {
+ "name" : "lcdshadow",
+ "image" : {
+ "x" : "1380",
+ "y" : "151.8429",
+ "width" : "640",
+ "height" : "128",
+ "texture" : "lcdshadow"
+ }
+ },
+ {
+ "name" : "ledPower",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1988.102",
+ "y" : "289.1428",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "pageOsc",
+ "image" : {
+ "x" : "450",
+ "y" : "428",
+ "width" : "2900",
+ "height" : "1525",
+ "texture" : "pageOsc"
+ },
+ "children" : [
+ {
+ "name" : "ledo1octave",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "125.2327",
+ "y" : "77.42664",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_9",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1Octave"
+ }
+ },
+ {
+ "name" : "o1octave",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "176.2327",
+ "y" : "128.4266",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1Octave"
+ }
+ },
+ {
+ "name" : "o1semi",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "56.11633",
+ "y" : "303.78",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1Semi"
+ }
+ },
+ {
+ "name" : "o1detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "296.349",
+ "y" : "303.78",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1Detune"
+ }
+ },
+ {
+ "name" : "ledo1shape",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "555.5817",
+ "y" : "143.8533",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_7",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1Shape"
+ }
+ },
+ {
+ "name" : "o1shape",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "606.5817",
+ "y" : "194.8533",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1Shape"
+ }
+ },
+ {
+ "name" : "o1pw",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "916.8143",
+ "y" : "126.6766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1PulseWidth"
+ }
+ },
+ {
+ "name" : "o1pwm",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1157.047",
+ "y" : "126.6766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1Pwm"
+ }
+ },
+ {
+ "name" : "o1pwmsource",
+ "parameterAttachment" : {
+ "parameter" : "O1PwmSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "LFO 1",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1100.931",
+ "y" : "71",
+ "width" : "220.2327",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "o1keytrack",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "916.8143",
+ "y" : "301.03",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1KeyTrack"
+ }
+ },
+ {
+ "name" : "o1bendrange",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1157.047",
+ "y" : "301.03",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1BendRange"
+ }
+ },
+ {
+ "name" : "o1fmsource",
+ "parameterAttachment" : {
+ "parameter" : "O1FmSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "LFO 2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1581.396",
+ "y" : "71",
+ "width" : "220.2327",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "o1fmamount",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1517.396",
+ "y" : "126.6766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1FmAmount"
+ }
+ },
+ {
+ "name" : "o1subvolume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1397.28",
+ "y" : "301.03",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1SubVolume"
+ }
+ },
+ {
+ "name" : "o1subfreqdiv",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1637.512",
+ "y" : "301.03",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1SubFreqDiv"
+ }
+ },
+ {
+ "name" : "o2octave",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "125.2327",
+ "y" : "601.5734",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_9",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2Octave"
+ }
+ },
+ {
+ "name" : "o2octave",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "176.2327",
+ "y" : "652.5734",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2Octave"
+ }
+ },
+ {
+ "name" : "o2semi",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "56.11633",
+ "y" : "827.9266",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2Semi"
+ }
+ },
+ {
+ "name" : "o2detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "296.349",
+ "y" : "827.9266",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2Detune"
+ }
+ },
+ {
+ "name" : "ledo2shape",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "555.5817",
+ "y" : "595.1467",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_7",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2Shape"
+ }
+ },
+ {
+ "name" : "o2shape",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "606.5817",
+ "y" : "646.1467",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2Shape"
+ }
+ },
+ {
+ "name" : "toggleWithLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "606.5817",
+ "y" : "828.1067",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Sync"
+ }
+ },
+ {
+ "name" : "o2pw",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "916.8143",
+ "y" : "650.8234",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2PulseWidth"
+ }
+ },
+ {
+ "name" : "o2pwm",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1157.047",
+ "y" : "650.8234",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2Pwm"
+ }
+ },
+ {
+ "name" : "o2pwmsource",
+ "parameterAttachment" : {
+ "parameter" : "O2PwmSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "LFO 1",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1100.931",
+ "y" : "595.1467",
+ "width" : "220.2327",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "o2keytrack",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "916.8143",
+ "y" : "825.1766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2KeyTrack"
+ }
+ },
+ {
+ "name" : "o2bendrange",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1157.047",
+ "y" : "825.1766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2BendRange"
+ }
+ },
+ {
+ "name" : "o2fmsource",
+ "parameterAttachment" : {
+ "parameter" : "O2FmSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "LFO 2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1581.396",
+ "y" : "595.1467",
+ "width" : "220.2327",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "o2fmamount",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1517.396",
+ "y" : "650.8234",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2FmAmount"
+ }
+ },
+ {
+ "name" : "o2subvolume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1397.28",
+ "y" : "825.1766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2SubVolume"
+ }
+ },
+ {
+ "name" : "o2subfreqdivide",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1637.512",
+ "y" : "825.1766",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2SubFreqDiv"
+ }
+ },
+ {
+ "name" : "ledo3octave",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "125.2327",
+ "y" : "1125.72",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_9",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3Octave"
+ }
+ },
+ {
+ "name" : "o2octave",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "176.2327",
+ "y" : "1176.72",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3Octave"
+ }
+ },
+ {
+ "name" : "o3semi",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "56.11633",
+ "y" : "1352.073",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3Semi"
+ }
+ },
+ {
+ "name" : "o3detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "296.349",
+ "y" : "1352.073",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3Detune"
+ }
+ },
+ {
+ "name" : "ledo3shape",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "555.5817",
+ "y" : "1192.147",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_5",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3Shape"
+ }
+ },
+ {
+ "name" : "o3shape",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "606.5817",
+ "y" : "1243.147",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3Shape"
+ }
+ },
+ {
+ "name" : "o3pulsewidth",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "916.8143",
+ "y" : "1174.97",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3PulseWidth"
+ }
+ },
+ {
+ "name" : "o3pwm",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1157.047",
+ "y" : "1174.97",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3Pwm"
+ }
+ },
+ {
+ "name" : "o3pwmsource",
+ "parameterAttachment" : {
+ "parameter" : "O3PwmSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "LFO 1",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1100.931",
+ "y" : "1119.293",
+ "width" : "220.2327",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "o3keytrack",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "916.8143",
+ "y" : "1349.323",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3KeyTrack"
+ }
+ },
+ {
+ "name" : "o3bendrange",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1157.047",
+ "y" : "1349.323",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3BendRange"
+ }
+ },
+ {
+ "name" : "o3fmsource",
+ "parameterAttachment" : {
+ "parameter" : "O3FmSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "LFO 2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1581.396",
+ "y" : "1119.293",
+ "width" : "220.2327",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "o3fm",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1517.396",
+ "y" : "1221.609",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3FmAmount"
+ }
+ },
+ {
+ "name" : "o1level",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1892.628",
+ "y" : "103.5",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1Level"
+ }
+ },
+ {
+ "name" : "o1balance",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2031.461",
+ "y" : "191.8359",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O1Balance"
+ }
+ },
+ {
+ "name" : "o2level",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1892.628",
+ "y" : "392.666",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2Level"
+ }
+ },
+ {
+ "name" : "o2balance",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2031.461",
+ "y" : "481.002",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O2Balance"
+ }
+ },
+ {
+ "name" : "o3volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1892.628",
+ "y" : "681.832",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3Level"
+ }
+ },
+ {
+ "name" : "o3balance",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2031.461",
+ "y" : "770.168",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "O3Balance"
+ }
+ },
+ {
+ "name" : "ringmodLevel",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1892.628",
+ "y" : "970.998",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "RingModLevel"
+ }
+ },
+ {
+ "name" : "ringmodBalance",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2031.461",
+ "y" : "1059.334",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "RingModBalance"
+ }
+ },
+ {
+ "name" : "noiseLevel",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1892.628",
+ "y" : "1260.164",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "NoiseLevel"
+ }
+ },
+ {
+ "name" : "noiseBalance",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2031.461",
+ "y" : "1348.5",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "NoiseBalance"
+ }
+ },
+ {
+ "name" : "glideRate",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2348.931",
+ "y" : "67.04742",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "GlideRate"
+ }
+ },
+ {
+ "name" : "toggleWithLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2634.73",
+ "y" : "50.69635",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "GlideEnable"
+ }
+ },
+ {
+ "name" : "glidemode",
+ "parameterAttachment" : {
+ "parameter" : "GlideMode"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Porta",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2565.23",
+ "y" : "239.4627",
+ "width" : "260.5",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "glideRate",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2268.061",
+ "y" : "422.1098",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "PitchModAmount"
+ }
+ },
+ {
+ "name" : "glidemode",
+ "parameterAttachment" : {
+ "parameter" : "PitchModSrc"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Porta",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2661.738",
+ "y" : "481.1098",
+ "width" : "218.7824",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "unisonocount",
+ "parameterAttachment" : {
+ "parameter" : "UnisonoCount"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Dual",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2475.862",
+ "y" : "724.0195",
+ "width" : "223.0056",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "unisonoDetune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2365.601",
+ "y" : "783.7233",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "UnisonoDetune"
+ }
+ },
+ {
+ "name" : "toggleWithLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2729.5",
+ "y" : "740.2233",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "toggleWithLED",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "VoiceMode"
+ }
+ },
+ {
+ "name" : "ampmodsource",
+ "parameterAttachment" : {
+ "parameter" : "AmpModSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "LFO 3",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2565.23",
+ "y" : "1078.12",
+ "width" : "260.5",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "ampVolume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2294.117",
+ "y" : "1138.616",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpVolume"
+ }
+ },
+ {
+ "name" : "ampVelocity",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2501.23",
+ "y" : "1138.616",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpVelocity"
+ }
+ },
+ {
+ "name" : "ampModAmount",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2708.344",
+ "y" : "1138.616",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpModAmount"
+ }
+ },
+ {
+ "name" : "noisemodef1",
+ "parameterAttachment" : {
+ "parameter" : "NoiseModeF1"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Ext L+R",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2402.846",
+ "y" : "1446.506",
+ "width" : "142.3848",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "noisemodef2",
+ "parameterAttachment" : {
+ "parameter" : "NoiseModeF2"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Ext L+R",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2727.615",
+ "y" : "1446.506",
+ "width" : "142.3848",
+ "height" : "49"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "pageFilters",
+ "image" : {
+ "x" : "450",
+ "y" : "428",
+ "width" : "2900",
+ "height" : "1525",
+ "texture" : "pageFilters"
+ },
+ "children" : [
+ {
+ "name" : "ledknob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "124.04",
+ "y" : "93.20001",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_11",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1Type"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "175.04",
+ "y" : "144.2",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1Type"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "75.04004",
+ "y" : "331.6794",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged_red",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1Cutoff"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "275.04",
+ "y" : "331.6794",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1Resonance"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "628.4436",
+ "y" : "331.6794",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1EnvMod"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "851.5564",
+ "y" : "331.6794",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1VelMod"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "F1ModSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "685.8899",
+ "y" : "77.00006",
+ "width" : "176",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "621.8899",
+ "y" : "144.8344",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1CutoffMod"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "F1FmSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1092.99",
+ "y" : "77.00006",
+ "width" : "176",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1028.99",
+ "y" : "144.8344",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1FmAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "F1PanModSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1500.09",
+ "y" : "77.00006",
+ "width" : "176",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1331.49",
+ "y" : "144.8344",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1Pan"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1540.69",
+ "y" : "144.8344",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1PanMod"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1211.844",
+ "y" : "331.6794",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1KeyTrack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1434.956",
+ "y" : "331.6794",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F1Drive"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "1",
+ "overImage" : "0",
+ "downImage" : "0",
+ "normalImageOn" : "0",
+ "overImageOn" : "0",
+ "downImageOn" : "0",
+ "x" : "890.5901",
+ "y" : "774",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterRouting"
+ }
+ },
+ {
+ "name" : "led (1)",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "669.3899",
+ "y" : "774",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterRouting"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "732.625",
+ "y" : "758",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterRouting"
+ }
+ },
+ {
+ "name" : "ledknob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "124.04",
+ "y" : "1098.625",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_11",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2Type"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "175.04",
+ "y" : "1149.625",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2Type"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "75.04004",
+ "y" : "1337.105",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged_red",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2Cutoff"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "275.04",
+ "y" : "1337.105",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2Resonance"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "628.4436",
+ "y" : "1337.105",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2EnvMod"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "851.5564",
+ "y" : "1337.105",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2VelMod"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "F2ModSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "685.8899",
+ "y" : "1082.426",
+ "width" : "176",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "621.8899",
+ "y" : "1150.26",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2CutoffMod"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "F2FmSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1092.99",
+ "y" : "1082.426",
+ "width" : "176",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1028.99",
+ "y" : "1150.26",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2FmAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "F2PanModSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1500.09",
+ "y" : "1082.426",
+ "width" : "176",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1331.49",
+ "y" : "1150.26",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2Pan"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1540.69",
+ "y" : "1150.26",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2PanMod"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1211.844",
+ "y" : "1337.105",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2KeyTrack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1434.956",
+ "y" : "1337.105",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "F2Drive"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "FilterEnvMode"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "ADSR",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2052.4",
+ "y" : "96.5",
+ "width" : "250",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2619.961",
+ "y" : "89",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterEnvTriggerMode"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2782.29",
+ "y" : "105",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterEnvTriggerMode"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1806.7",
+ "y" : "177.7593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterEnvAttack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1989.3",
+ "y" : "177.7593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterEnvDecay"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2171.9",
+ "y" : "177.7593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterEnvSustain"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2354.5",
+ "y" : "177.7593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterEnvDecay2"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2537.1",
+ "y" : "177.7593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterEnvSustain2"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2719.7",
+ "y" : "177.7593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FilterEnvRelease"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "AmpEnvMode"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "ADSR",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2052.4",
+ "y" : "477.75",
+ "width" : "250",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2619.961",
+ "y" : "470.25",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpEnvTriggerMode"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2782.29",
+ "y" : "486.25",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpEnvTriggerMode"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1806.7",
+ "y" : "559.0093",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpEnvAttack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1989.3",
+ "y" : "559.0093",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpEnvDecay"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2171.9",
+ "y" : "559.0093",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpEnvSustain"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2354.5",
+ "y" : "559.0093",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpEnvDecay2"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2537.1",
+ "y" : "559.0093",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpEnvSustain2"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2719.7",
+ "y" : "559.0093",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "AmpEnvRelease"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Env3Mode"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "ADSR",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2052.4",
+ "y" : "859",
+ "width" : "250",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2619.961",
+ "y" : "851.5",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env3TriggerMode"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2782.29",
+ "y" : "867.5",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env3TriggerMode"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1806.7",
+ "y" : "940.2593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env3Attack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1989.3",
+ "y" : "940.2593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env3Decay"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2171.9",
+ "y" : "940.2593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env3Sustain"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2354.5",
+ "y" : "940.2593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env3Decay2"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2537.1",
+ "y" : "940.2593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env3Sustain2"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2719.7",
+ "y" : "940.2593",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env3Release"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Env4Mode"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "ADSR",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2052.4",
+ "y" : "1240.25",
+ "width" : "250",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2619.961",
+ "y" : "1232.75",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env4TriggerMode"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2782.29",
+ "y" : "1248.75",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env4TriggerMode"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1806.7",
+ "y" : "1321.509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env4Attack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1989.3",
+ "y" : "1321.509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env4Decay"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2171.9",
+ "y" : "1321.509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env4Sustain"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2354.5",
+ "y" : "1321.509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env4Decay2"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2537.1",
+ "y" : "1321.509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env4Sustain2"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2719.7",
+ "y" : "1321.509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Env4Release"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "pageLfo",
+ "image" : {
+ "x" : "450",
+ "y" : "428",
+ "width" : "2900",
+ "height" : "1525",
+ "texture" : "pageLfo"
+ },
+ "children" : [
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "396.095",
+ "y" : "86.09998",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1Clocked"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "558.4236",
+ "y" : "102.1",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1Clocked"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "806.095",
+ "y" : "86.09998",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1Sync"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "968.4236",
+ "y" : "102.1",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1Sync"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "35.5",
+ "y" : "98.29999",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1Fade"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "26.83362",
+ "y" : "294.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1StartPhase"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "215.8336",
+ "y" : "294.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1Speed"
+ }
+ },
+ {
+ "name" : "ledknob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "423.8336",
+ "y" : "210.98",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_6",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1Shape"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "474.8336",
+ "y" : "261.98",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1Shape"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "733.8335",
+ "y" : "294.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1KeyTrack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "922.8335",
+ "y" : "294.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo1Delay"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "396.095",
+ "y" : "601.1",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2Clocked"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "558.4236",
+ "y" : "617.1",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2Clocked"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "806.095",
+ "y" : "601.1",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2Sync"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "968.4236",
+ "y" : "617.1",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2Sync"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "35.5",
+ "y" : "613.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2Fade"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "26.83362",
+ "y" : "809.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2StartPhase"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "215.8336",
+ "y" : "809.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2Speed"
+ }
+ },
+ {
+ "name" : "ledknob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "423.8336",
+ "y" : "725.98",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_6",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2Shape"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "474.8336",
+ "y" : "776.98",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2Shape"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "733.8335",
+ "y" : "809.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2KeyTrack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "922.8335",
+ "y" : "809.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo2Delay"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "396.095",
+ "y" : "1116.1",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3Clocked"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "558.4236",
+ "y" : "1132.1",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3Clocked"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "806.095",
+ "y" : "1116.1",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3Sync"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "968.4236",
+ "y" : "1132.1",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3Sync"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "35.5",
+ "y" : "1128.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3Fade"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "26.83362",
+ "y" : "1324.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3StartPhase"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "215.8336",
+ "y" : "1324.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3Speed"
+ }
+ },
+ {
+ "name" : "ledknob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "423.8336",
+ "y" : "1240.98",
+ "width" : "230",
+ "height" : "230",
+ "texture" : "ledcircle_6",
+ "tileSizeX" : "512",
+ "tileSizeY" : "512"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3Shape"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "474.8336",
+ "y" : "1291.98",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3Shape"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "733.8335",
+ "y" : "1324.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3KeyTrack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "922.8335",
+ "y" : "1324.48",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Lfo3Delay"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot1FSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1263.667",
+ "y" : "101.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1481.75",
+ "y" : "61.79999",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot1FAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot1FDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1759.833",
+ "y" : "101.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot2FSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1263.667",
+ "y" : "230.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1481.75",
+ "y" : "191.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot2FAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot2FDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1759.833",
+ "y" : "230.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot3FSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1263.667",
+ "y" : "360.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1481.75",
+ "y" : "320.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot3FAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot3FDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1759.833",
+ "y" : "360.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot4FSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1263.667",
+ "y" : "489.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1481.75",
+ "y" : "450.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot4FAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot4FDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1759.833",
+ "y" : "489.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot5FSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1263.667",
+ "y" : "619.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1481.75",
+ "y" : "579.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot5FAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot5FDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1759.833",
+ "y" : "619.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot6FSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1263.667",
+ "y" : "748.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1481.75",
+ "y" : "709.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot6FAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot6FDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1759.833",
+ "y" : "748.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot7FSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1263.667",
+ "y" : "878.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1481.75",
+ "y" : "838.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot7FAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot7FDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1759.833",
+ "y" : "878.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot8FSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1263.667",
+ "y" : "1007.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1481.75",
+ "y" : "968.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot8FAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot8FDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1759.833",
+ "y" : "1007.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod1Source1"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1122.667",
+ "y" : "1248.48",
+ "width" : "200",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod1Operator"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1317.959",
+ "y" : "1452.38",
+ "width" : "195.2916",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod1Source2"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1122.667",
+ "y" : "1371.28",
+ "width" : "200",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1360.067",
+ "y" : "1242.4",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Mod1Constant"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod2Source1"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1578.25",
+ "y" : "1248.48",
+ "width" : "200",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod2Operator"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1773.542",
+ "y" : "1452.38",
+ "width" : "195.2916",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod2Source2"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1578.25",
+ "y" : "1371.28",
+ "width" : "200",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1815.65",
+ "y" : "1242.4",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Mod2Constant"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot1SSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2174.834",
+ "y" : "101.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2392.917",
+ "y" : "61.79999",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot1SAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot1SDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2671",
+ "y" : "101.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot2SSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2174.834",
+ "y" : "230.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2392.917",
+ "y" : "191.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot2SAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot2SDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2671",
+ "y" : "230.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot3SSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2174.834",
+ "y" : "360.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2392.917",
+ "y" : "320.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot3SAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot3SDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2671",
+ "y" : "360.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot4SSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2174.834",
+ "y" : "489.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2392.917",
+ "y" : "450.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot4SAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot4SDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2671",
+ "y" : "489.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot5SSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2174.834",
+ "y" : "619.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2392.917",
+ "y" : "579.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot5SAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot5SDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2671",
+ "y" : "619.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot6SSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2174.834",
+ "y" : "748.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2392.917",
+ "y" : "709.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot6SAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot6SDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2671",
+ "y" : "748.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot7SSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2174.834",
+ "y" : "878.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2392.917",
+ "y" : "838.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot7SAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot7SDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2671",
+ "y" : "878.3",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot8SSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2174.834",
+ "y" : "1007.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2392.917",
+ "y" : "968.3",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Slot8SAmount"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Slot8SDestination"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2671",
+ "y" : "1007.8",
+ "width" : "204",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod3Source1"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2033.834",
+ "y" : "1248.48",
+ "width" : "200",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod3Operator"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2229.125",
+ "y" : "1452.38",
+ "width" : "195.2916",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod3Source2"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2033.834",
+ "y" : "1371.28",
+ "width" : "200",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2271.234",
+ "y" : "1242.4",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Mod3Constant"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod4Source1"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2489.417",
+ "y" : "1248.48",
+ "width" : "200",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod4Operator"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2684.708",
+ "y" : "1452.38",
+ "width" : "195.2916",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Mod4Source2"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2489.417",
+ "y" : "1371.28",
+ "width" : "200",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2726.817",
+ "y" : "1242.4",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Mod4Constant"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "pageFx",
+ "image" : {
+ "x" : "450",
+ "y" : "428",
+ "width" : "2900",
+ "height" : "1525",
+ "texture" : "pageFx"
+ },
+ "children" : [
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "FX1Type"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "31",
+ "y" : "137",
+ "width" : "268",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "102.12",
+ "y" : "240.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FX1Mix"
+ }
+ },
+ {
+ "name" : "fxChorus",
+ "image" : {
+ "x" : "338",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1Chorus"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX1Type",
+ "enableOnValues" : "1"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "171.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1ChorusSpeed"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "483.25",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1ChorusDepth"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "794.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1ChorusDelay"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxFlanger",
+ "image" : {
+ "x" : "338",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1Flanger"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX1Type",
+ "enableOnValues" : "2"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "171.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1FlangerSpeed"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "483.25",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1FlangerDepth"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "794.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1FlangerFeedback"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "669",
+ "y" : "61.59998",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1FlangerPolarity"
+ }
+ },
+ {
+ "name" : "ledNeg",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "808.6",
+ "y" : "77.59998",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1FlangerPolarity"
+ }
+ },
+ {
+ "name" : "ledPos",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "1",
+ "overImage" : "0",
+ "downImage" : "0",
+ "normalImageOn" : "0",
+ "overImageOn" : "0",
+ "downImageOn" : "0",
+ "x" : "622.8",
+ "y" : "77.59998",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1FlangerPolarity"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxPhaser",
+ "image" : {
+ "x" : "338",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1Phaser"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX1Type",
+ "enableOnValues" : "3"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "45.45001",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1PhaserSpeed"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "264.35",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1PhaserDepth"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "483.25",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1PhaserFeedback"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "702.1499",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1PhaserCenter"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "921.05",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1PhaserSpacing"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "669",
+ "y" : "61.59998",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1PhaserPolarity"
+ }
+ },
+ {
+ "name" : "ledNeg",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "808.6",
+ "y" : "77.59998",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1PhaserPolarity"
+ }
+ },
+ {
+ "name" : "ledPos",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "1",
+ "overImage" : "0",
+ "downImage" : "0",
+ "normalImageOn" : "0",
+ "overImageOn" : "0",
+ "downImageOn" : "0",
+ "x" : "622.8",
+ "y" : "77.59998",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1PhaserPolarity"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxOverdrive",
+ "image" : {
+ "x" : "338",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1Overdrive"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX1Type",
+ "enableOnValues" : "4"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "171.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1OverdriveDrive"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "483.25",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1OverdrivePostGain"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "794.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged_red",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1OverdriveCutoff"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxFiveFX",
+ "image" : {
+ "x" : "338",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1FiveFX"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX1Type",
+ "enableOnValues" : "5"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "72.8125",
+ "y" : "40.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX1ChorusSpeed"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "346.4375",
+ "y" : "40.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX1ChorusDepth"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "620.0625",
+ "y" : "40.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX1Delay"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "893.6875",
+ "y" : "40.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX1ChorusDelayL"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "72.8125",
+ "y" : "289.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX1SampleAndHold"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "346.4375",
+ "y" : "289.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX1Overdrive"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "FiveFX1RingModSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "549.0625",
+ "y" : "353.7853",
+ "width" : "270",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "893.6875",
+ "y" : "289.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX1RingModLevel"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxVocoder",
+ "image" : {
+ "x" : "338",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1Vocoder"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX1Type",
+ "enableOnValues" : "6"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "351.5",
+ "y" : "185.19",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderBands"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "460.5",
+ "y" : "14",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderAnalysisFreqLow"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "610.5",
+ "y" : "14",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderAnalysisFreqHigh"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "808",
+ "y" : "14",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderOffsetS"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "950.7803",
+ "y" : "14",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderOffsetHi"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "11",
+ "y" : "321.7",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderBandwidth"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "160.9997",
+ "y" : "321.7026",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderResonance"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "11",
+ "y" : "132",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderAttack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "161",
+ "y" : "132",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderDecay"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "462.187",
+ "y" : "322.6",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderEQLevelLow"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "614.562",
+ "y" : "322.6",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderEQBandMid"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "766.937",
+ "y" : "322.6",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderEQLevelMid"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "919.312",
+ "y" : "322.6",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderEQLevelHigh"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Fx1VocoderAnalysisSignal"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "17",
+ "y" : "65",
+ "width" : "298",
+ "height" : "41"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "FX2Type"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1493.5",
+ "y" : "137",
+ "width" : "268",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1564.62",
+ "y" : "240.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FX2Mix"
+ }
+ },
+ {
+ "name" : "fxChorus",
+ "image" : {
+ "x" : "1800.5",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1Chorus"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "1"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "171.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ChorusSpeed"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "483.25",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ChorusDepth"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "794.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ChorusDelay"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxFlanger",
+ "image" : {
+ "x" : "1800.5",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1Flanger"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "2"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "171.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2FlangerSpeed"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "483.25",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2FlangerDepth"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "794.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2FlangerFeedback"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "669",
+ "y" : "61.59998",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2FlangerPolarity"
+ }
+ },
+ {
+ "name" : "ledNeg",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "808.6001",
+ "y" : "77.59998",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2FlangerPolarity"
+ }
+ },
+ {
+ "name" : "ledPos",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "1",
+ "overImage" : "0",
+ "downImage" : "0",
+ "normalImageOn" : "0",
+ "overImageOn" : "0",
+ "downImageOn" : "0",
+ "x" : "622.8",
+ "y" : "77.59998",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2FlangerPolarity"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxPhaser",
+ "image" : {
+ "x" : "1800.5",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1Phaser"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "3"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "45.44995",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2PhaserSpeed"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "264.3501",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2PhaserDepth"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "483.25",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2PhaserFeedback"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "702.1499",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2PhaserCenter"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "921.05",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2PhaserSpacing"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "669",
+ "y" : "61.59998",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2PhaserPolarity"
+ }
+ },
+ {
+ "name" : "ledNeg",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "808.6001",
+ "y" : "77.59998",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2PhaserPolarity"
+ }
+ },
+ {
+ "name" : "ledPos",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "1",
+ "overImage" : "0",
+ "downImage" : "0",
+ "normalImageOn" : "0",
+ "overImageOn" : "0",
+ "downImageOn" : "0",
+ "x" : "622.8",
+ "y" : "77.59998",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2PhaserPolarity"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxOverdrive",
+ "image" : {
+ "x" : "1800.5",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1Overdrive"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "4"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "171.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2OverdriveDrive"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "483.25",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2OverdrivePostGain"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "794.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged_red",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2OverdriveCutoff"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxFiveFX",
+ "image" : {
+ "x" : "1800.5",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1FiveFX"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "5"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "72.8125",
+ "y" : "40.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX2ChorusSpeed"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "346.4375",
+ "y" : "40.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX2ChorusDepth"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "620.0625",
+ "y" : "40.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX2Delay"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "893.6875",
+ "y" : "40.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX2ChorusDelayL"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "72.8125",
+ "y" : "289.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX2SampleAndHold"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "346.4375",
+ "y" : "289.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX2Overdrive"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "FiveFX2RingModSource"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "549.0625",
+ "y" : "353.7853",
+ "width" : "270",
+ "height" : "41"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "893.6875",
+ "y" : "289.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "FiveFX2RingModLevel"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxVocoder",
+ "image" : {
+ "x" : "1800.5",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx1Vocoder"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "6"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "351.5",
+ "y" : "185.19",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderBands"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "460.5",
+ "y" : "14",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderAnalysisFreqLow"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "610.5",
+ "y" : "14",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderAnalysisFreqHigh"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "808",
+ "y" : "14",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderOffsetS"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "950.7803",
+ "y" : "14",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderOffsetHi"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "11",
+ "y" : "321.7",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderBandwidth"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "160.9998",
+ "y" : "321.7026",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderResonance"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "11",
+ "y" : "132",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderAttack"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "161",
+ "y" : "132",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderDecay"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "462.187",
+ "y" : "322.6",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderEQLevelLow"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "614.562",
+ "y" : "322.6",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderEQBandMid"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "766.937",
+ "y" : "322.6",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderEQLevelMid"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "919.312",
+ "y" : "322.6",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderEQLevelHigh"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "Fx2VocoderAnalysisSignal"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "17",
+ "y" : "65",
+ "width" : "298",
+ "height" : "41"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fx2Delay",
+ "image" : {
+ "x" : "1800.5",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx2Delay"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "7"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "61.75",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2DelayFeedback"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "313.25",
+ "y" : "185.1902",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2DelayCutoff"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "817.8501",
+ "y" : "162.171",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2DelayPolarity"
+ }
+ },
+ {
+ "name" : "ledNeg",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "957.45",
+ "y" : "178.171",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2DelayPolarity"
+ }
+ },
+ {
+ "name" : "ledPos",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "1",
+ "overImage" : "0",
+ "downImage" : "0",
+ "normalImageOn" : "0",
+ "overImageOn" : "0",
+ "downImageOn" : "0",
+ "x" : "771.6499",
+ "y" : "178.171",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2DelayPolarity"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "811.7114",
+ "y" : "272.2095",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2DelayAutopan"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "956.25",
+ "y" : "288.2095",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2DelayAutopan"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fxReverb",
+ "image" : {
+ "x" : "1800.5",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx2Reverb"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "8"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "72.8125",
+ "y" : "60.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ReverbSize"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "346.4375",
+ "y" : "60.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ReverbShape"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "620.0625",
+ "y" : "60.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ReverbDecay"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "893.6875",
+ "y" : "60.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ReverbPreDelay"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "72.8125",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ReverbLowpass"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "346.4375",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ReverbHighpass"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "620.0625",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ReverbDiffusion"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "893.6875",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx2ReverbDamping"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "fx251Delay",
+ "image" : {
+ "x" : "1800.5",
+ "y" : "55",
+ "width" : "1094.5",
+ "height" : "498.3804",
+ "texture" : "fx251Delay"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "9,10"
+ },
+ "children" : [
+ {
+ "name" : "delay",
+ "image" : {
+ "x" : "3.178711",
+ "y" : "12.09509",
+ "width" : "150",
+ "height" : "225",
+ "texture" : "fx2DelayNotClocked"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "9"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "11",
+ "y" : "48.5",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayDelay"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "length",
+ "image" : {
+ "x" : "3.178711",
+ "y" : "12.09509",
+ "width" : "150",
+ "height" : "225",
+ "texture" : "fx2DelayClocked"
+ },
+ "condition" : {
+ "enableOnParameter" : "FX2Type",
+ "enableOnValues" : "10"
+ },
+ "children" : [
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "11",
+ "y" : "48.5",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251ClockDelayDelay"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "170.5356",
+ "y" : "60.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayFeedback"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "326.8928",
+ "y" : "60.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayDelayML"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "483.25",
+ "y" : "60.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayDelayMR"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "639.6072",
+ "y" : "60.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayDelayS1L"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "795.9644",
+ "y" : "60.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayDelayS2L"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "952.3215",
+ "y" : "60.59509",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayDelayS1R"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "14.17871",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayInputHP"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "170.5356",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayLfeLP"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "326.8928",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayFSLVolume"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "483.25",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayFSRVolume"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "639.6072",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayCenterSVolume"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "795.9644",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayRearSLVolume"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "952.3215",
+ "y" : "309.7853",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Fx251DelayRearSRVolume"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "ArpMode"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "42.89917",
+ "y" : "724.4902",
+ "width" : "300",
+ "height" : "50"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "ArpDirection"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "392.8992",
+ "y" : "724.4902",
+ "width" : "300",
+ "height" : "50"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "ArpVeloMode"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "42.89917",
+ "y" : "874.4902",
+ "width" : "300",
+ "height" : "50"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "ArpClock"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "392.8992",
+ "y" : "874.4902",
+ "width" : "300",
+ "height" : "50"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "ArpPattern"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "42.89917",
+ "y" : "1024.49",
+ "width" : "300",
+ "height" : "50"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "ArpSortOrder"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "392.8992",
+ "y" : "1024.49",
+ "width" : "300",
+ "height" : "50"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "63.57947",
+ "y" : "1088.29",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Tempo"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "303.8995",
+ "y" : "1088.29",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "ArpMaxNotes"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "544.2195",
+ "y" : "1088.29",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "ArpTFactor"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "63.57947",
+ "y" : "1323.09",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "ArpOctaveRange"
+ }
+ },
+ {
+ "name" : "knob",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "303.8995",
+ "y" : "1323.09",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "ArpLength"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1120.972",
+ "y" : "662.2904",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "ArpSameNoteOverlap"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1283.301",
+ "y" : "678.2904",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "ArpSameNoteOverlap"
+ }
+ },
+ {
+ "name" : "txtColLabel",
+ "parameterAttachment" : {
+ "parameter" : "ArpPatternLength"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "Osc 1 Pitch",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1855.005",
+ "y" : "669.7904",
+ "width" : "266.1976",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "buttonH",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2635.972",
+ "y" : "662.2904",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "ArpPatternReset"
+ }
+ },
+ {
+ "name" : "led",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2798.301",
+ "y" : "678.2904",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "ArpPatternReset"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "808.7919",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp00Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "760.7919",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp00Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp00Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "778.8918",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "760.7919",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp00Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "760.7919",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp00Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "942.3538",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp01Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "894.3538",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp01Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp01Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "912.4537",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "894.3538",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp01Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "894.3538",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp01Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1075.916",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp02Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1027.916",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp02Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp02Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1046.016",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1027.916",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp02Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1027.916",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp02Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1209.477",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp03Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1161.477",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp03Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp03Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1179.577",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1161.477",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp03Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1161.477",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp03Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1343.039",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp04Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1295.039",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp04Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp04Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1313.139",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1295.039",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp04Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1295.039",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp04Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1476.601",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp05Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1428.601",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp05Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp05Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1446.701",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1428.601",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp05Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1428.601",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp05Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1610.163",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp06Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1562.163",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp06Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp06Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1580.263",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1562.163",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp06Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1562.163",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp06Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1743.725",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp07Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1695.725",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp07Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp07Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1713.825",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1695.725",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp07Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1695.725",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp07Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1877.286",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp08Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1829.286",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp08Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp08Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1847.386",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1829.286",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp08Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1829.286",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp08Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2010.848",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp09Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1962.848",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp09Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp09Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1980.948",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1962.848",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp09Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1962.848",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp09Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2144.41",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp10Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2096.41",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp10Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp10Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2114.51",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2096.41",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp10Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2096.41",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp10Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2277.972",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp11Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2229.972",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp11Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp11Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2248.072",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2229.972",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp11Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2229.972",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp11Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2411.533",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp12Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2363.533",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp12Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp12Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2381.633",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2363.533",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp12Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2363.533",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp12Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2545.095",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp13Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2497.095",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp13Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp13Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2515.195",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2497.095",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp13Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2497.095",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp13Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2678.657",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp14Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2630.657",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp14Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp14Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2648.757",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2630.657",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp14Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2630.657",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp14Length"
+ }
+ },
+ {
+ "name" : "glide",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2812.219",
+ "y" : "813.3804",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp15Glide"
+ }
+ },
+ {
+ "name" : "accent",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2764.219",
+ "y" : "894.4803",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp15Accent"
+ }
+ },
+ {
+ "name" : "dropdownArpStep",
+ "parameterAttachment" : {
+ "parameter" : "Arp15Step"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : ">>",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2782.319",
+ "y" : "1077.64",
+ "width" : "91.8",
+ "height" : "40"
+ }
+ },
+ {
+ "name" : "timing",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2764.219",
+ "y" : "1172.8",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp15Timing"
+ }
+ },
+ {
+ "name" : "length",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2764.219",
+ "y" : "1349.9",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "Arp15Length"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "pageMulti",
+ "image" : {
+ "x" : "450",
+ "y" : "428",
+ "width" : "2900",
+ "height" : "1525",
+ "texture" : "pageMulti"
+ },
+ "children" : [
+ {
+ "name" : "controls",
+ "component" : {
+ "x" : "0",
+ "y" : "55",
+ "width" : "2900",
+ "height" : "1470"
+ },
+ "children" : [
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "45.375",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI0Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "45.375",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI0Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI0KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "109.375",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI0KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "109.375",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI0VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "109.375",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI0VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "109.375",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "45.375",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI0Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "45.375",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI0Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI0MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "109.375",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI0Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "109.375",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "224.125",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI1Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "224.125",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI1Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI1KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "288.125",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI1KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "288.125",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI1VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "288.125",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI1VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "288.125",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "224.125",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI1Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "224.125",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI1Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI1MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "1",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "288.125",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI1Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "288.125",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "402.875",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI2Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "402.875",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI2Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI2KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "466.875",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI2KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "466.875",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI2VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "466.875",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI2VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "466.875",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "402.875",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI2Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "402.875",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI2Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI2MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "466.875",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI2Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "466.875",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "581.625",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI3Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "581.625",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI3Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI3KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "645.625",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI3KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "645.625",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI3VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "645.625",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI3VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "645.625",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "581.625",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI3Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "581.625",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI3Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI3MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "3",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "645.625",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI3Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "645.625",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "760.375",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI4Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "760.375",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI4Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI4KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "824.375",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI4KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "824.375",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI4VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "824.375",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI4VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "824.375",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "760.375",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI4Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "760.375",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI4Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI4MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "4",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "824.375",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI4Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "824.375",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "939.125",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI5Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "939.125",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI5Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI5KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1003.125",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI5KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1003.125",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI5VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1003.125",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI5VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1003.125",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "939.125",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI5Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "939.125",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI5Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI5MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "5",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1003.125",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI5Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1003.125",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1117.875",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI6Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1117.875",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI6Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI6KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1181.875",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI6KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1181.875",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI6VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1181.875",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI6VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1181.875",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1117.875",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI6Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1117.875",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI6Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI6MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "6",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1181.875",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI6Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1181.875",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1296.625",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI7Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1296.625",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI7Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI7KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1360.625",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI7KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1360.625",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI7VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1360.625",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI7VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1360.625",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1296.625",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI7Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1296.625",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI7Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI7MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "7",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1360.625",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI7Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1360.625",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1475.375",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI8Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1475.375",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI8Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI8KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1539.375",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI8KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1539.375",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI8VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1539.375",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI8VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1539.375",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1475.375",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI8Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1475.375",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI8Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI8MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1539.375",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI8Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1539.375",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1654.125",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI9Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1654.125",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI9Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI9KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1718.125",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI9KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1718.125",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI9VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1718.125",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI9VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1718.125",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1654.125",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI9Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1654.125",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI9Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI9MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "9",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1718.125",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI9Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1718.125",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1832.875",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI10Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1832.875",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI10Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI10KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1896.875",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI10KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1896.875",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI10VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1896.875",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI10VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1896.875",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1832.875",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI10Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1832.875",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI10Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI10MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "10",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1896.875",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI10Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "1896.875",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2011.625",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI11Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2011.625",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI11Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI11KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2075.625",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI11KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2075.625",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI11VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2075.625",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI11VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2075.625",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2011.625",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI11Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2011.625",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI11Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI11MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "11",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2075.625",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI11Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2075.625",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2190.375",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI12Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2190.375",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI12Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI12KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2254.375",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI12KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2254.375",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI12VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2254.375",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI12VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2254.375",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2190.375",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI12Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2190.375",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI12Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI12MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "12",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2254.375",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI12Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2254.375",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2369.125",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI13Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2369.125",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI13Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI13KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2433.125",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI13KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2433.125",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI13VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2433.125",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI13VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2433.125",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2369.125",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI13Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2369.125",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI13Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI13MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "13",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2433.125",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI13Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2433.125",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2547.875",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI14Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2547.875",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI14Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI14KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2611.875",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI14KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2611.875",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI14VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2611.875",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI14VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2611.875",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2547.875",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI14Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2547.875",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI14Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI14MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "14",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2611.875",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI14Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2611.875",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "transpose",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2726.625",
+ "y" : "17.72223",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI15Transpose"
+ }
+ },
+ {
+ "name" : "detune",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2726.625",
+ "y" : "216.6111",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI15Detune"
+ }
+ },
+ {
+ "name" : "keyhi",
+ "parameterAttachment" : {
+ "parameter" : "MI15KeyHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "G+8",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2790.625",
+ "y" : "415.5",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "keylo",
+ "parameterAttachment" : {
+ "parameter" : "MI15KeyLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "C-2",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2790.625",
+ "y" : "499.9445",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "velhi",
+ "parameterAttachment" : {
+ "parameter" : "MI15VeloHigh"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "127",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2790.625",
+ "y" : "619.8334",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "vello",
+ "parameterAttachment" : {
+ "parameter" : "MI15VeloLow"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2790.625",
+ "y" : "704.2778",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "pan",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2726.625",
+ "y" : "824.1667",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI15Pan"
+ }
+ },
+ {
+ "name" : "volume",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2726.625",
+ "y" : "1023.056",
+ "width" : "128",
+ "height" : "128",
+ "texture" : "encoder_ranged",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ },
+ "parameterAttachment" : {
+ "parameter" : "MI15Volume"
+ }
+ },
+ {
+ "name" : "midichannel",
+ "parameterAttachment" : {
+ "parameter" : "MI15MidiChannel"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "15",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2790.625",
+ "y" : "1221.944",
+ "width" : "80",
+ "height" : "49"
+ }
+ },
+ {
+ "name" : "output",
+ "parameterAttachment" : {
+ "parameter" : "MI15Output"
+ },
+ "combobox" : {
+ "offsetL" : "15",
+ "offsetT" : "1",
+ "offsetR" : "-15",
+ "offsetB" : "-1",
+ "text" : "0",
+ "textHeight" : "30",
+ "color" : "D1D1D1FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "bold" : "1",
+ "x" : "2790.625",
+ "y" : "1341.833",
+ "width" : "80",
+ "height" : "49"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name" : "pagePatchBrowser",
+ "image" : {
+ "x" : "450",
+ "y" : "428",
+ "width" : "2900",
+ "height" : "1525",
+ "texture" : "pagePatchBrowser"
+ },
+ "children" : [
+ {
+ "name" : "patchbrowser",
+ "component" : {
+ "x" : "-0.0004882813",
+ "y" : "55",
+ "width" : "2890",
+ "height" : "1460"
+ },
+ "children" : [
+ {
+ "name" : "ContainerFileSelector",
+ "component" : {
+ "x" : "0",
+ "y" : "0",
+ "width" : "1445",
+ "height" : "1460"
+ }
+ },
+ {
+ "name" : "ContainerPatchList",
+ "component" : {
+ "x" : "1445",
+ "y" : "0",
+ "width" : "1445",
+ "height" : "1408"
+ }
+ },
+ {
+ "name" : "ContainerPatchListSearchBox",
+ "component" : {
+ "x" : "1445",
+ "y" : "1410",
+ "width" : "1451",
+ "height" : "50"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name" : "FocusedParameterTooltip",
+ "label" : {
+ "text" : "42",
+ "textHeight" : "40",
+ "color" : "CF972EFF",
+ "backgroundColor" : "000000FF",
+ "alignH" : "C",
+ "alignV" : "C",
+ "x" : "1385",
+ "y" : "1225.5",
+ "width" : "96",
+ "height" : "55"
+ },
+ "componentProperties" : {
+ "offsetY" : "25"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "514.5938",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "530.5938",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "603.7813",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "619.7813",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "692.9688",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "708.9688",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "782.1563",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "798.1563",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "871.3438",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "887.3438",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "960.5313",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "976.5313",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "1049.719",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "1065.719",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "1138.906",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "1154.906",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "1228.094",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "1244.094",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "1317.281",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "1333.281",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "1406.469",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "1422.469",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "1495.656",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "1511.656",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "1584.844",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "1600.844",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "1674.031",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "1690.031",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "1763.219",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "1779.219",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectButton",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "164.9614",
+ "y" : "1852.406",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "partSelectLED",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "327.29",
+ "y" : "1868.406",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ }
+ ]
+}
+\ No newline at end of file
diff --git a/source/mqJucePlugin/skins/mqDefault/mqDefaultBG.png b/source/mqJucePlugin/skins/mqDefault/mqDefaultBG.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/pageDrum.png b/source/mqJucePlugin/skins/mqDefault/pageDrum.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/pageFilters.png b/source/mqJucePlugin/skins/mqDefault/pageFilters.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/pageFx.png b/source/mqJucePlugin/skins/mqDefault/pageFx.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/pageLfo.png b/source/mqJucePlugin/skins/mqDefault/pageLfo.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/pageMulti.png b/source/mqJucePlugin/skins/mqDefault/pageMulti.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/pageOsc.png b/source/mqJucePlugin/skins/mqDefault/pageOsc.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/pagePatchBrowser.png b/source/mqJucePlugin/skins/mqDefault/pagePatchBrowser.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqDefault/toggleWithLED.png b/source/mqJucePlugin/skins/mqDefault/toggleWithLED.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqFrontPanel/Digital.ttf b/source/mqJucePlugin/skins/mqFrontPanel/Digital.ttf
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqFrontPanel/alphadial.png b/source/mqJucePlugin/skins/mqFrontPanel/alphadial.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqFrontPanel/assets.cmake b/source/mqJucePlugin/skins/mqFrontPanel/assets.cmake
@@ -0,0 +1,10 @@
+set(ASSETS_mqFrontPanel
+ ${CMAKE_CURRENT_LIST_DIR}/alphadial.png
+ ${CMAKE_CURRENT_LIST_DIR}/bakedBG.png
+ ${CMAKE_CURRENT_LIST_DIR}/button.png
+ ${CMAKE_CURRENT_LIST_DIR}/Digital.ttf
+ ${CMAKE_CURRENT_LIST_DIR}/encoder.png
+ ${CMAKE_CURRENT_LIST_DIR}/led.png
+
+ ${CMAKE_CURRENT_LIST_DIR}/mqFrontPanel.json
+)
diff --git a/source/mqJucePlugin/skins/mqFrontPanel/bakedBG.png b/source/mqJucePlugin/skins/mqFrontPanel/bakedBG.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqFrontPanel/button.png b/source/mqJucePlugin/skins/mqFrontPanel/button.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqFrontPanel/encoder.png b/source/mqJucePlugin/skins/mqFrontPanel/encoder.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqFrontPanel/led.png b/source/mqJucePlugin/skins/mqFrontPanel/led.png
Binary files differ.
diff --git a/source/mqJucePlugin/skins/mqFrontPanel/mqFrontPanel.json b/source/mqJucePlugin/skins/mqFrontPanel/mqFrontPanel.json
@@ -0,0 +1,966 @@
+{
+ "name" : "Root",
+ "root" : {
+ "x" : "0",
+ "y" : "0",
+ "width" : "3800",
+ "height" : "704",
+ "scale" : "0.5"
+ },
+ "children" : [
+ {
+ "name" : "bakedBG",
+ "image" : {
+ "x" : "0",
+ "y" : "0",
+ "width" : "3800",
+ "height" : "704",
+ "texture" : "bakedBG"
+ }
+ },
+ {
+ "name" : "lcdLineA",
+ "label" : {
+ "text" : "01234567890123456789",
+ "textHeight" : "46",
+ "color" : "000000FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "fontFile" : "Digital",
+ "fontName" : "Register",
+ "x" : "358.6",
+ "y" : "288",
+ "width" : "630",
+ "height" : "64"
+ }
+ },
+ {
+ "name" : "lcdLineB",
+ "label" : {
+ "text" : "01234567890123456789",
+ "textHeight" : "46",
+ "color" : "000000FF",
+ "alignH" : "L",
+ "alignV" : "C",
+ "fontFile" : "Digital",
+ "fontName" : "Register",
+ "x" : "358.6",
+ "y" : "352",
+ "width" : "630",
+ "height" : "64"
+ }
+ },
+ {
+ "name" : "lcdArea",
+ "component" : {
+ "x" : "358.6",
+ "y" : "298",
+ "width" : "620",
+ "height" : "108"
+ },
+ "componentProperties" : {
+ "lcdBackgroundColor" : "248727"
+ }
+ },
+ {
+ "name" : "ledInst1",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "400.0637",
+ "y" : "65.10001",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonInst1",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "352.0637",
+ "y" : "115.1",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledInst2",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "568.4213",
+ "y" : "65.10001",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonInst2",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "520.4213",
+ "y" : "115.1",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledInst3",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "736.7787",
+ "y" : "65.10001",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonInst3",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "688.7787",
+ "y" : "115.1",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledInst4",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "905.1362",
+ "y" : "65.10001",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonInst4",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "857.1362",
+ "y" : "115.1",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "encoderAlphaDial",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "1081.2",
+ "y" : "224",
+ "width" : "256",
+ "height" : "256",
+ "texture" : "alphadial",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "encoderLCDLeft",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "448.6",
+ "y" : "523.9",
+ "width" : "140",
+ "height" : "140",
+ "texture" : "encoder",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "encoderLCDRight",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "748.6",
+ "y" : "523.9",
+ "width" : "140",
+ "height" : "140",
+ "texture" : "encoder",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonPlay",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1350.3",
+ "y" : "377.4",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledPlay",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1398.3",
+ "y" : "339.4",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonMultimode",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1023.25",
+ "y" : "581.59",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledMultimode",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "988.25",
+ "y" : "597.59",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonPeek",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1268.75",
+ "y" : "581.59",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledPeek",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1233.75",
+ "y" : "597.59",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonGlobal",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1597",
+ "y" : "99.32001",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledGlobal",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1556",
+ "y" : "115.6",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonMulti",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1597",
+ "y" : "209.32",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledMulti",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1556",
+ "y" : "225.6",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonEdit",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1597",
+ "y" : "319.32",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledEdit",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1556",
+ "y" : "335.6",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonSound",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1597",
+ "y" : "429.32",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledSound",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1556",
+ "y" : "445.6",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonShift",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1597",
+ "y" : "539.32",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledShift",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1556",
+ "y" : "555.6",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonU",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2020.93",
+ "y" : "186.2607",
+ "width" : "64",
+ "height" : "128",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonL",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "1888.93",
+ "y" : "318.2607",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonR",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2088.93",
+ "y" : "318.2607",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonD",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2020.93",
+ "y" : "386.2607",
+ "width" : "64",
+ "height" : "128",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledOsc1",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2431.14",
+ "y" : "89.82001",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledOsc2",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2463.14",
+ "y" : "89.82001",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledOsc3",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2495.14",
+ "y" : "89.82001",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledMixerRouting",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2431.14",
+ "y" : "154.82",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledFilter1",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2431.14",
+ "y" : "219.82",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledFilter2",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2463.14",
+ "y" : "219.82",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledAmpFxArp",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2431.14",
+ "y" : "284.82",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledEnv1",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2431.14",
+ "y" : "349.82",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledEnv2",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2463.14",
+ "y" : "349.82",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledEnv3",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2495.14",
+ "y" : "349.82",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledEnv4",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2527.14",
+ "y" : "349.82",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledLFOs",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2431.14",
+ "y" : "414.82",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledModMatrix",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "2431.14",
+ "y" : "479.82",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "encoderMatrix1",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2592.22",
+ "y" : "534.82",
+ "width" : "140",
+ "height" : "140",
+ "texture" : "encoder",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "encoderMatrix2",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "2822.22",
+ "y" : "534.82",
+ "width" : "140",
+ "height" : "140",
+ "texture" : "encoder",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "encoderMatrix3",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "3052.22",
+ "y" : "534.82",
+ "width" : "140",
+ "height" : "140",
+ "texture" : "encoder",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "encoderMatrix4",
+ "rotary" : {
+ },
+ "spritesheet" : {
+ "x" : "3282.22",
+ "y" : "534.82",
+ "width" : "140",
+ "height" : "140",
+ "texture" : "encoder",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "buttonPower",
+ "button" : {
+ "normalImage" : "0",
+ "overImage" : "0",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "3499.2",
+ "y" : "578.7",
+ "width" : "128",
+ "height" : "64",
+ "texture" : "button",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ },
+ {
+ "name" : "ledPower",
+ "button" : {
+ "isToggle" : "1",
+ "normalImage" : "0",
+ "overImage" : "1",
+ "downImage" : "1",
+ "normalImageOn" : "1",
+ "overImageOn" : "1",
+ "downImageOn" : "1",
+ "x" : "3547.2",
+ "y" : "540.7",
+ "width" : "32",
+ "height" : "32",
+ "texture" : "led",
+ "tileSizeX" : "128",
+ "tileSizeY" : "128"
+ }
+ }
+ ]
+}
+\ No newline at end of file
diff --git a/source/mqJucePlugin/version.h.in b/source/mqJucePlugin/version.h.in
@@ -0,0 +1,4 @@
+#pragma once
+
+static constexpr const char* const g_pluginVersionString = "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@";
+static constexpr uint32_t g_pluginVersion = @PROJECT_VERSION_MAJOR@@PROJECT_VERSION_MINOR@@PROJECT_VERSION_PATCH@;
diff --git a/source/mqLib/CMakeLists.txt b/source/mqLib/CMakeLists.txt
@@ -0,0 +1,53 @@
+cmake_minimum_required(VERSION 3.10)
+project(mqLib)
+
+include(bin2h.cmake)
+
+add_library(mqLib STATIC)
+
+set(EMBED_ROM OFF)
+set(ROM "${CMAKE_CURRENT_SOURCE_DIR}/microQ223.swapped.BIN")
+
+if(EXISTS ${ROM})
+ set(EMBED_ROM ON)
+ if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/romData.h)
+ bin2h(SOURCE_FILE ${ROM} HEADER_FILE "romData.h" VARIABLE_NAME "ROM_DATA")
+ endif()
+ target_compile_definitions(mqLib PRIVATE EMBED_ROM=1)
+else()
+ target_compile_definitions(mqLib PRIVATE EMBED_ROM=0)
+endif()
+
+set(SOURCES
+ am29f.cpp am29f.h
+ buttons.cpp buttons.h
+ device.cpp device.h
+ dspBootCode.h
+ lcd.cpp lcd.h
+ lcdfonts.cpp lcdfonts.h
+ leds.cpp leds.h
+ rom.cpp rom.h
+ microq.cpp microq.h
+ mqbuildconfig.h
+ mqdsp.cpp mqdsp.h
+ mqhardware.cpp mqhardware.h
+ mqmc.cpp mqmc.h
+ mqmidi.cpp mqmidi.h
+ mqstate.cpp mqstate.h
+ mqmiditypes.h
+ mqsysexremotecontrol.cpp mqsysexremotecontrol.h
+ mqtypes.h
+)
+
+if(${EMBED_ROM})
+ list(APPEND SOURCES romData.h)
+endif()
+
+target_sources(mqLib PRIVATE ${SOURCES})
+source_group("source" FILES ${SOURCES})
+
+target_link_libraries(mqLib PUBLIC synthLib 68kEmu)
+
+if(DSP56300_DEBUGGER)
+ target_link_libraries(mqLib PUBLIC dsp56kDebugger)
+endif()
diff --git a/source/mqLib/am29f.cpp b/source/mqLib/am29f.cpp
@@ -0,0 +1,136 @@
+#include "am29f.h"
+
+#include <cassert>
+
+#include "mqmc.h"
+
+#include "../mc68k/logging.h"
+
+Am29f::Am29f(uint8_t* _buffer, const size_t _size, bool _useWriteEnable, bool _bitreversedCmdAddr): m_buffer(_buffer), m_size(_size), m_useWriteEnable(_useWriteEnable), m_bitreverseCmdAddr(_bitreversedCmdAddr)
+{
+ auto br = [&](uint16_t x)
+ {
+ return m_bitreverseCmdAddr ? static_cast<uint16_t>(bitreverse(x) >> 4) : x;
+ };
+
+ // Chip Erase
+ m_commands.push_back({{{br(0x555),0xAA}, {br(0x2AA),0x55}, {br(0x555),0x80}, {br(0x555),0xAA}, {br(0x2AA),0x55}, {br(0x555),0x10}}});
+
+ // Sector Erase
+ m_commands.push_back({{{br(0x555),0xAA}, {br(0x2AA),0x55}, {br(0x555),0x80}, {br(0x555),0xAA}, {br(0x2AA),0x55}}});
+
+ // Program
+ m_commands.push_back({{{br(0x555),0xAA}, {br(0x2AA),0x55}, {br(0x555),0xA0}}});
+}
+
+void Am29f::write(const uint32_t _addr, const uint16_t _data)
+{
+ const auto reset = [this]()
+ {
+ m_currentBusCycle = 0;
+ m_currentCommand = -1;
+ };
+
+ if(!writeEnabled())
+ {
+ reset();
+ return;
+ }
+
+ bool anyMatch = false;
+
+ const auto d = _data & 0xff;
+
+ for (size_t i=0; i<m_commands.size(); ++i)
+ {
+ auto& cycles = m_commands[i].cycles;
+
+ if(m_currentBusCycle < cycles.size())
+ {
+ const auto& c = cycles[m_currentBusCycle];
+
+ if(c.addr == _addr && c.data == d)
+ {
+ anyMatch = true;
+
+ if(m_currentBusCycle == cycles.size() - 1)
+ m_currentCommand = static_cast<int32_t>(i);
+ }
+ }
+ }
+
+ if(!anyMatch)
+ {
+ if(m_currentCommand >= 0)
+ {
+ const auto c = static_cast<CommandType>(m_currentCommand);
+
+ execCommand(c, _addr, _data);
+ }
+
+ reset();
+ }
+ else
+ {
+ ++m_currentBusCycle;
+ }
+}
+
+void Am29f::execCommand(const CommandType _command, uint32_t _addr, const uint16_t _data)
+{
+ switch (_command)
+ {
+ case CommandType::ChipErase:
+ assert(false);
+ break;
+ case CommandType::SectorErase:
+ {
+ size_t sectorSizekB = 0;
+ switch (_addr)
+ {
+ case 0x00000: sectorSizekB = 16; break;
+ case 0x04000:
+ case 0x06000: sectorSizekB = 8; break;
+ case 0x08000: sectorSizekB = 32; break;
+ case 0x10000:
+ case 0x20000:
+ case 0x30000:
+ case 0x40000:
+ case 0x50000:
+ case 0x60000:
+ case 0x70000: sectorSizekB = 64; break;
+ case 0x78000:
+ case 0x7A000:
+ case 0x7C000:
+ // mq sends erase commands for a flash with top boot block even though a chip with bottom boot block is installed
+ _addr = 0x70000;
+ sectorSizekB = 64;
+ break;
+ default:
+ MCLOG("Unable to erase sector at " << MCHEX(_addr) << ", out of bounds!");
+ return;
+ }
+
+ MCLOG("Erasing Sector at " << MCHEX(_addr) << ", size " << MCHEX(1024 * sectorSizekB));
+ for(size_t i = _addr; i< _addr + sectorSizekB * 1024; ++i)
+ m_buffer[i] = 0xff;
+ }
+ break;
+ case CommandType::Program:
+ {
+ if(_addr >= m_size)
+ return;
+ MCLOG("Programming word at " << MCHEX(_addr) << ", value " << MCHEXN(_data, 4));
+ const auto old = mqLib::MqMc::readW(m_buffer, _addr);
+ // "A bit cannot be programmed from a 0 back to a 1"
+ const auto v = _data & old;
+ mqLib::MqMc::writeW(m_buffer, _addr, v);
+// assert(v == _data);
+ break;
+ }
+ case CommandType::Invalid:
+ default:
+ assert(false);
+ break;
+ }
+}
diff --git a/source/mqLib/am29f.h b/source/mqLib/am29f.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <cstdint>
+#include <cstddef>
+#include <vector>
+
+class Am29f
+{
+public:
+ struct BusCycle
+ {
+ uint16_t addr;
+ uint8_t data;
+ };
+
+ struct Command
+ {
+ std::vector<BusCycle> cycles;
+ };
+
+ enum class CommandType
+ {
+ Invalid = -1,
+ ChipErase,
+ SectorErase,
+ Program,
+ };
+
+ explicit Am29f(uint8_t* _buffer, size_t _size, bool _useWriteEnable, bool _bitreversedCmdAddr);
+
+ void writeEnable(bool _writeEnable)
+ {
+ m_writeEnable = _writeEnable;
+ }
+
+ void write(uint32_t _addr, uint16_t _data);
+
+private:
+ bool writeEnabled() const
+ {
+ return !m_useWriteEnable || m_writeEnable;
+ }
+
+ static constexpr uint16_t bitreverse(uint16_t x)
+ {
+ x = ((x & 0xaaaau) >> 1) | static_cast<uint16_t>((x & 0x5555u) << 1);
+ x = ((x & 0xccccu) >> 2) | static_cast<uint16_t>((x & 0x3333u) << 2);
+ x = ((x & 0xf0f0u) >> 4) | static_cast<uint16_t>((x & 0x0f0fu) << 4);
+
+ return ((x & 0xff00) >> 8) | static_cast<uint16_t>((x & 0x00ff) << 8);
+ }
+
+ void execCommand(CommandType _command, uint32_t _addr, uint16_t _data);
+
+ uint8_t* m_buffer;
+ const size_t m_size;
+ const bool m_useWriteEnable;
+ const bool m_bitreverseCmdAddr;
+
+ std::vector<Command> m_commands;
+ bool m_writeEnable = false;
+ uint32_t m_currentBusCycle = 0;
+ int32_t m_currentCommand = -1;
+};
diff --git a/source/mqLib/bin2h.cmake b/source/mqLib/bin2h.cmake
@@ -0,0 +1,83 @@
+include(CMakeParseArguments)
+
+# Function to wrap a given string into multiple lines at the given column position.
+# Parameters:
+# VARIABLE - The name of the CMake variable holding the string.
+# AT_COLUMN - The column position at which string will be wrapped.
+function(WRAP_STRING)
+ set(oneValueArgs VARIABLE AT_COLUMN)
+ cmake_parse_arguments(WRAP_STRING "${options}" "${oneValueArgs}" "" ${ARGN})
+
+ string(LENGTH ${${WRAP_STRING_VARIABLE}} stringLength)
+ math(EXPR offset "0")
+
+ while(stringLength GREATER 0)
+
+ if(stringLength GREATER ${WRAP_STRING_AT_COLUMN})
+ math(EXPR length "${WRAP_STRING_AT_COLUMN}")
+ else()
+ math(EXPR length "${stringLength}")
+ endif()
+
+ string(SUBSTRING ${${WRAP_STRING_VARIABLE}} ${offset} ${length} line)
+ set(lines "${lines}\n${line}")
+
+ math(EXPR stringLength "${stringLength} - ${length}")
+ math(EXPR offset "${offset} + ${length}")
+ endwhile()
+
+ set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE)
+endfunction()
+
+# Function to embed contents of a file as byte array in C/C++ header file(.h). The header file
+# will contain a byte array and integer variable holding the size of the array.
+# Parameters
+# SOURCE_FILE - The path of source file whose contents will be embedded in the header file.
+# VARIABLE_NAME - The name of the variable for the byte array. The string "_SIZE" will be append
+# to this name and will be used a variable name for size variable.
+# HEADER_FILE - The path of header file.
+# APPEND - If specified appends to the header file instead of overwriting it
+# NULL_TERMINATE - If specified a null byte(zero) will be append to the byte array. This will be
+# useful if the source file is a text file and we want to use the file contents
+# as string. But the size variable holds size of the byte array without this
+# null byte.
+# Usage:
+# bin2h(SOURCE_FILE "Logo.png" HEADER_FILE "Logo.h" VARIABLE_NAME "LOGO_PNG")
+function(BIN2H)
+ set(options APPEND NULL_TERMINATE)
+ set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE)
+ cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN})
+
+ # reads source file contents as hex string
+ file(READ ${BIN2H_SOURCE_FILE} hexString HEX)
+ string(LENGTH ${hexString} hexStringLength)
+
+ # appends null byte if asked
+ if(BIN2H_NULL_TERMINATE)
+ set(hexString "${hexString}00")
+ endif()
+
+ # wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line)
+ wrap_string(VARIABLE hexString AT_COLUMN 32)
+ math(EXPR arraySize "${hexStringLength} / 2")
+
+ # adds '0x' prefix and comma suffix before and after every byte respectively
+ string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString})
+ # removes trailing comma
+ string(REGEX REPLACE ", $" "" arrayValues ${arrayValues})
+
+ # converts the variable name into proper C identifier
+ string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
+ string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
+
+ # declares byte array and the length variables
+ set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };")
+ set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};")
+
+ set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n")
+ if(BIN2H_APPEND)
+ file(APPEND ${BIN2H_HEADER_FILE} "${declarations}")
+ else()
+ file(WRITE ${BIN2H_HEADER_FILE} "${declarations}")
+ endif()
+endfunction()
+\ No newline at end of file
diff --git a/source/mqLib/buttons.cpp b/source/mqLib/buttons.cpp
@@ -0,0 +1,125 @@
+#include "buttons.h"
+
+#include "mqtypes.h"
+
+#include "../mc68k/port.h"
+
+namespace mqLib
+{
+ Buttons::Buttons()
+ {
+ m_buttonStates.fill(0);
+ }
+
+ bool Buttons::processButtons(mc68k::Port& _gp, mc68k::Port& _e)
+ {
+ if(_gp.getDirection() == 0xff)
+ return false;
+
+ const auto w = _e.getWriteCounter();
+
+ const bool cycleEncoders = w != m_writeCounter;
+ m_writeCounter = w;
+
+ if (getButtonState(ButtonType::Power) && !_e.bittest(BtPower))
+ {
+ _e.setBitRX(BtPower);
+ }
+ else if (!getButtonState(ButtonType::Power) && _e.bittest(BtPower))
+ {
+ _e.clearBitRX(BtPower);
+ }
+
+ if(!_e.bittest(Buttons0CS))
+ {
+ uint8_t res = 0;
+ for(size_t i=0; i<8; ++i)
+ res |= m_buttonStates[i] << i;
+ _gp.writeRX(res);
+ return true;
+ }
+
+ if(!_e.bittest(Buttons1CS))
+ {
+ uint8_t res = 0;
+ for(size_t i=0; i<8; ++i)
+ res |= m_buttonStates[i+8] << i;
+ _gp.writeRX(res);
+ return true;
+ }
+
+ if(!_e.bittest(Encoders0CS))
+ {
+ uint8_t res = 0;
+
+ for(size_t i=0; i<4; ++i)
+ {
+ res |= processStepEncoder(static_cast<Encoders>(i), cycleEncoders) << (i<<1);
+ }
+ _gp.writeRX(res);
+ return true;
+ }
+
+ if(!_e.bittest(Encoders1CS))
+ {
+ uint8_t res = 0;
+
+ for(size_t i=4; i<static_cast<uint32_t>(Encoders::Master); ++i)
+ {
+ res |= processStepEncoder(static_cast<Encoders>(i), cycleEncoders) << ((i-4)<<1);
+ }
+
+ res |= processStepEncoder(Encoders::Master, cycleEncoders) << ((static_cast<uint32_t>(Encoders::Master)-4)<<1);
+
+ _gp.writeRX(res);
+ return true;
+ }
+
+ return false;
+ }
+
+ void Buttons::setButton(ButtonType _type, bool _pressed)
+ {
+ m_buttonStates[static_cast<uint32_t>(_type)] = _pressed;
+ }
+
+ void Buttons::toggleButton(ButtonType _type)
+ {
+ m_buttonStates[static_cast<uint32_t>(_type)] ^= 1;
+ }
+
+ void Buttons::rotate(Encoders _encoder, const int _amount)
+ {
+ auto& current = m_remainingRotations[static_cast<uint32_t>(_encoder)];
+
+ // The master encoder is a step encoder where 4 steps (one full cycle) is 1 tick
+ // The other encoders do not have any snapping, there are no ticks and there is some acceleration going on in the uc so use the smallest value that does something useful
+ current += _amount;// * (_encoder == Encoders::Master ? 4 : 3);
+ }
+
+ uint8_t Buttons::processStepEncoder(Encoders _encoder, bool cycleEncoders)
+ {
+ const auto i = static_cast<uint32_t>(_encoder);
+
+ auto& v = m_encoderValues[i];
+
+ constexpr uint8_t pattern[] = {0b00, 0b10, 0b11, 0b01};
+
+ if(!cycleEncoders)
+ return pattern[v&3];
+
+ auto& c = m_remainingRotations[i];
+
+ if(c > 0)
+ {
+ --v;
+ --c;
+ }
+ else if(c < 0)
+ {
+ ++v;
+ ++c;
+ }
+ return pattern[v&3];
+ }
+}
diff --git a/source/mqLib/buttons.h b/source/mqLib/buttons.h
@@ -0,0 +1,80 @@
+#pragma once
+
+#include <array>
+#include <cstdint>
+
+namespace mc68k
+{
+ class Port;
+}
+
+namespace mqLib
+{
+ class Buttons
+ {
+ public:
+ enum class ButtonType
+ {
+ Inst1,
+ Inst2,
+ Inst3,
+ Inst4,
+ Down,
+ Left,
+ Right,
+ Up,
+
+ Global,
+ Multi,
+ Edit,
+ Sound,
+ Shift,
+ Multimode,
+ Peek,
+ Play,
+
+ Power,
+
+ Count
+ };
+
+ enum class Encoders
+ {
+ Matrix4,
+ Matrix3,
+ Matrix2,
+ Matrix1,
+ LcdRight,
+ LcdLeft,
+ Master,
+
+ Count
+ };
+
+ Buttons();
+
+ bool processButtons(mc68k::Port& _gp, mc68k::Port& _e);
+
+ void setButton(ButtonType _type, bool _pressed);
+ void toggleButton(ButtonType _type);
+ void rotate(Encoders _encoder, int _amount);
+
+ uint8_t getButtonState(ButtonType _button) const
+ {
+ return m_buttonStates[static_cast<uint32_t>(_button)];
+ }
+
+ uint8_t getEncoderState(Encoders _encoder) const
+ {
+ return m_encoderValues[static_cast<uint32_t>(_encoder)];
+ }
+
+ private:
+ uint8_t processStepEncoder(Encoders _encoder, bool cycleEncoders);
+
+ std::array<uint8_t, static_cast<uint32_t>(ButtonType::Count)> m_buttonStates{};
+ std::array<int, static_cast<uint32_t>(Encoders::Count)> m_remainingRotations{};
+ std::array<uint8_t, static_cast<uint32_t>(Encoders::Count)> m_encoderValues{};
+ uint32_t m_writeCounter = 0;
+ };
+}
diff --git a/source/mqLib/device.cpp b/source/mqLib/device.cpp
@@ -0,0 +1,158 @@
+#include "device.h"
+
+#include "mqmiditypes.h"
+
+#include <cstring>
+
+#include "../synthLib/deviceTypes.h"
+
+#include "mqbuildconfig.h"
+#include "mqhardware.h"
+
+namespace mqLib
+{
+ Device::Device() : m_mq(BootMode::Default), m_state(m_mq), m_sysexRemote(m_mq)
+ {
+ // we need to hit the play button to resume boot if the used rom is an OS update. mQ will complain about an uninitialized ROM area in this case
+ m_mq.setButton(Buttons::ButtonType::Play, true);
+ while(!m_mq.isBootCompleted())
+ m_mq.process(8);
+ m_mq.setButton(Buttons::ButtonType::Play, false);
+
+ m_state.createInitState();
+
+ auto* hw = m_mq.getHardware();
+ hw->resetMidiCounter();
+ }
+
+ Device::~Device() = default;
+
+ uint32_t Device::getInternalLatencyMidiToOutput() const
+ {
+ return 0;
+ }
+
+ uint32_t Device::getInternalLatencyInputToOutput() const
+ {
+ return 0;
+ }
+
+ float Device::getSamplerate() const
+ {
+ return 44100.0f;
+ }
+
+ bool Device::isValid() const
+ {
+ return m_mq.isValid();
+ }
+
+ bool Device::getState(std::vector<uint8_t>& _state, synthLib::StateType _type)
+ {
+ return m_state.getState(_state, _type);
+ }
+
+ bool Device::setState(const std::vector<uint8_t>& _state, synthLib::StateType _type)
+ {
+ if constexpr (g_pluginDemo)
+ return false;
+
+ return m_state.setState(_state, _type);
+ }
+
+ uint32_t Device::getChannelCountIn()
+ {
+ return 2;
+ }
+
+ uint32_t Device::getChannelCountOut()
+ {
+ return 6;
+ }
+
+ void Device::readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut)
+ {
+ m_mq.receiveMidi(m_midiOutBuffer);
+ m_midiOutParser.write(m_midiOutBuffer);
+ m_midiOutParser.getEvents(_midiOut);
+ m_midiOutBuffer.clear();
+
+ Responses responses;
+
+ for (const auto& midiOut : _midiOut)
+ {
+ m_state.receive(responses, midiOut, State::Origin::Device);
+
+ for (auto& response : responses)
+ {
+ auto& r = _midiOut.emplace_back();
+ std::swap(response, r.sysex);
+ }
+ }
+
+ if(!m_customSysexOut.empty())
+ {
+ _midiOut.insert(_midiOut.begin(), m_customSysexOut.begin(), m_customSysexOut.end());
+ m_customSysexOut.clear();
+ }
+ }
+
+ void Device::processAudio(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, const size_t _samples)
+ {
+ const float* inputs[2] = {_inputs[0], _inputs[1]};
+ float* outputs[6] = {_outputs[0], _outputs[1], _outputs[2], _outputs[3], _outputs[4], _outputs[5]};
+
+ m_mq.process(inputs, outputs, static_cast<uint32_t>(_samples), getExtraLatencySamples());
+
+ const auto dirty = static_cast<uint32_t>(m_mq.getDirtyFlags());
+
+ m_sysexRemote.handleDirtyFlags(m_customSysexOut, dirty);
+ }
+
+ void Device::process(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, size_t _size, const std::vector<synthLib::SMidiEvent>& _midiIn, std::vector<synthLib::SMidiEvent>& _midiOut)
+ {
+ synthLib::Device::process(_inputs, _outputs, _size, _midiIn, _midiOut);
+
+ m_numSamplesProcessed += static_cast<uint32_t>(_size);
+ }
+
+ bool Device::sendMidi(const synthLib::SMidiEvent& _ev, std::vector<synthLib::SMidiEvent>& _response)
+ {
+ const auto& sysex = _ev.sysex;
+
+ if (!sysex.empty())
+ {
+ if (m_sysexRemote.receive(m_customSysexOut, sysex))
+ return true;
+ }
+
+ Responses responses;
+
+ const auto res = m_state.receive(responses, _ev, State::Origin::External);
+
+ for (auto& response : responses)
+ {
+ auto& r = _response.emplace_back();
+ std::swap(response, r.sysex);
+ }
+
+ // do not forward to device if our cache was able to reply. It might have sent something to the device already on its own if a cache miss occured
+ if(res)
+ return true;
+
+ if(_ev.sysex.empty())
+ {
+ auto e = _ev;
+ e.offset += m_numSamplesProcessed + getExtraLatencySamples();
+ m_mq.sendMidiEvent(e);
+ }
+ else
+ {
+ m_mq.sendMidiEvent(_ev);
+ }
+
+
+ return true;
+ }
+
+}
diff --git a/source/mqLib/device.h b/source/mqLib/device.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include "microq.h"
+#include "mqstate.h"
+#include "mqsysexremotecontrol.h"
+
+#include "../synthLib/device.h"
+#include "../synthLib/midiBufferParser.h"
+
+namespace mqLib
+{
+ class Device : public synthLib::Device
+ {
+ public:
+ Device();
+ ~Device() override;
+ uint32_t getInternalLatencyMidiToOutput() const override;
+ uint32_t getInternalLatencyInputToOutput() const override;
+ float getSamplerate() const override;
+ bool isValid() const override;
+ bool getState(std::vector<uint8_t>& _state, synthLib::StateType _type) override;
+ bool setState(const std::vector<uint8_t>& _state, synthLib::StateType _type) override;
+ uint32_t getChannelCountIn() override;
+ uint32_t getChannelCountOut() override;
+
+ protected:
+ void readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut) override;
+ void processAudio(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, size_t _samples) override;
+ void process(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, size_t _size, const std::vector<synthLib::SMidiEvent>& _midiIn, std::vector<synthLib::SMidiEvent>& _midiOut) override;
+ bool sendMidi(const synthLib::SMidiEvent& _ev, std::vector<synthLib::SMidiEvent>& _response) override;
+
+ private:
+ MicroQ m_mq;
+ State m_state;
+ SysexRemoteControl m_sysexRemote;
+ std::vector<uint8_t> m_midiOutBuffer;
+ synthLib::MidiBufferParser m_midiOutParser;
+ std::vector<synthLib::SMidiEvent> m_customSysexOut;
+ uint32_t m_numSamplesProcessed = 0;
+ };
+}
diff --git a/source/mqLib/dspBootCode.h b/source/mqLib/dspBootCode.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <cstdint>
+
+namespace mqLib
+{
+ static constexpr uint32_t g_dspBootCode[] =
+ {
+ 0x350013, 0x0afa23, 0xff0035, 0x0afa22,
+ 0xff000e, 0x0afa01, 0xff0022, 0x0afa20,
+ 0xff005e, 0x61f400, 0xff1000, 0x050c8f,
+ 0x0afa00, 0xff0021, 0x31a900, 0x0afa01,
+ 0xff0012, 0x0ad161, 0x04d191, 0x019191,
+ 0xff0013, 0x044894, 0x019191, 0xff0016,
+ 0x045094, 0x221100, 0x06c800, 0xff001f,
+ 0x019191, 0xff001c, 0x009814, 0x000000,
+ 0x050c5a, 0x050c5d, 0x62f400, 0xd00000,
+ 0x08f4b8, 0xd00409, 0x060680, 0xff0029,
+ 0x07da8a, 0x0c1c10, 0x219000, 0x219100,
+ 0x06c800, 0xff0033, 0x060380, 0xff0031,
+ 0x07da8a, 0x0c1c10, 0x07588c, 0x000000,
+ 0x050c46, 0x0afa02, 0xff005c, 0x0afa01,
+ 0xff003e, 0x0afa00, 0xff0046, 0x08f484,
+ 0x000038, 0x050c0b, 0x0afa20, 0xff0043,
+ 0x08f484, 0x005018, 0x050c06, 0x08f484,
+ 0x000218, 0x050c03, 0x08f484, 0x001c1e,
+ 0x0a8426, 0x0a8380, 0xff0049, 0x084806,
+ 0x0a8380, 0xff004c, 0x085006, 0x221100,
+ 0x06c800, 0xff0059, 0x0a83a0, 0xff0058,
+ 0x0a8383, 0xff0052, 0x00008c, 0x050c03,
+ 0x085846, 0x000000, 0x0000b9, 0x0ae180,
+ 0x0afa01, 0xff005f, 0x050c00, 0x66f41b,
+ 0xff0090, 0x0503a6, 0x04cfdd, 0x013f03,
+ 0x013e23, 0x045517, 0x060980, 0xff008b,
+ 0x07de85, 0x07de84, 0x07de86, 0x300013,
+ 0x70f400, 0x001600, 0x06d820, 0x4258a2,
+ 0x320013, 0x72f400, 0x000c00, 0x06da20,
+ 0x075a86, 0x300013, 0x06d800, 0xff007d,
+ 0x54e000, 0x200063, 0x200018, 0x5cd800,
+ 0x200043, 0x200018, 0x320013, 0x06da00,
+ 0xff0083, 0x07da8c, 0x200053, 0x200018,
+ 0x022d07, 0x08d73c, 0x0d104a, 0x000005,
+ 0x013d03, 0x00008c, 0x050c02, 0x017d03,
+ 0x000200, 0x000086
+ };
+}
diff --git a/source/mqLib/lcd.cpp b/source/mqLib/lcd.cpp
@@ -0,0 +1,238 @@
+#include "lcd.h"
+
+#include "mqtypes.h"
+
+#include <cassert>
+
+#include "../mc68k/port.h"
+
+#include "dsp56kEmu/logging.h"
+
+namespace mqLib
+{
+ LCD::LCD() = default;
+
+ bool LCD::exec(mc68k::Port& _portGp, mc68k::Port& _portF)
+ {
+ if(_portF.getWriteCounter() == m_lastWriteCounter)
+ return false;
+
+ m_lastWriteCounter = _portF.getWriteCounter();
+
+ const auto f = _portF.read();
+ const auto g = _portGp.read();
+ const auto df = _portF.getDirection();
+ //const auto dg = _portGp.getDirection();
+
+ const auto registerSelect = (f>>LcdRS) & 1;
+ const auto read = (f>>LcdRW) & 1;
+ const auto opEnable = ((f & df)>>LcdLatch) & 1;
+
+ // falling edge triggered
+ const auto execute = m_lastOpState && !opEnable;
+
+ m_lastOpState = opEnable;
+
+ if(!execute)
+ return false;
+
+ bool changed = false;
+ bool cgRamChanged = false;
+
+ if(!read)
+ {
+ if(!registerSelect)
+ {
+ if(g == 0x01)
+ {
+ LOG("LCD Clear Display");
+ m_dramData.fill(' ');
+ changed = true;
+ }
+ else if(g == 0x02)
+ {
+ LOG("LCD Return Home");
+ m_dramAddr = 0;
+ m_cursorPos = 0;
+ }
+ else if((g & 0xfc) == 0x04)
+ {
+ const int increment = (g >> 1) & 1;
+ const int shift = g & 1;
+ LOG("LCD Entry Mode Set, inc=" << increment << ", shift=" << shift);
+
+ m_addrIncrement = increment ? 1 : -1;
+ }
+ else if((g & 0xf8) == 0x08)
+ {
+ const int displayOnOff = (g >> 2) & 1;
+ const int cursorOnOff = (g >> 1) & 1;
+ const int cursorBlinking = g & 1;
+
+ LOG("LCD Display ON/OFF, display=" << displayOnOff << ", cursor=" << cursorOnOff << ", blinking=" << cursorBlinking);
+
+ m_displayOn = displayOnOff != 0;
+ m_cursorOn = cursorOnOff != 0;
+ m_cursorBlinking = cursorBlinking != 0;
+ }
+ else if((g & 0xf3) == 0x10)
+ {
+ const int scrl = (g >> 2) & 3;
+
+ LOG("LCD Cursor/Display Shift, scrl=" << scrl);
+ m_cursorShift = static_cast<CursorShiftMode>(scrl);
+ }
+ else if((g & 0xec) == 0x28)
+ {
+ const int dl = (g >> 4) & 1;
+ const int ft = g & 3;
+
+ LOG("LCD Function Set, dl=" << dl << ", ft=" << ft);
+ m_dataLength = static_cast<DataLength>(dl);
+ m_fontTable = static_cast<FontTable>(ft);
+ }
+ else if(g & (1<<7))
+ {
+ const int addr = g & 0x7f;
+// LOG("LCD Set DDRAM address, addr=" << addr);
+ m_dramAddr = addr;
+ m_addressMode = AddressMode::DDRam;
+ }
+ else if(g & (1<<6))
+ {
+ const int acg = g & 0x3f;
+
+// LOG("LCD Set CGRAM address, acg=" << acg);
+ m_cgramAddr = acg;
+ m_addressMode = AddressMode::CGRam;
+ }
+ else
+ {
+ LOG("LCD unknown command");
+ assert(false);
+ }
+ }
+ else
+ {
+ if(m_addressMode == AddressMode::CGRam)
+ {
+// changed = true;
+// LOG("LCD write data to CGRAM addr " << m_cgramAddr << ", data=" << static_cast<int>(g));
+
+ if (m_cgramData[m_cgramAddr] != g)
+ {
+ m_cgramData[m_cgramAddr] = g;
+ cgRamChanged = true;
+ }
+
+ m_cgramAddr += m_addrIncrement;
+
+ if((false && (m_cgramAddr & 0x7) == 0))
+ {
+ std::stringstream ss;
+ ss << "CG RAM character " << (m_cgramAddr/8 - 1) << ':' << std::endl;
+ ss << "##################" << std::endl;
+ for(auto i = m_cgramAddr - 8; i < m_cgramAddr - 1; ++i)
+ {
+ ss << '#';
+ for(int x=7; x >= 0; --x)
+ {
+ if(m_cgramData[i] & (1<<x))
+ ss << "[]";
+ else
+ ss << " ";
+ }
+ ss << '#' << std::endl;
+ }
+ ss << "##################" << std::endl;
+ const auto s(ss.str());
+ LOG(s);
+ }
+ }
+ else
+ {
+// LOG("LCD write data to DDRAM addr " << m_dramAddr << ", data=" << static_cast<int>(g) << ", char=" << static_cast<char>(g));
+
+ const auto old = m_dramData;
+
+ if(m_dramAddr >= 20 && m_dramAddr < 0x40)
+ {
+ for(size_t i=1; i<=20; ++i)
+ m_dramData[i-1] = m_dramData[i];
+ m_dramData[19] = g;
+ }
+ else if(m_dramAddr > 0x53)
+ {
+ for(size_t i=21; i<=40; ++i)
+ m_dramData[i-1] = m_dramData[i];
+
+ m_dramData[39] = g;
+ }
+ else
+ {
+ if(m_dramAddr < 20)
+ m_dramData[m_dramAddr] = g;
+ else
+ m_dramData[m_dramAddr - 0x40 + 20] = g;
+ }
+
+ if(m_dramAddr != 20 && m_dramAddr != 0x54)
+ m_dramAddr += m_addrIncrement;
+
+ if(m_dramData != old)
+ changed = true;
+ }
+ }
+ }
+ else
+ {
+ if(registerSelect)
+ {
+ LOG("LCD read data from CGRAM or DDRAM");
+ if(m_addressMode == AddressMode::CGRam)
+ _portGp.writeRX(m_cgramData[m_cgramAddr]);
+ else
+ _portGp.writeRX(m_dramData[m_dramAddr]);
+ }
+ else
+ {
+ LOG("LCD read busy flag & address");
+ if(m_addressMode == AddressMode::CGRam)
+ {
+ _portGp.writeRX(static_cast<uint8_t>(m_cgramAddr));
+ }
+ else
+ {
+ auto a = m_dramAddr;
+ if(a > 0x53)
+ a = 0x53;
+ if(a == 20)
+ a = 19;
+ _portGp.writeRX(static_cast<uint8_t>(m_dramAddr));
+ }
+ }
+ }
+
+ if(changed && m_changeCallback)
+ m_changeCallback();
+
+ if(cgRamChanged && m_cgRamChangeCallback)
+ m_cgRamChangeCallback();
+
+ return true;
+ }
+
+ bool LCD::getCgData(std::array<uint8_t, 8>& _data, uint32_t _charIndex) const
+ {
+ const auto idx = _charIndex * 8;
+ if(idx + 8 >= getCgRam().size())
+ return false;
+
+ uint32_t j = 0;
+
+ for(auto i = idx; i<idx+8; ++i)
+ _data[j++] = getCgRam()[i];
+
+ return true;
+ }
+}
diff --git a/source/mqLib/lcd.h b/source/mqLib/lcd.h
@@ -0,0 +1,92 @@
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <functional>
+
+namespace mc68k
+{
+ class Port;
+}
+
+namespace mqLib
+{
+ class LCD
+ {
+ public:
+ using ChangeCallback = std::function<void()>;
+
+ LCD();
+ bool exec(mc68k::Port& _portGp, mc68k::Port& _portF);
+
+ const std::array<char, 40>& getDdRam() const { return m_dramData; }
+ const auto& getCgRam() const { return m_cgramData; }
+ bool getCgData(std::array<uint8_t, 8>& _data, uint32_t _charIndex) const;
+
+ void setChangeCallback(const ChangeCallback& _callback)
+ {
+ m_changeCallback = _callback;
+ }
+
+ void setCgRamChangeCallback(const ChangeCallback& _callback)
+ {
+ m_cgRamChangeCallback = _callback;
+ }
+ private:
+ enum class CursorShiftMode
+ {
+ CursorLeft,
+ CursorRight,
+ DisplayLeft,
+ DisplayRight
+ };
+ enum class DisplayShiftMode
+ {
+ Right,
+ Left
+ };
+ enum class FontTable
+ {
+ EnglishJapanese,
+ WesternEuropean1,
+ EnglishRussian,
+ WesternEuropean2,
+ };
+ enum class DataLength
+ {
+ Bit8,
+ Bit4
+ };
+
+ enum class AddressMode
+ {
+ DDRam,
+ CGRam,
+ };
+
+ uint32_t m_lastWriteCounter = 0xffffffff;
+
+ uint32_t m_cursorPos = 0;
+ uint32_t m_dramAddr = 0;
+ uint32_t m_cgramAddr = 0;
+
+ CursorShiftMode m_cursorShift = CursorShiftMode::CursorLeft;
+ DisplayShiftMode m_displayShift = DisplayShiftMode::Left;
+ FontTable m_fontTable = FontTable::EnglishJapanese;
+ DataLength m_dataLength = DataLength::Bit8;
+ AddressMode m_addressMode = AddressMode::DDRam;
+
+ bool m_displayOn = true;
+ bool m_cursorOn = false;
+ bool m_cursorBlinking = false;
+
+ int m_addrIncrement = 1;
+
+ std::array<uint8_t, 0x40> m_cgramData{};
+ std::array<char, 40> m_dramData{};
+ uint32_t m_lastOpState = 0;
+
+ ChangeCallback m_changeCallback;
+ ChangeCallback m_cgRamChangeCallback;
+ };
+}
diff --git a/source/mqLib/lcdfonts.cpp b/source/mqLib/lcdfonts.cpp
@@ -0,0 +1,2673 @@
+#include <cstdint>
+#include <array>
+
+namespace mqLib
+{
+ constexpr uint8_t g_fontTable0[] =
+ {
+ // CGRam Data, initially empty
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 0
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 1
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 2
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 3
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 4
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 5
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 6
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 7
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 8
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 9
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 a
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 b
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 c
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 d
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 e
+ 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, // 0 f
+
+ 0b00000, // 1 0
+ 0b10001,
+ 0b01001,
+ 0b01110,
+ 0b10010,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 1
+ 0b11110,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 2
+ 0b00110,
+ 0b00010,
+ 0b00110,
+ 0b01010,
+ 0b01010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 3
+ 0b11111,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 4
+ 0b11111,
+ 0b00001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 5
+ 0b00110,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 6
+ 0b01110,
+ 0b00100,
+ 0b01000,
+ 0b00100,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 7
+ 0b11111,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 8
+ 0b10111,
+ 0b10101,
+ 0b10101,
+ 0b10001,
+ 0b01111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 9
+ 0b00110,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 a
+ 0b01111,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 b
+ 0b11110,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10000, // 1 c
+ 0b11111,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 d
+ 0b11111,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 e
+ 0b10110,
+ 0b01001,
+ 0b10001,
+ 0b10001,
+ 0b10111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 1 f
+ 0b00110,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 2 0
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 2 1 !
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01010, // 2 2 "
+ 0b01010,
+ 0b01010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01010, // 2 3 #
+ 0b01010,
+ 0b11111,
+ 0b01010,
+ 0b11111,
+ 0b01010,
+ 0b01010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 2 4 $
+ 0b01111,
+ 0b10100,
+ 0b01110,
+ 0b00101,
+ 0b11110,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11000, // 2 5 %
+ 0b11001,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b10011,
+ 0b00011,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01100, // 2 6 &
+ 0b10010,
+ 0b10100,
+ 0b01000,
+ 0b10101,
+ 0b10010,
+ 0b01101,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01100, // 2 7 '
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00010, // 2 8 (
+ 0b00100,
+ 0b01000,
+ 0b01000,
+ 0b01000,
+ 0b00100,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // 2 9 )
+ 0b00100,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 2 a *
+ 0b00100,
+ 0b10101,
+ 0b01110,
+ 0b10101,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 2 b +
+ 0b00100,
+ 0b00100,
+ 0b11111,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 2 c ,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b01100,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 2 d -
+ 0b00000,
+ 0b00000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 2 e .
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b01100,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 2 f /
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b10000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 3 0 0
+ 0b10001,
+ 0b10011,
+ 0b10101,
+ 0b11001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 3 1 1
+ 0b01100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 3 2 2
+ 0b10001,
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // 3 3 3
+ 0b00010,
+ 0b00100,
+ 0b00010,
+ 0b00001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00010, // 3 4 4
+ 0b00110,
+ 0b01010,
+ 0b10010,
+ 0b11111,
+ 0b00010,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // 3 5 5
+ 0b10000,
+ 0b11110,
+ 0b00001,
+ 0b00001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00110, // 3 6 6
+ 0b01000,
+ 0b10000,
+ 0b11110,
+ 0b10001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // 3 7 7
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b01000,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 3 8 8
+ 0b10001,
+ 0b10001,
+ 0b01110,
+ 0b10001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 3 9 9
+ 0b10001,
+ 0b10001,
+ 0b01111,
+ 0b00001,
+ 0b00010,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 3 a :
+ 0b01100,
+ 0b01100,
+ 0b00000,
+ 0b01100,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 3 b ;
+ 0b01100,
+ 0b01100,
+ 0b00000,
+ 0b01100,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00010, // 3 c <
+ 0b00100,
+ 0b01000,
+ 0b10000,
+ 0b01000,
+ 0b00100,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 3 d =
+ 0b00000,
+ 0b11111,
+ 0b00000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // 3 e >
+ 0b00100,
+ 0b00010,
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 3 f ?
+ 0b10001,
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b00000,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 4 0 @
+ 0b10001,
+ 0b00001,
+ 0b01101,
+ 0b10101,
+ 0b10101,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 4 1 A
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b11111,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11110, // 4 2 B
+ 0b10001,
+ 0b10001,
+ 0b11110,
+ 0b10001,
+ 0b10001,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 4 3 C
+ 0b10001,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11100, // 4 4 D
+ 0b10010,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10010,
+ 0b11100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // 4 5 E
+ 0b10000,
+ 0b10000,
+ 0b11110,
+ 0b10000,
+ 0b10000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // 4 6 F
+ 0b10000,
+ 0b10000,
+ 0b11110,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 4 7 G
+ 0b10001,
+ 0b10000,
+ 0b10111,
+ 0b10001,
+ 0b10001,
+ 0b01111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10001, // 4 8 H
+ 0b10001,
+ 0b10001,
+ 0b11111,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 4 9 I
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00111, // 4 a J
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b10010,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10001, // 4 b K
+ 0b10010,
+ 0b10100,
+ 0b11000,
+ 0b10100,
+ 0b10010,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10000, // 4 c L
+ 0b10000,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10001, // 4 d M
+ 0b11011,
+ 0b10101,
+ 0b10101,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10001, // 4 e N
+ 0b10001,
+ 0b11001,
+ 0b10101,
+ 0b10011,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 4 f O
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11110, // 5 0 P
+ 0b10001,
+ 0b10001,
+ 0b11110,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 5 1 Q
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10101,
+ 0b10010,
+ 0b01101,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11110, // 5 2 R
+ 0b10001,
+ 0b10001,
+ 0b11110,
+ 0b10100,
+ 0b10010,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01111, // 5 3 S
+ 0b10000,
+ 0b10000,
+ 0b01110,
+ 0b00001,
+ 0b00001,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // 5 4 T
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10001, // 5 5 U
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10001, // 5 6 V
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b01010,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10101, // 5 7 W
+ 0b10101,
+ 0b10101,
+ 0b10101,
+ 0b10101,
+ 0b10101,
+ 0b01010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10001, // 5 8 X
+ 0b10001,
+ 0b01010,
+ 0b00100,
+ 0b01010,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10001, // 5 9 Y
+ 0b10001,
+ 0b10001,
+ 0b01010,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // 5 a Z
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b10000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 5 b [
+ 0b01000,
+ 0b01000,
+ 0b01000,
+ 0b01000,
+ 0b01000,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10001, // 5 c
+ 0b01010,
+ 0b11111,
+ 0b00100,
+ 0b11111,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // 5 d ]
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 5 e ^
+ 0b01010,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 5 f _
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // 6 0 `
+ 0b00100,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 6 1 a
+ 0b00000,
+ 0b01110,
+ 0b00001,
+ 0b01111,
+ 0b10001,
+ 0b01111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10000, // 6 2 b
+ 0b10000,
+ 0b10110,
+ 0b11001,
+ 0b10001,
+ 0b10001,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 6 3 c
+ 0b00000,
+ 0b01110,
+ 0b10000,
+ 0b10000,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00001, // 6 4 d
+ 0b00001,
+ 0b01101,
+ 0b10011,
+ 0b10001,
+ 0b10001,
+ 0b01111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 6 5 e
+ 0b00000,
+ 0b01110,
+ 0b10001,
+ 0b11111,
+ 0b10000,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00110, // 6 6 f
+ 0b01001,
+ 0b01000,
+ 0b11100,
+ 0b01000,
+ 0b01000,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 6 7 g
+ 0b01111,
+ 0b10001,
+ 0b10001,
+ 0b01111,
+ 0b00001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10000, // 6 8 h
+ 0b10000,
+ 0b10110,
+ 0b11001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 6 9 i
+ 0b00000,
+ 0b01100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00010, // 6 a j
+ 0b00000,
+ 0b00110,
+ 0b00010,
+ 0b00010,
+ 0b10010,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10000, // 6 b k
+ 0b10000,
+ 0b10010,
+ 0b10100,
+ 0b11000,
+ 0b10100,
+ 0b10010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01100, // 6 c l
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 6 d m
+ 0b00000,
+ 0b11010,
+ 0b10101,
+ 0b10101,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 6 e n
+ 0b00000,
+ 0b10110,
+ 0b11001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 6 f o
+ 0b00000,
+ 0b01110,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 0 p
+ 0b00000,
+ 0b11110,
+ 0b10001,
+ 0b11110,
+ 0b10000,
+ 0b10000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 1 q
+ 0b00000,
+ 0b01101,
+ 0b10011,
+ 0b01111,
+ 0b00001,
+ 0b00001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 2 r
+ 0b00000,
+ 0b10110,
+ 0b11001,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 3 s
+ 0b00000,
+ 0b01110,
+ 0b10000,
+ 0b01110,
+ 0b00001,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // 7 4 t
+ 0b01000,
+ 0b11100,
+ 0b01000,
+ 0b01000,
+ 0b01001,
+ 0b00110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 5 u
+ 0b00000,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10011,
+ 0b01101,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 6 v
+ 0b00000,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b01010,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 7 w
+ 0b00000,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10101,
+ 0b01010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 8 x
+ 0b00000,
+ 0b10001,
+ 0b01010,
+ 0b00100,
+ 0b01010,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 9 y
+ 0b00000,
+ 0b10001,
+ 0b10001,
+ 0b01111,
+ 0b00001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 a z
+ 0b00000,
+ 0b11111,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00010, // 7 b {
+ 0b00100,
+ 0b00100,
+ 0b01000,
+ 0b00100,
+ 0b00100,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 7 c |
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // 7 d }
+ 0b00100,
+ 0b00100,
+ 0b00010,
+ 0b00100,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 e ->
+ 0b00100,
+ 0b00010,
+ 0b11111,
+ 0b00010,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 7 f <-
+ 0b00100,
+ 0b01000,
+ 0b11111,
+ 0b01000,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 0
+ 0b00110,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 1
+ 0b11111,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 2
+ 0b10001,
+ 0b10001,
+ 0b01001,
+ 0b00101,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 3
+ 0b01111,
+ 0b01001,
+ 0b01101,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 4
+ 0b11111,
+ 0b01001,
+ 0b01101,
+ 0b00001,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 5
+ 0b01001,
+ 0b00110,
+ 0b00010,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 6
+ 0b10001,
+ 0b01010,
+ 0b00100,
+ 0b00010,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 7
+ 0b11111,
+ 0b00001,
+ 0b10001,
+ 0b10110,
+ 0b10000,
+ 0b10000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 8
+ 0b11111,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 9
+ 0b10101,
+ 0b10101,
+ 0b10101,
+ 0b10101,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 a
+ 0b01111,
+ 0b01001,
+ 0b01001,
+ 0b01001,
+ 0b11001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 8 b
+ 0b01001,
+ 0b11101,
+ 0b00001,
+ 0b10001,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 c
+ 0b00010,
+ 0b00000,
+ 0b00010,
+ 0b00101,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 d
+ 0b00000,
+ 0b00000,
+ 0b00010,
+ 0b00101,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00010, // 8 e
+ 0b00100,
+ 0b01110,
+ 0b00000,
+ 0b00010,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 8 f
+ 0b00100,
+ 0b00000,
+ 0b00110,
+ 0b01000,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 9 0
+ 0b00101,
+ 0b11100,
+ 0b00000,
+ 0b00100,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 1
+ 0b00000,
+ 0b00100,
+ 0b01010,
+ 0b00001,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 2
+ 0b00010,
+ 0b00000,
+ 0b10011,
+ 0b10011,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 3
+ 0b00000,
+ 0b00000,
+ 0b00111,
+ 0b00101,
+ 0b11011,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 4
+ 0b01100,
+ 0b00010,
+ 0b01001,
+ 0b10101,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 9 5
+ 0b00000,
+ 0b01111,
+ 0b01001,
+ 0b00110,
+ 0b11001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 9 6
+ 0b01001,
+ 0b11101,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 7
+ 0b00101,
+ 0b00000,
+ 0b00010,
+ 0b00101,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 8
+ 0b00000,
+ 0b00000,
+ 0b01100,
+ 0b01010,
+ 0b01101,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 9
+ 0b01010,
+ 0b00000,
+ 0b01100,
+ 0b01010,
+ 0b01101,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 a
+ 0b00000,
+ 0b01111,
+ 0b01001,
+ 0b00110,
+ 0b11001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // 9 b
+ 0b00000,
+ 0b00100,
+ 0b01010,
+ 0b00001,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 c
+ 0b00101,
+ 0b00000,
+ 0b10011,
+ 0b10011,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 d
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // 9 e
+ 0b00000,
+ 0b00000,
+ 0b00100,
+ 0b01010,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00110, // 9 f
+ 0b11101,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a 0
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a 1
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b11100,
+ 0b10100,
+ 0b11100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00111, // a 2
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a 3
+ 0b00000,
+ 0b00000,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b11100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a 4
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b10000,
+ 0b01000,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a 5
+ 0b00000,
+ 0b00000,
+ 0b01100,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a 6
+ 0b11111,
+ 0b00001,
+ 0b11111,
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a 7
+ 0b00000,
+ 0b11111,
+ 0b00001,
+ 0b00110,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a 8
+ 0b00000,
+ 0b00010,
+ 0b00100,
+ 0b01100,
+ 0b10100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a 9
+ 0b00000,
+ 0b00100,
+ 0b11111,
+ 0b10001,
+ 0b00001,
+ 0b00110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a a
+ 0b00000,
+ 0b00000,
+ 0b11111,
+ 0b00100,
+ 0b00100,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a b
+ 0b00000,
+ 0b00010,
+ 0b11111,
+ 0b00110,
+ 0b01010,
+ 0b10010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a c
+ 0b00000,
+ 0b01000,
+ 0b11111,
+ 0b01001,
+ 0b01010,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a d
+ 0b00000,
+ 0b00000,
+ 0b01110,
+ 0b00010,
+ 0b00010,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a e
+ 0b00000,
+ 0b00000,
+ 0b11110,
+ 0b00010,
+ 0b11110,
+ 0b00010,
+ 0b11110,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // a f
+ 0b00000,
+ 0b00000,
+ 0b10101,
+ 0b10101,
+ 0b00001,
+ 0b00110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // b 0
+ 0b00000,
+ 0b00000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // b 1
+ 0b00001,
+ 0b00101,
+ 0b00110,
+ 0b00100,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00001, // b 2
+ 0b00010,
+ 0b00100,
+ 0b01100,
+ 0b10100,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // b 3
+ 0b11111,
+ 0b10001,
+ 0b10001,
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // b 4
+ 0b11111,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00010, // b 5
+ 0b11111,
+ 0b00010,
+ 0b00110,
+ 0b01010,
+ 0b10010,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // b 6
+ 0b11111,
+ 0b01001,
+ 0b01001,
+ 0b01001,
+ 0b01001,
+ 0b10010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // b 7
+ 0b11111,
+ 0b00100,
+ 0b11111,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // b 8
+ 0b01111,
+ 0b01001,
+ 0b10001,
+ 0b00001,
+ 0b00010,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // b 9
+ 0b01111,
+ 0b10010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // b a
+ 0b11111,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01010, // b b
+ 0b11111,
+ 0b01010,
+ 0b01010,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // b c
+ 0b11000,
+ 0b00001,
+ 0b11001,
+ 0b00001,
+ 0b00010,
+ 0b11100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // b d
+ 0b11111,
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b01010,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // b e
+ 0b11111,
+ 0b01001,
+ 0b01010,
+ 0b01000,
+ 0b01000,
+ 0b00111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // b f
+ 0b10001,
+ 0b10001,
+ 0b01001,
+ 0b00001,
+ 0b00010,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // c 0
+ 0b01111,
+ 0b01001,
+ 0b10101,
+ 0b00011,
+ 0b00010,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00010, // c 1
+ 0b11100,
+ 0b00100,
+ 0b11111,
+ 0b00100,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // c 2
+ 0b10101,
+ 0b10101,
+ 0b10101,
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // c 3
+ 0b00000,
+ 0b11111,
+ 0b00100,
+ 0b00100,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // c 4
+ 0b01000,
+ 0b01000,
+ 0b01100,
+ 0b01010,
+ 0b01000,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // c 5
+ 0b00100,
+ 0b11111,
+ 0b00100,
+ 0b00100,
+ 0b01000,
+ 0b10000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // c 6
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // c 7
+ 0b11111,
+ 0b00001,
+ 0b01010,
+ 0b00100,
+ 0b01010,
+ 0b10000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // c 8
+ 0b11111,
+ 0b00010,
+ 0b00100,
+ 0b01110,
+ 0b10101,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00010, // c 9
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // c a
+ 0b00100,
+ 0b00010,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10000, // c b
+ 0b10000,
+ 0b11111,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+ 0b01111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // c c
+ 0b11111,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+ 0b00010,
+ 0b01100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // c d
+ 0b01000,
+ 0b10100,
+ 0b00010,
+ 0b00001,
+ 0b00001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // c e
+ 0b11111,
+ 0b00100,
+ 0b10101,
+ 0b10101,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // c f
+ 0b11111,
+ 0b00001,
+ 0b00001,
+ 0b01010,
+ 0b00100,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d 0
+ 0b01110,
+ 0b00000,
+ 0b01110,
+ 0b00000,
+ 0b01110,
+ 0b00001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d 1
+ 0b00100,
+ 0b01000,
+ 0b10000,
+ 0b10001,
+ 0b11111,
+ 0b00001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d 2
+ 0b00001,
+ 0b00001,
+ 0b01010,
+ 0b00100,
+ 0b01010,
+ 0b10000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d 3
+ 0b11111,
+ 0b01000,
+ 0b11111,
+ 0b01000,
+ 0b01000,
+ 0b00111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // d 4
+ 0b01000,
+ 0b11111,
+ 0b01001,
+ 0b01010,
+ 0b01000,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d 5
+ 0b01110,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d 6
+ 0b11111,
+ 0b00001,
+ 0b11111,
+ 0b00001,
+ 0b00001,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // d 7
+ 0b00000,
+ 0b11111,
+ 0b00001,
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b10010, // d 8
+ 0b10010,
+ 0b10010,
+ 0b10010,
+ 0b00010,
+ 0b00100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d 9
+ 0b00100,
+ 0b10100,
+ 0b10100,
+ 0b10100,
+ 0b10101,
+ 0b10110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d a
+ 0b10000,
+ 0b10000,
+ 0b10001,
+ 0b10010,
+ 0b10100,
+ 0b11000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d b
+ 0b11111,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d c
+ 0b11111,
+ 0b10001,
+ 0b10001,
+ 0b00001,
+ 0b00010,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // d d
+ 0b11000,
+ 0b00000,
+ 0b00001,
+ 0b00001,
+ 0b00010,
+ 0b11100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00100, // d e
+ 0b10010,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11100, // d f
+ 0b10100,
+ 0b11100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // e 0
+ 0b01001,
+ 0b10101,
+ 0b10010,
+ 0b10010,
+ 0b01101,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01010, // e 1
+ 0b00000,
+ 0b01110,
+ 0b00001,
+ 0b01111,
+ 0b10001,
+ 0b01111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // e 2
+ 0b00000,
+ 0b01110,
+ 0b10001,
+ 0b11110,
+ 0b10001,
+ 0b11110,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+
+ 0b00000, // e 3
+ 0b00000,
+ 0b01110,
+ 0b10000,
+ 0b01100,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // e 4
+ 0b00000,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10011,
+ 0b11101,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+
+ 0b00000, // e 5
+ 0b00000,
+ 0b00000,
+ 0b01111,
+ 0b10100,
+ 0b10010,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // e 6
+ 0b00000,
+ 0b00110,
+ 0b01001,
+ 0b10001,
+ 0b10001,
+ 0b11110,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+
+ 0b00000, // e 7
+ 0b00000,
+ 0b01111,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b01111,
+ 0b00001,
+ 0b00001,
+ 0b01110,
+
+ 0b00000, // e 8
+ 0b00000,
+ 0b00111,
+ 0b00100,
+ 0b00100,
+ 0b10100,
+ 0b01000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // e 9
+ 0b00010,
+ 0b11010,
+ 0b00010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00010, // e a
+ 0b00000,
+ 0b00110,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b00010,
+ 0b10010,
+ 0b01100,
+
+ 0b00000, // e b
+ 0b10100,
+ 0b01000,
+ 0b10100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // e c
+ 0b00100,
+ 0b01110,
+ 0b10100,
+ 0b10101,
+ 0b01110,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01000, // e d
+ 0b01000,
+ 0b11100,
+ 0b01000,
+ 0b11100,
+ 0b01000,
+ 0b01111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01110, // e e
+ 0b00000,
+ 0b10110,
+ 0b11001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01010, // e f
+ 0b00000,
+ 0b01110,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // f 0
+ 0b00000,
+ 0b10110,
+ 0b11001,
+ 0b10001,
+ 0b10001,
+ 0b11110,
+ 0b10000,
+ 0b10000,
+ 0b10000,
+
+ 0b00000, // f 1
+ 0b00000,
+ 0b01101,
+ 0b10011,
+ 0b10001,
+ 0b10001,
+ 0b01111,
+ 0b00001,
+ 0b00001,
+ 0b00001,
+
+ 0b00000, // f 2
+ 0b01110,
+ 0b10001,
+ 0b11111,
+ 0b10001,
+ 0b10001,
+ 0b01110,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // f 3
+ 0b00000,
+ 0b00000,
+ 0b01011,
+ 0b10101,
+ 0b11010,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // f 4
+ 0b00000,
+ 0b01110,
+ 0b10001,
+ 0b10001,
+ 0b01010,
+ 0b11011,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b01010, // f 5
+ 0b00000,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10011,
+ 0b01101,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // f 6
+ 0b10000,
+ 0b01000,
+ 0b00100,
+ 0b01000,
+ 0b10000,
+ 0b11111,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // f 7
+ 0b00000,
+ 0b11111,
+ 0b01010,
+ 0b01010,
+ 0b01010,
+ 0b10011,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // f 8
+ 0b00000,
+ 0b10001,
+ 0b01010,
+ 0b00100,
+ 0b01010,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // f 9
+ 0b00000,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b10001,
+ 0b01111,
+ 0b00001,
+ 0b00001,
+ 0b01110,
+
+ 0b00000, // f a
+ 0b00001,
+ 0b11110,
+ 0b00100,
+ 0b11111,
+ 0b00100,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // f b
+ 0b00000,
+ 0b11111,
+ 0b01000,
+ 0b01111,
+ 0b01001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // f c
+ 0b00000,
+ 0b11111,
+ 0b10101,
+ 0b11111,
+ 0b10001,
+ 0b10001,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // f d
+ 0b00000,
+ 0b00100,
+ 0b00000,
+ 0b11111,
+ 0b00000,
+ 0b00100,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b00000, // f e
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+ 0b00000,
+
+ 0b11111, // f f
+ 0b11111,
+ 0b11111,
+ 0b11111,
+ 0b11111,
+ 0b11111,
+ 0b11111,
+ 0b11111,
+ 0b11111,
+ 0b11111,
+ };
+
+ static_assert(std::size(g_fontTable0) == 256 * 10);
+
+ const uint8_t* getCharacterData(const uint8_t _character)
+ {
+ return &g_fontTable0[_character * 10];
+ }
+}
diff --git a/source/mqLib/lcdfonts.h b/source/mqLib/lcdfonts.h
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace mqLib
+{
+ const uint8_t* getCharacterData(uint8_t _character);
+}
diff --git a/source/mqLib/leds.cpp b/source/mqLib/leds.cpp
@@ -0,0 +1,74 @@
+#include "leds.h"
+
+#include "mqtypes.h"
+
+#include "../mc68k/port.h"
+
+namespace mqLib
+{
+ bool Leds::exec(const mc68k::Port& _portF, const mc68k::Port& _portGP, const mc68k::Port& _portE)
+ {
+ bool changed = false;
+
+ if(_portE.getDirection() & (1<<LedPower))
+ {
+ const uint32_t powerLedState = (_portE.read() >> LedPower) & 1;
+
+ if(setLed(static_cast<uint32_t>(Led::Power), powerLedState))
+ changed = true;
+ }
+
+ if(!(_portF.getDirection() & (1<<LedWriteLatch)))
+ return ret(changed);
+
+ if(_portGP.getDirection() != 0xff)
+ return ret(changed);
+
+ const auto prevF7 = m_stateF7;
+ const auto stateF7 = _portF.read() & (1<<LedWriteLatch);
+ m_stateF7 = stateF7;
+
+ if(!stateF7 || prevF7)
+ return ret(changed);
+
+ const auto gp = _portGP.read();
+ const auto group = (gp >> 5) - 1;
+ const auto leds = gp & 0x1f;
+
+ if(group >= 5)
+ return ret(changed);
+
+ uint32_t ledIndex = group;
+
+ for(auto i=0; i<5; ++i)
+ {
+ changed |= setLed(ledIndex, (leds >> i) & 1);
+
+ ledIndex += 5;
+ }
+
+ return ret(changed);
+ }
+
+ bool Leds::setLed(uint32_t _index, uint32_t _value)
+ {
+ auto& led = m_ledState[_index];
+ const auto prev = led;
+ led = _value;
+
+ if(led == prev)
+ return false;
+
+// LOG("LED " << _index << " changed to " << _value);
+ return true;
+ }
+
+ bool Leds::ret(const bool _changed) const
+ {
+ if(!_changed)
+ return false;
+ if(m_changeCallback)
+ m_changeCallback();
+ return true;
+ }
+}
diff --git a/source/mqLib/leds.h b/source/mqLib/leds.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <array>
+#include <functional>
+#include <cstdint>
+
+namespace mc68k
+{
+ class Port;
+}
+namespace mqLib
+{
+ class Leds
+ {
+ public:
+ using ChangeCallback = std::function<void()>;
+
+ enum class Led
+ {
+ // group 1 2 3 4 5
+ /* led index 0 */ Osc1, Filters2, Inst1, Global, Play,
+ /* led index 1 */ Osc2, AmpFx, Inst2, Multi, Peek,
+ /* led index 2 */ Osc3, Env1, Inst3, Edit, Multimode,
+ /* led index 3 */ MixerRouting, Env2, Inst4, Sound, Shift,
+ /* led index 4 */ Filters1, Env3, ModMatrix, LFOs, Env4,
+ Power,
+ Count
+ };
+
+ Leds() = default;
+ bool exec(const mc68k::Port& _portF, const mc68k::Port& _portGP, const mc68k::Port& _portE);
+
+ auto getLedState(Led _led) const { return m_ledState[static_cast<uint32_t>(_led)]; }
+
+ void setChangeCallback(const ChangeCallback& _callback)
+ {
+ m_changeCallback = _callback;
+ }
+ private:
+ bool setLed(uint32_t _index, uint32_t _value);
+ bool ret(bool _changed) const;
+
+ std::array<uint32_t, static_cast<uint32_t>(Led::Count)> m_ledState;
+ uint32_t m_stateF7;
+ ChangeCallback m_changeCallback;
+ };
+}
diff --git a/source/mqLib/microq.cpp b/source/mqLib/microq.cpp
@@ -0,0 +1,240 @@
+#include "microq.h"
+
+#include "../synthLib/midiTypes.h"
+#include "../synthLib/os.h"
+#include "../synthLib/deviceException.h"
+
+#include "dsp56kEmu/threadtools.h"
+
+#include "mqhardware.h"
+
+#include "../mc68k/logging.h"
+
+namespace mqLib
+{
+ MicroQ::MicroQ(BootMode _bootMode/* = BootMode::Default*/)
+ {
+ // create hardware, will use in-memory ROM if no ROM provided
+// auto romFile = synthLib::findROM(512 * 1024); // TODO: validate the found ROMs before use, check if it starts with "2.23". Otherwise, a Virus ROM might be found and booted
+// if(romFile.empty())
+ const auto romFile = synthLib::findFile(".mid", 300 * 1024, 400 * 1024);
+ if(romFile.empty())
+ throw synthLib::DeviceException(synthLib::DeviceError::FirmwareMissing, "Failed to find device operating system file mq_2_23.mid.");
+
+ MCLOG("Boot using ROM " << romFile);
+
+ m_hw.reset(new Hardware(romFile));
+
+ if(!isValid())
+ return;
+
+ if(_bootMode != BootMode::Default)
+ m_hw->setBootMode(_bootMode);
+
+ m_midiOutBuffer.reserve(1024);
+
+ m_ucThread.reset(new std::thread([&]()
+ {
+ dsp56k::ThreadTools::setCurrentThreadName("MC68331");
+ while(!m_destroy)
+ processUcThread();
+ m_destroy = false;
+ m_hw->ucThreadTerminated();
+ }));
+
+ m_hw->getUC().getLeds().setChangeCallback([this]()
+ {
+ onLedsChanged();
+ });
+
+ m_hw->getUC().getLcd().setChangeCallback([this]()
+ {
+ onLcdChanged();
+ });
+
+ m_hw->getUC().getLcd().setCgRamChangeCallback([this]()
+ {
+ onLcdCgRamChanged();
+ });
+
+ setButton(Buttons::ButtonType::Play, true);
+ m_hw->initVoiceExpansion();
+ }
+
+ MicroQ::~MicroQ()
+ {
+ if(!isValid())
+ return;
+
+ // we need to have passed the boot stage
+ m_hw->processAudio(1);
+
+ m_destroy = true;
+
+ // DSP needs to run to let the uc thread wake up
+ const auto& esai = m_hw->getDSP().getPeriph().getEsai();
+ while(m_destroy)
+ {
+ if(!esai.getAudioOutputs().empty())
+ m_hw->processAudio(1);
+ else
+ std::this_thread::yield();
+ }
+
+ m_ucThread->join();
+ m_ucThread.reset();
+ m_hw.reset();
+ }
+
+ bool MicroQ::isValid() const
+ {
+ return m_hw && m_hw->isValid();
+ }
+
+ void MicroQ::process(const float** _inputs, float** _outputs, uint32_t _frames, uint32_t _latency)
+ {
+ std::lock_guard lock(m_mutex);
+
+ m_hw->ensureBufferSize(_frames);
+
+ // convert inputs from float to DSP words
+ auto& dspIns = m_hw->getAudioInputs();
+
+ for(size_t c=0; c<dspIns.size(); ++c)
+ {
+ for(uint32_t i=0; i<_frames; ++i)
+ dspIns[c][i] = dsp56k::sample2dsp(_inputs[c][i]);
+ }
+
+ internalProcess(_frames, _latency);
+
+ // convert outputs from DSP words to float
+ const auto& dspOuts = m_hw->getAudioOutputs();
+
+ for(size_t c=0; c<dspOuts.size(); ++c)
+ {
+ for(uint32_t i=0; i<_frames; ++i)
+ _outputs[c][i] = dsp56k::dsp2sample<float>(dspOuts[c][i]);
+ }
+ }
+
+ void MicroQ::process(uint32_t _frames, uint32_t _latency)
+ {
+ std::lock_guard lock(m_mutex);
+ internalProcess(_frames, _latency);
+ }
+
+ void MicroQ::internalProcess(uint32_t _frames, uint32_t _latency)
+ {
+ // process audio
+ m_hw->processAudio(_frames, _latency);
+
+ // receive midi output
+ m_hw->receiveMidi(m_midiOutBuffer);
+ }
+
+ TAudioInputs& MicroQ::getAudioInputs()
+ {
+ return m_hw->getAudioInputs();
+ }
+
+ TAudioOutputs& MicroQ::getAudioOutputs()
+ {
+ return m_hw->getAudioOutputs();
+ }
+
+ void MicroQ::sendMidiEvent(const synthLib::SMidiEvent& _ev)
+ {
+ m_hw->sendMidi(_ev);
+ }
+
+ void MicroQ::receiveMidi(std::vector<uint8_t>& _buffer)
+ {
+ std::lock_guard lock(m_mutex);
+ std::swap(_buffer, m_midiOutBuffer);
+ m_midiOutBuffer.clear();
+ }
+
+ bool MicroQ::getButton(Buttons::ButtonType _button)
+ {
+ return m_hw->getUC().getButtons().getButtonState(_button) != 0;
+ }
+
+ void MicroQ::setButton(Buttons::ButtonType _button, bool _pressed)
+ {
+ m_hw->getUC().getButtons().setButton(_button, _pressed);
+ }
+
+ uint8_t MicroQ::getEncoder(Buttons::Encoders _encoder)
+ {
+ return m_hw->getUC().getButtons().getEncoderState(_encoder);
+ }
+
+ void MicroQ::rotateEncoder(Buttons::Encoders _encoder, int _amount)
+ {
+ std::lock_guard lock(m_mutex);
+ m_hw->getUC().getButtons().rotate(_encoder, _amount);
+ }
+
+ bool MicroQ::getLedState(Leds::Led _led)
+ {
+ return m_hw->getUC().getLeds().getLedState(_led) != 0;
+ }
+
+ void MicroQ::readLCD(std::array<char, 40>& _data)
+ {
+ _data = m_hw->getUC().getLcd().getDdRam();
+ }
+
+ bool MicroQ::readCustomLCDCharacter(std::array<uint8_t, 8>& _data, uint32_t _characterIndex)
+ {
+ std::lock_guard lock(m_mutex);
+ return m_hw->getUC().getLcd().getCgData(_data, _characterIndex);
+ }
+
+ MicroQ::DirtyFlags MicroQ::getDirtyFlags()
+ {
+ const auto f = m_dirtyFlags.exchange(0);
+ return static_cast<DirtyFlags>(f);
+ }
+
+ Hardware* MicroQ::getHardware()
+ {
+ return m_hw.get();
+ }
+
+ bool MicroQ::isBootCompleted() const
+ {
+ return m_hw && m_hw->isBootCompleted();
+ }
+
+ void MicroQ::onLedsChanged()
+ {
+ m_dirtyFlags.fetch_or(static_cast<uint32_t>(DirtyFlags::Leds));
+ }
+
+ void MicroQ::onLcdChanged()
+ {
+ m_dirtyFlags.fetch_or(static_cast<uint32_t>(DirtyFlags::Lcd));
+ }
+
+ void MicroQ::onLcdCgRamChanged()
+ {
+ m_dirtyFlags.fetch_or(static_cast<uint32_t>(DirtyFlags::LcdCgRam));
+ }
+
+ void MicroQ::processUcThread() const
+ {
+ for(size_t i=0; i<32; ++i)
+ {
+ m_hw->process();
+ m_hw->process();
+ m_hw->process();
+ m_hw->process();
+ m_hw->process();
+ m_hw->process();
+ m_hw->process();
+ m_hw->process();
+ }
+ }
+}
diff --git a/source/mqLib/microq.h b/source/mqLib/microq.h
@@ -0,0 +1,130 @@
+#pragma once
+
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <thread>
+#include <atomic>
+
+#include "buttons.h"
+#include "leds.h"
+#include "mqtypes.h"
+
+namespace synthLib
+{
+ struct SMidiEvent;
+}
+
+namespace mqLib
+{
+ class Hardware;
+
+ class MicroQ
+ {
+ public:
+ MicroQ(BootMode _bootMode = BootMode::Default);
+ ~MicroQ();
+
+ // returns true if the instance is valid, false if the initialization failed
+ bool isValid() const;
+
+ // Process a block of audio data. Be sure to pass two channels for the inputs and six channels for the outputs
+ // _frames means the number of samples per channel
+ // _latency additional latency in samples that will be added to allow asynchronous processing
+ void process(const float** _inputs, float** _outputs, uint32_t _frames, uint32_t _latency = 0);
+
+ // Process a block of audio data. Data conversion is not performed, this allows to access raw DSP data
+ // The used input and output buffers can be queried below
+ void process(uint32_t _frames, uint32_t _latency = 0);
+
+ // Retrieve the DSP audio input. Two channels, 24 bits. Only the 24 LSBs of each 32 bit word are used
+ TAudioInputs& getAudioInputs();
+
+ // Retrieve the DSP audio output. Two channels, 24 bits. Only the 24 LSBs of each 32 bit word are used
+ TAudioOutputs& getAudioOutputs();
+
+ // send midi to the midi input of the device
+ void sendMidiEvent(const synthLib::SMidiEvent& _ev);
+
+ // Receive midi data that the device generated during the last call of process().
+ // Note that any midi output data not queried between two calls of process() is lost
+ void receiveMidi(std::vector<uint8_t>& _buffer);
+
+ // Get the status of one of the front panel buttons
+ bool getButton(Buttons::ButtonType _button);
+
+ // Set the status of one of the front panel buttons
+ void setButton(Buttons::ButtonType _button, bool _pressed);
+
+ // retrieve the current value of a front panel encoder
+ uint8_t getEncoder(Buttons::Encoders _encoder);
+
+ // Rotate an encoder on the front panel by a specific amount
+ void rotateEncoder(Buttons::Encoders _encoder, int _amount);
+
+ // Return the state of a front panel LED. true = lit
+ bool getLedState(Leds::Led _led);
+
+ // Read the current LCD content. Returns 40 characters that represent two lines with 20 characters each
+ // If the returned character is less than 8, it is a custom character with predefined pixel data
+ // You may query the pixel data via readCustomLCDCharacter below
+ void readLCD(std::array<char, 40>& _data);
+
+ // Read a custom LCD character. _characterIndex needs to be < 8
+ // A custom character has the dimensions 5*8 pixels
+ // The _data argument receives one byte per row, with the topmost row at index 0, the bottommost row at index 7
+ // Pixels per row are stored in the five LSBs of each byte, with the leftmost pixel at bit 4, the rightmost pixel at bit 0
+ // A set bit indicates a set pixel.
+ // As an example, the character 'P', encoded as a custom character, would look like this:
+ //
+ // bit 7 6 5 4 3 2 1 0
+ // byte 0 * * * * - top
+ // byte 1 * - - - *
+ // byte 2 * - - - *
+ // byte 3 * * * * -
+ // byte 4 * - - - -
+ // byte 5 * - - - -
+ // byte 6 * - - - -
+ // byte 7 * - - - - bottom
+ //
+ bool readCustomLCDCharacter(std::array<uint8_t, 8>& _data, uint32_t _characterIndex);
+
+ // Dirty flags indicate that the front panel of the device has changed. To be retrieved via getDirtyFlags()
+ enum class DirtyFlags : uint32_t
+ {
+ None = 0,
+ Leds = 0x01, // one or more LEDs changed its state
+ Lcd = 0x02, // the LCD has been refreshed and should be repainted
+ LcdCgRam = 0x04, // the LCD CG RAM has been modified, custom characters need to be repainted
+ };
+
+ // Retrieve dirty flags for the front panel. See enum DirtyFlags for a description
+ // Dirty flags are sticky but are reset upon calling this function
+ DirtyFlags getDirtyFlags();
+
+ // Gain access to the hardware implementation, intended for advanced use. Usually not required
+ Hardware* getHardware();
+
+ // returns after the device has booted and is ready to receive midi commands
+ bool isBootCompleted() const;
+
+ private:
+ void internalProcess(uint32_t _frames, uint32_t _latency);
+ void onLedsChanged();
+ void onLcdChanged();
+ void onLcdCgRamChanged();
+
+ void processUcThread() const;
+
+ std::unique_ptr<Hardware> m_hw;
+
+ std::mutex m_mutex;
+
+ std::vector<uint8_t> m_midiOutBuffer;
+
+ std::unique_ptr<std::thread> m_ucThread;
+ bool m_destroy = false;
+ std::atomic<uint32_t> m_dirtyFlags = 0;
+ };
+}
diff --git a/source/mqLib/mqbuildconfig.h b/source/mqLib/mqbuildconfig.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#define MQ_VOICE_EXPANSION 0
+
+namespace mqLib
+{
+ static constexpr bool g_pluginDemo = false;
+
+#if MQ_VOICE_EXPANSION
+ static constexpr bool g_useVoiceExpansion = true;
+#else
+ static constexpr bool g_useVoiceExpansion = false;
+#endif
+}
+
+
diff --git a/source/mqLib/mqdsp.cpp b/source/mqLib/mqdsp.cpp
@@ -0,0 +1,206 @@
+#include "mqdsp.h"
+
+#include "dspBootCode.h"
+#include "mqhardware.h"
+
+#if DSP56300_DEBUGGER
+#include "dsp56kDebugger/debugger.h"
+#endif
+
+#include "../mc68k/hdi08.h"
+
+namespace mqLib
+{
+ static dsp56k::DefaultMemoryValidator g_memoryValidator;
+ static constexpr dsp56k::TWord g_magicEsaiPacket = 0x654300;
+
+ MqDsp::MqDsp(Hardware& _hardware, mc68k::Hdi08& _hdiUC, const uint32_t _index)
+ : m_hardware(_hardware), m_hdiUC(_hdiUC)
+ , m_index(_index)
+ , m_name({static_cast<char>('A' + _index)})
+ , m_periphX(nullptr)
+ , m_memory(g_memoryValidator, g_pMemSize, g_xyMemSize, g_bridgedAddr, m_memoryBuffer)
+ , m_dsp(m_memory, &m_periphX, &m_periphNop)
+ {
+ m_periphX.getEsaiClock().setExternalClockFrequency(44100 * 768); // measured as being roughly 33,9MHz, this should be exact
+ m_periphX.getEsaiClock().setSamplerate(44100); // verified
+
+ auto config = m_dsp.getJit().getConfig();
+
+ config.aguSupportBitreverse = true;
+ config.linkJitBlocks = true;
+ config.dynamicPeripheralAddressing = true;
+ config.maxInstructionsPerBlock = 0; // TODO: needs to be 1 if DSP factory tests are run, to be investigated
+
+ m_dsp.getJit().setConfig(config);
+
+ // fill P memory with something that reminds us if we jump to garbage
+ for(dsp56k::TWord i=0; i<m_memory.sizeP(); ++i)
+ m_memory.set(dsp56k::MemArea_P, i, 0x000200); // debug instruction
+
+ // rewrite bootloader to work at address g_bootCodeBase instead of $ff0000
+ for(uint32_t i=0; i<std::size(g_dspBootCode); ++i)
+ {
+ uint32_t code = g_dspBootCode[i];
+ if((g_dspBootCode[i] & 0xffff00) == 0xff0000)
+ {
+ code = g_bootCodeBase | (g_dspBootCode[i] & 0xff);
+ }
+
+ m_memory.set(dsp56k::MemArea_P, i + g_bootCodeBase, code);
+ }
+
+// m_memory.saveAssembly("dspBootDisasm.asm", g_bootCodeBase, static_cast<uint32_t>(std::size(g_dspBootCode)), true, true, &m_periphX, nullptr);
+
+ // set OMR pins so that bootcode wants program data via HDI08 RX
+ m_dsp.setPC(g_bootCodeBase);
+ m_dsp.regs().omr.var |= OMR_MA | OMR_MB | OMR_MC | OMR_MD;
+
+ getPeriph().disableTimers(true); // only used to test DSP load, we report 0 all the time for now
+
+ m_periphX.getEsai().writeEmptyAudioIn(8);
+
+ hdi08().setRXRateLimit(0);
+ hdi08().setTransmitDataAlwaysEmpty(false);
+
+ m_hdiUC.setRxEmptyCallback([&](const bool needMoreData)
+ {
+ onUCRxEmpty(needMoreData);
+ });
+ m_hdiUC.setWriteTxCallback([&](const uint32_t _word)
+ {
+ hdiTransferUCtoDSP(_word);
+ });
+ m_hdiUC.setWriteIrqCallback([&](const uint8_t _irq)
+ {
+ hdiSendIrqToDSP(_irq);
+ });
+ m_hdiUC.setReadIsrCallback([&](const uint8_t _isr)
+ {
+ return hdiUcReadIsr(_isr);
+ });
+
+#if DSP56300_DEBUGGER
+ m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str(), std::make_shared<dsp56kDebugger::Debugger>(m_dsp.dsp())));
+#else
+ m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str()));
+#endif
+
+ m_thread->setLogToStdout(false);
+ }
+
+ void MqDsp::exec()
+ {
+ m_thread->join();
+ m_thread.reset();
+
+ m_hdiUC.setRxEmptyCallback({});
+ m_dsp.exec();
+ }
+
+ void MqDsp::dumpPMem(const std::string& _filename)
+ {
+ m_memory.saveAssembly((_filename + ".asm").c_str(), 0, g_pMemSize, true, false, &m_periphX);
+ }
+
+ void MqDsp::dumpXYMem(const std::string& _filename) const
+ {
+ m_memory.save((_filename + "_X.txt").c_str(), dsp56k::MemArea_X);
+ m_memory.save((_filename + "_Y.txt").c_str(), dsp56k::MemArea_Y);
+ }
+
+ void MqDsp::transferHostFlagsUc2Dsdp()
+ {
+ const uint32_t hf01 = m_hdiUC.icr() & 0x18;
+
+ if (hf01 != m_hdiHF01)
+ {
+// LOG('[' << m_name << "] HDI HF01=" << HEXN((hf01>>3),1));
+ waitDspRxEmpty();
+ m_hdiHF01 = hf01;
+ hdi08().setPendingHostFlags01(hf01);
+ }
+ }
+
+ void MqDsp::onUCRxEmpty(bool _needMoreData)
+ {
+ hdi08().injectTXInterrupt();
+
+ if (_needMoreData)
+ {
+ m_hardware.ucYieldLoop([&]()
+ {
+ return dsp().hasPendingInterrupts() || (hdi08().txInterruptEnabled() && !hdi08().hasTX());
+ });
+ }
+
+ hdiTransferDSPtoUC();
+ }
+
+ bool MqDsp::hdiTransferDSPtoUC()
+ {
+ if (m_hdiUC.canReceiveData() && hdi08().hasTX())
+ {
+ const auto v = hdi08().readTX();
+// LOG('[' << m_name << "] HDI dsp2uc=" << HEX(v));
+ if (v == g_magicEsaiPacket)
+ m_receivedMagicEsaiPacket = true;
+ m_hdiUC.writeRx(v);
+ return true;
+ }
+ return false;
+ }
+
+ void MqDsp::hdiTransferUCtoDSP(dsp56k::TWord _word)
+ {
+ m_haveSentTXtoDSP = true;
+// LOG('[' << m_name << "] toDSP writeRX=" << HEX(_word));
+ hdi08().writeRX(&_word, 1);
+ }
+
+ void MqDsp::hdiSendIrqToDSP(uint8_t _irq)
+ {
+ waitDspRxEmpty();
+
+ if(_irq == 0x92)
+ {
+// LOG('[' << m_name << "] DSP timeout, waiting...");
+ // this one is sent by the uc if the DSP taking too long to reset HF2 back to one. Instead of aborting here, we wait a bit longer for the DSP to finish on its own
+ m_hardware.ucYieldLoop([&]
+ {
+ return !bittest(hdi08().readControlRegister(), dsp56k::HDI08::HCR_HF2);
+ });
+// LOG('[' << m_name << "] DSP timeout wait done");
+ }
+// else
+// LOG('[' << m_name << "] Inject interrupt" << HEXN(_irq,2));
+
+ dsp().injectInterrupt(_irq);
+
+ m_hardware.ucYieldLoop([&]()
+ {
+ return dsp().hasPendingInterrupts();
+ });
+
+ hdiTransferDSPtoUC();
+ }
+
+ uint8_t MqDsp::hdiUcReadIsr(uint8_t _isr)
+ {
+ // transfer DSP host flags HF2&3 to uc
+ const auto hf23 = hdi08().readControlRegister() & 0x18;
+ _isr &= ~0x18;
+ _isr |= hf23;
+ return _isr;
+ }
+
+ void MqDsp::waitDspRxEmpty()
+ {
+ m_hardware.ucYieldLoop([&]()
+ {
+ return (hdi08().hasRXData() && hdi08().rxInterruptEnabled()) || dsp().hasPendingInterrupts();
+ });
+// LOG("writeRX wait over");
+ }
+
+}
diff --git a/source/mqLib/mqdsp.h b/source/mqLib/mqdsp.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include "dsp56kEmu/dsp.h"
+#include "dsp56kEmu/dspthread.h"
+#include "dsp56kEmu/peripherals.h"
+
+namespace mc68k
+{
+ class Hdi08;
+}
+
+namespace mqLib
+{
+ class Hardware;
+
+ class MqDsp
+ {
+ public:
+ static constexpr dsp56k::TWord g_bridgedAddr = 0x080000; // start of external SRAM, mapped to X and Y
+ static constexpr dsp56k::TWord g_xyMemSize = 0x800000; // due to weird AAR mapping we just allocate enough so that everything fits into it
+ static constexpr dsp56k::TWord g_pMemSize = 0x2000; // only $0000 < $1400 for DSP, rest for us
+ static constexpr dsp56k::TWord g_bootCodeBase = 0x1500;
+
+ MqDsp(Hardware& _hardware, mc68k::Hdi08& _hdiUC, uint32_t _index);
+ void exec();
+
+ dsp56k::HDI08& hdi08()
+ {
+ return m_periphX.getHDI08();
+ }
+
+ dsp56k::DSP& dsp()
+ {
+ return m_dsp;
+ }
+
+ dsp56k::Peripherals56362& getPeriph()
+ {
+ return m_periphX;
+ }
+
+ void dumpPMem(const std::string& _filename);
+ void dumpXYMem(const std::string& _filename) const;
+
+ void transferHostFlagsUc2Dsdp();
+ bool hdiTransferDSPtoUC();
+ void hdiSendIrqToDSP(uint8_t _irq);
+
+ dsp56k::DSPThread& thread() { return *m_thread; }
+ bool haveSentTXToDSP() const { return m_haveSentTXtoDSP; }
+
+ bool receivedMagicEsaiPacket() const { return m_receivedMagicEsaiPacket; }
+
+ private:
+ void onUCRxEmpty(bool _needMoreData);
+ void hdiTransferUCtoDSP(dsp56k::TWord _word);
+ uint8_t hdiUcReadIsr(uint8_t _isr);
+ void waitDspRxEmpty();
+
+ Hardware& m_hardware;
+ mc68k::Hdi08& m_hdiUC;
+ const uint32_t m_index;
+ const std::string m_name;
+
+ dsp56k::PeripheralsNop m_periphNop;
+ dsp56k::Peripherals56362 m_periphX;
+ dsp56k::Memory m_memory;
+ dsp56k::DSP m_dsp;
+ dsp56k::TWord m_memoryBuffer[dsp56k::Memory::calcMemSize(g_pMemSize, g_xyMemSize, g_bridgedAddr)]{0};
+
+ bool m_haveSentTXtoDSP = false;
+ uint32_t m_hdiHF01 = 0; // uc => DSP
+
+ std::unique_ptr<dsp56k::DSPThread> m_thread;
+
+ bool m_receivedMagicEsaiPacket = false;
+ };
+}
diff --git a/source/mqLib/mqhardware.cpp b/source/mqLib/mqhardware.cpp
@@ -0,0 +1,507 @@
+#include "mqhardware.h"
+
+#include "../synthLib/midiTypes.h"
+#include "../synthLib/midiBufferParser.h"
+#include "../synthLib/deviceException.h"
+
+#include "dsp56kEmu/interrupts.h"
+
+#if EMBED_ROM
+#include "romData.h"
+#else
+constexpr uint8_t* ROM_DATA = nullptr;
+#endif
+
+namespace mqLib
+{
+#if EMBED_ROM
+ static_assert(ROM_DATA_SIZE == ROM::g_romSize);
+#endif
+
+ constexpr uint32_t g_syncEsaiFrameRate = 16;
+ constexpr uint32_t g_syncHaltDspEsaiThreshold = 32;
+
+ static_assert((g_syncEsaiFrameRate & (g_syncEsaiFrameRate - 1)) == 0, "esai frame sync rate must be power of two");
+ static_assert(g_syncHaltDspEsaiThreshold >= g_syncEsaiFrameRate * 2, "esai DSP halt threshold must be greater than two times the sync rate");
+
+ Hardware::Hardware(std::string _romFilename)
+ : m_romFileName(std::move(_romFilename))
+ , m_rom(m_romFileName, ROM_DATA)
+ , m_uc(m_rom)
+#if MQ_VOICE_EXPANSION
+ , m_dsps{MqDsp(*this, m_uc.getHdi08A().getHdi08(), 0), MqDsp(*this, m_uc.getHdi08B().getHdi08(), 1) , MqDsp(*this, m_uc.getHdi08C().getHdi08(), 2)}
+#else
+ , m_dsps{MqDsp(*this, m_uc.getHdi08A().getHdi08(), 0)}
+#endif
+ , m_midi(m_uc.getQSM())
+ {
+ if(!m_rom.isValid())
+ throw synthLib::DeviceException(synthLib::DeviceError::FirmwareMissing);
+
+ m_uc.getPortF().setDirectionChangeCallback([&](const mc68k::Port& port)
+ {
+ if(port.getDirection() == 0xff)
+ setGlobalDefaultParameters();
+ });
+ }
+
+ Hardware::~Hardware()
+ {
+ m_dsps.front().getPeriph().getEsai().setCallback({}, 0);
+ }
+
+ bool Hardware::process()
+ {
+ processUcCycle();
+ return true;
+ }
+
+ void Hardware::sendMidi(const synthLib::SMidiEvent& _ev)
+ {
+ m_midiIn.push_back(_ev);
+ }
+
+ void Hardware::receiveMidi(std::vector<uint8_t>& _data)
+ {
+ m_midi.readTransmitBuffer(_data);
+ }
+
+ void Hardware::setBootMode(const BootMode _mode)
+ {
+ auto setButton = [&](const Buttons::ButtonType _type, const bool _pressed = true)
+ {
+ m_uc.getButtons().setButton(_type, _pressed);
+ };
+
+ switch (_mode)
+ {
+ case BootMode::Default:
+ setButton(Buttons::ButtonType::Inst1, false);
+ setButton(Buttons::ButtonType::Inst3, false);
+ setButton(Buttons::ButtonType::Shift, false);
+ setButton(Buttons::ButtonType::Global, false);
+ setButton(Buttons::ButtonType::Multi, false);
+ setButton(Buttons::ButtonType::Play, false);
+ break;
+ case BootMode::FactoryTest:
+ setButton(Buttons::ButtonType::Inst1);
+ setButton(Buttons::ButtonType::Global);
+ break;
+ case BootMode::EraseFlash:
+ setButton(Buttons::ButtonType::Inst3);
+ setButton(Buttons::ButtonType::Global);
+ break;
+ case BootMode::WaitForSystemDump:
+ setButton(Buttons::ButtonType::Shift);
+ setButton(Buttons::ButtonType::Global);
+ break;
+ case BootMode::DspClockResetAndServiceMode:
+ setButton(Buttons::ButtonType::Multi);
+ break;
+ case BootMode::ServiceMode:
+ setButton(Buttons::ButtonType::Global);
+ break;
+ case BootMode::MemoryGame:
+ setButton(Buttons::ButtonType::Global);
+ setButton(Buttons::ButtonType::Play);
+ break;
+ }
+ }
+
+ void Hardware::resetMidiCounter()
+ {
+ // wait for DSP to enter blocking state
+
+ const auto& esai = m_dsps.front().getPeriph().getEsai();
+
+ auto& inputs = esai.getAudioInputs();
+ auto& outputs = esai.getAudioOutputs();
+
+ while(inputs.size() > 2 && !outputs.full())
+ std::this_thread::yield();
+
+ m_midiOffsetCounter = 0;
+ }
+
+ void Hardware::hdiProcessUCtoDSPNMIIrq()
+ {
+ // QS6 is connected to DSP NMI pin but I've never seen this being triggered
+ const uint8_t requestNMI = m_uc.requestDSPinjectNMI();
+
+ if(m_requestNMI && !requestNMI)
+ {
+// LOG("uc request DSP NMI");
+ m_dsps.front().hdiSendIrqToDSP(dsp56k::Vba_NMI);
+
+ m_requestNMI = requestNMI;
+ }
+ }
+
+ void Hardware::ucYieldLoop(const std::function<bool()>& _continue)
+ {
+ const auto dspHalted = m_haltDSP;
+
+ resumeDSP();
+
+ while(_continue())
+ {
+ if(m_processAudio)
+ {
+ std::this_thread::yield();
+ }
+ else
+ {
+ std::unique_lock uLock(m_esaiFrameAddedMutex);
+ m_esaiFrameAddedCv.wait(uLock);
+ }
+ }
+
+ if(dspHalted)
+ haltDSP();
+ }
+
+ void Hardware::initVoiceExpansion()
+ {
+ if (m_dsps.size() < 3)
+ {
+ setupEsaiListener();
+ return;
+ }
+
+ m_dsps[1].getPeriph().getPortC().hostWrite(0x10); // set bit 4 of GPIO Port C, vexp DSPs are waiting for this
+ m_dsps[2].getPeriph().getPortC().hostWrite(0x10); // set bit 4 of GPIO Port C, vexp DSPs are waiting for this
+
+ bool done = false;
+
+ auto& esaiA = m_dsps[0].getPeriph().getEsai();
+ auto& esaiB = m_dsps[1].getPeriph().getEsai();
+ auto& esaiC = m_dsps[2].getPeriph().getEsai();
+
+ esaiA.setCallback([&](dsp56k::Audio*)
+ {
+ }, 0);
+
+ esaiB.setCallback([&](dsp56k::Audio*)
+ {
+ }, 0);
+
+ esaiC.setCallback([&](dsp56k::Audio*)
+ {
+ }, 0);
+
+ while (!m_dsps.front().receivedMagicEsaiPacket())
+ {
+ // vexp1 only needs the audio input
+ esaiB.getAudioInputs().push_back({0});
+
+ // transfer output from vexp1 to vexp2
+ auto out = esaiB.getAudioOutputs().pop_front();
+ std::array<dsp56k::TWord, 4> in{ out[0], out[1], out[2], 0};
+ esaiC.getAudioInputs().push_back(in);
+
+ // read output of vexp2 and send to main
+ out = esaiC.getAudioOutputs().pop_front();
+
+ // this should consist of RX0 = audio input, RX1/2 = vexp2 output TX1/TX2
+ in = {0, out[1], out[2]};
+ esaiA.getAudioInputs().push_back(in);
+
+ // final output 0,1,2 = audio outs
+ out = esaiA.getAudioOutputs().pop_front();
+ }
+ LOG("Voice Expansion initialization completed");
+ setupEsaiListener();
+ }
+
+ void Hardware::setupEsaiListener()
+ {
+ auto& esaiA = m_dsps.front().getPeriph().getEsai();
+
+ esaiA.setCallback([&](dsp56k::Audio*)
+ {
+ ++m_esaiFrameIndex;
+
+ if (m_esaiFrameIndex & 1)
+ processMidiInput();
+
+ if((m_esaiFrameIndex & (g_syncEsaiFrameRate-1)) == 0)
+ m_esaiFrameAddedCv.notify_one();
+
+ m_requestedFramesAvailableMutex.lock();
+
+ if(m_requestedFrames && esaiA.getAudioOutputs().size() >= m_requestedFrames)
+ {
+ m_requestedFramesAvailableMutex.unlock();
+ m_requestedFramesAvailableCv.notify_one();
+ }
+ else
+ {
+ m_requestedFramesAvailableMutex.unlock();
+ }
+
+ std::unique_lock uLock(m_haltDSPmutex);
+ m_haltDSPcv.wait(uLock, [&]{ return m_haltDSP == false; });
+ }, 0);
+ }
+
+ void Hardware::processUcCycle()
+ {
+ syncUcToDSP();
+
+ const auto deltaCycles = m_uc.exec();
+ if(m_esaiFrameIndex > 0)
+ m_remainingUcCycles -= static_cast<int64_t>(deltaCycles);
+
+ for (auto& dsp : m_dsps)
+ dsp.transferHostFlagsUc2Dsdp();
+
+ hdiProcessUCtoDSPNMIIrq();
+
+ for (auto& dsp : m_dsps)
+ dsp.hdiTransferDSPtoUC();
+
+ if(m_uc.requestDSPReset())
+ {
+ for (auto& dsp : m_dsps)
+ {
+ if(dsp.haveSentTXToDSP())
+ {
+ m_uc.dumpMemory("DSPreset");
+ assert(false && "DSP needs reset even though it got data already. Needs impl");
+ }
+ }
+ m_uc.notifyDSPBooted();
+ }
+ }
+
+ void Hardware::haltDSP()
+ {
+ if(m_haltDSP)
+ return;
+
+ std::lock_guard uLockHalt(m_haltDSPmutex);
+ m_haltDSP = true;
+ }
+
+ void Hardware::resumeDSP()
+ {
+ if(!m_haltDSP)
+ return;
+
+ {
+ std::lock_guard uLockHalt(m_haltDSPmutex);
+ m_haltDSP = false;
+ }
+ m_haltDSPcv.notify_one();
+ }
+
+ void Hardware::setGlobalDefaultParameters()
+ {
+ m_midi.writeMidi({0xf0,0x3e,0x10,0x7f,0x24,0x00,0x07,0x02,0xf7}); // Control Send = SysEx
+ m_midi.writeMidi({0xf0,0x3e,0x10,0x7f,0x24,0x00,0x08,0x01,0xf7}); // Control Receive = on
+ m_bootCompleted = true;
+ }
+
+ void Hardware::syncUcToDSP()
+ {
+ if(m_remainingUcCycles > 0)
+ return;
+
+ // we can only use ESAI to clock the uc once it has been enabled
+ if(m_esaiFrameIndex <= 0)
+ return;
+
+ if(m_esaiFrameIndex == m_lastEsaiFrameIndex)
+ {
+ resumeDSP();
+ std::unique_lock uLock(m_esaiFrameAddedMutex);
+ m_esaiFrameAddedCv.wait(uLock, [this]{return m_esaiFrameIndex > m_lastEsaiFrameIndex;});
+ }
+
+ const auto esaiFrameIndex = m_esaiFrameIndex;
+
+ const auto ucClock = m_uc.getSim().getSystemClockHz();
+
+ constexpr double divInv = 1.0 / (44100.0 * 2.0); // stereo interleaved
+ const double ucCyclesPerFrame = static_cast<double>(ucClock) * divInv;
+
+ const auto esaiDelta = esaiFrameIndex - m_lastEsaiFrameIndex;
+
+ m_remainingUcCyclesD += ucCyclesPerFrame * static_cast<double>(esaiDelta);
+ m_remainingUcCycles = static_cast<int64_t>(m_remainingUcCyclesD);
+ m_remainingUcCyclesD -= static_cast<double>(m_remainingUcCycles);
+
+ if(esaiDelta > g_syncHaltDspEsaiThreshold)
+ {
+ haltDSP();
+ }
+ else
+ {
+ resumeDSP();
+ }
+
+ m_lastEsaiFrameIndex = esaiFrameIndex;
+ }
+
+ void Hardware::processMidiInput()
+ {
+ ++m_midiOffsetCounter;
+
+ while(!m_midiIn.empty())
+ {
+ const auto& e = m_midiIn.front();
+
+ if(e.offset > m_midiOffsetCounter)
+ break;
+
+ if(!e.sysex.empty())
+ {
+ m_midi.writeMidi(e.sysex);
+ }
+ else
+ {
+ m_midi.writeMidi(e.a);
+ const auto len = synthLib::MidiBufferParser::lengthFromStatusByte(e.a);
+ if (len > 1)
+ m_midi.writeMidi(e.b);
+ if (len > 2)
+ m_midi.writeMidi(e.c);
+ }
+
+ m_midiIn.pop_front();
+ }
+ }
+
+ void Hardware::processAudio(uint32_t _frames, uint32_t _latency)
+ {
+ ensureBufferSize(_frames);
+
+ if(m_esaiFrameIndex == 0)
+ return;
+
+ m_midi.process(_frames);
+
+ m_processAudio = true;
+
+ auto& esai = m_dsps.front().getPeriph().getEsai();
+
+ const dsp56k::TWord* inputs[16]{nullptr};
+ dsp56k::TWord* outputs[16]{nullptr};
+
+ inputs[1] = &m_audioInputs[0].front();
+ inputs[0] = &m_audioInputs[1].front();
+
+ outputs[1] = &m_audioOutputs[0].front();
+ outputs[0] = &m_audioOutputs[1].front();
+ outputs[3] = &m_audioOutputs[2].front();
+ outputs[2] = &m_audioOutputs[3].front();
+ outputs[5] = &m_audioOutputs[4].front();
+ outputs[4] = &m_audioOutputs[5].front();
+
+ while (_frames)
+ {
+ const auto processCount = std::min(_frames, static_cast<uint32_t>(1024));
+ _frames -= processCount;
+
+ if constexpr (g_useVoiceExpansion)
+ {
+ auto& esaiA = m_dsps[0].getPeriph().getEsai();
+ auto& esaiB = m_dsps[1].getPeriph().getEsai();
+ auto& esaiC = m_dsps[2].getPeriph().getEsai();
+ /*
+ const auto tccrA = esaiA.getTccrAsString(); const auto rccrA = esaiA.getRccrAsString();
+ const auto tcrA = esaiA.getTcrAsString(); const auto rcrA = esaiA.getRcrAsString();
+
+ const auto tccrB = esaiB.getTccrAsString(); const auto rccrB = esaiB.getRccrAsString();
+ const auto tcrB = esaiB.getTcrAsString(); const auto rcrB = esaiB.getRcrAsString();
+
+ const auto tccrC = esaiC.getTccrAsString(); const auto rccrC = esaiC.getRccrAsString();
+ const auto tcrC = esaiC.getTcrAsString(); const auto rcrC = esaiC.getRcrAsString();
+
+ LOG("ESAI DSPmain:\n" << tccrA << '\n' << tcrA << '\n' << rccrA << '\n' << rcrA << '\n');
+ LOG("ESAI VexpA:\n" << tccrB << '\n' << tcrB << '\n' << rccrB << '\n' << rcrB << '\n');
+ LOG("ESAI VexpB:\n" << tccrC << '\n' << tcrC << '\n' << rccrC << '\n' << rcrC << '\n');
+ */
+ // vexp1 only needs the audio input
+ esaiB.processAudioInputInterleaved(inputs, processCount);
+
+ // transfer output from vexp1 to vexp2
+ esaiB.processAudioOutputInterleaved(outputs, processCount);
+
+ const dsp56k::TWord* in[] = { outputs[0], outputs[1], outputs[2], outputs[3], outputs[4], outputs[5], nullptr, nullptr };
+ esaiC.processAudioInputInterleaved(in, processCount);
+
+ // read output of vexp2 and send to main
+ esaiC.processAudioOutputInterleaved(outputs, processCount);
+
+ // RX1/2 = vexp2 output TX1/TX2
+ const dsp56k::TWord* inA[] = { inputs[1], inputs[0], outputs[2], outputs[3], outputs[4], outputs[5], nullptr, nullptr };
+ esaiA.processAudioInputInterleaved(inA, processCount);
+
+ // final output 0,1,2 = audio outs below
+ }
+ else
+ {
+ esai.processAudioInputInterleaved(inputs, processCount, _latency);
+ }
+
+ const auto requiredSize = processCount > 4 ? (processCount << 1) - 8 : 0;
+
+ if(esai.getAudioOutputs().size() < requiredSize)
+ {
+ // reduce thread contention by waiting for output buffer to be full enough to let us grab the data without entering the read mutex too often
+
+ std::unique_lock uLock(m_requestedFramesAvailableMutex);
+ m_requestedFrames = requiredSize;
+ m_requestedFramesAvailableCv.wait(uLock, [&]()
+ {
+ if(esai.getAudioOutputs().size() < requiredSize)
+ return false;
+ m_requestedFrames = 0;
+ return true;
+ });
+ }
+
+ esai.processAudioOutputInterleaved(outputs, processCount);
+
+ if constexpr (g_useVoiceExpansion)
+ {
+ for (uint32_t i = 1; i < 3; ++i)
+ {
+ auto& e = m_dsps[i].getPeriph().getEsai();
+
+ dsp56k::TWord* outs[16]{ nullptr };
+ if (e.getAudioOutputs().size() >= 512)
+ e.processAudioOutputInterleaved(outs, static_cast<uint32_t>(e.getAudioOutputs().size() >> 1));
+ }
+ }
+
+ inputs[0] += processCount;
+ inputs[1] += processCount;
+
+ outputs[0] += processCount;
+ outputs[1] += processCount;
+ outputs[2] += processCount;
+ outputs[3] += processCount;
+ outputs[4] += processCount;
+ outputs[5] += processCount;
+ }
+
+ m_processAudio = false;
+ }
+
+ void Hardware::ensureBufferSize(uint32_t _frames)
+ {
+ if(m_audioInputs.front().size() < _frames)
+ {
+ for (auto& input : m_audioInputs)
+ input.resize(_frames);
+ }
+
+ if(m_audioOutputs.front().size() < _frames)
+ {
+ for (auto& output : m_audioOutputs)
+ output.resize(_frames);
+ }
+ }
+}
diff --git a/source/mqLib/mqhardware.h b/source/mqLib/mqhardware.h
@@ -0,0 +1,104 @@
+#pragma once
+
+#include <string>
+
+#include "mqbuildconfig.h"
+#include "mqdsp.h"
+#include "mqmc.h"
+#include "mqtypes.h"
+#include "mqmidi.h"
+#include "rom.h"
+
+#include "dsp56kEmu/dspthread.h"
+
+#include "../synthLib/midiTypes.h"
+
+namespace mqLib
+{
+ class Hardware
+ {
+ static constexpr uint32_t g_dspCount = g_useVoiceExpansion ? 3 : 1;
+
+ public:
+ explicit Hardware(std::string _romFilename);
+ ~Hardware();
+
+ bool process();
+
+ MqMc& getUC() { return m_uc; }
+ MqDsp& getDSP(uint32_t _index = 0) { return m_dsps[_index]; }
+ uint64_t getUcCycles() const { return m_uc.getCycles(); }
+
+ auto& getAudioInputs() { return m_audioInputs; }
+ auto& getAudioOutputs() { return m_audioOutputs; }
+
+ void sendMidi(const synthLib::SMidiEvent& _ev);
+ void receiveMidi(std::vector<uint8_t>& _data);
+
+ dsp56k::DSPThread& getDspThread(uint32_t _index = 0) { return m_dsps[_index].thread(); }
+
+ void processAudio(uint32_t _frames, uint32_t _latency = 0);
+
+ void ensureBufferSize(uint32_t _frames);
+
+ void ucThreadTerminated()
+ {
+ resumeDSP();
+ }
+
+ void setBootMode(BootMode _mode);
+
+ bool isValid() const { return m_rom.isValid(); }
+
+ bool isBootCompleted() const { return m_bootCompleted; }
+ void resetMidiCounter();
+
+ void ucYieldLoop(const std::function<bool()>& _continue);
+ void initVoiceExpansion();
+
+ private:
+ void setupEsaiListener();
+ void hdiProcessUCtoDSPNMIIrq();
+ void processUcCycle();
+ void haltDSP();
+ void resumeDSP();
+ void setGlobalDefaultParameters();
+ void syncUcToDSP();
+ void processMidiInput();
+
+ const std::string m_romFileName;
+
+ const ROM m_rom;
+
+ bool m_requestNMI = false;
+
+ // timing
+ uint32_t m_esaiFrameIndex = 0;
+ uint32_t m_lastEsaiFrameIndex = 0;
+ int64_t m_remainingUcCycles = 0;
+ double m_remainingUcCyclesD = 0;
+
+ MqMc m_uc;
+ std::array<MqDsp,g_dspCount> m_dsps;
+
+ Midi m_midi;
+ dsp56k::RingBuffer<synthLib::SMidiEvent, 1024, true> m_midiIn;
+ uint32_t m_midiOffsetCounter = 0;
+
+ TAudioInputs m_audioInputs;
+ TAudioOutputs m_audioOutputs;
+
+ std::mutex m_esaiFrameAddedMutex;
+ std::condition_variable m_esaiFrameAddedCv;
+
+ std::mutex m_requestedFramesAvailableMutex;
+ std::condition_variable m_requestedFramesAvailableCv;
+ size_t m_requestedFrames = 0;
+
+ bool m_haltDSP = false;
+ std::condition_variable m_haltDSPcv;
+ std::mutex m_haltDSPmutex;
+ bool m_processAudio = false;
+ bool m_bootCompleted = false;
+ };
+}
diff --git a/source/mqLib/mqmc.cpp b/source/mqLib/mqmc.cpp
@@ -0,0 +1,345 @@
+#include "mqmc.h"
+
+#include <array>
+#include <fstream>
+
+#include "rom.h"
+
+#include <cstring> // memcpy
+
+#include "mqbuildconfig.h"
+#include "../mc68k/logging.h"
+
+namespace mqLib
+{
+ constexpr uint32_t g_romAddress = 0x80000;
+ constexpr uint32_t g_pcInitial = 0x80130;
+ constexpr uint32_t g_memorySize = 0x40000;
+
+ static std::string logChar(char _val)
+ {
+ std::stringstream ss;
+ if(_val > 32 && _val < 127)
+ ss << _val;
+ else
+ ss << "[" << MCHEXN(static_cast<uint8_t>(_val), 2) << "]";
+ return ss.str();
+ }
+
+ MqMc::MqMc(const ROM& _rom) : m_rom(_rom)
+ {
+ if(!_rom.isValid())
+ return;
+ m_romRuntimeData.resize(ROM::getSize());
+ memcpy(m_romRuntimeData.data(), m_rom.getData(), ROM::getSize());
+
+ m_flash.reset(new Am29f(m_romRuntimeData.data(), m_romRuntimeData.size(), false, true));
+
+ m_memory.resize(g_memorySize, 0);
+
+ reset();
+
+ setPC(g_pcInitial);
+
+#if 0
+ dumpAssembly(g_romAddress, g_romSize);
+#endif
+ }
+
+ MqMc::~MqMc() = default;
+
+// uint64_t g_instructionCounter = 0;
+// std::deque<uint32_t> g_lastPCs;
+
+ uint32_t MqMc::exec()
+ {
+// if(g_instructionCounter >= 17300000)
+// MCLOG("Exec @ " << MCHEX(getPC()));
+/*
+ g_lastPCs.push_back(getPC());
+ if(g_lastPCs.size() > 100)
+ g_lastPCs.pop_front();
+*/
+// ++g_instructionCounter;
+
+ const uint32_t deltaCycles = Mc68k::exec();
+
+ m_hdi08a.exec(deltaCycles);
+
+ if constexpr (g_useVoiceExpansion)
+ {
+ m_hdi08b.exec(deltaCycles);
+ m_hdi08c.exec(deltaCycles);
+ }
+
+ const bool resetIsOutput = getPortQS().getDirection() & (1<<3);
+ if(resetIsOutput)
+ {
+ if(!(getPortQS().read() & (1<<3)))
+ {
+ if(!m_dspResetRequest)
+ {
+#ifdef _DEBUG
+ MCLOG("Request DSP RESET");
+#endif
+ m_dspResetRequest = true;
+ m_dspResetCompleted = false;
+ }
+ }
+ }
+ else
+ {
+ if(m_dspResetCompleted)
+ {
+ m_dspResetRequest = false;
+ getPortQS().writeRX(1<<3);
+ }
+ }
+
+ if(getPortQS().getDirection() & (1<<6))
+ {
+ m_dspInjectNmiRequest = (getPortQS().read() >> 6) & 1;
+ if(m_dspInjectNmiRequest)
+ int debug=0;
+ }
+
+ m_buttons.processButtons(getPortGP(), getPortE());
+
+ if(m_lcd.exec(getPortGP(), getPortF()))
+ {
+// const std::string s(&m_lcd.getDdRam().front());
+// if(s.find("SIG") != std::string::npos)
+// dumpMemory("SIG");
+ }
+ else
+ {
+ m_leds.exec(getPortF(), getPortGP(), getPortE());
+ }
+
+ return deltaCycles;
+ }
+
+ void MqMc::notifyDSPBooted()
+ {
+ if(!m_dspResetCompleted)
+ MCLOG("DSP has booted");
+ m_dspResetCompleted = true;
+ }
+
+ uint16_t MqMc::readImm16(uint32_t addr)
+ {
+ if(addr < g_memorySize)
+ {
+ return readW(m_memory, addr);
+ }
+
+ if(addr >= g_romAddress && addr < g_romAddress + ROM::getSize())
+ {
+ const auto r = readW(m_romRuntimeData, addr - g_romAddress);
+// LOG("read16 from ROM addr=" << HEXN(addr, 8) << " val=" << HEXN(r, 4));
+ return r;
+ }
+// __debugbreak();
+ return 0;
+ }
+
+ uint16_t MqMc::read16(uint32_t addr)
+ {
+ if(addr < g_memorySize)
+ {
+ return readW(m_memory, addr);
+ }
+
+ if(addr >= g_romAddress && addr < g_romAddress + ROM::getSize())
+ {
+ const auto r = readW(m_romRuntimeData, addr - g_romAddress);
+// LOG("read16 from ROM addr=" << HEXN(addr, 8) << " val=" << HEXN(r, 4));
+ return r;
+ }
+
+ const auto pa = static_cast<mc68k::PeriphAddress>(addr & mc68k::g_peripheralMask);
+
+ if (m_hdi08a.isInRange(pa))
+ return m_hdi08a.read16(pa);
+
+ if constexpr (g_useVoiceExpansion)
+ {
+ if (m_hdi08b.isInRange(pa))
+ return m_hdi08b.read16(pa);
+ if (m_hdi08c.isInRange(pa))
+ return m_hdi08c.read16(pa);
+ }
+
+// LOG("read16 addr=" << HEXN(addr, 8) << ", pc=" << HEXN(getPC(), 8));
+
+ return Mc68k::read16(addr);
+ }
+
+ uint8_t MqMc::read8(uint32_t addr)
+ {
+ if(addr < g_memorySize)
+ return m_memory[addr];
+
+ if(addr >= g_romAddress && addr < g_romAddress + ROM::getSize())
+ return m_romRuntimeData[addr - g_romAddress];
+
+ const auto pa = static_cast<mc68k::PeriphAddress>(addr & mc68k::g_peripheralMask);
+
+ if (m_hdi08a.isInRange(pa))
+ return m_hdi08a.read8(pa);
+ if constexpr (g_useVoiceExpansion)
+ {
+ if (m_hdi08b.isInRange(pa))
+ return m_hdi08b.read8(pa);
+ if (m_hdi08c.isInRange(pa))
+ return m_hdi08c.read8(pa);
+ }
+
+// LOG("read8 addr=" << HEXN(addr, 8) << ", pc=" << HEXN(getPC(), 8));
+
+ return Mc68k::read8(addr);
+ }
+
+ void MqMc::write16(uint32_t addr, uint16_t val)
+ {
+ // Dump memory if DSP test reaches error state
+ if(addr == 0x384A8)
+ {
+ if(val > 0 && val <= 0xff)
+ dumpMemory((std::string("DSPTest_Error_") + std::to_string(val)).c_str());
+ }
+
+ if(addr < g_memorySize)
+ {
+ writeW(m_memory, addr, val);
+ return;
+ }
+
+ if(addr >= g_romAddress && addr < g_romAddress + ROM::getSize())
+ {
+ MCLOG("write16 TO ROM addr=" << MCHEXN(addr, 8) << ", value=" << MCHEXN(val,4) << ", pc=" << MCHEXN(getPC(), 8));
+ m_flash->write(addr - g_romAddress, val);
+ return;
+ }
+
+ const auto pa = static_cast<mc68k::PeriphAddress>(addr & mc68k::g_peripheralMask);
+
+ if (m_hdi08a.isInRange(pa))
+ {
+ m_hdi08a.write16(pa, val);
+ return;
+ }
+
+ if constexpr (g_useVoiceExpansion)
+ {
+ if (m_hdi08b.isInRange(pa))
+ {
+ m_hdi08b.write16(pa, val);
+ return;
+ }
+ if (m_hdi08c.isInRange(pa))
+ {
+ m_hdi08c.write16(pa, val);
+ return;
+ }
+ }
+
+ Mc68k::write16(addr, val);
+ }
+
+ void MqMc::write8(uint32_t addr, uint8_t val)
+ {
+ // Dump memory if DSP test reaches error state
+ if(addr == 0x384A8)
+ {
+ if(val > 0)
+ dumpMemory((std::string("DSPTest_Error_") + std::to_string(val)).c_str());
+ }
+
+ if(addr < g_memorySize)
+ {
+ m_memory[addr] = val;
+ return;
+ }
+
+ if(addr >= g_romAddress && addr < g_romAddress + ROM::getSize())
+ {
+ MCLOG("write8 TO ROM addr=" << MCHEXN(addr, 8) << ", value=" << MCHEXN(val,2) << " char=" << logChar(val) << ", pc=" << MCHEXN(getPC(), 8));
+ m_flash->write(addr - g_romAddress, val);
+ return;
+ }
+
+// LOG("write8 addr=" << HEXN(addr, 8) << ", value=" << HEXN(val,2) << " char=" << logChar(val) << ", pc=" << HEXN(getPC(), 8));
+
+ const auto pa = static_cast<mc68k::PeriphAddress>(addr & mc68k::g_peripheralMask);
+ if (m_hdi08a.isInRange(pa))
+ {
+ m_hdi08a.write8(pa, val);
+ return;
+ }
+
+ if constexpr (g_useVoiceExpansion)
+ {
+ if (m_hdi08b.isInRange(pa))
+ {
+ m_hdi08b.write8(pa, val);
+ return;
+ }
+ if (m_hdi08c.isInRange(pa))
+ {
+ m_hdi08c.write8(pa, val);
+ return;
+ }
+ }
+
+ Mc68k::write8(addr, val);
+ }
+
+ void MqMc::dumpMemory(const char* _filename) const
+ {
+ FILE* hFile = fopen((std::string(_filename) + ".bin").c_str(), "wb");
+ fwrite(m_memory.data(), 1, m_memory.size(), hFile);
+ fclose(hFile);
+ }
+
+ void MqMc::dumpROM(const char* _filename) const
+ {
+ FILE* hFile = fopen((std::string(_filename) + ".bin").c_str(), "wb");
+ fwrite(m_romRuntimeData.data(), 1, ROM::getSize(), hFile);
+ fclose(hFile);
+ }
+
+ void MqMc::dumpAssembly(const uint32_t _first, const uint32_t _count)
+ {
+ std::stringstream ss;
+ ss << "mq_68k_" << _first << '-' << (_first + _count) << ".asm";
+
+ std::ofstream f(ss.str(), std::ios::out);
+
+ for(uint32_t i=_first; i<_first + _count;)
+ {
+ char disasm[64];
+ const auto opSize = disassemble(i, disasm);
+ f << MCHEXN(i,5) << ": " << disasm << std::endl;
+ if(!opSize)
+ ++i;
+ else
+ i += opSize;
+ }
+ f.close();
+ }
+
+ void MqMc::onReset()
+ {
+ dumpMemory("dump_reset");
+ }
+
+ uint32_t MqMc::onIllegalInstruction(uint32_t opcode)
+ {
+ std::stringstream ss;
+ ss << "illegalInstruction_" << MCHEXN(getPC(), 8) << "_op" << MCHEXN(opcode,8);
+ dumpMemory(ss.str().c_str());
+
+ return Mc68k::onIllegalInstruction(opcode);
+ }
+}
diff --git a/source/mqLib/mqmc.h b/source/mqLib/mqmc.h
@@ -0,0 +1,73 @@
+#pragma once
+
+#include <list>
+#include <memory>
+
+#include "buttons.h"
+#include "lcd.h"
+#include "leds.h"
+#include "am29f.h"
+
+#include "../mc68k/mc68k.h"
+#include "../mc68k/hdi08periph.h"
+
+namespace mqLib
+{
+ class ROM;
+
+ using MqHdi08A = mc68k::Hdi08Periph<0xfd000>;
+ using MqHdi08B = mc68k::Hdi08Periph<0xfb000>;
+ using MqHdi08C = mc68k::Hdi08Periph<0xfc000>;
+
+ class MqMc final : public mc68k::Mc68k
+ {
+ public:
+ explicit MqMc(const ROM& _rom);
+ ~MqMc() override;
+
+ uint32_t exec() override;
+
+ Buttons& getButtons() { return m_buttons; }
+ Leds& getLeds() { return m_leds; }
+ LCD& getLcd() { return m_lcd; }
+ MqHdi08A& getHdi08A() { return m_hdi08a; }
+ MqHdi08B& getHdi08B() { return m_hdi08b; }
+ MqHdi08C& getHdi08C() { return m_hdi08c; }
+
+ bool requestDSPReset() const { return m_dspResetRequest; }
+ void notifyDSPBooted();
+
+ uint8_t requestDSPinjectNMI() const { return m_dspInjectNmiRequest; }
+
+ void dumpMemory(const char* _filename) const;
+ void dumpROM(const char* _filename) const;
+ void dumpAssembly(uint32_t _first, uint32_t _count);
+
+ private:
+ uint16_t readImm16(uint32_t _addr) override;
+ uint16_t read16(uint32_t addr) override;
+ uint8_t read8(uint32_t addr) override;
+ void write16(uint32_t addr, uint16_t val) override;
+ void write8(uint32_t addr, uint8_t val) override;
+
+ void onReset() override;
+ uint32_t onIllegalInstruction(uint32_t opcode) override;
+
+ const ROM& m_rom;
+ std::vector<uint8_t> m_romRuntimeData;
+ std::unique_ptr<Am29f> m_flash;
+ LCD m_lcd;
+ Buttons m_buttons;
+ Leds m_leds;
+
+ MqHdi08A m_hdi08a;
+ MqHdi08B m_hdi08b;
+ MqHdi08C m_hdi08c;
+
+ std::vector<uint8_t> m_memory;
+ std::list<uint32_t> m_lastPCs;
+ bool m_dspResetRequest = false;
+ bool m_dspResetCompleted = false;
+ uint8_t m_dspInjectNmiRequest = 0;
+ };
+}
diff --git a/source/mqLib/mqmidi.cpp b/source/mqLib/mqmidi.cpp
@@ -0,0 +1,84 @@
+#include "mqmidi.h"
+
+#include <deque>
+
+#include "../mc68k/qsm.h"
+
+namespace mqLib
+{
+ static constexpr float g_sysexSendDelaySeconds = 0.2f;
+ static constexpr uint32_t g_sysexSendDelaySamples = static_cast<uint32_t>(44100.0f * g_sysexSendDelaySeconds);
+
+ Midi::Midi(mc68k::Qsm& _qsm) : m_qsm(_qsm)
+ {
+ }
+
+ void Midi::process(const uint32_t _numSamples)
+ {
+ if(m_remainingSysexDelay)
+ m_remainingSysexDelay -= std::min(m_remainingSysexDelay, _numSamples);
+
+ while(m_remainingSysexDelay == 0 && !m_transmittingSysex && !m_pendingSysexBuffers.empty())
+ {
+ const auto& msg = m_pendingSysexBuffers.front();
+
+ for (const auto b : msg)
+ m_qsm.writeSciRX(b);
+
+ if(msg.size() > 0xf)
+ m_remainingSysexDelay = g_sysexSendDelaySamples;
+
+ m_pendingSysexBuffers.pop_front();
+ }
+ }
+
+ void Midi::writeMidi(const uint8_t _byte)
+ {
+ if(_byte == 0xf0)
+ {
+ m_receivingSysex = true;
+ }
+
+ if(m_receivingSysex)
+ {
+ m_pendingSysexMessage.push_back(_byte);
+ }
+ else
+ {
+ m_qsm.writeSciRX(_byte);
+ }
+
+ if (_byte == 0xf7)
+ {
+ m_receivingSysex = false;
+
+ if (!m_pendingSysexMessage.empty())
+ m_pendingSysexBuffers.push_back(std::move(m_pendingSysexMessage));
+
+ m_pendingSysexMessage.clear();
+ }
+ }
+
+ void Midi::readTransmitBuffer(std::vector<uint8_t>& _result)
+ {
+ std::deque<uint16_t> midiData;
+ m_qsm.readSciTX(midiData);
+ if (midiData.empty())
+ return;
+
+ _result.clear();
+ _result.reserve(midiData.size());
+
+ for (const auto data : midiData)
+ {
+ const uint8_t d = data & 0xff;
+
+ if(d == 0xf0)
+ m_transmittingSysex = true;
+ else if(d == 0xf7)
+ m_transmittingSysex = false;
+
+ _result.push_back(d);
+ }
+ }
+}
diff --git a/source/mqLib/mqmidi.h b/source/mqLib/mqmidi.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <deque>
+#include <vector>
+#include <cstdint>
+
+namespace mc68k
+{
+ class Qsm;
+}
+
+namespace mqLib
+{
+ class Midi
+ {
+ public:
+ explicit Midi(mc68k::Qsm& _qsm);
+
+ void process(uint32_t _numSamples);
+
+ void writeMidi(uint8_t _byte);
+ void writeMidi(const std::initializer_list<uint8_t>& _bytes)
+ {
+ for (const uint8_t byte : _bytes)
+ writeMidi(byte);
+ }
+ void writeMidi(const std::vector<uint8_t>& _bytes)
+ {
+ for (const uint8_t byte : _bytes)
+ writeMidi(byte);
+ }
+ void readTransmitBuffer(std::vector<uint8_t>& _result);
+
+ private:
+ mc68k::Qsm& m_qsm;
+
+ bool m_transmittingSysex = false;
+ bool m_receivingSysex = false;
+ uint32_t m_remainingSysexDelay = 0;
+
+ std::deque< std::vector<uint8_t> > m_pendingSysexBuffers;
+ std::vector<uint8_t> m_pendingSysexMessage;
+ };
+}
diff --git a/source/mqLib/mqmiditypes.h b/source/mqLib/mqmiditypes.h
@@ -0,0 +1,227 @@
+#pragma once
+
+#include <cstdint>
+
+namespace mqLib
+{
+ enum MidiHeaderByte : uint8_t
+ {
+ IdWaldorf = 0x3e,
+ IdQ = 0x0f,
+ IdMicroQ = 0x10,
+ IdDeviceOmni = 0x7f
+ };
+
+ enum class SysexCommand : uint8_t
+ {
+ Invalid = 0xff,
+
+ SingleRequest = 0x00, SingleDump = 0x10, SingleParameterChange = 0x20, SingleParameterRequest = 0x30,
+ MultiRequest = 0x01, MultiDump = 0x11, MultiParameterChange = 0x21, MultiParameterRequest = 0x31,
+ DrumRequest = 0x02, DrumDump = 0x12, DrumParameterChange = 0x22, DrumParameterRequest = 0x32,
+ GlobalRequest = 0x04, GlobalDump = 0x14, GlobalParameterChange = 0x24, GlobalParameterRequest = 0x34,
+ ModeRequest = 0x07, ModeDump = 0x17, ModeParameterChange = 0x27, ModeParameterRequest = 0x37,
+
+ EmuLCD = 0x50,
+ EmuLEDs = 0x51,
+ EmuButtons = 0x52,
+ EmuRotaries = 0x53,
+ EmuLCDCGRata = 0x54
+ };
+
+ enum class MidiBufferNum : uint8_t
+ {
+ DeprecatedSingleBankA = 0x00,
+ DeprecatedSingleBankB = 0x01,
+ DeprecatedSingleBankC = 0x02,
+ DeprecatedSingleBankX = 0x03,
+ AllSounds = 0x10,
+ SingleEditBufferSingleMode = 0x20,
+ SingleEditBufferMultiMode = 0x30,
+ EditBufferSingleLayer = 0x30,
+ EditBufferDrumMap = 0x30,
+ SingleBankA = 0x40,
+ SingleBankB = 0x41,
+ SingleBankC = 0x42,
+ SingleBankX = 0x48,
+
+ DeprecatedMultiBankInternal = 0x00,
+ DeprecatedMultiBankCard = 0x03,
+ MultiEditBuffer = 0x20,
+ MultiBankInternal = 0x40,
+ MultiBankCard = 0x48,
+
+ DeprecatedDrumBankInternal = 0x00,
+ DeprecatedDrumBankCard = 0x01,
+ DrumEditBuffer = 0x20,
+ DrumBankInternal = 0x40,
+ DrumBankCard = 0x48, // ?? midi doc says $40, but that is for the internal one, assuming 48 is meant, similar to all other card buffers
+ };
+
+ enum class MidiSoundLocation : uint8_t
+ {
+ AllSinglesBankA = 0x40,
+ AllSinglesBankB,
+ AllSinglesBankC,
+ AllSinglesBankX = 0x48,
+ EditBufferCurrentSingle = 0x00,
+ EditBufferFirstMultiSingle = 0x00,
+ EditBufferFirstSingleLayer = 0x00,
+ EditBufferFirstDrumMapInstrument = 0x10,
+ };
+
+ enum class GlobalParameter : uint8_t
+ {
+ // Global data
+ Version,
+
+ // Initial instrument settings
+ InstrumentSelection = 20,
+ SingleMultiMode = 21,
+ MultiNumber = 22,
+
+ // Instr. 1-4
+ InstrumentASingleNumber = 1, InstrumentBSingleNumber, InstrumentCSingleNumber, InstrumentDSingleNumber,
+ InstrumentABankNumber = 9, InstrumentBBankNumber, InstrumentCBankNumber, InstrumentDBankNumber,
+
+ // Pedal/CV
+ PedalOffset = 70,
+ PedalGain,
+ PedalCurve,
+ PedalControl,
+
+ // MIDI Setup
+ Tuning = 5,
+ Transpose,
+ ControllerSend,
+ ControllerReceive,
+ ControllerW = 53,
+ ControllerX,
+ ControllerY,
+ ControllerZ,
+ ArpSend = 15,
+ Clock = 19,
+ MidiChannel = 24,
+ SysExDeviceId,
+ LocalControl,
+
+ // Program change
+ ProgramChangeRx = 57,
+ ProgramChangeTx = 74,
+
+ // Display Setup
+ PopupTime = 27,
+ LabelTime,
+ DisplayContrast,
+
+ // Keyboard setp
+ OnVelocityCurve = 30,
+ ReleaseVelocityCurve,
+ PressureCurve,
+
+ // External input
+ InputGain = 33,
+
+ // FX setup
+ GlobalLinkFX2 = 35,
+
+ // Mix in
+ Send = 58,
+ Level
+ };
+
+ enum class MultiParameter
+ {
+ Volume = 0,
+
+ ControlW = 1, ControlX, ControlY, ControlZ,
+
+ Name00 = 16, Name01, Name02, Name03, Name04, Name05, Name06, Name07, Name08, Name09, Name10, Name11, Name12, Name13, Name14, Name15,
+
+ Inst0SoundBank = 32,
+ Inst0SoundNumber,
+ Inst0MidiChannel,
+ Inst0Volume,
+ Inst0Transpose,
+ Inst0Detune,
+ Inst0Output,
+ Inst0Flags,
+ Inst0Pan,
+ Inst0ReservedA,
+ Inst0ReservedB,
+ Inst0Pattern,
+ Inst0VeloLow,
+ Inst0VeloHigh,
+ Inst0KeyLow,
+ Inst0KeyHigh,
+ Inst0MidiRxFlags,
+
+ Inst0 = Inst0SoundBank, Inst1 = 54, Inst2 = 76, Inst3 = 98,
+ Inst4 = 120, Inst5 = 142, Inst6 = 164, Inst7 = 186,
+ Inst8 = 208, Inst9 = 230, Inst10 = 252, Inst11 = 274,
+ Inst12 = 296, Inst13 = 318, Inst14 = 340, Inst15 = 362,
+
+ Last = 378,
+
+ Padding0,
+ Padding1,
+ Padding2,
+ Padding3,
+ Padding4,
+
+ Count
+ };
+
+ enum SysexIndex
+ {
+ IdxSysexBegin = 0,
+ IdxIdWaldorf = 1,
+ IdxIdMicroQ = 2,
+ IdxDeviceId = 3,
+ IdxCommand = 4,
+
+ // dumps / dump requests
+ IdxBuffer = 5,
+ IdxLocation = 6,
+
+ // first parameter of a dump
+ IdxSingleParamFirst = 7,
+ IdxMultiParamFirst = IdxSingleParamFirst,
+ IdxDrumParamFirst = IdxSingleParamFirst,
+ IdxGlobalParamFirst = IdxBuffer,
+ IdxModeParamFirst = IdxBuffer,
+
+ IdxSingleParamIndexH = IdxBuffer + 1,
+ IdxSingleParamIndexL = IdxSingleParamIndexH + 1,
+ IdxSingleParamValue = IdxSingleParamIndexL + 1,
+
+ IdxMultiParamIndexH = IdxBuffer,
+ IdxMultiParamIndexL = IdxMultiParamIndexH + 1,
+ IdxMultiParamValue = IdxMultiParamIndexL + 1,
+
+ IdxDrumParamIndexH = IdxBuffer,
+ IdxDrumParamIndexL = IdxMultiParamIndexH + 1,
+ IdxDrumParamValue = IdxMultiParamIndexL + 1,
+
+ IdxGlobalParamIndexH = IdxBuffer,
+ IdxGlobalParamIndexL = IdxGlobalParamIndexH + 1,
+ IdxGlobalParamValue = IdxGlobalParamIndexL + 1,
+
+ IdxModeParamIndexH = IdxBuffer,
+ IdxModeParamIndexL = IdxModeParamIndexH + 1,
+ IdxModeParamValue = IdxModeParamIndexL + 1
+ };
+
+ enum class BankSelectLSB : uint8_t
+ {
+ BsDeprecatedSingleBankA = 0x0,
+ BsDeprecatedSingleBankB = 0x1,
+ BsDeprecatedSingleBankC = 0x2,
+ BsDeprecatedDrumBank = 0x4,
+ BsSingleBankA = 0x40,
+ BsSingleBankB = 0x41,
+ BsSingleBankC = 0x42,
+ BsDrumBank = 0x50,
+ BsMultiBank = 0x60,
+ };
+}
diff --git a/source/mqLib/mqstate.cpp b/source/mqLib/mqstate.cpp
@@ -0,0 +1,840 @@
+#include "mqstate.h"
+
+#include <cassert>
+#include <cstring>
+
+#include "mqmiditypes.h"
+#include "microq.h"
+
+#include "../synthLib/os.h"
+#include "../synthLib/midiToSysex.h"
+#include "../synthLib/midiBufferParser.h"
+#include "dsp56kEmu/logging.h"
+
+namespace mqLib
+{
+ static_assert(std::size(State::g_dumps) == static_cast<uint32_t>(State::DumpType::Count), "data definition missing");
+
+ State::State(MicroQ& _mq) : m_mq(_mq)
+ {
+ }
+
+ bool State::loadState(const SysEx& _sysex)
+ {
+ std::vector<std::vector<uint8_t>> messages;
+ synthLib::MidiToSysex::splitMultipleSysex(messages, _sysex);
+
+ if(messages.empty())
+ return false;
+
+ Responses nop;
+
+ for (const auto& message : messages)
+ receive(nop, message, Origin::External);
+
+ // if device receives a multi, it switches to multi mode. Switch back to single mode if single mode was requested
+ if(getGlobalParameter(GlobalParameter::SingleMultiMode) == 0)
+ sendGlobalParameter(GlobalParameter::SingleMultiMode, 0);
+
+ return true;
+ }
+
+ bool State::receive(Responses& _responses, const synthLib::SMidiEvent& _data, Origin _sender)
+ {
+ if(!_data.sysex.empty())
+ {
+ return receive(_responses, _data.sysex, _sender);
+ }
+
+ if (_sender == Origin::Device)
+ LOG("Recv: " << HEXN(_data.a, 2) << ' ' << HEXN(_data.b, 2) << ' ' << HEXN(_data.c, 2))
+
+ switch(_data.a & 0xf0)
+ {
+ case synthLib::M_CONTROLCHANGE:
+ switch(_data.b)
+ {
+ case synthLib::MC_BANKSELECTMSB:
+ m_lastBankSelectMSB = _data;
+ break;
+ case synthLib::MC_BANKSELECTLSB:
+ m_lastBankSelectLSB = _data;
+ break;
+ default:
+ return false;
+ }
+ break;
+ case synthLib::M_PROGRAMCHANGE:
+ switch(static_cast<BankSelectLSB>(m_lastBankSelectLSB.c))
+ {
+ case BankSelectLSB::BsDeprecatedSingleBankA:
+ case BankSelectLSB::BsDeprecatedSingleBankB:
+ case BankSelectLSB::BsDeprecatedSingleBankC:
+ case BankSelectLSB::BsSingleBankA:
+ case BankSelectLSB::BsSingleBankB:
+ case BankSelectLSB::BsSingleBankC:
+ if(getGlobalParameter(GlobalParameter::SingleMultiMode) == 0)
+ requestSingle(MidiBufferNum::SingleEditBufferSingleMode, MidiSoundLocation::EditBufferCurrentSingle);
+ break;
+ case BankSelectLSB::BsMultiBank:
+ if(getGlobalParameter(GlobalParameter::SingleMultiMode) != 0)
+ requestMulti(MidiBufferNum::MultiEditBuffer, 0);
+ break;
+ default:
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ return false;
+ }
+
+ bool State::receive(Responses& _responses, const SysEx& _data, Origin _sender)
+ {
+ const auto cmd = getCommand(_data);
+
+ if(cmd == SysexCommand::Invalid)
+ return false;
+
+ m_sender = _sender;
+ m_isEditBuffer = false;
+
+ switch (cmd)
+ {
+ case SysexCommand::SingleRequest: return getDump(DumpType::Single, _responses, _data);
+ case SysexCommand::MultiRequest: return getDump(DumpType::Multi,_responses, _data);
+ case SysexCommand::DrumRequest: return getDump(DumpType::Drum, _responses, _data);
+ case SysexCommand::GlobalRequest: return getDump(DumpType::Global, _responses, _data);
+ case SysexCommand::ModeRequest: return getDump(DumpType::Mode, _responses, _data);
+
+ case SysexCommand::SingleDump: return parseDump(DumpType::Single, _data);
+ case SysexCommand::MultiDump: return parseDump(DumpType::Multi, _data);
+ case SysexCommand::DrumDump: return parseDump(DumpType::Drum, _data);
+ case SysexCommand::GlobalDump: return parseDump(DumpType::Global, _data);
+ case SysexCommand::ModeDump: return parseDump(DumpType::Mode, _data);
+
+ case SysexCommand::SingleParameterChange: return modifyDump(DumpType::Single, _data);
+ case SysexCommand::MultiParameterChange: return modifyDump(DumpType::Multi, _data);
+ case SysexCommand::DrumParameterChange: return modifyDump(DumpType::Drum, _data);
+ case SysexCommand::GlobalParameterChange: return modifyDump(DumpType::Global, _data);
+ case SysexCommand::ModeParameterChange: return modifyDump(DumpType::Mode, _data);
+
+ case SysexCommand::SingleParameterRequest: return requestDumpParameter(DumpType::Single, _responses, _data);
+ case SysexCommand::MultiParameterRequest: return requestDumpParameter(DumpType::Multi, _responses, _data);
+ case SysexCommand::DrumParameterRequest: return requestDumpParameter(DumpType::Drum, _responses, _data);
+ case SysexCommand::GlobalParameterRequest: return requestDumpParameter(DumpType::Global, _responses, _data);
+ case SysexCommand::ModeParameterRequest: return requestDumpParameter(DumpType::Mode, _responses, _data);
+
+/* case SysexCommand::EmuLCD:
+ case SysexCommand::EmuLEDs:
+ case SysexCommand::EmuButtons:
+ case SysexCommand::EmuRotaries:
+ return false;
+*/ default:
+ return false;
+ }
+ }
+
+ void State::createInitState()
+ {
+ // request global settings and wait for them. Once they are valid, send init state
+ requestGlobal();
+// m_mq.sendMidi({0xf0, IdWaldorf, IdMicroQ, IdDeviceOmni, static_cast<uint8_t>(SysexCommand::ModeRequest), 0xf7});
+
+ synthLib::MidiBufferParser parser;
+ Responses unused;
+ std::vector<uint8_t> midi;
+ std::vector<synthLib::SMidiEvent> events;
+
+ while(!isValid(m_global))// || !isValid(m_mode))
+ {
+ m_mq.process(8);
+ midi.clear();
+ m_mq.receiveMidi(midi);
+ parser.write(midi);
+
+ events.clear();
+ parser.getEvents(events);
+
+ for (const auto & event : events)
+ {
+ if(!event.sysex.empty())
+ {
+ if(!receive(unused, event.sysex, Origin::Device))
+ assert(false);
+ }
+ }
+ }
+
+ auto setParam = [&](GlobalParameter _param, uint8_t _value)
+ {
+ setGlobalParameter(_param, _value);
+ };
+
+ setParam(GlobalParameter::InstrumentSelection, 0); // Select first instrument
+ setParam(GlobalParameter::MultiNumber, 0); // First Multi
+
+ setParam(GlobalParameter::InstrumentABankNumber, 0); // First bank for all, singles 0-3
+ setParam(GlobalParameter::InstrumentASingleNumber, 0); //
+ setParam(GlobalParameter::InstrumentBBankNumber, 0); //
+ setParam(GlobalParameter::InstrumentBSingleNumber, 1); //
+ setParam(GlobalParameter::InstrumentCBankNumber, 0); //
+ setParam(GlobalParameter::InstrumentCSingleNumber, 2); //
+ setParam(GlobalParameter::InstrumentDBankNumber, 0); //
+ setParam(GlobalParameter::InstrumentDSingleNumber, 3); //
+
+ setParam(GlobalParameter::Tuning, 64); // 440 Hz
+ setParam(GlobalParameter::Transpose, 64); // +/- 0
+ setParam(GlobalParameter::ControllerSend, 2); // SysEx
+ setParam(GlobalParameter::ControllerReceive, 1); // On
+ setParam(GlobalParameter::ArpSend, 0); // Off
+ setParam(GlobalParameter::Clock, 2); // Auto
+ setParam(GlobalParameter::MidiChannel, 0); // omni
+ setParam(GlobalParameter::SysExDeviceId, 0); // 0
+ setParam(GlobalParameter::LocalControl, 1); // On
+ setParam(GlobalParameter::ProgramChangeRx, 2); // Number + Bank
+ setParam(GlobalParameter::ProgramChangeTx, 2); // Number + Bank
+ setParam(GlobalParameter::SingleMultiMode, 0); // Single mode
+
+ receive(unused, convertTo(m_global), Origin::External);
+
+ // send default multi
+ std::vector<uint8_t> defaultMultiData;
+ createSequencerMultiData(defaultMultiData);
+ sendMulti(defaultMultiData);
+
+ std::vector<uint8_t> sysex;
+
+ // accept files up to 300k as larger files might be the OS
+ const auto midifile = synthLib::findFile(".mid", 0, 300 * 1024);
+ if(!midifile.empty())
+ {
+ synthLib::MidiToSysex::readFile(sysex, midifile.c_str());
+ }
+
+ if(sysex.empty())
+ {
+ const auto syxFile = synthLib::findFile(".syx", 0, 300 * 1024);
+ if(!syxFile.empty())
+ synthLib::readFile(sysex, syxFile);
+ }
+
+ if (!sysex.empty())
+ {
+ std::vector<std::vector<uint8_t>> messages;
+ synthLib::MidiToSysex::splitMultipleSysex(messages, sysex);
+
+ for (const auto& message : messages)
+ {
+ switch (getCommand(message))
+ {
+ case SysexCommand::SingleDump:
+ case SysexCommand::MultiDump:
+ case SysexCommand::DrumDump:
+ {
+ auto m = message;
+ m[IdxDeviceId] = IdDeviceOmni;
+ loadState(m);
+ }
+ break;
+ default:;
+ }
+ }
+ }
+
+ // switch to Single mode as the multi dump causes it to go to Multi mode
+ sendGlobalParameter(GlobalParameter::SingleMultiMode, 0);
+
+ if(isValid(m_romSingles[0]))
+ {
+ auto dump = convertTo(m_romSingles[0]);
+
+ dump[IdxBuffer] = static_cast<uint8_t>(MidiBufferNum::SingleEditBufferSingleMode);
+ dump[IdxLocation] = 0;
+
+ forwardToDevice(dump);
+ }
+ }
+
+ bool State::getState(std::vector<uint8_t>& _state, synthLib::StateType _type) const
+ {
+ append(_state, m_global);
+
+ const auto isMultiMode = getGlobalParameter(GlobalParameter::SingleMultiMode) != 0;
+
+// append(_state, m_currentDrumMap);
+
+ append(_state, m_currentMulti);
+
+ for(size_t i=0; i<m_currentMultiSingles.size(); ++i)
+ {
+ const auto& s = (isMultiMode || i >= m_currentInstrumentSingles.size()) ? m_currentMultiSingles[i] : m_currentInstrumentSingles[i];
+ append(_state, s);
+ }
+
+// for (const auto& s : m_currentDrumMapSingles)
+// append(_state, s);
+
+ return !_state.empty();
+ }
+
+ bool State::setState(const std::vector<uint8_t>& _state, synthLib::StateType _type)
+ {
+ return loadState(_state);
+ }
+
+ bool State::parseSingleDump(const SysEx& _data)
+ {
+ Single single;
+
+ if(!convertTo(single, _data))
+ return false;
+
+ const auto buf = static_cast<MidiBufferNum>(_data[IdxBuffer]);
+ const auto loc = _data[IdxLocation];
+
+ Single* dst = getSingle(buf, loc);
+
+ if(!dst)
+ return false;
+ *dst = single;
+ return true;
+ }
+
+ bool State::parseMultiDump(const SysEx& _data)
+ {
+ Multi multi;
+
+ if(!convertTo(multi, _data))
+ return false;
+
+ const auto buf = static_cast<MidiBufferNum>(_data[IdxBuffer]);
+ const auto loc = _data[IdxLocation];
+
+ auto* m = getMulti(buf, loc);
+ if(!m)
+ return false;
+ *m = multi;
+ return true;
+ }
+
+ bool State::parseDrumDump(const SysEx& _data)
+ {
+ DrumMap drum;
+
+ if(!convertTo(drum, _data))
+ return false;
+
+ const auto buf = static_cast<MidiBufferNum>(_data[IdxBuffer]);
+ const auto loc = _data[IdxLocation];
+
+ auto* d = getDrumMap(buf, loc);
+ if(!d)
+ return false;
+ *d = drum;
+ return true;
+ }
+
+ bool State::parseGlobalDump(const SysEx& _data)
+ {
+ return convertTo(m_global, _data);
+ }
+
+ bool State::parseModeDump(const SysEx& _data)
+ {
+ return convertTo(m_mode, _data);
+ }
+
+ bool State::modifySingle(const SysEx& _data)
+ {
+ auto* p = getSingleParameter(_data);
+ if(!p)
+ return false;
+ *p = _data[IdxSingleParamValue];
+ return true;
+ }
+
+ bool State::modifyMulti(const SysEx& _data)
+ {
+ auto* p = getMultiParameter(_data);
+ if(!p)
+ return false;
+
+ *p = _data[IdxMultiParamValue];
+ return true;
+ }
+
+ bool State::modifyDrum(const SysEx& _data)
+ {
+ auto* p = getDrumParameter(_data);
+ if(!p)
+ return false;
+
+ *p = _data[IdxDrumParamValue];
+ return true;
+ }
+
+ bool State::modifyGlobal(const SysEx& _data)
+ {
+ auto* p = getGlobalParameter(_data);
+ if(!p)
+ return false;
+
+ if(*p == _data[IdxGlobalParamValue])
+ return true;
+
+ *p = _data[IdxGlobalParamValue];
+
+ // if the play mode is changed, request the edit buffer for the first single again, because on the mQ, that edit buffer is shared between multi & single
+ const auto param = (_data[IdxGlobalParamIndexH] << 7) | _data[IdxGlobalParamIndexL];
+
+ if(param == static_cast<uint32_t>(GlobalParameter::SingleMultiMode))
+ {
+ const auto v = _data[IdxGlobalParamValue];
+
+ requestSingle(v ? MidiBufferNum::SingleEditBufferMultiMode : MidiBufferNum::SingleEditBufferSingleMode, MidiSoundLocation::EditBufferFirstMultiSingle);
+ }
+
+ return true;
+ }
+
+ bool State::modifyMode(const SysEx& _data)
+ {
+ auto* p = getModeParameter(_data);
+ if(!p)
+ return false;
+
+ *p = _data[IdxModeParamValue];
+ return true;
+ }
+
+ template<size_t Size>
+ static uint8_t* getParameter(std::array<uint8_t, Size>& _dump, const SysEx& _data, State::DumpType _type)
+ {
+ const auto& dump = State::g_dumps[static_cast<uint32_t>(_type)];
+
+ if(dump.idxParamIndexH >= _data.size() || dump.idxParamIndexL >= _data.size())
+ return nullptr;
+
+ const auto i = dump.firstParamIndex + ((static_cast<uint32_t>(_data[dump.idxParamIndexH]) << 7) | static_cast<uint32_t>(_data[dump.idxParamIndexL]));
+
+ if(i > _dump.size())
+ return nullptr;
+ return &_dump[i];
+ }
+
+ uint8_t* State::getSingleParameter(const SysEx& _data)
+ {
+ const auto loc = _data[IdxBuffer];
+
+ Single* s = getSingle(getGlobalParameter(GlobalParameter::SingleMultiMode) ? MidiBufferNum::SingleEditBufferMultiMode : MidiBufferNum::SingleEditBufferSingleMode, loc);
+ if(!s)
+ return nullptr;
+ return getParameter(*s, _data, DumpType::Single);
+ }
+
+ uint8_t* State::getMultiParameter(const SysEx& _data)
+ {
+ return getParameter(m_currentMulti, _data, DumpType::Multi);
+ }
+
+ uint8_t* State::getDrumParameter(const SysEx& _data)
+ {
+ return getParameter(m_currentDrumMap, _data, DumpType::Drum);
+ }
+
+ uint8_t* State::getGlobalParameter(const SysEx& _data)
+ {
+ return getParameter(m_global, _data, DumpType::Global);
+ }
+
+ uint8_t* State::getModeParameter(const SysEx& _data)
+ {
+ return getParameter(m_mode, _data, DumpType::Mode);
+ }
+
+ bool State::getSingle(Responses& _responses, const SysEx& _data)
+ {
+ const auto buf = static_cast<MidiBufferNum>(_data[IdxBuffer]);
+ const auto loc = _data[IdxLocation];
+
+ const auto* s = getSingle(buf, loc);
+ if(!s || !isValid(*s))
+ return false;
+ _responses.push_back(convertTo(*s));
+ return true;
+ }
+
+ State::Single* State::getSingle(MidiBufferNum _buf, uint8_t _loc)
+ {
+ switch (_buf)
+ {
+ case MidiBufferNum::SingleBankA:
+ case MidiBufferNum::DeprecatedSingleBankA:
+ if(_loc >= 100)
+ return nullptr;
+ return &m_romSingles[_loc];
+ case MidiBufferNum::SingleBankB:
+ case MidiBufferNum::DeprecatedSingleBankB:
+ if(_loc >= 100)
+ return nullptr;
+ return &m_romSingles[_loc + 100];
+ case MidiBufferNum::SingleBankC:
+ case MidiBufferNum::DeprecatedSingleBankC:
+ if(_loc >= 100)
+ return nullptr;
+ return &m_romSingles[_loc + 200];
+ case MidiBufferNum::SingleBankX:
+ case MidiBufferNum::DeprecatedSingleBankX:
+ // mQ doesn't have a card, no idea why its mentioned in the MIDI implementaiton
+ return nullptr;
+ case MidiBufferNum::SingleEditBufferSingleMode:
+ m_isEditBuffer = true;
+ return m_currentInstrumentSingles.data();//getGlobalParameter(GlobalParameter::InstrumentSelection)];
+ case MidiBufferNum::SingleEditBufferMultiMode:
+// case MidiBufferNum::EditBufferSingleLayer: (same value as above)
+ {
+ m_isEditBuffer = true;
+ if(_loc >= 0x10 && _loc <= 0x2f)
+ return &m_currentDrumMapSingles[_loc - 0x10];
+ if(getGlobalParameter(GlobalParameter::SingleMultiMode) > 0)
+ {
+ if(_loc >= m_currentMultiSingles.size())
+ return nullptr;
+ return &m_currentMultiSingles[_loc];
+ }
+ if(_loc < m_currentInstrumentSingles.size())
+ return &m_currentInstrumentSingles[_loc];
+ if (_loc < m_currentMultiSingles.size())
+ return &m_currentMultiSingles[_loc];
+ return nullptr;
+ }
+ default:
+ return nullptr;
+ }
+ }
+
+ bool State::getMulti(Responses& _responses, const SysEx& _data)
+ {
+ const auto buf = static_cast<MidiBufferNum>(_data[IdxBuffer]);
+ const auto loc = _data[IdxLocation];
+
+ auto* m = getMulti(buf, loc);
+ if(!m || !isValid(*m))
+ return false;
+ _responses.push_back(convertTo(*m));
+ return true;
+ }
+
+ State::Multi* State::getMulti(MidiBufferNum buf, uint8_t loc)
+ {
+ switch (buf)
+ {
+ case MidiBufferNum::MultiEditBuffer:
+ m_isEditBuffer = true;
+ return &m_currentMulti;
+ case MidiBufferNum::DeprecatedMultiBankInternal:
+ case MidiBufferNum::MultiBankInternal:
+ if(loc >= 100)
+ return nullptr;
+ return &m_romMultis[loc];
+/* case MidiBufferNum::DeprecatedMultiBankCard:
+ case MidiBufferNum::MultiBankCard:
+ // mQ doesn't have a card even though MIDI doc mentions it
+ return nullptr;
+*/ default:
+ return nullptr;
+ }
+ }
+
+ bool State::getDrumMap(Responses& _responses, const SysEx& _data)
+ {
+ const auto buf = static_cast<MidiBufferNum>(_data[IdxBuffer]);
+ const auto loc = _data[IdxLocation];
+
+ auto* d = getDrumMap(buf, loc);
+ if(!d || !isValid(*d))
+ return false;
+ _responses.push_back(convertTo(*d));
+ return true;
+ }
+
+ State::DrumMap* State::getDrumMap(const MidiBufferNum _buf, const uint8_t _loc)
+ {
+ switch (_buf)
+ {
+ case MidiBufferNum::DeprecatedDrumBankInternal:
+ case MidiBufferNum::DrumBankInternal:
+ if(_loc >= 20)
+ return nullptr;
+ return &m_romDrumMaps[_loc];
+ case MidiBufferNum::DeprecatedDrumBankCard:
+ case MidiBufferNum::DrumBankCard:
+ return nullptr;
+ case MidiBufferNum::DrumEditBuffer:
+ m_isEditBuffer = true;
+ return &m_currentDrumMap;
+ default:
+ return nullptr;
+ }
+ }
+
+ bool State::getGlobal(Responses& _responses)
+ {
+ const auto* g = getGlobal();
+ if(g == nullptr)
+ return false;
+ _responses.push_back(convertTo(*g));
+ return true;
+ }
+
+ State::Global* State::getGlobal()
+ {
+ if(isValid(m_global))
+ {
+ m_isEditBuffer = true;
+ return &m_global;
+ }
+ return nullptr;
+ }
+
+ bool State::getMode(Responses& _responses)
+ {
+ const auto* m = getMode();
+ if(m == nullptr)
+ return false;
+ _responses.push_back(convertTo(*m));
+ return true;
+ }
+
+ State::Mode* State::getMode()
+ {
+ if(isValid(m_mode))
+ {
+ m_isEditBuffer = true;
+ return &m_mode;
+ }
+ return nullptr;
+ }
+
+ bool State::getDump(DumpType _type, Responses& _responses, const SysEx& _data)
+ {
+ bool res;
+
+ switch (_type)
+ {
+ case DumpType::Single: res = getSingle(_responses, _data); break;
+ case DumpType::Multi: res = getMulti(_responses, _data); break;
+ case DumpType::Drum: res = getDrumMap(_responses, _data); break;
+ case DumpType::Global: res = getGlobal(_responses); break;
+ case DumpType::Mode: res = getMode(_responses); break;
+ default:
+ return false;
+ }
+
+ if(!res)
+ forwardToDevice(_data);
+ return true;
+ }
+
+ bool State::parseDump(DumpType _type, const SysEx& _data)
+ {
+ bool res;
+ switch (_type)
+ {
+ case DumpType::Single: res = parseSingleDump(_data); break;
+ case DumpType::Multi: res = parseMultiDump(_data); break;
+ case DumpType::Drum: res = parseDrumDump(_data); break;
+ case DumpType::Global: res = parseGlobalDump(_data); break;
+ case DumpType::Mode: res = parseModeDump(_data); break;
+ default:
+ return false;
+ }
+
+ if(res)
+ forwardToDevice(_data);
+ return res;
+ }
+
+ bool State::modifyDump(DumpType _type, const SysEx& _data)
+ {
+ bool res;
+ switch (_type)
+ {
+ case DumpType::Single: res = modifySingle(_data); break;
+ case DumpType::Multi: res = modifyMulti(_data); break;
+ case DumpType::Drum: res = modifyDrum(_data); break;
+ case DumpType::Global: res = modifyGlobal(_data); break;
+ case DumpType::Mode: res = modifyMode(_data); break;
+ default:
+ return false;
+ }
+ if(res)
+ forwardToDevice(_data);
+ return res;
+ }
+
+ bool State::requestDumpParameter(DumpType _type, Responses& _responses, const SysEx& _data)
+ {
+ auto parameterRequestResponse = [&](uint8_t* p)
+ {
+ if(!p)
+ return false;
+ auto& data = _responses.emplace_back(_data);
+ data.pop_back();
+ data.push_back(*p);
+ data.push_back(0xf7);
+ return true;
+ };
+
+ switch (_type)
+ {
+ case DumpType::Single: return parameterRequestResponse(getSingleParameter(_data));
+ case DumpType::Multi: return parameterRequestResponse(getMultiParameter(_data));
+ case DumpType::Drum: return parameterRequestResponse(getDrumParameter(_data));
+ case DumpType::Global: return parameterRequestResponse(getGlobalParameter(_data));
+ case DumpType::Mode: return parameterRequestResponse(getModeParameter(_data));
+ default:
+ return false;
+ }
+
+ // we do not need to forward this to the device as it doesn't support it anyway
+ }
+
+ uint8_t State::getGlobalParameter(GlobalParameter _parameter) const
+ {
+ return m_global[static_cast<uint32_t>(_parameter) + IdxGlobalParamFirst];
+ }
+
+ void State::setGlobalParameter(GlobalParameter _parameter, uint8_t _value)
+ {
+ m_global[static_cast<uint32_t>(_parameter) + IdxGlobalParamFirst] = _value;
+ }
+
+ SysexCommand State::getCommand(const SysEx& _data)
+ {
+ if (_data.size() < 5)
+ return SysexCommand::Invalid;
+
+ if (_data.front() != 0xf0 || _data.back() != 0xf7)
+ return SysexCommand::Invalid;
+
+ if (_data[IdxIdWaldorf] != IdWaldorf || _data[IdxIdMicroQ] != IdMicroQ)
+ return SysexCommand::Invalid;
+
+ return static_cast<SysexCommand>(_data[IdxCommand]);
+ }
+
+ void State::forwardToDevice(const SysEx& _data) const
+ {
+ if(m_sender != Origin::External)
+ return;
+
+ sendSysex(_data);
+ }
+
+ void State::requestGlobal() const
+ {
+ sendSysex({0xf0, IdWaldorf, IdMicroQ, IdDeviceOmni, static_cast<uint8_t>(SysexCommand::GlobalRequest), 0xf7});
+ }
+
+ void State::requestSingle(MidiBufferNum _buf, MidiSoundLocation _location) const
+ {
+ sendSysex({0xf0, IdWaldorf, IdMicroQ, IdDeviceOmni, static_cast<uint8_t>(SysexCommand::SingleRequest), static_cast<uint8_t>(_buf), static_cast<uint8_t>(_location), 0xf7});
+ }
+
+ void State::requestMulti(MidiBufferNum _buf, uint8_t _location) const
+ {
+ sendSysex({0xf0, IdWaldorf, IdMicroQ, IdDeviceOmni, static_cast<uint8_t>(SysexCommand::MultiRequest), static_cast<uint8_t>(_buf), _location, 0xf7});
+ }
+
+ inline void State::sendMulti(const std::vector<uint8_t>& _multiData) const
+ {
+ std::vector<uint8_t> data = { 0xf0, IdWaldorf, IdMicroQ, IdDeviceOmni, static_cast<uint8_t>(SysexCommand::MultiDump), static_cast<uint8_t>(MidiBufferNum::DeprecatedMultiBankInternal), 0};
+ data.insert(data.end(), _multiData.begin(), _multiData.end());
+
+ uint8_t checksum = 0;
+ for(size_t i=4; i<data.size(); ++i)
+ checksum += data[i];
+ data.push_back(checksum & 0x7f);
+ data.push_back(0xf7);
+ sendSysex(data);
+ }
+
+ void State::sendGlobalParameter(GlobalParameter _param, uint8_t _value)
+ {
+ setGlobalParameter(_param, _value);
+
+ const auto p = static_cast<uint8_t>(_param);
+
+ sendSysex({0xf0, IdWaldorf, IdMicroQ, IdDeviceOmni, static_cast<uint8_t>(SysexCommand::GlobalParameterChange),
+ static_cast<uint8_t>(p >> 7), static_cast<uint8_t>(p & 0x7f), _value, 0xf7});
+ }
+
+ void State::sendSysex(const std::initializer_list<uint8_t>& _data) const
+ {
+ synthLib::SMidiEvent e;
+ e.sysex = _data;
+ m_mq.sendMidiEvent(e);
+ }
+
+ void State::sendSysex(const SysEx& _data) const
+ {
+ synthLib::SMidiEvent e;
+ e.sysex = _data;
+ m_mq.sendMidiEvent(e);
+ }
+
+ void State::createSequencerMultiData(std::vector<uint8_t>& _data)
+ {
+ static_assert(
+ (static_cast<uint32_t>(MultiParameter::Inst15) - static_cast<uint32_t>(MultiParameter::Inst0)) ==
+ (static_cast<uint32_t>(MultiParameter::Inst1) - static_cast<uint32_t>(MultiParameter::Inst0)) * 15,
+ "we need a consecutive offset");
+
+ _data.assign(static_cast<uint32_t>(mqLib::MultiParameter::Count), 0);
+
+ constexpr char name[] = "From TUS with <3";
+ static_assert(std::size(name) == 17, "wrong name length");
+ memcpy(&_data[static_cast<uint32_t>(MultiParameter::Name00)], name, sizeof(name) - 1);
+
+ auto setParam = [&](MultiParameter _param, const uint8_t _value)
+ {
+ _data[static_cast<uint32_t>(_param)] = _value;
+ };
+
+ auto setInstParam = [&](const uint8_t _instIndex, const MultiParameter _param, const uint8_t _value)
+ {
+ auto index = static_cast<uint32_t>(MultiParameter::Inst0) + (static_cast<uint32_t>(MultiParameter::Inst1) - static_cast<uint32_t>(MultiParameter::Inst0)) * _instIndex;
+ index += static_cast<uint32_t>(_param) - static_cast<uint32_t>(MultiParameter::Inst0);
+ _data[index] = _value;
+ };
+
+ setParam(MultiParameter::Volume, 127); // max volume
+
+ setParam(MultiParameter::ControlW, 121); // global
+ setParam(MultiParameter::ControlX, 121); // global
+ setParam(MultiParameter::ControlY, 121); // global
+ setParam(MultiParameter::ControlZ, 121); // global
+
+ for (uint8_t i = 0; i < 16; ++i)
+ {
+ setInstParam(i, MultiParameter::Inst0SoundBank, 0); // bank A
+ setInstParam(i, MultiParameter::Inst0SoundNumber, i); // sound number i
+ setInstParam(i, MultiParameter::Inst0MidiChannel, 2+i); // midi channel i
+ setInstParam(i, MultiParameter::Inst0Volume, 127); // max volume
+ setInstParam(i, MultiParameter::Inst0Transpose, 64); // no transpose
+ setInstParam(i, MultiParameter::Inst0Detune, 64); // no detune
+ setInstParam(i, MultiParameter::Inst0Output, 0); // main out
+ setInstParam(i, MultiParameter::Inst0Flags, 3); // RX = Local+MIDI / TX = off / Engine = Play
+ setInstParam(i, MultiParameter::Inst0Pan, 64); // center
+ setInstParam(i, MultiParameter::Inst0Pattern, 0); // no pattern
+ setInstParam(i, MultiParameter::Inst0VeloLow, 1); // full velocity range
+ setInstParam(i, MultiParameter::Inst0VeloHigh, 127);
+ setInstParam(i, MultiParameter::Inst0KeyLow, 0); // full key range
+ setInstParam(i, MultiParameter::Inst0KeyHigh, 127);
+ setInstParam(i, MultiParameter::Inst0MidiRxFlags, 63); // enable Pitchbend, Modwheel, Aftertouch, Sustain, Button 1/2, Program Change
+ }
+ }
+}
diff --git a/source/mqLib/mqstate.h b/source/mqLib/mqstate.h
@@ -0,0 +1,244 @@
+#pragma once
+
+#include <array>
+#include <vector>
+#include <cstddef>
+#include <cstdint>
+
+#include "mqmiditypes.h"
+
+#include "../synthLib/deviceTypes.h"
+#include "../synthLib/midiTypes.h"
+
+namespace synthLib
+{
+ struct SMidiEvent;
+}
+
+namespace mqLib
+{
+ class MicroQ;
+
+ using SysEx = std::vector<uint8_t>;
+ using Responses = std::vector<SysEx>;
+
+ class State
+ {
+ public:
+ enum class Origin
+ {
+ Device,
+ External
+ };
+
+ enum class DumpType
+ {
+ Single,
+ Multi,
+ Drum,
+ Global,
+ Mode,
+ SingleQ,
+
+ Count
+ };
+
+ struct Dump
+ {
+ DumpType type;
+ SysexCommand cmdRequest;
+ SysexCommand cmdDump;
+ SysexCommand cmdParamChange;
+ SysexCommand cmdParamRequest;
+ uint32_t firstParamIndex;
+ uint32_t idxParamIndexH;
+ uint32_t idxParamIndexL;
+ uint32_t idxParamValue;
+ uint32_t dumpSize;
+ };
+
+ static constexpr Dump g_dumps[] =
+ {
+ {DumpType::Single, SysexCommand::SingleRequest, SysexCommand::SingleDump, SysexCommand::SingleParameterChange, SysexCommand::SingleParameterRequest, IdxSingleParamFirst, IdxSingleParamIndexH, IdxSingleParamIndexL, IdxSingleParamValue, 392},
+ {DumpType::Multi, SysexCommand::MultiRequest, SysexCommand::MultiDump, SysexCommand::MultiParameterChange, SysexCommand::MultiParameterRequest, IdxMultiParamFirst, IdxMultiParamIndexH, IdxMultiParamIndexL, IdxMultiParamValue, 393},
+ {DumpType::Drum, SysexCommand::DrumRequest, SysexCommand::DrumDump, SysexCommand::DrumParameterChange, SysexCommand::DrumParameterRequest, IdxDrumParamFirst, IdxDrumParamIndexH, IdxDrumParamIndexL, IdxDrumParamValue, 393},
+ {DumpType::Global, SysexCommand::GlobalRequest, SysexCommand::GlobalDump, SysexCommand::GlobalParameterChange, SysexCommand::GlobalParameterRequest, IdxGlobalParamFirst, IdxGlobalParamIndexH, IdxGlobalParamIndexL, IdxGlobalParamValue, 207},
+ {DumpType::Mode, SysexCommand::ModeRequest, SysexCommand::ModeDump , SysexCommand::ModeParameterChange, SysexCommand::ModeParameterRequest, IdxModeParamFirst, IdxModeParamIndexH, IdxModeParamIndexL, IdxModeParamValue, 8},
+
+ {DumpType::SingleQ, SysexCommand::SingleRequest, SysexCommand::SingleDump, SysexCommand::SingleParameterChange, SysexCommand::SingleParameterRequest, IdxSingleParamFirst, IdxSingleParamIndexH, IdxSingleParamIndexL, IdxSingleParamValue, 393},
+ };
+
+ using Single = std::array<uint8_t, g_dumps[static_cast<uint32_t>(DumpType::Single)].dumpSize>;
+ using Multi = std::array<uint8_t, g_dumps[static_cast<uint32_t>(DumpType::Multi)].dumpSize>;
+ using DrumMap = std::array<uint8_t, g_dumps[static_cast<uint32_t>(DumpType::Drum)].dumpSize>;
+ using Global = std::array<uint8_t, g_dumps[static_cast<uint32_t>(DumpType::Global)].dumpSize>;
+ using Mode = std::array<uint8_t, g_dumps[static_cast<uint32_t>(DumpType::Mode)].dumpSize>;
+
+ using SingleQ = std::array<uint8_t, g_dumps[static_cast<uint32_t>(DumpType::SingleQ)].dumpSize>;
+
+ State(MicroQ& _mq);
+
+ bool loadState(const SysEx& _sysex);
+
+ bool receive(Responses& _responses, const synthLib::SMidiEvent& _data, Origin _sender);
+ bool receive(Responses& _responses, const SysEx& _data, Origin _sender);
+ void createInitState();
+
+ bool getState(std::vector<uint8_t>& _state, synthLib::StateType _type) const;
+ bool setState(const std::vector<uint8_t>& _state, synthLib::StateType _type);
+
+ static void createSequencerMultiData(std::vector<uint8_t>& _data);
+
+ private:
+ template<size_t Size> static bool convertTo(std::array<uint8_t, Size>& _dst, const SysEx& _data)
+ {
+ if(_data.size() != Size)
+ return false;
+ std::copy(_data.begin(), _data.end(), _dst.begin());
+ return true;
+ }
+
+ template<size_t Size> static SysEx convertTo(const std::array<uint8_t, Size>& _src)
+ {
+ SysEx dst;
+ dst.insert(dst.begin(), _src.begin(), _src.end());
+ return dst;
+ }
+
+ template<size_t Size> static bool append(SysEx& _dst, const std::array<uint8_t, Size>& _src)
+ {
+ if(!isValid(_src))
+ return false;
+ auto src = _src;
+ updateChecksum(src);
+ _dst.insert(_dst.end(), src.begin(), src.end());
+ return true;
+ }
+
+ template<size_t Size> static void updateChecksum(std::array<uint8_t, Size>& _src)
+ {
+ uint8_t& c = _src[_src.size() - 2];
+ c = 0;
+ for(size_t i=IdxCommand; i<_src.size()-2; ++i)
+ c += _src[i];
+ c &= 0x7f;
+ }
+
+ static bool updateChecksum(SysEx& _src)
+ {
+ if(_src.size() < 3)
+ return false;
+ uint8_t& c = _src[_src.size() - 2];
+ c = 0;
+ for(size_t i=IdxCommand; i<_src.size()-2; ++i)
+ c += _src[i];
+ c &= 0x7f;
+ return true;
+ }
+
+ bool parseSingleDump(const SysEx& _data);
+ bool parseMultiDump(const SysEx& _data);
+ bool parseDrumDump(const SysEx& _data);
+ bool parseGlobalDump(const SysEx& _data);
+ bool parseModeDump(const SysEx& _data);
+
+ bool modifySingle(const SysEx& _data);
+ bool modifyMulti(const SysEx& _data);
+ bool modifyDrum(const SysEx& _data);
+ bool modifyGlobal(const SysEx& _data);
+ bool modifyMode(const SysEx& _data);
+
+ uint8_t* getSingleParameter(const SysEx& _data);
+ uint8_t* getMultiParameter(const SysEx& _data);
+ uint8_t* getDrumParameter(const SysEx& _data);
+ uint8_t* getGlobalParameter(const SysEx& _data);
+ uint8_t* getModeParameter(const SysEx& _data);
+
+ bool getSingle(Responses& _responses, const SysEx& _data);
+ Single* getSingle(MidiBufferNum _buf, uint8_t _loc);
+
+ bool getMulti(Responses& _responses, const SysEx& _data);
+ Multi* getMulti(MidiBufferNum _buf, uint8_t _loc);
+
+ bool getDrumMap(Responses& _responses, const SysEx& _data);
+ DrumMap* getDrumMap(MidiBufferNum _buf, uint8_t _loc);
+
+ bool getGlobal(Responses& _responses);
+ Global* getGlobal();
+
+ bool getMode(Responses& _responses);
+ Mode* getMode();
+
+ bool getDump(DumpType _type, Responses& _responses, const SysEx& _data);
+ bool parseDump(DumpType _type, const SysEx& _data);
+ bool modifyDump(DumpType _type, const SysEx& _data);
+ bool requestDumpParameter(DumpType _type, Responses& _responses, const SysEx& _data);
+
+ uint8_t getGlobalParameter(GlobalParameter _parameter) const;
+ void setGlobalParameter(GlobalParameter _parameter, uint8_t _value);
+
+ static bool isValid(const Single& _single)
+ {
+ return _single[IdxSingleParamFirst] == 1;
+ }
+
+ static bool isValid(const Multi& _multi)
+ {
+ return _multi.front() == 0xf0;
+ // we cannot do this anymore as drum map & multi have the same length
+// return _multi[IdxMultiParamFirst] > 0; // Note: no version number in a multi, assume that Multi volume has to be > 0 to be valid
+ }
+ /*
+ static bool isValid(const DrumMap& _drum)
+ {
+ return _drum[IdxDrumParamFirst + 5] >= 4; // Note: no version number in a drum map, check for transpose value for instrument 0
+ }
+ */
+ static bool isValid(const Global& _global)
+ {
+ return _global[IdxGlobalParamFirst] == '1'; // yes, this is not an int but an ascii '1' = 49
+ }
+
+ static bool isValid(const Mode& _mode)
+ {
+ return _mode.front() == 0xf0; // unable to derive from the packet itself
+ }
+
+ static SysexCommand getCommand(const SysEx& _data);
+
+ void forwardToDevice(const SysEx& _data) const;
+
+ void requestGlobal() const;
+ void requestSingle(MidiBufferNum _buf, MidiSoundLocation _location) const;
+ void requestMulti(MidiBufferNum _buf, uint8_t _location) const;
+ void sendMulti(const std::vector<uint8_t>& _multiData) const;
+ void sendGlobalParameter(GlobalParameter _param, uint8_t _value);
+ void sendSysex(const std::initializer_list<uint8_t>& _data) const;
+ void sendSysex(const SysEx& _data) const;
+
+ MicroQ& m_mq;
+
+ // ROM
+ std::array<Single, 300> m_romSingles{Single{}};
+ std::array<DrumMap, 20> m_romDrumMaps{DrumMap{}};
+ std::array<Multi, 100> m_romMultis{Multi{}};
+
+ // Edit Buffers
+ std::array<Single, 16> m_currentMultiSingles{Single{}};
+ std::array<Single, 1> m_currentInstrumentSingles{Single{}};
+ std::array<Single, 32> m_currentDrumMapSingles{Single{}};
+ Multi m_currentMulti{};
+ DrumMap m_currentDrumMap{};
+
+ // Global settings, only available once
+ Global m_global{};
+ Mode m_mode{};
+
+ // current state, valid while receiving data
+ Origin m_sender = Origin::External;
+ bool m_isEditBuffer = false;
+
+ synthLib::SMidiEvent m_lastBankSelectMSB;
+ synthLib::SMidiEvent m_lastBankSelectLSB;
+ };
+}
diff --git a/source/mqLib/mqsysexremotecontrol.cpp b/source/mqLib/mqsysexremotecontrol.cpp
@@ -0,0 +1,166 @@
+#include "mqsysexremotecontrol.h"
+
+#include "buttons.h"
+#include "mqmiditypes.h"
+#include "microq.h"
+
+#include "../synthLib/midiTypes.h"
+
+namespace mqLib
+{
+ void SysexRemoteControl::createSysexHeader(std::vector<uint8_t>& _dst, SysexCommand _cmd)
+ {
+ constexpr uint8_t devId = 0;
+ _dst.assign({0xf0, IdWaldorf, IdMicroQ, devId, static_cast<uint8_t>(_cmd)});
+ }
+
+ void SysexRemoteControl::sendSysexLCD(std::vector<synthLib::SMidiEvent>& _dst) const
+ {
+ std::array<char, 40> lcdData{};
+ m_mq.readLCD(lcdData);
+
+ synthLib::SMidiEvent ev;
+ createSysexHeader(ev.sysex, SysexCommand::EmuLCD);
+ ev.sysex.insert(ev.sysex.end(), lcdData.begin(), lcdData.end());
+
+ _dst.emplace_back(ev);
+ }
+
+ void SysexRemoteControl::sendSysexLCDCGRam(std::vector<synthLib::SMidiEvent>& _dst) const
+ {
+ std::array<char, 64> lcdData{};
+
+ std::array<uint8_t, 8> data{};
+
+ for (auto i=0, k=0; i<8; ++i)
+ {
+ m_mq.readCustomLCDCharacter(data, i);
+ for (auto j=0; j<8; ++j)
+ lcdData[k++] = static_cast<char>(data[j]);
+ }
+
+ synthLib::SMidiEvent ev;
+ createSysexHeader(ev.sysex, SysexCommand::EmuLCDCGRata);
+ ev.sysex.insert(ev.sysex.end(), lcdData.begin(), lcdData.end());
+
+ _dst.emplace_back(ev);
+ }
+
+ void SysexRemoteControl::sendSysexButtons(std::vector<synthLib::SMidiEvent>& _dst) const
+ {
+ static_assert(static_cast<uint32_t>(Buttons::ButtonType::Count) < 24, "too many buttons");
+ uint32_t buttons = 0;
+ for(uint32_t i=0; i<static_cast<uint32_t>(Buttons::ButtonType::Count); ++i)
+ {
+ if(m_mq.getButton(static_cast<Buttons::ButtonType>(i)))
+ buttons |= (1<<i);
+ }
+
+ auto& ev = _dst.emplace_back();
+
+ createSysexHeader(ev.sysex, SysexCommand::EmuButtons);
+
+ ev.sysex.push_back((buttons>>16) & 0xff);
+ ev.sysex.push_back((buttons>>8) & 0xff);
+ ev.sysex.push_back(buttons & 0xff);
+ }
+
+ void SysexRemoteControl::sendSysexLEDs(std::vector<synthLib::SMidiEvent>& _dst) const
+ {
+ static_assert(static_cast<uint32_t>(Leds::Led::Count) < 32, "too many LEDs");
+ uint32_t leds = 0;
+ for(uint32_t i=0; i<static_cast<uint32_t>(Leds::Led::Count); ++i)
+ {
+ if(m_mq.getLedState(static_cast<Leds::Led>(i)))
+ leds |= (1<<i);
+ }
+ auto& ev = _dst.emplace_back();
+ auto& response = ev.sysex;
+ createSysexHeader(response, SysexCommand::EmuLEDs);
+ response.push_back((leds>>24) & 0xff);
+ response.push_back((leds>>16) & 0xff);
+ response.push_back((leds>>8) & 0xff);
+ response.push_back(leds & 0xff);
+ }
+
+ void SysexRemoteControl::sendSysexRotaries(std::vector<synthLib::SMidiEvent>& _dst) const
+ {
+ auto& ev= _dst.emplace_back();
+ auto& response = ev.sysex;
+
+ createSysexHeader(response, SysexCommand::EmuRotaries);
+
+ for(uint32_t i=0; i<static_cast<uint32_t>(Buttons::Encoders::Count); ++i)
+ {
+ const auto value = m_mq.getEncoder(static_cast<Buttons::Encoders>(i));
+ response.push_back(value);
+ }
+ }
+
+ bool SysexRemoteControl::receive(std::vector<synthLib::SMidiEvent>& _output, const std::vector<unsigned char>& _input) const
+ {
+ if(_input.size() < 5)
+ return false;
+
+ if(_input[1] != IdWaldorf || _input[2] != IdMicroQ)
+ return false;
+
+ const auto cmd = _input[4];
+
+ switch (static_cast<SysexCommand>(cmd))
+ {
+ case SysexCommand::EmuLCD:
+ sendSysexLCD(_output);
+ return true;
+ case SysexCommand::EmuLCDCGRata:
+ sendSysexLCDCGRam(_output);
+ return true;
+ case SysexCommand::EmuButtons:
+ {
+ if(_input.size() > 6)
+ {
+ const auto button = static_cast<Buttons::ButtonType>(_input[5]);
+ const auto state = _input[6];
+ m_mq.setButton(button, state != 0);
+ }
+ else
+ {
+ sendSysexButtons(_output);
+ }
+ }
+ return true;
+ case SysexCommand::EmuLEDs:
+ {
+ sendSysexLEDs(_output);
+ }
+ return true;
+ case SysexCommand::EmuRotaries:
+ {
+ if(_input.size() > 6)
+ {
+ const auto encoder = static_cast<Buttons::Encoders>(_input[5]);
+ const auto amount = static_cast<int>(_input[6]) - 64;
+ if(amount)
+ m_mq.rotateEncoder(encoder, amount);
+ }
+ else
+ {
+ sendSysexRotaries(_output);
+ }
+ }
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void SysexRemoteControl::handleDirtyFlags(std::vector<synthLib::SMidiEvent>& _output, uint32_t _dirtyFlags) const
+ {
+ if(_dirtyFlags & static_cast<uint32_t>(MicroQ::DirtyFlags::Lcd))
+ sendSysexLCD(_output);
+ if(_dirtyFlags & static_cast<uint32_t>(MicroQ::DirtyFlags::LcdCgRam))
+ sendSysexLCDCGRam(_output);
+ if(_dirtyFlags & static_cast<uint32_t>(MicroQ::DirtyFlags::Leds))
+ sendSysexLEDs(_output);
+ }
+}
diff --git a/source/mqLib/mqsysexremotecontrol.h b/source/mqLib/mqsysexremotecontrol.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <vector>
+
+#include "mqmiditypes.h"
+
+namespace synthLib
+{
+ struct SMidiEvent;
+}
+
+namespace mqLib
+{
+ class MicroQ;
+
+ class SysexRemoteControl
+ {
+ public:
+ SysexRemoteControl(MicroQ& _mq) : m_mq(_mq) {}
+
+ static void createSysexHeader(std::vector<uint8_t>& _dst, SysexCommand _cmd);
+
+ void sendSysexLCD(std::vector<synthLib::SMidiEvent>& _dst) const;
+ void sendSysexLCDCGRam(std::vector<synthLib::SMidiEvent>& _dst) const;
+ void sendSysexButtons(std::vector<synthLib::SMidiEvent>& _dst) const;
+ void sendSysexLEDs(std::vector<synthLib::SMidiEvent>& _dst) const;
+ void sendSysexRotaries(std::vector<synthLib::SMidiEvent>& _dst) const;
+
+ bool receive(std::vector<synthLib::SMidiEvent>& _output, const std::vector<uint8_t>& _input) const;
+ void handleDirtyFlags(std::vector<synthLib::SMidiEvent>& _output, uint32_t _dirtyFlags) const;
+
+ private:
+ MicroQ& m_mq;
+ };
+}
diff --git a/source/mqLib/mqtypes.h b/source/mqLib/mqtypes.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "dsp56kEmu/types.h"
+
+namespace mqLib
+{
+ enum PortBits
+ {
+ // Port E
+ Encoders0CS = 0,
+ Encoders1CS = 1,
+ Buttons0CS = 2,
+ Buttons1CS = 3,
+ BtPower = 5, // Power Button
+ LedPower = 6,
+
+ // Port F
+ LcdRS = 1,
+ LcdRW = 2,
+ LcdLatch = 3,
+ LedWriteLatch = 7,
+
+ // Port QS
+ DspNMI = 6,
+ };
+
+ template<size_t Count> using TAudioBuffer = std::array<std::vector<dsp56k::TWord>, Count>;
+
+ using TAudioOutputs = TAudioBuffer<6>;
+ using TAudioInputs = TAudioBuffer<2>;
+
+ enum class BootMode
+ {
+ Default,
+ FactoryTest,
+ EraseFlash,
+ WaitForSystemDump,
+ DspClockResetAndServiceMode,
+ ServiceMode,
+ MemoryGame
+ };
+}
diff --git a/source/mqLib/rom.cpp b/source/mqLib/rom.cpp
@@ -0,0 +1,109 @@
+#include "rom.h"
+
+#include "mqmiditypes.h"
+#include "../synthLib/os.h"
+#include "../synthLib/midiToSysex.h"
+
+namespace mqLib
+{
+ bool ROM::loadFromFile(const std::string& _filename)
+ {
+ FILE* hFile = fopen(_filename.c_str(), "rb");
+ if(!hFile)
+ return false;
+
+ fseek(hFile, 0, SEEK_END);
+ const auto size = ftell(hFile);
+ fseek(hFile, 0, SEEK_SET);
+
+ m_buffer.resize(size);
+ const auto numRead = fread(m_buffer.data(), 1, size, hFile);
+ fclose(hFile);
+
+ if(numRead != static_cast<size_t>(size))
+ {
+ m_buffer.clear();
+ return false;
+ }
+
+ if(numRead != getSize())
+ {
+ loadFromMidi(m_buffer, _filename);
+
+ if (!m_buffer.empty() && m_buffer.size() < getSize())
+ m_buffer.resize(getSize(), 0xff);
+ }
+
+ if(!m_buffer.empty())
+ {
+ m_data = m_buffer.data();
+ return true;
+ }
+ return false;
+ }
+
+ bool ROM::loadFromMidi(std::vector<unsigned char>& _buffer, const std::string& _filename)
+ {
+ _buffer.clear();
+
+ std::vector<uint8_t> data;
+ if(!synthLib::MidiToSysex::readFile(data, _filename.c_str()) || data.empty())
+ return false;
+
+ return loadFromSysExBuffer(_buffer, data);
+ }
+
+ bool ROM::loadFromSysExFile(std::vector<uint8_t>& _buffer, const std::string& _filename)
+ {
+ _buffer.clear();
+
+ std::vector<uint8_t> buf;
+ if (!synthLib::readFile(buf, _filename))
+ return false;
+ return loadFromSysExBuffer(_buffer, buf);
+ }
+
+ bool ROM::loadFromSysExBuffer(std::vector<unsigned char>& _buffer, const std::vector<uint8_t>& _sysex)
+ {
+ _buffer.reserve(getSize());
+
+ std::vector<std::vector<uint8_t>> messages;
+ synthLib::MidiToSysex::splitMultipleSysex(messages, _sysex);
+
+ uint16_t expectedCounter = 1;
+
+ for (const auto& message : messages)
+ {
+ if(message.size() < 0xfc)
+ continue;
+
+ if(message[1] != IdWaldorf)
+ continue;
+
+ if(message[3] != 0x7f)
+ continue;
+
+ if(message[4] != 0x71 && message[4] != 0x72 && message[4] != 0x73) // MW2, Q, mQ
+ continue;
+
+ const auto counter = (message[6] << 7) | message[7];
+ if(expectedCounter != counter && counter != 1)
+ return false;
+ expectedCounter = static_cast<uint16_t>(counter);
+ ++expectedCounter;
+
+ size_t i = 10;
+ while(i + 5 < message.size())
+ {
+ const auto lsbs = message[i];
+ _buffer.push_back((message[i+1] << 1) | ((lsbs >> 0) & 1));
+ _buffer.push_back((message[i+2] << 1) | ((lsbs >> 1) & 1));
+ _buffer.push_back((message[i+3] << 1) | ((lsbs >> 2) & 1));
+ _buffer.push_back((message[i+4] << 1) | ((lsbs >> 3) & 1));
+ i += 5;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/source/mqLib/rom.h b/source/mqLib/rom.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace mqLib
+{
+ class ROM
+ {
+ public:
+ static constexpr uint32_t g_romSize = 524288;
+
+ explicit ROM(const std::string& _filename, const uint8_t* _data = nullptr) : m_data(_data)
+ {
+ if (!_filename.empty())
+ loadFromFile(_filename);
+ }
+
+ const uint8_t* getData() const { return m_data; }
+ static constexpr uint32_t getSize() { return g_romSize; }
+ bool isValid() const { return !m_buffer.empty(); }
+
+ static bool loadFromMidi(std::vector<uint8_t>& _buffer, const std::string& _filename);
+ static bool loadFromSysExFile(std::vector<uint8_t>& _buffer, const std::string& _filename);
+ static bool loadFromSysExBuffer(std::vector<uint8_t> &_buffer, const std::vector<uint8_t> &_sysex);
+ private:
+ bool loadFromFile(const std::string& _filename);
+
+ const uint8_t* m_data;
+ std::vector<uint8_t> m_buffer;
+ };
+}
diff --git a/source/mqPerformanceTest/CMakeLists.txt b/source/mqPerformanceTest/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(mqPerformanceTest)
+
+add_executable(mqPerformanceTest)
+
+set(SOURCES
+ mqPerformanceTest.cpp
+)
+
+target_sources(mqPerformanceTest PRIVATE ${SOURCES})
+source_group("source" FILES ${SOURCES})
+
+target_link_libraries(mqPerformanceTest PUBLIC mqConsoleLib)
+
+if(UNIX AND NOT APPLE)
+ target_link_libraries(mqPerformanceTest PUBLIC -static-libgcc -static-libstdc++)
+endif()
+
+install(TARGETS mqPerformanceTest DESTINATION . COMPONENT VavraPerformanceTest)
diff --git a/source/mqPerformanceTest/mqPerformanceTest.cpp b/source/mqPerformanceTest/mqPerformanceTest.cpp
@@ -0,0 +1,163 @@
+#include <iostream>
+
+#include "../synthLib/wavWriter.h"
+
+#include "../mqLib/microq.h"
+#include "../mqLib/mqhardware.h"
+
+#include "dsp56kEmu/jitunittests.h"
+#include "dsp56kEmu/threadtools.h"
+
+#include <vector>
+
+#include "../mqConsoleLib/mqSettingsGui.h"
+
+using ButtonType = mqLib::Buttons::ButtonType;
+using EncoderType = mqLib::Buttons::Encoders;
+
+int main(int _argc, char* _argv[])
+{
+ std::cout << "Running unit tests..." << std::endl;
+
+ try
+ {
+// dsp56k::InterpreterUnitTests tests; // only valid if Interpreter is active
+ dsp56k::JitUnittests tests; // only valid if JIT runtime is active
+// return 0;
+ }
+ catch(std::string& _err)
+ {
+ std::cout << "Unit Test failed: " << _err << std::endl;
+ return -1;
+ }
+
+ std::cout << "Unit tests passed" << std::endl;
+
+ // create hardware
+ mqLib::MicroQ mq(mqLib::BootMode::Default);
+
+ if(!mq.isValid())
+ {
+ std::cout << "Failed to find OS update midi file. Put mq_2_23.mid next to this program" << std::endl;
+ std::cin.ignore();
+ return -2;
+ }
+
+ dsp56k::ThreadTools::setCurrentThreadName("main");
+
+ synthLib::AsyncWriter writer("mqOutput.wav", 44100, false);
+
+ constexpr uint32_t blockSize = 64;
+ std::vector<dsp56k::TWord> stereoOutput;
+ stereoOutput.resize(blockSize * 2);
+
+ bool waitingForBoot = true;
+ int counter = -1;
+
+ std::cout << "Device booting..." << std::endl;
+
+ mq.setButton(ButtonType::Play, true);
+
+ std::array<char,40> lastLcdContent{};
+ constexpr int lcdPrintDelay = 5000 / blockSize;
+ int lcdPrintTimer = -1;
+ bool foundEndText = false;
+
+ while(true)
+ {
+ if(waitingForBoot && mq.isBootCompleted())
+ {
+ std::cout << "Boot completed" << std::endl;
+
+ waitingForBoot = false;
+ mq.setButton(ButtonType::Play, false);
+ counter = 80000 / blockSize;
+ }
+
+ if(counter > 0)
+ {
+ --counter;
+ if(counter == 50000 / blockSize)
+ {
+ mq.setButton(ButtonType::Multimode, true);
+ std::cout << "Pressing Multimode + Peek" << std::endl;
+ }
+ if(counter == 40000 / blockSize)
+ {
+ mq.setButton(ButtonType::Peek, true);
+ }
+ else if(counter == (20000 / blockSize))
+ {
+ mq.setButton(ButtonType::Peek, false);
+ mq.setButton(ButtonType::Multimode, false);
+ }
+ else if(counter == (10000 / blockSize))
+ {
+ mq.setButton(ButtonType::Play, true);
+ std::cout << "Pressing Play to start demo playback" << std::endl;
+ }
+ else if(counter == 0)
+ {
+ mq.setButton(ButtonType::Play, false);
+
+ mq.getHardware()->getDspThread().setLogToStdout(true);
+ }
+ }
+ else
+ {
+ const auto& lcdData = mq.getHardware()->getUC().getLcd().getDdRam();
+ if(lcdData != lastLcdContent)
+ {
+ lcdPrintTimer = lcdPrintDelay;
+ lastLcdContent = lcdData;
+ }
+ else if(lcdPrintTimer > 0 && --lcdPrintTimer == 0)
+ {
+ auto printData = lcdData;
+ for (char& c : printData)
+ {
+ const auto u = static_cast<uint8_t>(c);
+ if(u < 32 || u > 127)
+ c = '?';
+ }
+
+ const auto lineA = std::string(&printData[0], 20);
+ const auto lineB = std::string(&printData[20], 20);
+ std::cout << "LCD: '" << lineA << "'" << std::endl << "LCD: '" << lineB << "'" << std::endl;
+
+ const auto hasEndText = lineA.find(" ...DEEPER ") != std::string::npos;
+
+ if(hasEndText)
+ foundEndText = true;
+ else if(foundEndText)
+ break;
+ }
+ }
+
+ mq.process(blockSize, 0);
+
+ if(counter == 0)
+ {
+ auto& outs = mq.getAudioOutputs();
+
+ for(size_t i=0; i<blockSize; ++i)
+ {
+ stereoOutput[(i<<1) ] = outs[0][i];
+ stereoOutput[(i<<1)+1] = outs[1][i];
+ }
+
+ writer.append([&](std::vector<dsp56k::TWord>& _wavOut)
+ {
+ _wavOut.insert(_wavOut.end(), stereoOutput.begin(), stereoOutput.end());
+ });
+ }
+ }
+
+ std::cout << '\a';
+
+ std::cout << "Demo Playback has ended, press key to exit" << std::endl;
+
+ std::cin.ignore();
+
+ return 0;
+}
diff --git a/source/mqTestConsole/CMakeLists.txt b/source/mqTestConsole/CMakeLists.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(mqTestConsole)
+
+add_executable(mqTestConsole)
+
+set(SOURCES
+ mqTestConsole.cpp
+)
+
+if(NOT ANDROID)
+ list(APPEND SOURCES ../portmidi/pm_common/portmidi.h)
+endif()
+
+target_sources(mqTestConsole PRIVATE ${SOURCES})
+source_group("source" FILES ${SOURCES})
+
+target_link_libraries(mqTestConsole PUBLIC mqConsoleLib portaudio_static)
+if(ANDROID)
+ target_compile_definitions(mqTestConsole PUBLIC "ANDROID")
+else()
+ target_link_libraries(mqTestConsole PUBLIC portmidi-static)
+endif()
+
+if(UNIX AND NOT APPLE)
+ target_link_libraries(mqTestConsole PUBLIC -static-libgcc -static-libstdc++)
+endif()
+
+#install(TARGETS mqTestConsole DESTINATION . COMPONENT STANDALONE)
diff --git a/source/mqTestConsole/mqTestConsole.cpp b/source/mqTestConsole/mqTestConsole.cpp
@@ -0,0 +1,261 @@
+#include <fstream>
+#include <iostream>
+#include <memory>
+
+#include "../synthLib/os.h"
+#include "../synthLib/wavWriter.h"
+#include "../synthLib/midiTypes.h"
+#include "../synthLib/configFile.h"
+
+#include "../mqLib/microq.h"
+#include "../mqLib/mqhardware.h"
+#include "../mqLib/rom.h"
+
+#include "dsp56kEmu/threadtools.h"
+
+#include "dsp56kEmu/jitunittests.h"
+
+#include <cpp-terminal/input.hpp>
+
+#include <vector>
+
+#include "../mqConsoleLib/audioOutputPA.h"
+#include "../mqConsoleLib/midiInput.h"
+#include "../mqConsoleLib/midiOutput.h"
+#include "../mqConsoleLib/mqGui.h"
+#include "../mqConsoleLib/mqKeyInput.h"
+
+#include "../mqConsoleLib/mqSettingsGui.h"
+
+using Term::Terminal;
+using Term::Key;
+
+using ButtonType = mqLib::Buttons::ButtonType;
+using EncoderType = mqLib::Buttons::Encoders;
+
+int main(int _argc, char* _argv[])
+{
+ /*
+ // Load a .mid or .syx and convert to bin (OS updates)
+ std::vector<uint8_t> buf;
+ mqLib::ROM::loadFromMidi(buf, _argv[1]);
+// mqLib::ROM::loadFromSysExFile(buf, _argv[1]);
+ if (!buf.empty())
+ synthLib::writeFile(std::string(_argv[1]) + ".bin", buf);
+
+ return 0;
+ */
+ try
+ {
+// dsp56k::InterpreterUnitTests tests; // only valid if Interpreter is active
+// dsp56k::JitUnittests tests; // only valid if JIT runtime is active
+// return 0;
+ }
+ catch(std::string& _err)
+ {
+ LOG("Unit Test failed: " << _err);
+ return -1;
+ }
+
+ auto exit = false;
+
+ // create hardware
+ mqLib::MicroQ mq(mqLib::BootMode::Default);
+
+ // create terminal-GUI
+ Terminal term(true, true, false, true);
+ mqConsoleLib::Gui gui(mq);
+
+ mqConsoleLib::SettingsGui settings;
+
+ int devIdMidiOut = -1;
+ int devIdMidiIn = -1;
+ int devIdAudioOut = -1;
+
+ std::string devNameMidiIn;
+ std::string devNameMidiOut;
+ std::string devNameAudioOut;
+
+ try
+ {
+ synthLib::ConfigFile cfg((synthLib::getModulePath() + "config.cfg").c_str());
+ for (const auto& v : cfg.getValues())
+ {
+ if(v.first == "MidiIn")
+ devNameMidiIn = v.second;
+ else if(v.first == "MidiOut")
+ devNameMidiOut = v.second;
+ else if(v.first == "AudioOut")
+ devNameAudioOut = v.second;
+ }
+ }
+ catch(const std::runtime_error&)
+ {
+ // no config file available
+ }
+
+ bool settingsChanged = false;
+ bool showSettings = false;
+
+ // do not continously render our terminal GUI but only if something has changed
+ dsp56k::RingBuffer<uint32_t, 1024, true> renderTrigger;
+
+ std::thread renderer([&]
+ {
+ dsp56k::ThreadTools::setCurrentThreadName("guiRender");
+ bool prevShowSettings = true;
+
+ while(!exit)
+ {
+ renderTrigger.pop_front();
+ if(renderTrigger.empty())
+ {
+ if(prevShowSettings != showSettings)
+ {
+ prevShowSettings = showSettings;
+ if(showSettings)
+ settings.onOpen();
+ else
+ gui.onOpen();
+ }
+ if(showSettings)
+ {
+ settings.render(devIdMidiIn, devIdMidiOut, devIdAudioOut);
+
+ bool changed = true;
+ if(!settings.getMidiInput().empty())
+ devNameMidiIn = settings.getMidiInput();
+ else if(!settings.getMidiOutput().empty())
+ devNameMidiOut = settings.getMidiOutput();
+ else if(!settings.getAudioOutput().empty())
+ devNameAudioOut = settings.getAudioOutput();
+ else
+ changed = false;
+
+ if(changed)
+ settingsChanged = true;
+ }
+ else
+ gui.render();
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+ }
+ });
+
+ std::thread oneSecondUpdater([&]
+ {
+ dsp56k::ThreadTools::setCurrentThreadName("1secUpdater");
+ while(!exit)
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+ renderTrigger.push_back(1);
+ }
+ });
+
+ mqConsoleLib::KeyInput mqKeyInput(mq);
+
+ // threaded key reader
+ std::thread inputReader([&]
+ {
+ dsp56k::ThreadTools::setCurrentThreadName("inputReader");
+ while(!exit)
+ {
+ const auto k = Term::read_key();
+ if(k)
+ {
+ if(k == Key::ESC)
+ showSettings = !showSettings;
+ if(showSettings)
+ settings.processKey(k);
+ else
+ mqKeyInput.processKey(k);
+ renderTrigger.push_back(1);
+ }
+ }
+ });
+
+ std::unique_ptr<mqConsoleLib::AudioOutputPA> audio;
+ std::unique_ptr<mqConsoleLib::MidiInput> midiIn;
+ std::unique_ptr<mqConsoleLib::MidiOutput> midiOut;
+
+ std::vector<synthLib::SMidiEvent> midiInBuffer;
+ std::vector<uint8_t> midiOutBuffer;
+
+ std::mutex mutexDevices;
+
+ std::function process = [&](uint32_t _blockSize, const mqLib::TAudioOutputs*& _dst)
+ {
+ mq.process(_blockSize);
+ _dst = &mq.getAudioOutputs();
+
+ if(mq.getDirtyFlags() != mqLib::MicroQ::DirtyFlags::None)
+ renderTrigger.push_back(1);
+ };
+
+ auto createDevices = [&]()
+ {
+ std::lock_guard lockDevices(mutexDevices);
+
+ if(!audio || (!devNameAudioOut.empty() && audio->getDeviceName() != devNameAudioOut))
+ {
+ audio.reset();
+ audio.reset(new mqConsoleLib::AudioOutputPA(process, devNameAudioOut));
+ }
+ if(!midiIn || (!devNameMidiIn.empty() && midiIn->getDeviceName() != devNameMidiIn))
+ {
+ midiIn.reset();
+ midiIn.reset(new mqConsoleLib::MidiInput(devNameMidiIn));
+ }
+ if(!midiOut || (!devNameMidiOut.empty() && midiOut->getDeviceName() != devNameMidiOut))
+ {
+ midiOut.reset();
+ midiOut.reset(new mqConsoleLib::MidiOutput(devNameMidiOut));
+ }
+
+ devNameMidiIn.clear();
+ devNameMidiOut.clear();
+ devNameAudioOut.clear();
+
+ std::ofstream fs(synthLib::getModulePath() + "config.cfg");
+
+ if(fs.is_open())
+ {
+ fs << "MidiIn=" << midiIn->getDeviceName() << std::endl;
+ fs << "MidiOut=" << midiOut->getDeviceName() << std::endl;
+ fs << "AudioOut=" << audio->getDeviceName() << std::endl;
+ fs.close();
+ }
+
+ devIdMidiIn = midiIn->getDeviceId();
+ devIdMidiOut = midiOut->getDeviceId();
+ devIdAudioOut = audio->getDeviceId();
+ settingsChanged = false;
+ };
+
+ createDevices();
+
+ dsp56k::ThreadTools::setCurrentThreadName("main");
+
+ while(true)
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+
+ if(settingsChanged)
+ {
+ createDevices();
+ settingsChanged = false;
+ }
+
+ if(midiIn)
+ midiIn->process(midiInBuffer);
+
+ for (const auto& e : midiInBuffer)
+ mq.sendMidiEvent(e);
+ midiInBuffer.clear();
+
+ mq.receiveMidi(midiOutBuffer);
+ if(midiOut)
+ midiOut->write(midiOutBuffer);
+ midiOutBuffer.clear();
+ }
+}
diff --git a/source/mqVst2/.gitignore b/source/mqVst2/.gitignore
@@ -0,0 +1,3 @@
+Info.plist
+version.h
+versionRC.h
diff --git a/source/synthLib/midiBufferParser.cpp b/source/synthLib/midiBufferParser.cpp
@@ -57,7 +57,7 @@ namespace synthLib
void MidiBufferParser::getEvents(std::vector<synthLib::SMidiEvent>& _events)
{
- std::swap(m_midiEvents, _events);
+ _events.insert(_events.end(), m_midiEvents.begin(), m_midiEvents.end());
m_midiEvents.clear();
}