commit 6df0da0e47605785bed343638edf5220e34d236b
parent 2bc955ffb2cf57dd8cec2cdbd3e58a7a8ad56a17
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Tue, 24 Sep 2024 22:53:00 +0200
wave editor first realtime preview now working
Diffstat:
14 files changed, 221 insertions(+), 14 deletions(-)
diff --git a/source/xtJucePlugin/weGraph.cpp b/source/xtJucePlugin/weGraph.cpp
@@ -7,7 +7,7 @@ namespace xtJucePlugin
{
Graph::Graph(WaveEditor& _editor) : m_editor(_editor), m_data(_editor.getGraphData())
{
- m_onSourceChanged.set(m_data.onSourceChanged, [this](const xt::WaveData&)
+ m_onDataChanged.set(m_data.onChanged, [this]()
{
onSourceChanged();
});
@@ -257,7 +257,7 @@ namespace xtJucePlugin
if(i < 0)
continue;
- if(i >= getDataSize())
+ if(i >= static_cast<int32_t>(getDataSize()))
return;
modifyValue(i, v);
diff --git a/source/xtJucePlugin/weGraph.h b/source/xtJucePlugin/weGraph.h
@@ -78,7 +78,7 @@ namespace xtJucePlugin
WaveEditor& m_editor;
GraphData& m_data;
- pluginLib::EventListener<xt::WaveData> m_onSourceChanged;
+ pluginLib::EventListener<> m_onDataChanged;
std::set<uint32_t> m_highlightedIndices;
uint32_t m_hoveredIndex = InvalidIndex;
diff --git a/source/xtJucePlugin/weGraphData.cpp b/source/xtJucePlugin/weGraphData.cpp
@@ -4,6 +4,8 @@
namespace xtJucePlugin
{
+ // ReSharper disable CppClangTidyClangDiagnosticFloatEqual
+
constexpr float g_pi = 3.1415926535f;
constexpr auto g_size = std::tuple_size_v<xt::WaveData>;
@@ -30,7 +32,7 @@ namespace xtJucePlugin
updateFrequenciesAndPhases();
- onSourceChanged(m_source);
+ sendChangedEvents();
}
void GraphData::setData(const uint32_t _index, const float _value)
@@ -40,7 +42,7 @@ namespace xtJucePlugin
m_data[_index] = _value;
m_data[m_data.size() - _index - 1] = -_value;
updateFrequenciesAndPhases();
- onSourceChanged(m_source);
+ sendChangedEvents();
}
void GraphData::setFreq(const uint32_t _index, const float _value)
@@ -49,7 +51,7 @@ namespace xtJucePlugin
return;
m_frequencies[_index] = _value;
updateDataFromFrequenciesAndPhases();
- onSourceChanged(m_source);
+ sendChangedEvents();
}
void GraphData::setPhase(const uint32_t _index, const float _value)
@@ -58,7 +60,7 @@ namespace xtJucePlugin
return;
m_phases[_index] = _value;
updateDataFromFrequenciesAndPhases();
- onSourceChanged(m_source);
+ sendChangedEvents();
}
void GraphData::updateFrequenciesAndPhases()
@@ -109,8 +111,29 @@ namespace xtJucePlugin
m_fft.perform(m_fftInData.data(), m_fftOutData.data(), true);
for(uint32_t i=0; i<m_data.size(); ++i)
- {
m_data[i] = m_fftOutData[i].real();
+ }
+
+ bool GraphData::updateSourceFromData()
+ {
+ bool changed = false;
+ for(size_t i=0; i<m_data.size(); ++i)
+ {
+ const auto d = static_cast<int8_t>(std::clamp<int32_t>(static_cast<int32_t>(std::round(m_data[i] * 128.0f)), -127, 127));
+
+ if(m_source[i] == d)
+ continue;
+ m_source[i] = d;
+ changed = true;
}
+ return changed;
+ }
+
+ void GraphData::sendChangedEvents()
+ {
+ onChanged();
+
+ if(updateSourceFromData())
+ onIntegerChanged(m_source);
}
}
diff --git a/source/xtJucePlugin/weGraphData.h b/source/xtJucePlugin/weGraphData.h
@@ -12,7 +12,8 @@ namespace xtJucePlugin
class GraphData
{
public:
- pluginLib::Event<xt::WaveData> onSourceChanged;
+ pluginLib::Event<> onChanged;
+ pluginLib::Event<xt::WaveData> onIntegerChanged;
GraphData();
@@ -21,6 +22,7 @@ namespace xtJucePlugin
const auto& getData() const { return m_data; }
const auto& getFrequencies() const { return m_frequencies; }
const auto& getPhases() const { return m_phases; }
+ const auto& getSource() const { return m_source; }
void setData(uint32_t _index, float _value);
void setFreq(uint32_t _index, float _value);
@@ -29,6 +31,8 @@ namespace xtJucePlugin
private:
void updateFrequenciesAndPhases();
void updateDataFromFrequenciesAndPhases();
+ bool updateSourceFromData();
+ void sendChangedEvents();
xt::WaveData m_source;
diff --git a/source/xtJucePlugin/xtController.h b/source/xtJucePlugin/xtController.h
@@ -65,6 +65,7 @@ namespace xtJucePlugin
bool sendSysEx(MidiPacketType _type) const;
bool sendSysEx(MidiPacketType _type, std::map<pluginLib::MidiDataType, uint8_t>& _params) const;
+ using pluginLib::Controller::sendSysEx;
bool isMultiMode() const;
void setPlayMode(bool _multiMode);
diff --git a/source/xtJucePlugin/xtWaveEditor.cpp b/source/xtJucePlugin/xtWaveEditor.cpp
@@ -7,8 +7,10 @@
#include "weGraphFreq.h"
#include "weGraphPhase.h"
#include "weGraphTime.h"
+#include "xtController.h"
#include "xtEditor.h"
+#include "xtLib/xtState.h"
namespace xtJucePlugin
{
@@ -23,6 +25,11 @@ namespace xtJucePlugin
setSelectedWave(_waveIndex, true);
});
+
+ m_graphData.onIntegerChanged.addListener([this](const xt::WaveData& _data)
+ {
+ onWaveDataChanged(_data);
+ });
}
WaveEditor::~WaveEditor()
@@ -121,6 +128,15 @@ namespace xtJucePlugin
m_ledWavetablePreview->setToggleState(_enabled, juce::dontSendNotification);
}
+ void WaveEditor::onWaveDataChanged(const xt::WaveData& _data) const
+ {
+ if(m_btWavePreview->getToggleState())
+ {
+ const auto sysex = xt::State::createWaveData(_data, m_editor.getXtController().getCurrentPart(), true);
+ m_editor.getXtController().sendSysEx(sysex);
+ }
+ }
+
void WaveEditor::onReceiveWave(const pluginLib::MidiPacket::Data& _data, const std::vector<uint8_t>& _msg)
{
m_data.onReceiveWave(_data, _msg);
@@ -148,6 +164,9 @@ namespace xtJucePlugin
m_selectedWave = _waveIndex;
if(const auto wave = m_data.getWave(_waveIndex))
+ {
m_graphData.set(*wave);
+ onWaveDataChanged(*wave);
+ }
}
}
diff --git a/source/xtJucePlugin/xtWaveEditor.h b/source/xtJucePlugin/xtWaveEditor.h
@@ -59,6 +59,8 @@ namespace xtJucePlugin
void toggleWavePreview(bool _enabled);
void toggleWavetablePreview(bool _enabled);
+ void onWaveDataChanged(const xt::WaveData& _data) const;
+
Editor& m_editor;
std::unique_ptr<WaveTree> m_waveTree;
diff --git a/source/xtLib/xtDevice.cpp b/source/xtLib/xtDevice.cpp
@@ -9,7 +9,7 @@ namespace mqLib
namespace xt
{
- Device::Device() : m_state(m_xt), m_sysexRemote(m_xt)
+ Device::Device() : m_wavePreview(m_xt), m_state(m_xt, m_wavePreview), m_sysexRemote(m_xt)
{
while(!m_xt.isBootCompleted())
m_xt.process(8);
diff --git a/source/xtLib/xtDevice.h b/source/xtLib/xtDevice.h
@@ -3,6 +3,7 @@
#include "xt.h"
#include "xtState.h"
#include "xtSysexRemoteControl.h"
+#include "xtWavePreview.h"
#include "wLib/wDevice.h"
namespace dsp56k
@@ -33,6 +34,7 @@ namespace xt
private:
Xt m_xt;
+ WavePreview m_wavePreview;
State m_state;
SysexRemoteControl m_sysexRemote;
};
diff --git a/source/xtLib/xtMidiTypes.h b/source/xtLib/xtMidiTypes.h
@@ -30,6 +30,8 @@ namespace xt
WaveRequestP = 0x09, WaveDumpP = 0x19, WaveParameterChangeP = 0x29, WaveStoreP = 0x39, WaveRecallP = 0x49, WaveCompareP = 0x59,
WaveCtlRequestP = 0x0a, WaveCtlDumpP = 0x1a, WaveCtlParameterChangeP = 0x2a, WaveCtlStoreP = 0x3a, WaveCtlRecallP = 0x4a, WaveCtlCompareP = 0x5a,
+ WavePreviewMode = WaveStoreP,
+
// emu remote control support
EmuLCD = 0x60,
EmuLEDs = 0x61,
@@ -76,7 +78,10 @@ namespace xt
IdxModeParamIndexH = wLib::IdxBuffer,
IdxModeParamIndexL = IdxModeParamIndexH,
- IdxModeParamValue = wLib::IdxBuffer
+ IdxModeParamValue = wLib::IdxBuffer,
+
+ IdxWaveIndexH = wLib::IdxBuffer,
+ IdxWaveIndexL = IdxWaveIndexH + 1
};
enum class GlobalParameter
diff --git a/source/xtLib/xtState.cpp b/source/xtLib/xtState.cpp
@@ -4,6 +4,7 @@
#include "xtMidiTypes.h"
#include "xt.h"
+#include "xtWavePreview.h"
#include "synthLib/os.h"
#include "synthLib/midiToSysex.h"
@@ -15,7 +16,7 @@ namespace xt
{
static_assert(std::size(State::Dumps) == static_cast<uint32_t>(State::DumpType::Count), "data definition missing");
- State::State(Xt& _xt) : m_xt(_xt)
+ State::State(Xt& _xt, WavePreview& _preview) : m_xt(_xt), m_wavePreview(_preview)
{
}
@@ -160,6 +161,10 @@ namespace xt
case SysexCommand::GlobalParameterChange: return modifyDump(DumpType::Global, _data);
case SysexCommand::ModeParameterChange: return modifyDump(DumpType::Mode, _data);
+ case SysexCommand::WaveDumpP: return m_wavePreview.receiveWave(_data);
+ case SysexCommand::WaveCtlDumpP: return m_wavePreview.receiveWaveControlTable(_data);
+ case SysexCommand::WavePreviewMode: return m_wavePreview.receiveWavePreviewMode(_data);
+
/* case SysexCommand::EmuLCD:
case SysexCommand::EmuLEDs:
case SysexCommand::EmuButtons:
@@ -721,7 +726,8 @@ namespace xt
for(uint32_t i=0; i<_wave.size()>>1; ++i)
{
- auto sample = (_sysex[off + (i<<1)]) << 4 | _sysex[off + (i<<1) + 1];
+ const auto idx = off + (i<<1);
+ auto sample = (_sysex[idx]) << 4 | _sysex[idx+1];
sample = sample ^ 0x80;
_wave[i] = static_cast<int8_t>(sample);
@@ -729,6 +735,31 @@ namespace xt
}
}
+ SysEx State::createWaveData(const WaveData& _wave, const uint16_t _waveIndex, const bool _preview)
+ {
+ const auto hh = static_cast<uint8_t>(_waveIndex >> 7);
+ const auto ll = static_cast<uint8_t>(_waveIndex & 0x7f);
+
+ const std::initializer_list<uint8_t> header{0xf0, wLib::IdWaldorf, IdMw2, wLib::IdDeviceOmni, static_cast<uint8_t>(_preview ? SysexCommand::WaveDumpP : SysexCommand::WaveDump), hh, ll};
+ SysEx sysex{header};
+ sysex.reserve(sysex.size() + _wave.size());
+
+ for(uint32_t i=0; i<_wave.size()>>1; ++i)
+ {
+ const auto sample = _wave[i] ^ 0x80;
+
+ sysex.push_back(static_cast<uint8_t>(sample >> 4));
+ sysex.push_back(static_cast<uint8_t>(sample & 0xf));
+ }
+
+ sysex.push_back(0);
+ sysex.push_back(0xf7);
+
+ updateChecksum(sysex, static_cast<uint32_t>(std::size(header)));
+
+ return sysex;
+ }
+
void State::parseTableData(TableData& _table, const SysEx& _sysex)
{
constexpr uint32_t off = 7;
@@ -746,6 +777,34 @@ namespace xt
}
}
+ SysEx State::createTableData(const TableData& _table, uint32_t _tableIndex, bool _preview)
+ {
+ const auto hh = static_cast<uint8_t>(_tableIndex >> 7);
+ const auto ll = static_cast<uint8_t>(_tableIndex & 0x7f);
+
+ const std::initializer_list<uint8_t> header{0xf0, wLib::IdWaldorf, IdMw2, wLib::IdDeviceOmni, static_cast<uint8_t>(_preview ? SysexCommand::WaveCtlDumpP : SysexCommand::WaveCtlDump), hh, ll};
+ SysEx sysex{header};
+ sysex.reserve(sysex.size() + _table.size() * 4);
+
+ for(uint32_t i=0; i<_table.size(); ++i)
+ {
+ const auto i4 = i<<2;
+
+ const auto waveIndex = _table[i];
+ sysex.push_back((waveIndex >> 12) & 0xf);
+ sysex.push_back((waveIndex >> 8) & 0xf);
+ sysex.push_back((waveIndex >> 4) & 0xf);
+ sysex.push_back((waveIndex ) & 0xf);
+ }
+
+ sysex.push_back(0);
+ sysex.push_back(0xf7);
+
+ updateChecksum(sysex, static_cast<uint32_t>(std::size(header)));
+
+ return sysex;
+ }
+
void State::onPlayModeChanged()
{
// if the play mode is changed, force a re-request of the edit buffer for the first single again, because on the device, that edit buffer is shared between multi & single
diff --git a/source/xtLib/xtState.h b/source/xtLib/xtState.h
@@ -20,6 +20,7 @@ namespace synthLib
namespace xt
{
+ class WavePreview;
class Xt;
using SysEx = wLib::SysEx;
@@ -70,7 +71,7 @@ namespace xt
using Global = std::array<uint8_t, Dumps[static_cast<uint32_t>(DumpType::Global)].dumpSize>;
using Mode = std::array<uint8_t, Dumps[static_cast<uint32_t>(DumpType::Mode)].dumpSize>;
- State(Xt& _xt);
+ State(Xt& _xt, WavePreview& _wavePreview);
bool loadState(const SysEx& _sysex);
@@ -84,7 +85,10 @@ namespace xt
static void createSequencerMultiData(std::vector<uint8_t>& _data);
static void parseWaveData(WaveData& _wave, const SysEx& _sysex);
+ static SysEx createWaveData(const WaveData& _wave, uint16_t _waveIndex, bool _preview);
+
static void parseTableData(TableData& _table, const SysEx& _sysex);
+ static SysEx createTableData(const TableData& _table, uint32_t _tableIndex, bool _preview);
private:
@@ -202,6 +206,9 @@ namespace xt
Origin m_sender = Origin::External;
bool m_isEditBuffer = false;
+ // Emulator specific: preview wave editing
+ WavePreview& m_wavePreview;
+
synthLib::SMidiEvent m_lastBankSelectMSB;
synthLib::SMidiEvent m_lastBankSelectLSB;
};
diff --git a/source/xtLib/xtWavePreview.cpp b/source/xtLib/xtWavePreview.cpp
@@ -5,9 +5,79 @@
namespace xt
{
+ static constexpr uint32_t g_waveMemBase = 0x20000;
+ static constexpr uint32_t g_waveMemWaveSize = 256; // 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 + 1
+ static constexpr uint32_t g_waveMemWavesPerPart = 64;
+ static constexpr uint32_t g_waveMemPartBufferSize = g_waveMemWaveSize * g_waveMemWavesPerPart;
+
WavePreview::WavePreview(Xt& _xt)
: m_xt(_xt)
, m_dspMem(m_xt.getHardware()->getDSP(0).dsp().memory())
{
}
+
+ bool WavePreview::receiveWave(const SysEx& _data)
+ {
+ const auto part = _data[SysexIndex::IdxWaveIndexL];
+ const auto waveIndex = _data[SysexIndex::IdxWaveIndexH];
+
+ if(part >= m_partDatas.size())
+ return false;
+
+ auto& partData = m_partDatas[part];
+
+ if(waveIndex >= partData.waves.size())
+ return false;
+
+ State::parseWaveData(partData.waves[waveIndex], _data);
+
+ for(uint8_t i=0; i<64; ++i)
+ sendToDSP(partData.waves[waveIndex], 0, i);
+
+ return true;
+ }
+
+ bool WavePreview::receiveWaveControlTable(const SysEx& _data)
+ {
+ return true;
+ }
+
+ bool WavePreview::receiveWavePreviewMode(const SysEx& _data)
+ {
+ return true;
+ }
+
+ void WavePreview::sendToDSP(const WaveData& _data, const uint8_t _part, const uint8_t _wave)
+ {
+ const auto memBase = g_waveMemBase + _part * g_waveMemPartBufferSize + _wave * g_waveMemWaveSize;
+
+ std::array<int8_t, g_waveMemWaveSize> waveData;
+
+ waveData.back() = 0;
+
+ std::copy(_data.begin(), _data.end(), waveData.begin());
+
+ // super simple repeating factor-two reduction by averaging two samples. No idea how the hardware does it
+ uint32_t size = static_cast<uint32_t>(_data.size()) >> 1;
+ uint32_t readOffset = 0;
+ uint32_t writeOffset = size<<1;
+ while(size)
+ {
+ for(uint32_t i=0; i<size; ++i)
+ {
+ const auto s = (waveData[readOffset+i] + waveData[readOffset+i+1]) >> 1;
+ waveData[writeOffset] = static_cast<int8_t>(s);
+ }
+ readOffset += size<<1;
+ writeOffset += size;
+ size >>= 1;
+ }
+
+ waveData[waveData.size()-1] = waveData[waveData.size()-2] = 0;
+
+ for(uint32_t i=0; i<waveData.size(); ++i)
+ {
+ m_dspMem.set(dsp56k::MemArea_Y, memBase + i, static_cast<int32_t>(waveData[i]) << 16);
+ }
+ }
}
diff --git a/source/xtLib/xtWavePreview.h b/source/xtLib/xtWavePreview.h
@@ -1,5 +1,7 @@
#pragma once
+#include "xtState.h"
+
namespace dsp56k
{
class Memory;
@@ -12,10 +14,23 @@ namespace xt
class WavePreview
{
public:
+ struct PartData
+ {
+ std::array<WaveData, 64> waves{};
+ std::array<uint16_t, 64> control{};
+ };
+
WavePreview(Xt& _xt);
+ bool receiveWave(const SysEx& _data);
+ bool receiveWaveControlTable(const SysEx& _data);
+ bool receiveWavePreviewMode(const SysEx& _data);
+
private:
+ void sendToDSP(const WaveData& _data, uint8_t _part, uint8_t _wave);
+
Xt& m_xt;
dsp56k::Memory& m_dspMem;
+ std::array<PartData, 8> m_partDatas;
};
}