commit 15834c2dbb80ff5145abe441d0013539fcd6badb parent ea9e967fc8dcb3e321bcfd94ec20c2752cc69e6f Author: dsp56300 <dsp56300@users.noreply.github.com> Date: Sat, 15 Jun 2024 20:41:59 +0200 add wavetables list and selected wavetable list Diffstat:
22 files changed, 538 insertions(+), 14 deletions(-)
diff --git a/source/xtJucePlugin/CMakeLists.txt b/source/xtJucePlugin/CMakeLists.txt @@ -5,7 +5,11 @@ set(SOURCES parameterDescriptions_xt.json PluginEditorState.cpp PluginEditorState.h PluginProcessor.cpp PluginProcessor.h + weControlTree.cpp weControlTree.h + weControlTreeItem.cpp weControlTreeItem.h weData.cpp weData.h + weTablesTree.cpp weTablesTree.h + weTablesTreeItem.cpp weTablesTreeItem.h weTree.cpp weTree.h weTreeItem.cpp weTreeItem.h weTypes.h diff --git a/source/xtJucePlugin/parameterDescriptions_xt.json b/source/xtJucePlugin/parameterDescriptions_xt.json @@ -2038,6 +2038,43 @@ {"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"} + ], + "requestTable": [ + {"type": "byte", "value": "f0"}, + {"type": "byte", "value": "3e"}, + {"type": "byte", "value": "0e"}, + {"type": "deviceid"}, + {"type": "byte", "value": "03"}, + {"type": "bank"}, + {"type": "program"}, + {"type": "byte", "value": "f7"} + ], + "tableDump": [ + {"type": "byte", "value": "f0"}, + {"type": "byte", "value": "3e"}, + {"type": "byte", "value": "0e"}, + {"type": "deviceid"}, + {"type": "byte", "value": "13"}, + {"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": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, + {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "null"}, {"type": "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": 262}, + {"type": "byte", "value": "f7"} ] } } diff --git a/source/xtJucePlugin/weControlTree.cpp b/source/xtJucePlugin/weControlTree.cpp @@ -0,0 +1,37 @@ +#include "weControlTree.h" + +#include "weControlTreeItem.h" +#include "xtWaveEditor.h" + +namespace xtJucePlugin +{ + ControlTree::ControlTree(WaveEditor& _editor) : Tree(_editor) + { + m_onTableChanged.set(_editor.getData().onTableChanged, [this](const uint32_t& _index) + { + if(_index == m_table) + onTableChanged(); + }); + + for(uint32_t i=0; i<m_items.size(); ++i) + { + m_items[i] = new ControlTreeItem(_editor, i); + getRootItem()->addSubItem(m_items[i]); + } + setIndentSize(5); + } + + void ControlTree::setTable(const uint32_t _index) + { + if(_index == m_table) + return; + m_table = _index; + onTableChanged(); + } + + void ControlTree::onTableChanged() + { + for (auto* item : m_items) + item->setTable(m_table); + } +} diff --git a/source/xtJucePlugin/weControlTree.h b/source/xtJucePlugin/weControlTree.h @@ -0,0 +1,25 @@ +#pragma once + +#include "weControlTreeItem.h" +#include "weTree.h" + +#include "../jucePluginLib/event.h" +#include "../xtLib/xtMidiTypes.h" + +namespace xtJucePlugin +{ + class ControlTree : public Tree + { + public: + ControlTree(WaveEditor& _editor); + + void setTable(uint32_t _index); + + private: + void onTableChanged(); + + pluginLib::EventListener<uint32_t> m_onTableChanged; + uint32_t m_table = ~0; + std::array<ControlTreeItem*, xt::Wave::g_wavesPerTable> m_items; + }; +} diff --git a/source/xtJucePlugin/weControlTreeItem.cpp b/source/xtJucePlugin/weControlTreeItem.cpp @@ -0,0 +1,54 @@ +#include "weControlTreeItem.h" + +#include "weWaveTreeItem.h" +#include "xtWaveEditor.h" + +namespace xtJucePlugin +{ + class WaveEditor; + + ControlTreeItem::ControlTreeItem(WaveEditor& _editor, const uint32_t _index) : m_editor(_editor), m_index(_index) + { + m_onWaveChanged.set(_editor.getData().onWaveChanged, [this](const unsigned& _wave) + { + if(_wave == m_wave) + onWaveChanged(); + }); + + setPaintRootItemInBold(false); + setDrawsInLeftMargin(true); + } + + void ControlTreeItem::paintItem(juce::Graphics& _g, const int _width, const int _height) + { + if(const auto wave = m_editor.getData().getWave(m_table, m_index)) + WaveTreeItem::paintWave(*wave, _g, _width>>1, 0, _width>>1, _height, juce::Colour(0xffffffff)); + + TreeItem::paintItem(_g, _width, _height); + } + + void ControlTreeItem::setWave(const uint32_t _wave) + { + if(m_wave == _wave) + return; + m_wave = _wave; + const auto name = WaveTreeItem::getWaveName(m_wave); + char prefix[16] = {0}; + snprintf(prefix, std::size(prefix), "%02d: ", m_index); + setText(prefix + (name.empty() ? "-" : name)); + repaintItem(); + } + + void ControlTreeItem::setTable(const uint32_t _table) + { + if(m_table == _table) + return; + m_table = _table; + setWave(m_editor.getData().getWaveIndex(_table, m_index)); + } + + void ControlTreeItem::onWaveChanged() const + { + repaintItem(); + } +} diff --git a/source/xtJucePlugin/weControlTreeItem.h b/source/xtJucePlugin/weControlTreeItem.h @@ -0,0 +1,34 @@ +#pragma once + +#include "weTreeItem.h" + +#include "../jucePluginLib/event.h" + +namespace xtJucePlugin +{ + class WaveEditor; + + class ControlTreeItem : public TreeItem + { + public: + ControlTreeItem(WaveEditor& _editor, uint32_t _index); + + bool mightContainSubItems() override { return false; } + + void paintItem(juce::Graphics& _g, int _width, int _height) override; + + void setWave(uint32_t _wave); + void setTable(uint32_t _table); + + private: + void onWaveChanged() const; + + WaveEditor& m_editor; + const uint32_t m_index; + + uint32_t m_wave = ~0; + uint32_t m_table = ~0; + + pluginLib::EventListener<uint32_t> m_onWaveChanged; + }; +} diff --git a/source/xtJucePlugin/weData.cpp b/source/xtJucePlugin/weData.cpp @@ -1,6 +1,7 @@ #include "weData.h" #include "xtController.h" +#include "dsp56kEmu/logging.h" namespace xtJucePlugin { @@ -10,9 +11,19 @@ namespace xtJucePlugin void WaveEditorData::requestData() { - if(isWaitingForWave()) + if(isWaitingForData()) return; + for(uint32_t i=0; i<m_tables.size(); ++i) + { + if(!m_tables[i] && !isAlgorithmicTable(i)) + { + LOG("Request table " << i); + requestTable(i); + return; + } + } + for(uint32_t i=0; i<m_romWaves.size(); ++i) { if(!m_romWaves[i]) @@ -34,10 +45,7 @@ namespace xtJucePlugin 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; + const auto index = toIndex(_data); if(!xt::Wave::isValidWaveIndex(index)) return; @@ -79,6 +87,38 @@ namespace xtJucePlugin } } + void WaveEditorData::onReceiveTable(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg) + { + const auto index = toIndex(_data); + + if(!xt::Wave::isValidTableIndex(index)) + return; + + constexpr uint32_t off = 7; + + TableData table; + + for(uint32_t i=0; i<64; ++i) + { + const auto i4 = i<<2; + + auto waveIdx = _msg[i4+off] << 12; + waveIdx |= _msg[i4+off+1] << 8; + waveIdx |= _msg[i4+off+2] << 4; + waveIdx |= _msg[i4+off+3]; + + table[i] = static_cast<uint16_t>(waveIdx); + } + + setTable(index, table); + + if(m_currentTableRequestIndex == index) + { + m_currentTableRequestIndex = InvalidWaveIndex; + requestData(); + } + } + std::optional<WaveData> WaveEditorData::getWave(uint32_t _waveIndex) const { if(_waveIndex < m_romWaves.size()) @@ -96,9 +136,34 @@ namespace xtJucePlugin return m_ramWaves[_waveIndex]; } + uint32_t WaveEditorData::getWaveIndex(uint32_t _tableIndex, uint32_t _indexInTable) const + { + if(_tableIndex >= m_tables.size()) + return InvalidWaveIndex; + if(_indexInTable >= std::tuple_size<TableData>()) + return InvalidWaveIndex; + const auto table = m_tables[_tableIndex]; + if(!table) + return InvalidWaveIndex; + return (*table)[_indexInTable]; + } + + std::optional<WaveData> WaveEditorData::getWave(const uint32_t _tableIndex, const uint32_t _indexInTable) const + { + return getWave(getWaveIndex(_tableIndex, _indexInTable)); + } + + uint32_t WaveEditorData::toIndex(const pluginLib::MidiPacket::Data& _data) + { + const auto hh = _data.at(pluginLib::MidiDataType::Bank); + const auto ll = _data.at(pluginLib::MidiDataType::Program); + + return (hh << 7) | ll; + } + bool WaveEditorData::requestWave(const uint32_t _index) { - if(isWaitingForWave()) + if(isWaitingForData()) return false; if(!m_controller.requestWave(_index)) @@ -107,6 +172,17 @@ namespace xtJucePlugin return true; } + bool WaveEditorData::requestTable(const uint32_t _index) + { + if(isWaitingForData()) + return false; + + if(!m_controller.requestTable(_index)) + return false; + m_currentTableRequestIndex = _index; + return true; + } + bool WaveEditorData::setWave(uint32_t _index, const WaveData& _data) { if(_index < m_romWaves.size()) @@ -124,7 +200,27 @@ namespace xtJucePlugin return false; m_ramWaves[_index] = _data; - onWaveChanged(_index); + onWaveChanged(_index + xt::Wave::g_firstRamWaveIndex); + return true; + } + + bool WaveEditorData::setTable(uint32_t _index, const TableData& _data) + { + if(_index >= m_tables.size()) + return false; + + m_tables[_index] = _data; + onTableChanged(_index); return true; } + + bool WaveEditorData::isAlgorithmicTable(const uint32_t _index) + { + for (const uint32_t i : xt::Wave::g_algorithmicWavetables) + { + if(_index == i) + return true; + } + return false; + } } diff --git a/source/xtJucePlugin/weData.h b/source/xtJucePlugin/weData.h @@ -22,24 +22,40 @@ namespace xtJucePlugin static constexpr uint32_t InvalidWaveIndex = std::numeric_limits<uint32_t>::max(); pluginLib::Event<uint32_t> onWaveChanged; + pluginLib::Event<uint32_t> onTableChanged; WaveEditorData(Controller& _controller); void requestData(); bool isWaitingForWave() const { return m_currentWaveRequestIndex != InvalidWaveIndex; } + bool isWaitingForTable() const { return m_currentTableRequestIndex != InvalidWaveIndex; } + bool isWaitingForData() const { return isWaitingForWave() || isWaitingForTable(); } + void onReceiveWave(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg); + void onReceiveTable(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg); std::optional<WaveData> getWave(uint32_t _waveIndex) const; + uint32_t getWaveIndex(uint32_t _tableIndex, uint32_t _indexInTable) const; + std::optional<WaveData> getWave(uint32_t _tableIndex, uint32_t _indexInTable) const; + + static uint32_t toIndex(const pluginLib::MidiPacket::Data& _data); + static bool isAlgorithmicTable(uint32_t _index); private: + bool requestWave(uint32_t _index); + bool requestTable(uint32_t _index); + bool setWave(uint32_t _index, const WaveData& _data); + bool setTable(uint32_t _index, const TableData& _data); Controller& m_controller; uint32_t m_currentWaveRequestIndex = InvalidWaveIndex; + uint32_t m_currentTableRequestIndex = InvalidWaveIndex; std::array<std::optional<WaveData>, xt::Wave::g_romWaveCount> m_romWaves; std::array<std::optional<WaveData>, xt::Wave::g_ramWaveCount> m_ramWaves; + std::array<std::optional<TableData>, xt::Wave::g_tableCount> m_tables; }; } diff --git a/source/xtJucePlugin/weTablesTree.cpp b/source/xtJucePlugin/weTablesTree.cpp @@ -0,0 +1,22 @@ +#include "weTablesTree.h" + +#include "weTablesTreeItem.h" +#include "weTypes.h" +#include "weData.h" + +#include "../xtLib/xtMidiTypes.h" + +namespace xtJucePlugin +{ + TablesTree::TablesTree(WaveEditor& _editor): Tree(_editor) + { + for(uint32_t i=0; i<xt::Wave::g_tableCount; ++i) + { + if(WaveEditorData::isAlgorithmicTable(i)) + continue; + + getRootItem()->addSubItem(new TablesTreeItem(_editor, i)); + } + setIndentSize(5); + } +} diff --git a/source/xtJucePlugin/weTablesTree.h b/source/xtJucePlugin/weTablesTree.h @@ -0,0 +1,12 @@ +#pragma once + +#include "weTree.h" + +namespace xtJucePlugin +{ + class TablesTree : public Tree + { + public: + explicit TablesTree(WaveEditor& _editor); + }; +} diff --git a/source/xtJucePlugin/weTablesTreeItem.cpp b/source/xtJucePlugin/weTablesTreeItem.cpp @@ -0,0 +1,42 @@ +#include "weTablesTreeItem.h" + +#include "xtController.h" +#include "xtEditor.h" +#include "xtWaveEditor.h" + +namespace xtJucePlugin +{ + TablesTreeItem::TablesTreeItem(WaveEditor& _editor, const uint32_t _tableIndex) : m_editor(_editor), m_index(_tableIndex) + { + setPaintRootItemInBold(false); + setDrawsInLeftMargin(true); + + const auto& wavetableNames = _editor.getEditor().getXtController().getParameterDescriptions().getValueList("waveType"); + + const auto name = wavetableNames->valueToText(_tableIndex); + + setText(name); + + m_onTableChanged.set(_editor.getData().onTableChanged, [this](const uint32_t& _index) + { + onTableChanged(_index); + }); + } + + void TablesTreeItem::itemSelectionChanged(bool _isNowSelected) + { + TreeItem::itemSelectionChanged(_isNowSelected); + m_editor.setSelectedTable(m_index); + } + + void TablesTreeItem::onTableChanged(uint32_t _index) + { + if(_index != m_index) + return; + onTableChanged(); + } + + void TablesTreeItem::onTableChanged() + { + } +} diff --git a/source/xtJucePlugin/weTablesTreeItem.h b/source/xtJucePlugin/weTablesTreeItem.h @@ -0,0 +1,28 @@ +#pragma once + +#include "weTreeItem.h" + +#include "../jucePluginLib/event.h" + +namespace xtJucePlugin +{ + class WaveEditor; + + class TablesTreeItem : public TreeItem + { + public: + TablesTreeItem(WaveEditor& _editor, uint32_t _tableIndex); + + bool mightContainSubItems() override { return false; } + + void itemSelectionChanged(bool _isNowSelected) override; + + private: + void onTableChanged(uint32_t _index); + void onTableChanged(); + + WaveEditor& m_editor; + const uint32_t m_index; + pluginLib::EventListener<uint32_t> m_onTableChanged; + }; +} diff --git a/source/xtJucePlugin/weTreeItem.cpp b/source/xtJucePlugin/weTreeItem.cpp @@ -24,7 +24,7 @@ namespace xtJucePlugin { if (auto f = style->getFont()) { - f->setBold(getParentItem() == getOwnerView()->getRootItem()); + f->setBold(paintInBold()); _g.setFont(*f); haveFont = true; } @@ -32,7 +32,7 @@ namespace xtJucePlugin if(!haveFont) { auto fnt = _g.getCurrentFont(); - fnt.setBold(getParentItem() == getOwnerView()->getRootItem()); + fnt.setBold(paintInBold()); _g.setFont(fnt); } @@ -42,4 +42,10 @@ namespace xtJucePlugin TreeViewItem::paintItem(_g, _width, _height); } + + bool TreeItem::paintInBold() const + { + return m_paintRootItemInBold && getParentItem() == getOwnerView()->getRootItem(); + } + } diff --git a/source/xtJucePlugin/weTreeItem.h b/source/xtJucePlugin/weTreeItem.h @@ -12,6 +12,13 @@ namespace xtJucePlugin protected: void paintItem(juce::Graphics& _g, int _width, int _height) override; + bool paintInBold() const; + + void setPaintRootItemInBold(const bool _enabled) + { + m_paintRootItemInBold = _enabled; + } + private: bool mightContainSubItems() override { @@ -19,5 +26,6 @@ namespace xtJucePlugin } std::string m_text; + bool m_paintRootItemInBold = true; }; } diff --git a/source/xtJucePlugin/weTypes.h b/source/xtJucePlugin/weTypes.h @@ -6,6 +6,7 @@ namespace xtJucePlugin { using WaveData = std::array<int8_t, 128>; + using TableData = std::array<uint16_t,64>; enum class WaveCategory { diff --git a/source/xtJucePlugin/weWaveTreeItem.cpp b/source/xtJucePlugin/weWaveTreeItem.cpp @@ -5,7 +5,7 @@ namespace xtJucePlugin { - WaveTreeItem::WaveTreeItem(WaveEditor& _editor, WaveCategory _category, const uint32_t _waveIndex) + WaveTreeItem::WaveTreeItem(WaveEditor& _editor, const WaveCategory _category, const uint32_t _waveIndex) : m_editor(_editor) , m_category(_category) , m_waveIndex(_waveIndex) @@ -15,7 +15,7 @@ namespace xtJucePlugin onWaveChanged(_waveIndex); }); - setText(WaveCategoryTreeItem::getCategoryName(_category) + ' ' + std::to_string(_waveIndex)); + setText(getWaveName(_waveIndex)); } void WaveTreeItem::paintWave(const WaveData& _data, juce::Graphics& _g, const int _x, const int _y, const int _width, const int _height, const juce::Colour& _colour) @@ -37,6 +37,23 @@ namespace xtJucePlugin } } + std::string WaveTreeItem::getWaveName(const uint32_t _waveIndex) + { + if(!xt::Wave::isValidWaveIndex(_waveIndex)) + return {}; + const auto category = getCategory(_waveIndex); + return WaveCategoryTreeItem::getCategoryName(category) + ' ' + std::to_string(_waveIndex); + } + + WaveCategory WaveTreeItem::getCategory(const uint32_t _waveIndex) + { + if(_waveIndex < xt::Wave::g_romWaveCount) + return WaveCategory::Rom; + if(_waveIndex >= xt::Wave::g_firstRamWaveIndex && _waveIndex < xt::Wave::g_firstRamWaveIndex + xt::Wave::g_ramWaveCount) + return WaveCategory::User; + return WaveCategory::Invalid; + } + void WaveTreeItem::onWaveChanged(const uint32_t _index) { if(_index != m_waveIndex) @@ -52,9 +69,7 @@ namespace xtJucePlugin void WaveTreeItem::paintItem(juce::Graphics& g, int width, int height) { if(const auto wave = m_editor.getData().getWave(m_waveIndex)) - { paintWave(*wave, g, width>>1, 0, width>>1, height, juce::Colour(0xffffffff)); - } TreeItem::paintItem(g, width, height); } diff --git a/source/xtJucePlugin/weWaveTreeItem.h b/source/xtJucePlugin/weWaveTreeItem.h @@ -19,6 +19,9 @@ namespace xtJucePlugin static void paintWave(const WaveData& _data, juce::Graphics& _g, int _x, int _y, int _width, int _height, const juce::Colour& _colour); + static std::string getWaveName(uint32_t _waveIndex); + static WaveCategory getCategory(uint32_t _waveIndex); + private: void onWaveChanged(uint32_t _index); void onWaveChanged(); diff --git a/source/xtJucePlugin/xtController.cpp b/source/xtJucePlugin/xtController.cpp @@ -37,7 +37,9 @@ namespace "emuSendButton", "emuSendRotary", "requestWave", - "waveDump" + "waveDump", + "requestTable", + "tableDump" }; static_assert(std::size(g_midiPacketNames) == static_cast<size_t>(Controller::MidiPacketType::Count)); @@ -388,6 +390,11 @@ bool Controller::parseSysexMessage(const pluginLib::SysEx& _msg, synthLib::MidiE if(m_waveEditor) m_waveEditor->onReceiveWave(data, _msg); } + else if(name == midiPacketName(TableDump)) + { + if(m_waveEditor) + m_waveEditor->onReceiveTable(data, _msg); + } else { LOG("Received unknown sysex of size " << _msg.size()); @@ -669,6 +676,19 @@ bool Controller::requestWave(const uint32_t _number) const return sendSysEx(RequestWave, params); } +bool Controller::requestTable(const uint32_t _number) const +{ + if(!xt::Wave::isValidTableIndex(_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(RequestTable, 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 @@ -43,6 +43,8 @@ public: EmuSendRotary, RequestWave, WaveDump, + RequestTable, + TableDump, Count }; @@ -88,6 +90,7 @@ public: void setWaveEditor(xtJucePlugin::WaveEditor* _waveEditor); bool requestWave(uint32_t _number) const; + bool requestTable(uint32_t _number) const; private: void selectPreset(int _offset); diff --git a/source/xtJucePlugin/xtWaveEditor.cpp b/source/xtJucePlugin/xtWaveEditor.cpp @@ -2,6 +2,8 @@ #include "weTree.h" #include "weWaveTree.h" +#include "weTablesTree.h" +#include "weControlTree.h" #include "xtEditor.h" namespace xtJucePlugin @@ -14,6 +16,8 @@ namespace xtJucePlugin WaveEditor::~WaveEditor() { m_waveTree.reset(); + m_controlTree.reset(); + m_tablesTree.reset(); removeComponentListener(this); } @@ -21,8 +25,16 @@ namespace xtJucePlugin void WaveEditor::initialize() { auto* waveListParent = m_editor.findComponent("wecWaveList"); + auto* tablesListParent = m_editor.findComponent("wecWavetableList"); + auto* controlListParent = m_editor.findComponent("wecWaveControlTable"); + m_waveTree.reset(new WaveTree(*this)); + m_tablesTree.reset(new TablesTree(*this)); + m_controlTree.reset(new ControlTree(*this)); + waveListParent->addAndMakeVisible(m_waveTree.get()); + tablesListParent->addAndMakeVisible(m_tablesTree.get()); + controlListParent->addAndMakeVisible(m_controlTree.get()); } void WaveEditor::checkFirstTimeVisible() @@ -43,4 +55,18 @@ namespace xtJucePlugin { m_data.onReceiveWave(_data, _msg); } + + void WaveEditor::onReceiveTable(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg) + { + m_data.onReceiveTable(_data, _msg); + } + + void WaveEditor::setSelectedTable(uint32_t _index) + { + if(m_selectedTable == _index) + return; + + m_selectedTable = _index; + m_controlTree->setTable(_index); + } } diff --git a/source/xtJucePlugin/xtWaveEditor.h b/source/xtJucePlugin/xtWaveEditor.h @@ -1,12 +1,15 @@ #pragma once #include "weData.h" + #include "juce_gui_basics/juce_gui_basics.h" #include "../jucePluginLib/midipacket.h" namespace xtJucePlugin { + class ControlTree; + class TablesTree; class WaveTree; class Editor; @@ -14,17 +17,26 @@ namespace xtJucePlugin { public: explicit WaveEditor(Editor& _editor); + WaveEditor() = delete; + WaveEditor(const WaveEditor&) = delete; + WaveEditor(WaveEditor&&) = delete; ~WaveEditor() override; + WaveEditor& operator = (const WaveEditor&) = delete; + WaveEditor& operator = (WaveEditor&&) = delete; + void initialize(); void onReceiveWave(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg); + void onReceiveTable(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg); const WaveEditorData& getData() const { return m_data; } WaveEditorData& getData() { return m_data; } Editor& getEditor() const { return m_editor; } + void setSelectedTable(uint32_t _index); + private: // ComponentMovementWatcher void componentVisibilityChanged() override { checkFirstTimeVisible(); } @@ -36,7 +48,12 @@ namespace xtJucePlugin Editor& m_editor; std::unique_ptr<WaveTree> m_waveTree; + std::unique_ptr<ControlTree> m_controlTree; + std::unique_ptr<TablesTree> m_tablesTree; WaveEditorData m_data; bool m_wasVisible = false; + + uint32_t m_selectedTable = ~0; + uint32_t m_selectedWave = ~0; }; } diff --git a/source/xtLib/xtMidiTypes.h b/source/xtLib/xtMidiTypes.h @@ -175,6 +175,18 @@ namespace xt static constexpr uint32_t g_romWaveCount = 300; static constexpr uint32_t g_ramWaveCount = 250; static constexpr uint32_t g_firstRamWaveIndex = 1000; + static constexpr uint32_t g_tableCount = 128; + static constexpr uint32_t g_wavesPerTable = 64; + + // these are either algorithmic or invalid, we cannot request them via MIDI + static constexpr uint32_t g_algorithmicWavetables[] = {28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, + 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}; inline bool isValidWaveIndex(const uint32_t _index) { @@ -184,5 +196,11 @@ namespace xt return false; return true; } + + inline bool isValidTableIndex(const uint32_t _index) + { + return _index < g_tableCount; + } } + }