commit b8a3ed45ec056443d8389fc7f6566c715c706de7 parent 66ed426bb34477ada9604be9b1c511fb4655291e Author: dsp56300 <dsp56300@users.noreply.github.com> Date: Thu, 13 Jun 2024 21:14:53 +0200 first wave editor steps, waves are being requested and stored Diffstat:
22 files changed, 411 insertions(+), 2 deletions(-)
diff --git a/source/xtJucePlugin/CMakeLists.txt b/source/xtJucePlugin/CMakeLists.txt @@ -5,6 +5,13 @@ set(SOURCES parameterDescriptions_xt.json PluginEditorState.cpp PluginEditorState.h PluginProcessor.cpp PluginProcessor.h + weData.cpp weData.h + weTree.cpp weTree.h + weTreeItem.cpp weTreeItem.h + weTypes.h + weWaveform.cpp weWaveform.h + weWaveTree.cpp weWaveTree.h + weWaveTreeItem.cpp weWaveTreeItem.h xtController.cpp xtController.h xtEditor.cpp xtEditor.h xtFrontPanel.cpp xtFrontPanel.h @@ -14,13 +21,13 @@ set(SOURCES xtPartName.cpp xtPartName.h xtParts.cpp xtParts.h xtPatchManager.cpp xtPatchManager.h + xtWaveEditor.cpp xtWaveEditor.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_xt.json") -#include(skins/xtFrontPanel/assets.cmake) include(skins/xtDefault/assets.cmake) juce_add_binary_data(xtJucePlugin_BinaryData SOURCES ${ASSETS} ${ASSETS_xtDefault}) diff --git a/source/xtJucePlugin/parameterDescriptions_xt.json b/source/xtJucePlugin/parameterDescriptions_xt.json @@ -2001,6 +2001,43 @@ {"type": "paramindex"}, {"type": "paramvalue"}, {"type": "byte", "value": "f7"} + ], + "requestWave": [ + {"type": "byte", "value": "f0"}, + {"type": "byte", "value": "3e"}, + {"type": "byte", "value": "0e"}, + {"type": "deviceid"}, + {"type": "byte", "value": "02"}, + {"type": "bank"}, + {"type": "program"}, + {"type": "byte", "value": "f7"} + ], + "waveDump": [ + {"type": "byte", "value": "f0"}, + {"type": "byte", "value": "3e"}, + {"type": "byte", "value": "0e"}, + {"type": "deviceid"}, + {"type": "byte", "value": "12"}, + {"type": "bank"}, + {"type": "program"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "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": 7, "last": 134}, + {"type": "byte", "value": "f7"} ] } } diff --git a/source/xtJucePlugin/weData.cpp b/source/xtJucePlugin/weData.cpp @@ -0,0 +1,111 @@ +#include "weData.h" + +#include "xtController.h" + +namespace xtJucePlugin +{ + WaveEditorData::WaveEditorData(Controller& _controller) : m_controller(_controller) + { + } + + void WaveEditorData::requestData() + { + if(isWaitingForWave()) + return; + + for(uint32_t i=0; i<m_romWaves.size(); ++i) + { + if(!m_romWaves[i]) + { + requestWave(i); + return; + } + } + + for(uint32_t i=0; i<m_ramWaves.size(); ++i) + { + if(!m_ramWaves[i]) + { + requestWave(i + xt::Wave::g_firstRamWaveIndex); + return; + } + } + } + + void WaveEditorData::onReceiveWave(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg) + { + const auto hh = _data.at(pluginLib::MidiDataType::Bank); + const auto ll = _data.at(pluginLib::MidiDataType::Program); + + const uint32_t index = (hh << 7) | ll; + + if(!xt::Wave::isValidWaveIndex(index)) + return; + + /* + mw2_sysex.pdf: + + "A Wave consists of 128 eight Bit samples, but only the first 64 of them are + stored/transmitted, the second half is same as first except the values are + negated and the order is reversed: + + Wave[64+n] = -Wave[63-n] for n=0..63 + + Note that samples are not two's complement format, to get a signed byte, + the most significant bit must be flipped: + + signed char s = Wave[n] ^ 0x80" + */ + + WaveData data; + + constexpr auto off = 7; + + for(uint32_t i=0; i<data.size()>>1; ++i) + { + auto sample = (_msg[off + i]) << 4 | _msg[off + i + 1]; + sample = sample ^ 0x80; + + data[i] = static_cast<int8_t>(sample); + data[64+i] = static_cast<int8_t>(-sample); + } + + setWave(index, data); + + if(m_currentWaveRequestIndex == index) + { + m_currentWaveRequestIndex = InvalidWaveIndex; + requestData(); + } + } + + bool WaveEditorData::requestWave(const uint32_t _index) + { + if(isWaitingForWave()) + return false; + + if(!m_controller.requestWave(_index)) + return false; + m_currentWaveRequestIndex = _index; + return true; + } + + bool WaveEditorData::setWave(uint32_t _index, const WaveData& _data) + { + if(_index < m_romWaves.size()) + { + m_romWaves[_index] = _data; + return true; + } + if(_index < xt::Wave::g_firstRamWaveIndex) + return false; + + _index -= xt::Wave::g_firstRamWaveIndex; + + if(_index >= m_ramWaves.size()) + return false; + + m_ramWaves[_index] = _data; + return true; + } +} diff --git a/source/xtJucePlugin/weData.h b/source/xtJucePlugin/weData.h @@ -0,0 +1,41 @@ +#pragma once + +#include <cstdint> +#include <optional> +#include <limits> +#include <vector> + +#include "weTypes.h" + +#include "../xtLib/xtMidiTypes.h" + +#include "../jucePluginLib/midipacket.h" + +class Controller; + +namespace xtJucePlugin +{ + class WaveEditorData + { + public: + static constexpr uint32_t InvalidWaveIndex = std::numeric_limits<uint32_t>::max(); + + WaveEditorData(Controller& _controller); + + void requestData(); + + bool isWaitingForWave() const { return m_currentWaveRequestIndex != InvalidWaveIndex; } + void onReceiveWave(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg); + + private: + bool requestWave(uint32_t _index); + bool setWave(uint32_t _index, const WaveData& _data); + + Controller& m_controller; + + uint32_t m_currentWaveRequestIndex = InvalidWaveIndex; + + std::array<std::optional<WaveData>, xt::Wave::g_romWaveCount> m_romWaves; + std::array<std::optional<WaveData>, xt::Wave::g_ramWaveCount> m_ramWaves; + }; +} diff --git a/source/xtJucePlugin/weTree.cpp b/source/xtJucePlugin/weTree.cpp @@ -0,0 +1,5 @@ +#include "weTree.h" + +namespace xtJucePlugin +{ +} diff --git a/source/xtJucePlugin/weTree.h b/source/xtJucePlugin/weTree.h @@ -0,0 +1,5 @@ +#pragma once + +namespace xtJucePlugin +{ +} diff --git a/source/xtJucePlugin/weTreeItem.cpp b/source/xtJucePlugin/weTreeItem.cpp @@ -0,0 +1,5 @@ +#include "weTreeItem.h" + +namespace xtJucePlugin +{ +} diff --git a/source/xtJucePlugin/weTreeItem.h b/source/xtJucePlugin/weTreeItem.h @@ -0,0 +1,5 @@ +#pragma once + +namespace xtJucePlugin +{ +} diff --git a/source/xtJucePlugin/weTypes.h b/source/xtJucePlugin/weTypes.h @@ -0,0 +1,9 @@ +#pragma once + +#include <array> +#include <cstdint> + +namespace xtJucePlugin +{ + using WaveData = std::array<int8_t, 128>; +} diff --git a/source/xtJucePlugin/weWaveTree.cpp b/source/xtJucePlugin/weWaveTree.cpp @@ -0,0 +1,5 @@ +#include "weWaveTree.h" + +namespace xtJucePlugin +{ +} diff --git a/source/xtJucePlugin/weWaveTree.h b/source/xtJucePlugin/weWaveTree.h @@ -0,0 +1,5 @@ +#pragma once + +namespace xtJucePlugin +{ +} diff --git a/source/xtJucePlugin/weWaveTreeItem.cpp b/source/xtJucePlugin/weWaveTreeItem.cpp @@ -0,0 +1,5 @@ +#include "weWaveTreeItem.h" + +namespace xtJucePlugin +{ +} diff --git a/source/xtJucePlugin/weWaveTreeItem.h b/source/xtJucePlugin/weWaveTreeItem.h @@ -0,0 +1,5 @@ +#pragma once + +namespace xtJucePlugin +{ +} diff --git a/source/xtJucePlugin/weWaveform.cpp b/source/xtJucePlugin/weWaveform.cpp @@ -0,0 +1,5 @@ +#include "weWaveform.h" + +namespace xtJucePlugin +{ +} diff --git a/source/xtJucePlugin/weWaveform.h b/source/xtJucePlugin/weWaveform.h @@ -0,0 +1,5 @@ +#pragma once + +namespace xtJucePlugin +{ +} diff --git a/source/xtJucePlugin/xtController.cpp b/source/xtJucePlugin/xtController.cpp @@ -12,6 +12,7 @@ #include "dsp56kEmu/logging.h" #include "xtFrontPanel.h" +#include "xtWaveEditor.h" namespace { @@ -34,7 +35,9 @@ namespace "emuRequestLcd", "emuRequestLeds", "emuSendButton", - "emuSendRotary" + "emuSendRotary", + "requestWave", + "waveDump" }; static_assert(std::size(g_midiPacketNames) == static_cast<size_t>(Controller::MidiPacketType::Count)); @@ -380,6 +383,11 @@ bool Controller::parseSysexMessage(const pluginLib::SysEx& _msg, synthLib::MidiE m_globalData[index] = value; } } + else if(name == midiPacketName(WaveDump)) + { + if(m_waveEditor) + m_waveEditor->onReceiveWave(data, _msg); + } else { LOG("Received unknown sysex of size " << _msg.size()); @@ -498,6 +506,11 @@ void Controller::setFrontPanel(xtJucePlugin::FrontPanel* _frontPanel) m_frontPanel = _frontPanel; } +void Controller::setWaveEditor(xtJucePlugin::WaveEditor* _waveEditor) +{ + m_waveEditor = _waveEditor; +} + void Controller::selectPreset(const int _offset) { auto& current = isMultiMode() ? m_currentSingles[getCurrentPart()] : m_currentSingle; @@ -643,6 +656,19 @@ void Controller::requestMulti(xt::LocationH _buf, uint8_t _location) const sendSysEx(RequestMulti, params); } +bool Controller::requestWave(const uint32_t _number) const +{ + if(!xt::Wave::isValidWaveIndex(_number)) + return false; + + std::map<pluginLib::MidiDataType, uint8_t> params; + + params[pluginLib::MidiDataType::Bank] = static_cast<uint8_t>(_number >> 7); + params[pluginLib::MidiDataType::Program] = _number & 0x7f; + + return sendSysEx(RequestWave, params); +} + uint8_t Controller::getGlobalParam(xt::GlobalParameter _type) const { return m_globalData[static_cast<uint32_t>(_type)]; diff --git a/source/xtJucePlugin/xtController.h b/source/xtJucePlugin/xtController.h @@ -6,6 +6,7 @@ namespace xtJucePlugin { + class WaveEditor; class FrontPanel; } @@ -40,6 +41,8 @@ public: EmuRequestLeds, EmuSendButton, EmuSendRotary, + RequestWave, + WaveDump, Count }; @@ -82,6 +85,9 @@ public: bool setString(pluginLib::MidiPacket::AnyPartParamValues& _values, const std::string& _prefix, size_t _len, const std::string& _value) const; void setFrontPanel(xtJucePlugin::FrontPanel* _frontPanel); + void setWaveEditor(xtJucePlugin::WaveEditor* _waveEditor); + + bool requestWave(uint32_t _number) const; private: void selectPreset(int _offset); @@ -122,4 +128,5 @@ private: std::array<uint32_t, 8> m_currentSingles{0}; uint32_t m_currentSingle = 0; xtJucePlugin::FrontPanel* m_frontPanel = nullptr; + xtJucePlugin::WaveEditor* m_waveEditor = nullptr; }; diff --git a/source/xtJucePlugin/xtEditor.cpp b/source/xtJucePlugin/xtEditor.cpp @@ -8,6 +8,8 @@ #include "xtFrontPanel.h" #include "xtPartName.h" #include "xtPatchManager.h" +#include "xtWaveEditor.h" + #include "../jucePluginLib/parameterbinding.h" namespace xtJucePlugin @@ -100,12 +102,20 @@ namespace xtJucePlugin getPatchManager()->selectNextPreset(m_controller.getCurrentPart()); }; +#if defined(_DEBUG) && defined(_WIN32) + auto* waveEditorContainer = findComponent("waveEditorContainer"); + m_waveEditor = std::make_unique<WaveEditor>(waveEditorContainer, *this); + getXtController().setWaveEditor(m_waveEditor.get()); +#else auto* waveEditorButtonParent = findComponent("waveEditorButtonParent"); waveEditorButtonParent->setVisible(false); +#endif } Editor::~Editor() { + getXtController().setWaveEditor(nullptr); + m_waveEditor.reset(); m_frontPanel.reset(); } diff --git a/source/xtJucePlugin/xtEditor.h b/source/xtJucePlugin/xtEditor.h @@ -22,6 +22,7 @@ namespace pluginLib namespace xtJucePlugin { + class WaveEditor; class FocusedParameter; class FrontPanel; class PatchManager; @@ -60,6 +61,7 @@ namespace xtJucePlugin std::unique_ptr<FocusedParameter> m_focusedParameter; std::unique_ptr<FrontPanel> m_frontPanel; std::unique_ptr<Parts> m_parts; + std::unique_ptr<WaveEditor> m_waveEditor; pluginLib::EventListener<bool> m_playModeChangeListener; diff --git a/source/xtJucePlugin/xtWaveEditor.cpp b/source/xtJucePlugin/xtWaveEditor.cpp @@ -0,0 +1,57 @@ +#include "xtWaveEditor.h" + +#include "xtEditor.h" + +namespace xtJucePlugin +{ + constexpr float g_scale = 2.0f; + + WaveEditor::WaveEditor(Component* _parent, Editor& _editor) : ComponentMovementWatcher(this), m_editor(_editor), m_data(_editor.getXtController()) + { + setTransform(juce::AffineTransform::scale(g_scale, g_scale)); + setSize(static_cast<int>(static_cast<float>(_parent->getWidth()) / g_scale), static_cast<int>(static_cast<float>(_parent->getHeight()) / g_scale)); + _parent->addAndMakeVisible(this); + addComponentListener(this); + } + + WaveEditor::~WaveEditor() + { + removeComponentListener(this); + } + + void WaveEditor::visibilityChanged() + { + Component::visibilityChanged(); + checkFirstTimeVisible(); + } + + void WaveEditor::parentHierarchyChanged() + { + Component::parentHierarchyChanged(); + checkFirstTimeVisible(); + } + + void WaveEditor::onReceiveWave(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg) + { + m_data.onReceiveWave(_data, _msg); + } + + void WaveEditor::componentVisibilityChanged() + { + checkFirstTimeVisible(); + } + + void WaveEditor::checkFirstTimeVisible() + { + if(isShowing() && !m_wasVisible) + { + m_wasVisible = true; + onFirstTimeVisible(); + } + } + + void WaveEditor::onFirstTimeVisible() + { + m_data.requestData(); + } +} diff --git a/source/xtJucePlugin/xtWaveEditor.h b/source/xtJucePlugin/xtWaveEditor.h @@ -0,0 +1,36 @@ +#pragma once + +#include "weData.h" +#include "juce_gui_basics/juce_gui_basics.h" + +#include "../jucePluginLib/midipacket.h" + +namespace xtJucePlugin +{ + class Editor; + + class WaveEditor : public juce::Component, juce::ComponentMovementWatcher + { + public: + explicit WaveEditor(juce::Component* _parent, Editor& _editor); + ~WaveEditor(); + + void visibilityChanged() override; + void parentHierarchyChanged() override; + + void onReceiveWave(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg); + + private: + // ComponentMovementWatcher + void componentVisibilityChanged() override; + void componentPeerChanged() override {} + void componentMovedOrResized(bool wasMoved, bool wasResized) override {} + + void checkFirstTimeVisible(); + void onFirstTimeVisible(); + + Editor& m_editor; + WaveEditorData m_data; + bool m_wasVisible = false; + }; +} diff --git a/source/xtLib/xtMidiTypes.h b/source/xtLib/xtMidiTypes.h @@ -169,4 +169,20 @@ namespace xt static constexpr uint32_t g_singleNameLength = 16; static constexpr uint32_t g_singleNamePosition = 153; }; + + namespace Wave + { + static constexpr uint32_t g_romWaveCount = 300; + static constexpr uint32_t g_ramWaveCount = 250; + static constexpr uint32_t g_firstRamWaveIndex = 1000; + + inline bool isValidWaveIndex(const uint32_t _index) + { + if(_index >= g_firstRamWaveIndex + g_ramWaveCount) + return false; + if(_index >= g_romWaveCount && _index < g_firstRamWaveIndex) + return false; + return true; + } + } }