diff --git a/CMakeLists.txt b/CMakeLists.txt index 8123a4e..4e4200f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,8 @@ target_link_libraries(${PROJECT_NAME}-impl PUBLIC clap clap-helpers clap-wrapper-extensions fmt-header-only mts-esp-client - sst-basic-blocks sst-voicemanager sst-jucegui sst-cpputils sst-plugininfra sst-plugininfra::filesystem + sst-basic-blocks sst-voicemanager sst-jucegui sst-cpputils + sst-plugininfra sst-plugininfra::filesystem sst-plugininfra::tinyxml sst::clap_juce_shim sst::clap_juce_shim_headers) diff --git a/doc/design.md b/doc/design.md index a5602a1..dcbfee8 100644 --- a/doc/design.md +++ b/doc/design.md @@ -1,57 +1,41 @@ -Individual Oscillator - -- Keytrack - - if on: ratio - - if off: absolute frequency -- Waveshape (quadrant picker) -- lfo with depth to ratio -- ratio uses 2^x table - -FM Matrix Node -- LFO to add to float depth - -Self matrix node -+ DAHDSR to scale fb depth -+ LFO to add to fb depth - -Mixer -+ Each node gets an envelope -- Each node an LFO to level or a pan - -Macros and MIDI -- A macros section - 6 macros each with a target list of 4 params at depth? -- and maybe do midi routing the same way? (put a midi button above the macro vertical) -- Other (monophonic) MIDI? - -Other ToDos -- Envelope Rate Linear to 2x provider -- General UI -- Interpolate ratio across the block in source -- Max voice count -- Portamento -- Patch to XML - -Installer -- mac signing -- win/lin zip only -- pipelines on all three platforms +TODO BEFORE 1JAN2025 -Probably ship at this point but then +SOURCE: +- Keytrack on/off, and if off ratio -> offset from 440hz +- TX waveshapes +- Ratio uses 2^x table rather than pow +- Ratio lerps across block -Non-monophonic voice level modulation -- CLAP advanced features like polymod -- Note expressions? -- MPE? +MIXER: +- LFO on Mixer Node to level or pan +MODULATION: +- Each item gets a modulation list from source set +- Monophonic Midi source +- Macro source +- Polyphonic midi source +- MPE source +- Note expressions? -Sunday -- LFO on Mixer Node to level or pan -- LFO mul or plus on consistently on all three nodes -- LFO Level Built In -- AM is somehow not quiet right. Signal to zero seems 'no modulation' not 'no output' +PRESETS: - preset factory set with cmakerc - preset scan user directory to make tree -- param split for voices and base/top -- temposync lfo -- deal with ui attached or not for messages -- edit area says "click a knbo to edit on startup" \ No newline at end of file + +PLAY MODE: +- Voice limit count +- Pitch Bend support +- Portamento maybe + +GENERAL AND CLEANUPS +- AM is somehow not quiet right. Signal to zero seems 'no modulation' not 'no output' +- Temposync the LFO +- Don't send VU etc when editor not attached +- Edit area says "click a knbo to edit on startup" +- SRProvider uses table, not pow + +INFRASTRUCTURE: +- mac signing +- win/lin zip only +- pipelines on all three platforms + + diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 02ef524..29e457c 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -13,7 +13,7 @@ add_subdirectory(sst/sst-jucegui) add_subdirectory(sst/sst-voicemanager) add_subdirectory(sst/sst-cpputils) -set(SST_PLUGININFRA_PROVIDE_TINYXML OFF CACHE BOOL "no xml") +set(SST_PLUGININFRA_PROVIDE_TINYXML ON CACHE BOOL "no xml") add_subdirectory(sst/sst-plugininfra) add_library(mts-esp-client STATIC MTS-ESP/Client/libMTSClient.cpp) diff --git a/src/synth/patch.cpp b/src/synth/patch.cpp index d6dd443..fc0d377 100644 --- a/src/synth/patch.cpp +++ b/src/synth/patch.cpp @@ -15,28 +15,114 @@ #include "patch.h" #include +#include "tinyxml/tinyxml.h" namespace baconpaul::six_sines { +void Patch::resetToInit() +{ + // Sweep any new param since this stream to default + for (auto [id, p] : paramMap) + { + p->value = p->meta.defaultVal; + } +} std::string Patch::toState() const { - std::ostringstream oss; - oss << "SIXSINES;V=1\n"; + TiXmlDocument doc; + TiXmlElement rootNode("patch"); + rootNode.SetAttribute("id", "org.baconpaul.six-sines"); + rootNode.SetAttribute("version", "2"); + + TiXmlElement paramsNode("params"); + for (auto p : params) { - oss << p->meta.id << "|" << p->value << "\n"; + TiXmlElement param("p"); + param.SetAttribute("id", p->meta.id); + param.SetDoubleAttribute("v", p->value); + paramsNode.InsertEndChild(param); } + + rootNode.InsertEndChild(paramsNode); + doc.InsertEndChild(rootNode); + + std::ostringstream oss; + oss << doc; return oss.str(); } bool Patch::fromState(const std::string &idata) { - // Sweep any new param since this stream to default - for (auto [id, p] : paramMap) + resetToInit(); + if (idata.substr(0, 12) == "SIXSINES;V=1") { - p->value = p->meta.defaultVal; + return fromStateV1(idata); } + TiXmlDocument doc; + doc.Parse(idata.c_str()); + + auto rn = doc.FirstChildElement("patch"); + if (!rn) + { + SXSNLOG("No Patch"); + return false; + } + if (strcmp(rn->Attribute("id"), "org.baconpaul.six-sines") != 0) + { + SXSNLOG("Wrong ID"); + return false; + } + int ver; + if (rn->QueryIntAttribute("version", &ver) != TIXML_SUCCESS) + { + SXSNLOG("No Version"); + return false; + } + if (ver != 2) + { + SXSNLOG("Unknown version " << ver); + return false; + } + + auto pars = rn->FirstChildElement("params"); + if (!pars) + { + SXSNLOG("No Params"); + return false; + } + + auto *par = pars->FirstChildElement("p"); + while (par) + { + int id; + double value; + if (par->QueryIntAttribute("id", &id) == TIXML_SUCCESS && + par->QueryDoubleAttribute("v", &value) == TIXML_SUCCESS) + { + auto it = paramMap.find(id); + if (it == paramMap.end()) + { + // SXSNLOG(" - vestigal param " << id); + } + else + { + it->second->value = value; + } + } + else + { + SXSNLOG("Par missing id or value"); + } + par = par->NextSiblingElement("p"); + } + + return true; +} + +bool Patch::fromStateV1(const std::string &idata) +{ std::string data = idata; auto p = data.find('\n'); bool first{true}; diff --git a/src/synth/patch.h b/src/synth/patch.h index 6f3843e..b8b64e1 100644 --- a/src/synth/patch.h +++ b/src/synth/patch.h @@ -533,8 +533,12 @@ struct Patch std::array macroNodes; OutputNode output; + void resetToInit(); std::string toState() const; bool fromState(const std::string &); + + private: + bool fromStateV1(const std::string &); }; } // namespace baconpaul::six_sines #endif // PATCH_H diff --git a/src/ui/six-sines-editor.cpp b/src/ui/six-sines-editor.cpp index b4fd78c..2ce88d4 100644 --- a/src/ui/six-sines-editor.cpp +++ b/src/ui/six-sines-editor.cpp @@ -483,10 +483,7 @@ void SixSinesEditor::doLoadPatch() void SixSinesEditor::resetToDefault() { SXSNLOG("Resetting to default"); - for (auto [id, p] : patchCopy.paramMap) - { - p->value = p->meta.defaultVal; - } + patchCopy.resetToInit(); sendEntirePatchToAudio(); repaint(); }