From c65ac61a98cbd3fdde6f8b3f67b442198f7002fd Mon Sep 17 00:00:00 2001 From: hanez Date: Fri, 6 Nov 2026 02:06:27 +0100 Subject: [PATCH] feat(presets): add creative internal effect chain templates (phase 94) --- docs/CREATIVE_WORKFLOWS.md | 9 +++++++++ docs/ROADMAP.md | 8 ++++---- include/xtc/xtc_plugin.h | 5 +++++ src/gui/track_inserts.hpp | 9 +++++++++ src/project/chain_tools.c | 22 ++++++++++++++++++++++ tests/integration/test_gui.cpp | 3 +++ tests/unit/test_creative.c | 10 ++++++++++ 7 files changed, 62 insertions(+), 4 deletions(-) diff --git a/docs/CREATIVE_WORKFLOWS.md b/docs/CREATIVE_WORKFLOWS.md index 04dfd5f..4f79cf4 100644 --- a/docs/CREATIVE_WORKFLOWS.md +++ b/docs/CREATIVE_WORKFLOWS.md @@ -35,3 +35,12 @@ copies metadata without affecting undo or loading modules. Recall prepares a fresh chain and is undoable; histories and opaque plugin states are excluded. The two comparison slots clear on successful New/Open. Comparisons stop transport when recalled, following the existing safe insert-edit workflow. + +## Creative starting points + +**Add effect → Templates (replace)** offers Slapback (80 ms delay), Dub Echo +(375 ms feedback delay into a short reverb), and Ambient Space (long reverb into +500 ms delay). They replace the current chain in one undoable edit and require +no external plugins. All controls remain editable. Times are milliseconds; +feedback and wet levels are deliberate starting points, without automatic +loudness matching or a claim that the presets suit every source. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index cca9b14..b88121e 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -2118,12 +2118,12 @@ Implemented: two independent track-bound chain comparison slots, with disabled R Offer original dependency-free Slapback, Dub Echo and Ambient Space starting points using internal devices, including safe replacement and editable controls. ### Tasks and acceptance criteria -- [ ] Implement the workflow with bounded validation, safe preparation and documented limits. -- [ ] Pass meaningful core and/or GUI checks, including error and undo behavior. -- [ ] Document results and commit this phase separately. +- [x] Implement the workflow with bounded validation, safe preparation and documented limits. +- [x] Pass meaningful core and/or GUI checks, including error and undo behavior. +- [x] Document results and commit this phase separately. ### Implementation status -Planned. +Implemented: Slapback, Dub Echo and Ambient Space templates use original internal Delay/Reverb configurations with editable controls and explicit chain replacement. C99 generation validates tempo/index, preserves errors and loads no external modules. All templates compile/render finite nonzero output; GUI template adoption and undo checks passed. --- diff --git a/include/xtc/xtc_plugin.h b/include/xtc/xtc_plugin.h index 644ae09..92bed02 100644 --- a/include/xtc/xtc_plugin.h +++ b/include/xtc/xtc_plugin.h @@ -38,6 +38,11 @@ typedef struct { xtc_plugin_spec plugin; unsigned int enabled; float wet; } xtc_ typedef struct { size_t count; float output_gain; xtc_track_insert slots[XTC_TRACK_INSERTS]; } xtc_track_chain; /* Copies the selected slot immediately after itself, preserving all metadata. * Input/output may alias; errors preserve output. */ +/* Original internal-device starting points: 0 Slapback, 1 Dub Echo, + * 2 Ambient Space. Name returns NULL for an unknown index. No modules opened; + * template errors preserve out. Delay times are milliseconds, not beat sync. */ +const char *xtc_track_chain_template_name(unsigned int index); +xtc_result xtc_track_chain_template(unsigned int index,unsigned int tempo,xtc_track_chain *out); xtc_result xtc_track_chain_duplicate(const xtc_track_chain *chain,size_t index,xtc_track_chain *out); xtc_result xtc_track_chain_preset_save(const xtc_track_chain *chain,const char *path); xtc_result xtc_track_chain_preset_load(const char *path,xtc_track_chain *out); diff --git a/src/gui/track_inserts.hpp b/src/gui/track_inserts.hpp index 1215366..f486bcc 100644 --- a/src/gui/track_inserts.hpp +++ b/src/gui/track_inserts.hpp @@ -39,6 +39,10 @@ class TrackInsertsPanel : public Fl_Group { add->add("Internal Delay",0,[](Fl_Widget *,void *c){static_cast(c)->add(XTC_BUILTIN_PATH,2);},this); add->add("Internal Reverb",0,[](Fl_Widget *,void *c){static_cast(c)->add(XTC_BUILTIN_PATH,3);},this); add->add("Plugin library...",0,[](Fl_Widget *,void *c){auto *p=static_cast(c);std::string file;if(!xtc_choose_plugin_file(p->owner_->settings(),file))return;unsigned int index=0;if(xtc_choose_plugin(file.c_str(),p->owner_->sample_rate(),index))p->add(file.c_str(),index);},this); + for(unsigned int preset=0;preset<3;++preset) { + std::string label=std::string("Templates (replace)/")+xtc_track_chain_template_name(preset); + add->add(label.c_str(),0,[](Fl_Widget *w,void *c){auto *p=static_cast(c);auto *menu=static_cast(w);unsigned int index=0;for(;index<3;++index)if(!strcmp(menu->mvalue()->label(),xtc_track_chain_template_name(index)))break;p->result(p->apply_template(index));},this); + } struct Action {const char *name;int command;}; int left=x+486; for(const auto &action:{Action{"Up",-1},Action{"Down",1},Action{"Bypass",2},Action{"Remove",3}}) { @@ -73,6 +77,11 @@ class TrackInsertsPanel : public Fl_Group { void select_track(uint64_t id) {track_=id;refresh(true);} const xtc_graph *graph() const {return model_;} void add(const char *file,unsigned int index) { result(owner_->add_track_effect(track_,file,index)); } + xtc_result apply_template(unsigned int index) { + xtc_track_chain chain{};auto r=xtc_track_chain_template(index,xtc_project_tempo(owner_->project_.get()),&chain); + if(r==XTC_OK)r=owner_->set_track_chain(track_,chain); + return r; + } void clear_session() {clipboard_={};clipboard_ready_=false;for(auto &slot:comparisons_)slot=Comparison{};update_actions();} bool clipboard_ready() const {return clipboard_ready_;} bool comparison_ready(unsigned int slot) const {return slot<2 && comparisons_[slot].ready && comparisons_[slot].track==track_;} diff --git a/src/project/chain_tools.c b/src/project/chain_tools.c index 8eddec9..b47e9e9 100644 --- a/src/project/chain_tools.c +++ b/src/project/chain_tools.c @@ -9,3 +9,25 @@ xtc_result xtc_track_chain_duplicate(const xtc_track_chain *chain,size_t index,x for(i=copy.count;i>index+1;--i)copy.slots[i]=copy.slots[i-1]; copy.slots[index+1]=copy.slots[index];++copy.count;*out=copy;return XTC_OK; } + +const char *xtc_track_chain_template_name(unsigned int index) +{ + static const char *names[]={"Slapback","Dub Echo","Ambient Space"}; + return index<3 ? names[index] : NULL; +} +xtc_result xtc_track_chain_template(unsigned int index,unsigned int tempo,xtc_track_chain *out) +{ + xtc_track_chain c={0};size_t i;xtc_result r=XTC_OK; + if(!out || index>=3 || tempo<20000 || tempo>300000)return XTC_ERR_INVALID_ARGUMENT; + c.output_gain=index ? .8f : 1;c.count=index ? 2 : 1; + for(i=0;r==XTC_OK && irefresh(true); CHECK(xtc_project_get_chain(s->project(),first_track,&chain)==XTC_OK && chain.output_gain==.6f); panel->select_track(second_track);CHECK(!panel->comparison_ready(0) && panel->comparison(0,false)==XTC_ERR_NOT_FOUND); + CHECK(panel->apply_template(1)==XTC_OK); + CHECK(xtc_project_get_chain(s->project(),second_track,&chain)==XTC_OK && chain.count==2 && chain.slots[0].plugin.values[0]==375); + CHECK(window.undo()==XTC_OK);panel->refresh(true);CHECK(xtc_project_get_chain(s->project(),second_track,&chain)==XTC_OK && chain.count==0); CHECK(window.new_project()==XTC_OK && !panel->clipboard_ready() && panel->paste_chain()==XTC_ERR_NOT_FOUND); CHECK(!panel->comparison_ready(0) && !panel->comparison_ready(1)); window.do_callback();puts("Creative workflow GUI checks passed.");return failures ? 1 : 0; diff --git a/tests/unit/test_creative.c b/tests/unit/test_creative.c index 0f00f7c..95ca751 100644 --- a/tests/unit/test_creative.c +++ b/tests/unit/test_creative.c @@ -2,6 +2,7 @@ #include #include #include +#include int main(void) { xtc_track_chain c={0},out={0}; @@ -15,5 +16,14 @@ int main(void) for(size_t i=3;i0); + xtc_plan_destroy(plan);xtc_graph_destroy(g); + } + out=c;assert(!xtc_track_chain_template_name(3) && xtc_track_chain_template(3,120000,&c)==XTC_ERR_INVALID_ARGUMENT && !memcmp(&out,&c,sizeof(c))); puts("Creative editing core checks passed.");return 0; }