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 2014c5eb6f3c69372eddaf0962aad1fc66b70581
parent 96a172eb22f833744cc17a4e6d401ea0a67410ae
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Sat, 22 Jun 2024 18:38:00 +0200

graph editing WIP

Diffstat:
Msource/xtJucePlugin/CMakeLists.txt | 1+
Msource/xtJucePlugin/weGraph.cpp | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msource/xtJucePlugin/weGraph.h | 45++++++++++++++++++++++++++++++++++++++++++++-
Msource/xtJucePlugin/weGraphData.cpp | 26++++++++++++++++++++++++++
Msource/xtJucePlugin/weGraphData.h | 4++++
Msource/xtJucePlugin/weGraphFreq.cpp | 5+++++
Msource/xtJucePlugin/weGraphFreq.h | 1+
Msource/xtJucePlugin/weGraphPhase.cpp | 5+++++
Msource/xtJucePlugin/weGraphPhase.h | 1+
Msource/xtJucePlugin/weGraphTime.cpp | 6++++++
Msource/xtJucePlugin/weGraphTime.h | 1+
Msource/xtJucePlugin/xtWaveEditor.h | 5+++++
Asource/xtJucePlugin/xtWaveEditorStyle.cpp | 0
Asource/xtJucePlugin/xtWaveEditorStyle.h | 15+++++++++++++++
14 files changed, 322 insertions(+), 6 deletions(-)

