xtc/src/gui/plugin_dialog.cpp

103 lines
6.1 KiB
C++
Raw Normal View History

#include "plugin_dialog.hpp"
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Hold_Browser.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>
#include <FL/Fl_Value_Input.H>
#include <FL/Fl_Scroll.H>
#include <FL/fl_ask.H>
#include <array>
#include <vector>
#include <string>
#include <cmath>
namespace {
struct Dialog : Fl_Double_Window {
bool accepted = false;
Dialog(int width, int height, const char *title) : Fl_Double_Window(width,height,title) { set_modal(); }
static void accept(Fl_Widget *, void *data) { auto *d = static_cast<Dialog *>(data); d->accepted = true; d->hide(); }
static void cancel(Fl_Widget *, void *data) { static_cast<Dialog *>(data)->hide(); }
bool run() { callback(cancel,this); end(); show(); while (shown()) Fl::wait(); return accepted; }
};
}
bool xtc_choose_plugin(const char *path, double rate, unsigned int &index)
{
Dialog dialog(760,450,"LADSPA descriptors");
auto *heading = new Fl_Box(15,10,730,40);
heading->copy_label(path); heading->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CLIP);
auto *list = new Fl_Hold_Browser(15,55,730,325);
list->format_char(0); // Plugin names are data, never browser formatting.
std::vector<unsigned int> indices;
size_t rejected = 0;
for (unsigned int i = 0; i < 1024; ++i) {
xtc_plugin_spec spec{}; xtc_plugin_details details{};
xtc_result r = xtc_plugin_probe(path,i,rate,&spec);
if (r == XTC_ERR_NOT_FOUND) break;
if (r != XTC_OK) { ++rejected; continue; }
if (xtc_plugin_describe(&spec,rate,&details) != XTC_OK) { ++rejected; continue; }
std::string row = std::to_string(i) + ": " + details.name + " (" + spec.label + ") — " + details.maker +
(spec.inputs ? " [effect]" : " [source]");
list->add(row.c_str()); indices.push_back(i);
}
auto *status = new Fl_Box(15,385,520,50);
std::string summary = std::to_string(indices.size()) + " compatible; " + std::to_string(rejected) +
" unsupported/unavailable. Select a descriptor.";
status->copy_label(summary.c_str()); status->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP);
auto *cancel = new Fl_Button(540,400,90,30,"Cancel"); cancel->callback(Dialog::cancel,&dialog);
auto *choose = new Fl_Return_Button(645,400,100,30,"Choose"); choose->callback(Dialog::accept,&dialog);
if (indices.empty()) choose->deactivate(); else list->select(1);
if (!dialog.run() || list->value() < 1) return false;
index = indices[static_cast<size_t>(list->value()-1)]; return true;
}
bool xtc_edit_plugin(const xtc_plugin_spec &spec, const xtc_plugin_details &details, xtc_plugin_spec &edited)
{
Dialog dialog(800,600,"Plugin identity and controls");
auto *identity = new Fl_Box(15,10,770,125);
std::string text = std::string(details.name) + " — " + details.maker + "\n" + spec.label +
" | LADSPA ID " + std::to_string(spec.unique_id) + " | descriptor " + std::to_string(spec.index) +
"\n" + spec.path + "\n" + details.copyright +
"\nMetadata verified; Apply validates initialization. Units unspecified by LADSPA.";
identity->copy_label(text.c_str()); identity->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP);
auto *scroll = new Fl_Scroll(10,140,780,370);
std::array<Fl_Value_Input *,XTC_PLUGIN_CONTROLS> inputs{};
for (unsigned int i = 0; i < spec.control_count; ++i) {
int y = 150 + static_cast<int>(i)*65;
const auto &p = details.parameters[i];
std::string name = std::to_string(p.id) + ": " + p.name;
auto *label = new Fl_Box(25,y,425,25); label->copy_label(name.c_str()); label->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
inputs[i] = new Fl_Value_Input(460,y,150,25); inputs[i]->value(spec.values[i]);
inputs[i]->step((details.flags[i] & (XTC_PLUGIN_INTEGER|XTC_PLUGIN_TOGGLED)) ? 1 : 0.001);
std::string hint = "Range " + std::string(details.flags[i] & XTC_PLUGIN_BOUNDED_BELOW ? std::to_string(p.minimum) : "unbounded") +
" .. " + (details.flags[i] & XTC_PLUGIN_BOUNDED_ABOVE ? std::to_string(p.maximum) : "unbounded") +
"; default " + std::to_string(p.default_value) +
((details.flags[i] & XTC_PLUGIN_TOGGLED) ? "; toggle 0/1" : (details.flags[i] & XTC_PLUGIN_INTEGER) ? "; integer" : "; continuous") +
((details.flags[i] & XTC_PLUGIN_LOGARITHMIC) ? "; logarithmic hint" : "");
auto *range = new Fl_Box(25,y+27,715,25); range->copy_label(hint.c_str()); range->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
}
scroll->end();
auto *note = new Fl_Box(15,515,770,30,"Automation overrides static controls. Reset defaults also clears all plugin automation.");
note->labelsize(12);
struct Reset { const xtc_plugin_details *details; std::array<Fl_Value_Input *,XTC_PLUGIN_CONTROLS> *inputs; unsigned int count; bool used; } reset{&details,&inputs,spec.control_count,false};
auto *defaults = new Fl_Button(20,555,170,30,"Reset defaults");
defaults->callback([](Fl_Widget *, void *data) {
auto *r = static_cast<Reset *>(data); r->used = true;
for (unsigned int i = 0; i < r->count; ++i) (*r->inputs)[i]->value(r->details->parameters[i].default_value);
},&reset);
auto *cancel = new Fl_Button(570,555,90,30,"Cancel"); cancel->callback(Dialog::cancel,&dialog);
auto *apply = new Fl_Return_Button(675,555,105,30,"Apply"); apply->callback(Dialog::accept,&dialog);
if (!dialog.run()) return false;
xtc_plugin_spec candidate = spec;
for (unsigned int i = 0; i < spec.control_count; ++i) {
double value = inputs[i]->value(); const auto &p = details.parameters[i]; unsigned int flags = details.flags[i];
if (!std::isfinite(value) || value < p.minimum || value > p.maximum ||
((flags & XTC_PLUGIN_INTEGER) && value != std::floor(value)) ||
((flags & XTC_PLUGIN_TOGGLED) && value != 0 && value != 1)) {
fl_alert("Invalid value for port %u (%s); no controls were changed.",p.id,p.name); return false;
}
candidate.values[i] = static_cast<float>(value);
}
if (reset.used) candidate.point_count = 0;
edited = candidate; return true;
}