gearmulator

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

commit 5d10c754fc61c9cb7909a3fbd18dacad56b9bcb7
parent 58a00a7078b046070f399edc51b76d330de4a631
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Thu, 29 Jun 2023 00:07:19 +0200

vavra initial commit

Diffstat:
M.gitmodules | 6++++++
Msource/CMakeLists.txt | 31++++++++++++++++++++++++++++++-
Asource/cpp-terminal | 1+
Asource/mc68k | 1+
Asource/mqConsoleLib/CMakeLists.txt | 24++++++++++++++++++++++++
Asource/mqConsoleLib/audioOutput.cpp | 1+
Asource/mqConsoleLib/audioOutput.h | 25+++++++++++++++++++++++++
Asource/mqConsoleLib/audioOutputPA.cpp | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/audioOutputPA.h | 37+++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/audioOutputWAV.cpp | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/audioOutputWAV.h | 26++++++++++++++++++++++++++
Asource/mqConsoleLib/device.cpp | 41+++++++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/device.h | 32++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/midiDevice.cpp | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/midiDevice.h | 22++++++++++++++++++++++
Asource/mqConsoleLib/midiInput.cpp | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/midiInput.h | 31+++++++++++++++++++++++++++++++
Asource/mqConsoleLib/midiOutput.cpp | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/midiOutput.h | 32++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/mqGui.cpp | 336+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/mqGui.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/mqGuiBase.cpp | 25+++++++++++++++++++++++++
Asource/mqConsoleLib/mqGuiBase.h | 16++++++++++++++++
Asource/mqConsoleLib/mqKeyInput.cpp | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/mqKeyInput.h | 22++++++++++++++++++++++
Asource/mqConsoleLib/mqSettingsGui.cpp | 299+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqConsoleLib/mqSettingsGui.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/.gitignore | 1+
Asource/mqJucePlugin/CMakeLists.txt | 30++++++++++++++++++++++++++++++
Asource/mqJucePlugin/PluginEditor.cpp | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/PluginEditor.h | 30++++++++++++++++++++++++++++++
Asource/mqJucePlugin/PluginEditorState.cpp | 39+++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/PluginEditorState.h | 21+++++++++++++++++++++
Asource/mqJucePlugin/PluginProcessor.cpp | 290+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/PluginProcessor.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqController.cpp | 485+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqController.h | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqEditor.cpp | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqEditor.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqFrontPanel.cpp | 230+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqFrontPanel.h | 40++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqLcd.cpp | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqLcd.h | 33+++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqLcdBase.cpp | 0
Asource/mqJucePlugin/mqLcdBase.h | 12++++++++++++
Asource/mqJucePlugin/mqLcdText.cpp | 29+++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqLcdText.h | 21+++++++++++++++++++++
Asource/mqJucePlugin/mqPartSelect.cpp | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqPartSelect.h | 34++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqPatchBrowser.cpp | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/mqPatchBrowser.h | 26++++++++++++++++++++++++++
Asource/mqJucePlugin/parameterDescriptions_mq.json | 2819+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/skins/mqDefault/Digital.ttf | 0
Asource/mqJucePlugin/skins/mqDefault/assets.cmake | 34++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/skins/mqDefault/button.png | 0
Asource/mqJucePlugin/skins/mqDefault/encoder_ranged.png | 0
Asource/mqJucePlugin/skins/mqDefault/encoder_ranged_red.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx1Chorus.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx1FiveFX.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx1Flanger.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx1Overdrive.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx1Phaser.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx1Vocoder.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx251Delay.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx2Delay.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx2DelayClocked.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx2DelayNotClocked.png | 0
Asource/mqJucePlugin/skins/mqDefault/fx2Reverb.png | 0
Asource/mqJucePlugin/skins/mqDefault/lcdshadow.png | 0
Asource/mqJucePlugin/skins/mqDefault/led.png | 0
Asource/mqJucePlugin/skins/mqDefault/ledcircle_11.png | 0
Asource/mqJucePlugin/skins/mqDefault/ledcircle_5.png | 0
Asource/mqJucePlugin/skins/mqDefault/ledcircle_6.png | 0
Asource/mqJucePlugin/skins/mqDefault/ledcircle_7.png | 0
Asource/mqJucePlugin/skins/mqDefault/ledcircle_9.png | 0
Asource/mqJucePlugin/skins/mqDefault/mqDefault.json | 12927+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/skins/mqDefault/mqDefaultBG.png | 0
Asource/mqJucePlugin/skins/mqDefault/pageDrum.png | 0
Asource/mqJucePlugin/skins/mqDefault/pageFilters.png | 0
Asource/mqJucePlugin/skins/mqDefault/pageFx.png | 0
Asource/mqJucePlugin/skins/mqDefault/pageLfo.png | 0
Asource/mqJucePlugin/skins/mqDefault/pageMulti.png | 0
Asource/mqJucePlugin/skins/mqDefault/pageOsc.png | 0
Asource/mqJucePlugin/skins/mqDefault/pagePatchBrowser.png | 0
Asource/mqJucePlugin/skins/mqDefault/toggleWithLED.png | 0
Asource/mqJucePlugin/skins/mqFrontPanel/Digital.ttf | 0
Asource/mqJucePlugin/skins/mqFrontPanel/alphadial.png | 0
Asource/mqJucePlugin/skins/mqFrontPanel/assets.cmake | 10++++++++++
Asource/mqJucePlugin/skins/mqFrontPanel/bakedBG.png | 0
Asource/mqJucePlugin/skins/mqFrontPanel/button.png | 0
Asource/mqJucePlugin/skins/mqFrontPanel/encoder.png | 0
Asource/mqJucePlugin/skins/mqFrontPanel/led.png | 0
Asource/mqJucePlugin/skins/mqFrontPanel/mqFrontPanel.json | 967+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqJucePlugin/version.h.in | 4++++
Asource/mqLib/CMakeLists.txt | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/am29f.cpp | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/am29f.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/bin2h.cmake | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/buttons.cpp | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/buttons.h | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/device.cpp | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/device.h | 41+++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/dspBootCode.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/lcd.cpp | 238+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/lcd.h | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/lcdfonts.cpp | 2673+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/lcdfonts.h | 6++++++
Asource/mqLib/leds.cpp | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/leds.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/microq.cpp | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/microq.h | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqbuildconfig.h | 16++++++++++++++++
Asource/mqLib/mqdsp.cpp | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqdsp.h | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqhardware.cpp | 507+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqhardware.h | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqmc.cpp | 345+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqmc.h | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqmidi.cpp | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqmidi.h | 44++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqmiditypes.h | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqstate.cpp | 840+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqstate.h | 244+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqsysexremotecontrol.cpp | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/mqsysexremotecontrol.h | 35+++++++++++++++++++++++++++++++++++
Asource/mqLib/mqtypes.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/rom.cpp | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqLib/rom.h | 32++++++++++++++++++++++++++++++++
Asource/mqPerformanceTest/CMakeLists.txt | 20++++++++++++++++++++
Asource/mqPerformanceTest/mqPerformanceTest.cpp | 163+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqTestConsole/CMakeLists.txt | 29+++++++++++++++++++++++++++++
Asource/mqTestConsole/mqTestConsole.cpp | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/mqVst2/.gitignore | 3+++
Msource/synthLib/midiBufferParser.cpp | 2+-
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(); }