diff --git a/source/xtJucePlugin/CMakeLists.txt b/source/xtJucePlugin/CMakeLists.txt @@ -32,6 +32,7 @@ set(SOURCES xtParts.cpp xtParts.h xtPatchManager.cpp xtPatchManager.h xtWaveEditor.cpp xtWaveEditor.h + xtWaveEditorStyle.cpp xtWaveEditorStyle.h ) # https://forum.juce.com/t/help-needed-using-binarydata-with-cmake-juce-6/40486 diff --git a/source/xtJucePlugin/weGraph.cpp b/source/xtJucePlugin/weGraph.cpp @@ -1,6 +1,7 @@ #include "weGraph.h" #include "xtWaveEditor.h" +#include "dsp56kEmu/fastmath.h" namespace xtJucePlugin { @@ -32,12 +33,12 @@ namespace xtJucePlugin void Graph::paint(const float* _data, const size_t _size, juce::Graphics& _g, const int _x, const int _y, const int _width, const int _height) const { - _g.setColour(juce::Colour(0xffffffff)); + const auto& style = m_editor.getStyle(); - const float scaleX = static_cast<float>(_width - 1) / static_cast<float>(_size -1); + const float scaleX = static_cast<float>(_width) / static_cast<float>(_size); const float scaleY = static_cast<float>(_height); - const float x = static_cast<float>(_x); + const float x = static_cast<float>(_x) + scaleX * 0.5f; const float y = static_cast<float>(_y); const float h = static_cast<float>(_height); @@ -51,14 +52,216 @@ namespace xtJucePlugin const auto y1 = h - (normalize(_data[i ])) * scaleY + y - 1; if(i) - _g.drawLine(x0, y0, x1, y1, 3.0f); + { + _g.setColour(juce::Colour(style.colGraphLine)); + _g.drawLine(x0, y0, x1, y1, style.graphLineThickness); + } - _g.fillEllipse(x0 - 5, y0 - 5, 10, 10); + if(m_highlightedIndices.find(i) != m_highlightedIndices.end()) + { + _g.setColour(style.colGraphLineHighlighted); + const auto s = style.graphPointSizeHighlighted; + _g.fillEllipse(x1 - s * 0.5f, y1 - s * 0.5f, s, s); + } + else + { + _g.setColour(style.colGraphLine); + const auto s = style.graphPointSize; + _g.fillEllipse(x1 - s * 0.5f, y1 - s * 0.5f, s, s); + } } } + bool Graph::updateHoveredIndex(const juce::MouseEvent& _e) + { + const auto index = mouseToIndex(_e); + if(!isValidIndex(index)) + { + setHoveredIndex(InvalidIndex); + return false; + } + setHoveredIndex(static_cast<uint32_t>(index)); + return true; + } + + void Graph::mouseDown(const juce::MouseEvent& _e) + { + Component::mouseDown(_e); + setLastMouseEvent(_e); + const auto i = mouseToIndex(_e); + if(isValidIndex(i)) + modifyValue(static_cast<uint32_t>(i), mouseToUnnormalizedValue(_e)); + } + + void Graph::mouseMove(const juce::MouseEvent& _e) + { + Component::mouseMove(_e); + updateHoveredIndex(_e); + } + + void Graph::mouseEnter(const juce::MouseEvent& _e) + { + Component::mouseEnter(_e); + updateHoveredIndex(_e); + } + + void Graph::mouseExit(const juce::MouseEvent& _e) + { + Component::mouseExit(_e); + setHoveredIndex(InvalidIndex); + } + + void Graph::mouseDrag(const juce::MouseEvent& _e) + { + Component::mouseDrag(_e); + + if(!_e.mods.isShiftDown()) + { + updateHoveredIndex(_e); + + if(m_lastMouseEvent) + { + modifyValuesForRange(*m_lastMouseEvent, _e); + } + else if(isValidIndex(m_hoveredIndex)) + { + modifyValue(m_hoveredIndex, mouseToUnnormalizedValue(_e)); + } + } + else + { + if(isValidIndex(m_hoveredIndex)) + modifyValue(m_hoveredIndex, mouseToUnnormalizedValue(_e)); + } + + setLastMouseEvent(_e); + } + + int32_t Graph::mouseToIndex(const juce::MouseEvent& _e) const + { + return _e.x * static_cast<int32_t>(getDataSize()) / (int32_t)getWidth(); + } + + float Graph::mouseToNormalizedValue(const juce::MouseEvent& _e) const + { + const auto v = 1.0f - static_cast<float>(_e.y) / static_cast<float>(getHeight() - 1); + return dsp56k::clamp(v, 0.0f, 1.0f); + } + + float Graph::mouseToUnnormalizedValue(const juce::MouseEvent& _e) const + { + return unnormalize(mouseToNormalizedValue(_e)); + } + + const juce::MouseEvent* Graph::lastMouseEvent() const + { + return m_lastMouseEvent.get(); + } + void Graph::onSourceChanged() { repaint(); } + + void Graph::onHoveredIndexChanged(const uint32_t _index) + { + if(_index == InvalidIndex) + { + if(!m_highlightedIndices.empty()) + m_highlightedIndices.clear(); + + onHighlightedIndicesChanged(m_highlightedIndices); + } + + setHighlightedIndices({_index}); + } + + void Graph::onHighlightedIndicesChanged(const std::set<uint32_t>& _indices) + { + repaint(); + } + + void Graph::setHighlightedIndices(const std::set<uint32_t>& _indices) + { + bool changed = false; + + for (auto existing : m_highlightedIndices) + { + if(_indices.find(existing) == _indices.end()) + { + changed = true; + break; + } + } + + if(!changed) + { + for (uint32_t newIndex : _indices) + { + if(m_highlightedIndices.find(newIndex) == m_highlightedIndices.end()) + { + changed = true; + break; + } + } + } + + if(!changed) + return; + + m_highlightedIndices = _indices; + + onHighlightedIndicesChanged(m_highlightedIndices); + } + + void Graph::modifyValue(uint32_t _index, float _unnormalizedValue) + { + } + + void Graph::setHoveredIndex(uint32_t _index) + { + if(_index == m_hoveredIndex) + return; + m_hoveredIndex = _index; + onHoveredIndexChanged(m_hoveredIndex); + } + + void Graph::setLastMouseEvent(const juce::MouseEvent& _e) + { + m_lastMouseEvent = std::make_unique<juce::MouseEvent>(_e); + } + + void Graph::modifyValuesForRange(const juce::MouseEvent& _a, const juce::MouseEvent& _b) + { + auto indexA = mouseToIndex(_a); + auto indexB = mouseToIndex(_b); + + auto valueA = mouseToUnnormalizedValue(_a); + auto valueB = mouseToUnnormalizedValue(_b); + + if(indexA > indexB) + { + std::swap(indexA, indexB); + std::swap(valueA, valueB); + } + + const auto count = indexB - indexA + 1; + + const auto valueDiff = valueB - valueA; + const auto factor = valueDiff / static_cast<float>(count); + + auto v = valueA; + + for(auto i = indexA; i <= indexB; ++i) + { + if(i < 0) + continue; + + if(i >= getDataSize()) + return; + + modifyValue(i, v); + v += factor; + } + } } diff --git a/source/xtJucePlugin/weGraph.h b/source/xtJucePlugin/weGraph.h @@ -13,6 +13,8 @@ namespace xtJucePlugin class Graph : public juce::Component { public: + static constexpr uint32_t InvalidIndex = ~0; + explicit Graph(WaveEditor& _editor); WaveEditor& getEditor() const { return m_editor; } @@ -33,13 +35,54 @@ namespace xtJucePlugin virtual const float* getData() const = 0; virtual size_t getDataSize() const = 0; - protected: + void mouseDown(const juce::MouseEvent& _e) override; + void mouseMove(const juce::MouseEvent& _e) override; + void mouseEnter(const juce::MouseEvent& _e) override; + void mouseExit(const juce::MouseEvent& _e) override; + void mouseDrag(const juce::MouseEvent& _e) override; + GraphData& getGraphData() const { return m_data; } + + int32_t mouseToIndex(const juce::MouseEvent& _e) const; + float mouseToNormalizedValue(const juce::MouseEvent& _e) const; + float mouseToUnnormalizedValue(const juce::MouseEvent& _e) const; + + bool isValidIndex(const uint32_t _index) const { return _index < getDataSize(); } + bool isValidIndex(const int32_t _index) const { return _index >= 0 && _index < getDataSize(); } + + const juce::MouseEvent* lastMouseEvent() const; + + void setUpdateHoveredPositionWhileDragging(bool _enable) + { + m_updateHoveredPositionWhileDragging = _enable; + } + + protected: virtual void onSourceChanged(); + virtual void onHoveredIndexChanged(uint32_t _index); + virtual void onHighlightedIndicesChanged(const std::set<uint32_t>& _indices); + + void setHighlightedIndices(const std::set<uint32_t>& _indices); + + virtual void modifyValue(uint32_t _index, float _unnormalizedValue) = 0; + + bool updateHoveredIndex(const juce::MouseEvent& _e); + + uint32_t getHoveredIndex() const { return m_hoveredIndex; } private: + void setHoveredIndex(uint32_t _index); + void setLastMouseEvent(const juce::MouseEvent& _e); + void modifyValuesForRange(const juce::MouseEvent& _a, const juce::MouseEvent& _b); + WaveEditor& m_editor; GraphData& m_data; + pluginLib::EventListener<WaveData> m_onSourceChanged; + + std::set<uint32_t> m_highlightedIndices; + uint32_t m_hoveredIndex = InvalidIndex; + std::unique_ptr<juce::MouseEvent> m_lastMouseEvent; + bool m_updateHoveredPositionWhileDragging = false; }; } diff --git a/source/xtJucePlugin/weGraphData.cpp b/source/xtJucePlugin/weGraphData.cpp @@ -33,6 +33,32 @@ namespace xtJucePlugin onSourceChanged(m_source); } + void GraphData::setData(const uint32_t _index, const float _value) + { + if(m_data[_index] == _value) + return; + m_data[_index] = _value; + m_data[m_data.size() - _index - 1] = -_value; + updateFrequenciesAndPhases(); + onSourceChanged(m_source); + } + + void GraphData::setFreq(const uint32_t _index, const float _value) + { + if(m_frequencies[_index] == _value) + return; + m_frequencies[_index] = _value; + onSourceChanged(m_source); + } + + void GraphData::setPhase(const uint32_t _index, const float _value) + { + if(m_phases[_index] == _value) + return; + m_phases[_index] = _value; + onSourceChanged(m_source); + } + void GraphData::updateFrequenciesAndPhases() { for(size_t i=0; i<m_data.size(); ++i) diff --git a/source/xtJucePlugin/weGraphData.h b/source/xtJucePlugin/weGraphData.h @@ -22,6 +22,10 @@ namespace xtJucePlugin const auto& getFrequencies() const { return m_frequencies; } const auto& getPhases() const { return m_phases; } + void setData(uint32_t _index, float _value); + void setFreq(uint32_t _index, float _value); + void setPhase(uint32_t _index, float _value); + private: void updateFrequenciesAndPhases(); diff --git a/source/xtJucePlugin/weGraphFreq.cpp b/source/xtJucePlugin/weGraphFreq.cpp @@ -23,4 +23,9 @@ namespace xtJucePlugin { return getGraphData().getFrequencies().size(); } + + void GraphFreq::modifyValue(uint32_t _index, float _unnormalizedValue) + { + getGraphData().setFreq(_index, _unnormalizedValue); + } } diff --git a/source/xtJucePlugin/weGraphFreq.h b/source/xtJucePlugin/weGraphFreq.h @@ -16,6 +16,7 @@ namespace xtJucePlugin const float* getData() const override; size_t getDataSize() const override; + void modifyValue(uint32_t _index, float _unnormalizedValue) override; private: }; } diff --git a/source/xtJucePlugin/weGraphPhase.cpp b/source/xtJucePlugin/weGraphPhase.cpp @@ -27,4 +27,9 @@ namespace xtJucePlugin { return getGraphData().getPhases().size(); } + + void GraphPhase::modifyValue(uint32_t _index, float _unnormalizedValue) + { + getGraphData().setPhase(_index, _unnormalizedValue); + } } diff --git a/source/xtJucePlugin/weGraphPhase.h b/source/xtJucePlugin/weGraphPhase.h @@ -13,6 +13,7 @@ namespace xtJucePlugin float unnormalize(float _in) const override; const float* getData() const override; size_t getDataSize() const override; + void modifyValue(uint32_t _index, float _unnormalizedValue) override; private: }; } diff --git a/source/xtJucePlugin/weGraphTime.cpp b/source/xtJucePlugin/weGraphTime.cpp @@ -6,6 +6,7 @@ namespace xtJucePlugin { GraphTime::GraphTime(WaveEditor& _editor): Graph(_editor) { + setUpdateHoveredPositionWhileDragging(true); } float GraphTime::normalize(const float _in) const @@ -27,4 +28,9 @@ namespace xtJucePlugin { return getGraphData().getData().size(); } + + void GraphTime::modifyValue(const uint32_t _index, const float _unnormalizedValue) + { + getGraphData().setData(_index, _unnormalizedValue); + } } diff --git a/source/xtJucePlugin/weGraphTime.h b/source/xtJucePlugin/weGraphTime.h @@ -13,6 +13,7 @@ namespace xtJucePlugin float unnormalize(float _in) const override; const float* getData() const override; size_t getDataSize() const override; + void modifyValue(uint32_t _index, float _unnormalizedValue) override; private: }; } diff --git a/source/xtJucePlugin/xtWaveEditor.h b/source/xtJucePlugin/xtWaveEditor.h @@ -2,6 +2,7 @@ #include "weData.h" #include "weGraphData.h" +#include "xtWaveEditorStyle.h" #include "juce_gui_basics/juce_gui_basics.h" @@ -38,6 +39,8 @@ namespace xtJucePlugin const WaveEditorData& getData() const { return m_data; } WaveEditorData& getData() { return m_data; } + const WaveEditorStyle& getStyle() const { return m_style; } + Editor& getEditor() const { return m_editor; } GraphData& getGraphData() { return m_graphData; } @@ -70,5 +73,7 @@ namespace xtJucePlugin uint32_t m_selectedTable = ~0; uint32_t m_selectedWave = ~0; + + WaveEditorStyle m_style; }; } diff --git a/source/xtJucePlugin/xtWaveEditorStyle.cpp b/source/xtJucePlugin/xtWaveEditorStyle.cpp diff --git a/source/xtJucePlugin/xtWaveEditorStyle.h b/source/xtJucePlugin/xtWaveEditorStyle.h @@ -0,0 +1,15 @@ +#pragma once + +#include "juce_gui_basics/juce_gui_basics.h" + +namespace xtJucePlugin +{ + struct WaveEditorStyle + { + juce::Colour colGraphLine = juce::Colour(0xffffffff); + juce::Colour colGraphLineHighlighted = juce::Colour(0xffffaa00); + float graphLineThickness = 3.0f; + float graphPointSize = 10.0f; + float graphPointSizeHighlighted = 30.0f; + }; +}