xtc/src/gui/track_inserts.hpp

95 lines
6.5 KiB
C++
Raw Normal View History

#ifndef XTC_TRACK_INSERTS_HPP
#define XTC_TRACK_INSERTS_HPP
#include "main_window.hpp"
#include "graph_widget.hpp"
#include "plugin_library.hpp"
#include "plugin_dialog.hpp"
#include "sequencer_widget.hpp"
#include <FL/Fl_Choice.H>
#include <FL/Fl_Menu_Button.H>
#include <FL/Fl_Hold_Browser.H>
#include <FL/Fl_Box.H>
#include <cstring>
#include <algorithm>
class TrackInsertsPanel : public Fl_Group {
MainWindow *owner_;
Fl_Choice *tracks_;
Fl_Hold_Browser *slots_;
Fl_Box *message_;
GraphWidget *graph_;
xtc_graph *model_=nullptr;
uint64_t track_=UINT64_MAX;
xtc_track_chain cached_{};
std::vector<uint64_t> ids_;
void result(xtc_result r) { if(r!=XTC_OK)owner_->report_error("Track inserts",r);refresh(true); }
public:
TrackInsertsPanel(int x,int y,int w,int h,MainWindow *owner):Fl_Group(x,y,w,h,"Track inserts"),owner_(owner) {
tracks_=new Fl_Choice(x+64,y+12,240,28,"Track");tracks_->callback([](Fl_Widget *,void *c){auto *p=static_cast<TrackInsertsPanel *>(c);int i=p->tracks_->value();if(i>=0 && static_cast<size_t>(i)<p->ids_.size()){p->track_=p->ids_[static_cast<size_t>(i)];p->refresh(true);}},this);
auto *add=new Fl_Menu_Button(x+320,y+12,150,28,"Add effect");
add->add("Internal Delay",0,[](Fl_Widget *,void *c){static_cast<TrackInsertsPanel *>(c)->add(XTC_BUILTIN_PATH,2);},this);
add->add("Internal Reverb",0,[](Fl_Widget *,void *c){static_cast<TrackInsertsPanel *>(c)->add(XTC_BUILTIN_PATH,3);},this);
add->add("Plugin library...",0,[](Fl_Widget *,void *c){auto *p=static_cast<TrackInsertsPanel *>(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);
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}}) {
auto *button=new Fl_Button(left,y+12,82,28,action.name);left+=90;
button->callback([](Fl_Widget *w,void *c){auto *p=static_cast<TrackInsertsPanel *>(c);int command=!strcmp(w->label(),"Up") ? -1 : !strcmp(w->label(),"Down") ? 1 : !strcmp(w->label(),"Bypass") ? 2 : 3;p->result(p->edit_slot(command));},this);
}
auto *inspect=new Fl_Button(x+850,y+12,120,28,"Inspect / edit");
inspect->callback([](Fl_Widget *,void *c){auto *p=static_cast<TrackInsertsPanel *>(c);p->result(p->inspect_slot());},this);
message_=new Fl_Box(x+16,y+50,w-32,32,"Track input > inserts > output gain > track fader > bus/master");message_->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
slots_=new Fl_Hold_Browser(x+16,y+94,230,h-110);slots_->format_char(0);
graph_=new GraphWidget(x+260,y+94,w-276,h-110,nullptr,[](void *,const xtc_graph_edit &,size_t *){return XTC_ERR_UNSUPPORTED;},nullptr,message_);
resizable(graph_);end();
}
~TrackInsertsPanel() override { graph_->set_graph(nullptr);xtc_graph_destroy(model_); }
uint64_t selected_track() const {return track_;}
size_t selected_slot() const {return slots_->value()>0 ? static_cast<size_t>(slots_->value()-1) : SIZE_MAX;}
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 inspect_slot() {
xtc_track_chain chain{};auto r=xtc_project_get_chain(owner_->project_.get(),track_,&chain);size_t index=selected_slot();
if(r!=XTC_OK)return r;
if(index>=chain.count)return XTC_ERR_INVALID_STATE;
xtc_plugin_details details{};xtc_plugin_spec edited{};
r=xtc_plugin_describe(&chain.slots[index].plugin,owner_->sample_rate(),&details);if(r!=XTC_OK)return r;
if(!xtc_edit_plugin(chain.slots[index].plugin,details,edited))return XTC_OK;
chain.slots[index].plugin=edited;return owner_->set_track_chain(track_,chain);
}
/* Commands: -1 up, 1 down, 2 toggle bypass, 3 remove. */
xtc_result edit_slot(int command) {
xtc_track_chain chain{};auto r=xtc_project_get_chain(owner_->project_.get(),track_,&chain);size_t index=selected_slot();
if(r!=XTC_OK)return r;
if(index>=chain.count)return XTC_ERR_INVALID_STATE;
size_t selected=index;
if(command==-1 || command==1) {
if((command==-1 && !index) || (command==1 && index+1>=chain.count))return XTC_ERR_INVALID_ARGUMENT;
selected=command==-1 ? index-1 : index+1;std::swap(chain.slots[index],chain.slots[selected]);
}else if(command==2)chain.slots[index].enabled=!chain.slots[index].enabled;
else if(command==3) {for(size_t i=index+1;i<chain.count;++i)chain.slots[i-1]=chain.slots[i];chain.slots[--chain.count]={};}
else return XTC_ERR_INVALID_ARGUMENT;
r=owner_->set_track_chain(track_,chain);
if(r==XTC_OK && chain.count)slots_->select(static_cast<int>(std::min(selected,chain.count-1)+1));
return r;
}
void refresh(bool force=false) {
const xtc_project *project=owner_->project_.get();size_t count=0,clips=0;xtc_project_get_counts(project,&count,&clips);
std::vector<uint64_t> ids;std::vector<std::string> names;
for(size_t i=0;i<count;++i){xtc_track_info t{};xtc_project_get_track(project,i,&t);if(!t.is_bus){ids.push_back(t.id);names.emplace_back(t.name);}}
bool rebuild=ids!=ids_ || force;
if(rebuild){ids_=ids;tracks_->clear();for(const auto &name:names) {tracks_->add("track");tracks_->replace(tracks_->size()-2,name.c_str());}}
if(std::find(ids_.begin(),ids_.end(),track_)==ids_.end())track_=ids_.empty() ? UINT64_MAX : ids_[0];
for(size_t i=0;i<ids_.size();++i)if(ids_[i]==track_)tracks_->value(static_cast<int>(i));
xtc_track_chain chain{};chain.output_gain=1;
if(track_!=UINT64_MAX)xtc_project_get_chain(project,track_,&chain);
if(!rebuild && !std::memcmp(&cached_,&chain,sizeof(chain)))return;
cached_=chain;int selected=slots_->value();slots_->clear();
for(size_t i=0;i<chain.count;++i){std::string label=std::to_string(i+1)+" "+chain.slots[i].plugin.label+(chain.slots[i].enabled ? "" : " [bypass]");slots_->add(label.c_str());}
if(chain.count)slots_->select(selected>0 && static_cast<size_t>(selected)<=chain.count ? selected : 1);
xtc_graph *next=nullptr;auto r=xtc_track_chain_graph(&chain,&next);
if(r==XTC_OK){graph_->set_graph(next);xtc_graph_destroy(model_);model_=next;graph_->auto_layout();}
redraw();
}
};
#endif