diff options
author | Robin Gareus <robin@gareus.org> | 2014-01-04 05:54:20 +0100 |
---|---|---|
committer | Robin Gareus <robin@gareus.org> | 2014-01-04 05:54:20 +0100 |
commit | 2b5a04d3f48e7f632c778c8bcd705aab10b9dcf5 (patch) | |
tree | 5bad315d289a95d6151005f8a31e709e4238206d /gtk2_ardour | |
parent | e8e4e677aab030c0aae4eaf1f92cfc8a614defe6 (diff) |
continued work on processor-routing visualization
major re-design.
* change splitting-icon into routing-icon
* add it to all processors (not only plugin-inserts)
* subscribe to ChanCount configuration changes
* add 'wiring' to BlankProcessorEntry pre-fader placeholder
* visualize routing for non-matching port-counts
currently still wire+color design with #if'ed debug messages
and optional #ifdef wires for matching connections.
Diffstat (limited to 'gtk2_ardour')
-rw-r--r-- | gtk2_ardour/processor_box.cc | 290 | ||||
-rw-r--r-- | gtk2_ardour/processor_box.h | 63 |
2 files changed, 262 insertions, 91 deletions
diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index 08079f4f91..2e1942f7d3 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -17,6 +17,8 @@ */ +//#define ALWAYS_DISPLAY_WIRES + #ifdef WAF_BUILD #include "gtk2ardour-config.h" #endif @@ -98,6 +100,8 @@ ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptr<Processo , _processor (p) , _width (w) , _visual_state (Gtk::STATE_NORMAL) + , _input_icon(true) + , _output_icon(false) { _vbox.show (); @@ -109,13 +113,23 @@ ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptr<Processo if (_processor) { + _vbox.pack_start (_routing_icon); + _vbox.pack_start (_input_icon); _vbox.pack_start (_button, true, true); + _vbox.pack_end (_output_icon); _button.set_active (_processor->active()); + _button.show (); +#ifdef ALWAYS_DISPLAY_WIRES + _routing_icon.show(); +#endif + _input_icon.show(); + _output_icon.show(); _processor->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_active_changed, this), gui_context()); _processor->PropertyChanged.connect (name_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_property_changed, this, _1), gui_context()); + _processor->ConfigurationChanged.connect (config_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_configuration_changed, this, _1, _2), gui_context()); set<Evoral::Parameter> p = _processor->what_can_be_automated (); for (set<Evoral::Parameter>::iterator i = p.begin(); i != p.end(); ++i) { @@ -135,6 +149,12 @@ ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptr<Processo } } + _input_icon.set_ports(_processor->input_streams()); + _output_icon.set_ports(_processor->output_streams()); + + _routing_icon.set_sources(_processor->input_streams()); + _routing_icon.set_sinks(_processor->output_streams()); + setup_tooltip (); setup_visuals (); } else { @@ -190,14 +210,17 @@ ProcessorEntry::setup_visuals () switch (_position) { case PreFader: _button.set_name ("processor prefader"); + _routing_icon.set_name ("ProcessorPreFader"); break; case Fader: _button.set_name ("processor fader"); + _routing_icon.set_name ("ProcessorFader"); break; case PostFader: _button.set_name ("processor postfader"); + _routing_icon.set_name ("ProcessorPostFader"); break; } } @@ -246,6 +269,24 @@ ProcessorEntry::processor_property_changed (const PropertyChange& what_changed) } void +ProcessorEntry::processor_configuration_changed (const ChanCount in, const ChanCount out) +{ +#if 0 // debug, devel information + printf("ProcessorEntry::processor_config_changed %s %d %d\n", + name(Wide).c_str(), + in.n_audio(), out.n_audio()); +#endif + + _input_icon.set_ports(in); + _output_icon.set_ports(out); + _routing_icon.set_sources(in); + _routing_icon.set_sinks(out); + _input_icon.queue_draw(); + _output_icon.queue_draw(); + _routing_icon.queue_draw(); +} + +void ProcessorEntry::setup_tooltip () { if (_processor) { @@ -593,9 +634,20 @@ ProcessorEntry::Control::state_id () const return string_compose (X_("control %1"), c->id().to_s ()); } -BlankProcessorEntry::BlankProcessorEntry (ProcessorBox* b, Width w) +BlankProcessorEntry::BlankProcessorEntry (ProcessorBox* b, Width w, ChanCount cc) : ProcessorEntry (b, boost::shared_ptr<Processor>(), w) { +#ifdef ALWAYS_DISPLAY_WIRES + _vbox.pack_start (_routing_icon); + _routing_icon.set_sources(cc); + _routing_icon.set_sinks(cc); + _routing_icon.show(); + setup_visuals (); + _routing_icon.set_size_request (-1, 8); + _vbox.set_size_request (-1, 8); +#else + _vbox.set_size_request (-1, 0); +#endif } PluginInsertProcessorEntry::PluginInsertProcessorEntry (ProcessorBox* b, boost::shared_ptr<ARDOUR::PluginInsert> p, Width w) @@ -606,31 +658,74 @@ PluginInsertProcessorEntry::PluginInsertProcessorEntry (ProcessorBox* b, boost:: _splitting_connection, invalidator (*this), boost::bind (&PluginInsertProcessorEntry::plugin_insert_splitting_changed, this), gui_context() ); - _splitting_icon.set_size_request (-1, 12); - - _vbox.pack_start (_splitting_icon); - _vbox.reorder_child (_splitting_icon, 0); - plugin_insert_splitting_changed (); } void PluginInsertProcessorEntry::plugin_insert_splitting_changed () { - _splitting_icon.set_inputs(_plugin_insert->input_streams()); - _splitting_icon.set_outputs(_plugin_insert->output_streams()); +#if 0 // debug, devel information + printf("--%s--\n", _plugin_insert->name().c_str()); + printf("AUDIO src: %d -> in:%d | * cnt %d * | out: %d -> snk: %d\n", + _plugin_insert->input_streams().n_audio(), + _plugin_insert->natural_input_streams().n_audio(), + _plugin_insert->get_count(), + _plugin_insert->natural_output_streams().n_audio(), + _plugin_insert->output_streams().n_audio()); + printf("MIDI src: %d -> in:%d | * cnt %d * | out: %d -> snk: %d\n", + _plugin_insert->input_streams().n_midi(), + _plugin_insert->natural_input_streams().n_midi(), + _plugin_insert->get_count(), + _plugin_insert->natural_output_streams().n_midi(), + _plugin_insert->output_streams().n_midi()); + printf("TOTAL src: %d -> in:%d | * cnt %d * | out: %d -> snk: %d\n", + _plugin_insert->input_streams().n_total(), + _plugin_insert->natural_input_streams().n_total(), + _plugin_insert->get_count(), + _plugin_insert->natural_output_streams().n_total(), + _plugin_insert->output_streams().n_total()); +#endif + + _output_icon.set_ports(_plugin_insert->output_streams()); + _routing_icon.set_splitting(_plugin_insert->splitting ()); + + ChanCount sources = _plugin_insert->input_streams(); + ChanCount sinks = _plugin_insert->natural_input_streams(); + + /* replicated instances */ + if (!_plugin_insert->splitting () && _plugin_insert->get_count() > 1) { + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + sinks.set(*t, sinks.get(*t) * _plugin_insert->get_count()); + } + } + /* MIDI bypass */ + if (_plugin_insert->natural_output_streams().n_midi() == 0 && + _plugin_insert->output_streams().n_midi() == 1) { + sinks.set(DataType::MIDI, 1); + sources.set(DataType::MIDI, 1); + } - if (_plugin_insert->splitting () || ( - _plugin_insert->input_streams().n_midi() == 0 - && _plugin_insert->input_streams().n_audio() < _plugin_insert->output_streams().n_audio() - ) + _input_icon.set_ports(sinks); + _routing_icon.set_sinks(sinks); + _routing_icon.set_sources(sources); + + if (_plugin_insert->splitting () || + _plugin_insert->input_streams().n_audio() < _plugin_insert->natural_input_streams().n_audio() ) { - _splitting_icon.show (); - _splitting_icon.queue_draw(); + _routing_icon.set_size_request (-1, 8); + _routing_icon.show(); } else { - _splitting_icon.hide (); +#ifdef ALWAYS_DISPLAY_WIRES + _routing_icon.set_size_request (-1, 4); +#else + _routing_icon.hide(); +#endif } + + _input_icon.queue_draw(); + _output_icon.queue_draw(); + _routing_icon.queue_draw(); } void @@ -640,28 +735,59 @@ PluginInsertProcessorEntry::hide_things () plugin_insert_splitting_changed (); } -void -PluginInsertProcessorEntry::setup_visuals () + +bool +ProcessorEntry::PortIcon::on_expose_event (GdkEventExpose* ev) { - switch (_position) { - case PreFader: - _splitting_icon.set_name ("ProcessorPreFader"); - break; + cairo_t* cr = gdk_cairo_create (get_window()->gobj()); - case Fader: - _splitting_icon.set_name ("ProcessorFader"); - break; + cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height); + cairo_clip (cr); - case PostFader: - _splitting_icon.set_name ("ProcessorPostFader"); - break; + cairo_set_line_width (cr, 5.0); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + + Gtk::Allocation a = get_allocation(); + double const width = a.get_width(); + double const height = a.get_height(); + + Gdk::Color const bg = get_style()->get_bg (STATE_NORMAL); + cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ()); + + cairo_rectangle (cr, 0, 0, width, height); + cairo_fill (cr); + + const double y0 = _input ? height-.5 : .5; + + if (_ports.n_total() > 1) { + for (uint32_t i = 0; i < _ports.n_total(); ++i) { + if (i < _ports.n_midi()) { + cairo_set_source_rgb (cr, .8, .3, .3); + } else { + cairo_set_source_rgb (cr, .4, .4, .8); + } + const float x = rintf(width * (.2f + .6f * i / (_ports.n_total() - 1.f))) + .5f; + cairo_move_to (cr, x, y0); + cairo_close_path(cr); + cairo_stroke(cr); + } + } else if (_ports.n_total() == 1) { + if (_ports.n_midi() == 1) { + cairo_set_source_rgb (cr, .8, .3, .3); + } else { + cairo_set_source_rgb (cr, .4, .4, .8); + } + cairo_move_to (cr, rintf(width * .5) + .5f, y0); + cairo_close_path(cr); + cairo_stroke(cr); } - ProcessorEntry::setup_visuals (); + cairo_destroy(cr); + return true; } bool -PluginInsertProcessorEntry::SplittingIcon::on_expose_event (GdkEventExpose* ev) +ProcessorEntry::RoutingIcon::on_expose_event (GdkEventExpose* ev) { cairo_t* cr = gdk_cairo_create (get_window()->gobj()); @@ -684,54 +810,58 @@ PluginInsertProcessorEntry::SplittingIcon::on_expose_event (GdkEventExpose* ev) Gdk::Color const fg = get_style()->get_fg (STATE_NORMAL); cairo_set_source_rgb (cr, fg.get_red_p (), fg.get_green_p (), fg.get_blue_p ()); - const uint32_t inputs = _inputs.n_audio(); - const uint32_t outputs = _outputs.n_audio(); + const uint32_t sources = _sources.n_total(); + const uint32_t sinks = _sinks.n_total(); - const float si_m = rintf(height * 0.5) + .5f; + /* MIDI */ + cairo_set_source_rgb (cr, .6, .2, .2); + const uint32_t midi_sources = _sources.n_midi(); + const uint32_t midi_sinks = _sinks.n_midi(); - if (inputs == 1) { - const float si_l = rintf(width * 0.2) + .5f; - const float si_c = rintf(width * 0.5) + .5f; - const float si_r = rintf(width * 0.8) + .5f; - - cairo_move_to (cr, si_l, height); - cairo_line_to (cr, si_l, si_m); - cairo_line_to (cr, si_r, si_m); - cairo_line_to (cr, si_r, height); + if (midi_sources > 0 && midi_sinks > 0 && sinks > 1 && sources > 1) { + for (uint32_t i = 0 ; i < midi_sources; ++i) { + const float si_x = rintf(width * (.2f + .6f * i / (sinks - 1.f))) + .5f; + const float si_x0 = rintf(width * (.2f + .6f * i / (sources - 1.f))) + .5f; + cairo_move_to (cr, si_x, height); + cairo_curve_to (cr, si_x, 0, si_x0, height, si_x0, 0); + cairo_stroke (cr); + } + } else if (midi_sources == 1 && midi_sinks == 1 && sinks == 1 && sources == 1) { + const float si_x = rintf(width * .5f) + .5f; + cairo_move_to (cr, si_x, height); + cairo_line_to (cr, si_x, 0); cairo_stroke (cr); + } - cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT); + /* AUDIO */ + cairo_set_source_rgb (cr, .6, .6, .8); + const uint32_t audio_sources = _sources.n_audio(); + const uint32_t audio_sinks = _sinks.n_audio(); - const uint32_t outputs = _outputs.n_audio(); - for (uint32_t i = 2; i < outputs; ++i) { - const float si_b = rintf(width * (.2f + .6f * (i - 1.f) / (outputs - 1.f))) + .5f; - cairo_move_to (cr, si_b, height); - cairo_line_to (cr, si_b, si_m); + if (_splitting) { + assert(audio_sinks > 1); + const float si_x0 = rintf(width * .5f) + .5f; + for (uint32_t i = midi_sinks; i < sinks; ++i) { + const float si_x = rintf(width * (.2f + .6f * i / (sinks - 1.f))) + .5f; + cairo_move_to (cr, si_x, height); + cairo_curve_to (cr, si_x, 0, si_x0, height, si_x0, 0); cairo_stroke (cr); } - - cairo_move_to (cr, si_c, si_m); - cairo_line_to (cr, si_c, 0); - cairo_stroke (cr); - } else { - cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); - for (uint32_t i = 0 ; i < outputs; ++i) { - const float si_x = rintf(width * (.2f + .6f * i / (outputs - 1.f))) + .5f; - if (i < inputs) { - cairo_move_to (cr, si_x, height); - cairo_line_to (cr, si_x, 0); - cairo_stroke (cr); - } else { - cairo_move_to (cr, si_x, si_m); - cairo_line_to (cr, si_x, height); - cairo_stroke (cr); - cairo_move_to (cr, si_x+4, si_m); - cairo_line_to (cr, si_x-4, si_m); - cairo_stroke (cr); - } + } else if (audio_sources > 1) { + for (uint32_t i = 0 ; i < audio_sources; ++i) { + const float si_x = rintf(width * (.2f + .6f * (i + midi_sinks) / (sinks - 1.f))) + .5f; + const float si_x0 = rintf(width * (.2f + .6f * (i + midi_sources) / (sources - 1.f))) + .5f; + cairo_move_to (cr, si_x, height); + cairo_curve_to (cr, si_x, 0, si_x0, height, si_x0, 0); + cairo_stroke (cr); } + } else if (audio_sources == 1 && audio_sinks == 1) { + const float si_x = rintf(width * .5f) + .5f; + cairo_move_to (cr, si_x, height); + cairo_line_to (cr, si_x, 0); + cairo_stroke (cr); } - + cairo_destroy(cr); return true; } @@ -761,7 +891,7 @@ ProcessorBox::ProcessorBox (ARDOUR::Session* sess, boost::function<PluginSelecto processor_display.set_name ("ProcessorList"); processor_display.set_data ("processorbox", this); processor_display.set_size_request (48, -1); - processor_display.set_spacing (2); + processor_display.set_spacing (0); processor_display.signal_enter_notify_event().connect (sigc::mem_fun(*this, &ProcessorBox::enter_notify), false); processor_display.signal_leave_notify_event().connect (sigc::mem_fun(*this, &ProcessorBox::leave_notify), false); @@ -821,6 +951,12 @@ ProcessorBox::set_route (boost::shared_ptr<Route> r) _route->PropertyChanged.connect ( _route_connections, invalidator (*this), boost::bind (&ProcessorBox::route_property_changed, this, _1), gui_context() ); +#ifdef ALWAYS_DISPLAY_WIRES + /* update BlankProcessorEntry wire-count */ + _route->io_changed.connect ( + _route_connections, invalidator (*this), boost::bind (&ProcessorBox::io_changed_proxy, this), gui_context () + ); +#endif redisplay_processors (); } @@ -1414,6 +1550,7 @@ ProcessorBox::redisplay_processors () { ENSURE_GUI_THREAD (*this, &ProcessorBox::redisplay_processors); bool fader_seen; + ChanCount amp_chan_count; if (no_processor_redisplay) { return; @@ -1425,10 +1562,10 @@ ProcessorBox::redisplay_processors () fader_seen = false; _route->foreach_processor (sigc::bind (sigc::mem_fun (*this, &ProcessorBox::help_count_visible_prefader_processors), - &_visible_prefader_processors, &fader_seen)); + &_visible_prefader_processors, &fader_seen, &_chan_count)); if (_visible_prefader_processors == 0) { // fader only - BlankProcessorEntry* bpe = new BlankProcessorEntry (this, _width); + BlankProcessorEntry* bpe = new BlankProcessorEntry (this, _width, amp_chan_count); processor_display.add_child (bpe); } @@ -1486,6 +1623,12 @@ ProcessorBox::redisplay_processors () setup_entry_positions (); } +void +ProcessorBox::io_changed_proxy () +{ + Glib::signal_idle().connect_once (sigc::mem_fun (*this, &ProcessorBox::redisplay_processors)); +} + /** Add a ProcessorWindowProxy for a processor to our list, if that processor does * not already have one. */ @@ -1552,7 +1695,7 @@ ProcessorBox::maybe_add_processor_to_ui_list (boost::weak_ptr<Processor> w) } void -ProcessorBox::help_count_visible_prefader_processors (boost::weak_ptr<Processor> p, uint32_t* cnt, bool* amp_seen) +ProcessorBox::help_count_visible_prefader_processors (boost::weak_ptr<Processor> p, uint32_t* cnt, bool* amp_seen, ChanCount *cc) { boost::shared_ptr<Processor> processor (p.lock ()); @@ -1560,6 +1703,9 @@ ProcessorBox::help_count_visible_prefader_processors (boost::weak_ptr<Processor> if (boost::dynamic_pointer_cast<Amp>(processor)) { *amp_seen = true; + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + cc->set(*t, processor->input_streams().get(*t)); + } } else { if (!*amp_seen) { (*cnt)++; diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h index 6aed08d17e..9534488f32 100644 --- a/gtk2_ardour/processor_box.h +++ b/gtk2_ardour/processor_box.h @@ -150,6 +150,7 @@ private: void led_clicked(); void processor_active_changed (); void processor_property_changed (const PBD::PropertyChange&); + void processor_configuration_changed (const ARDOUR::ChanCount in, const ARDOUR::ChanCount out); std::string name (Width) const; void setup_tooltip (); @@ -159,6 +160,7 @@ private: Gtk::StateType _visual_state; PBD::ScopedConnection active_connection; PBD::ScopedConnection name_connection; + PBD::ScopedConnection config_connection; class Control : public sigc::trackable { public: @@ -204,12 +206,49 @@ private: std::list<Control*> _controls; void toggle_control_visibility (Control *); + + class PortIcon : public Gtk::DrawingArea { + public: + PortIcon(bool input) { + _input = input; + _ports = ARDOUR::ChanCount(ARDOUR::DataType::AUDIO, 1); + set_size_request (-1, 3); + } + void set_ports(ARDOUR::ChanCount const ports) { _ports = ports; } + private: + bool on_expose_event (GdkEventExpose *); + bool _input; + ARDOUR::ChanCount _ports; + }; + + class RoutingIcon : public Gtk::DrawingArea { + public: + RoutingIcon() { + _sources = ARDOUR::ChanCount(ARDOUR::DataType::AUDIO, 1); + _sinks = ARDOUR::ChanCount(ARDOUR::DataType::AUDIO, 1); + _splitting = false; + set_size_request (-1, 4); + } + void set_sources(ARDOUR::ChanCount const sources) { _sources = sources; } + void set_sinks(ARDOUR::ChanCount const sinks) { _sinks = sinks; } + void set_splitting(const bool splitting) { _splitting = splitting; } + private: + bool on_expose_event (GdkEventExpose *); + ARDOUR::ChanCount _sources; // signals available to feed into the processor(s) + ARDOUR::ChanCount _sinks; // combined number of outputs of the processor + bool _splitting; + }; + +protected: + RoutingIcon _routing_icon; + PortIcon _input_icon; + PortIcon _output_icon; }; class BlankProcessorEntry : public ProcessorEntry { public: - BlankProcessorEntry (ProcessorBox *, Width w); + BlankProcessorEntry (ProcessorBox *, Width w, ARDOUR::ChanCount cc); }; class PluginInsertProcessorEntry : public ProcessorEntry @@ -220,25 +259,9 @@ public: void hide_things (); private: - void setup_visuals (); void plugin_insert_splitting_changed (); - - class SplittingIcon : public Gtk::DrawingArea { - public: - SplittingIcon() { - _inputs = ARDOUR::ChanCount(ARDOUR::DataType::AUDIO, 1); - _outputs = ARDOUR::ChanCount(ARDOUR::DataType::AUDIO, 2); - } - void set_inputs(ARDOUR::ChanCount const inputs) { _inputs = inputs; } - void set_outputs(ARDOUR::ChanCount const outputs) { _outputs = outputs; } - private: - bool on_expose_event (GdkEventExpose *); - ARDOUR::ChanCount _inputs; - ARDOUR::ChanCount _outputs; - }; - boost::shared_ptr<ARDOUR::PluginInsert> _plugin_insert; - SplittingIcon _splitting_icon; + PBD::ScopedConnection _splitting_connection; }; @@ -349,11 +372,12 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD bool processor_button_release_event (GdkEventButton *, ProcessorEntry *); void redisplay_processors (); void add_processor_to_display (boost::weak_ptr<ARDOUR::Processor>); - void help_count_visible_prefader_processors (boost::weak_ptr<ARDOUR::Processor>, uint32_t*, bool*); + void help_count_visible_prefader_processors (boost::weak_ptr<ARDOUR::Processor>, uint32_t*, bool*, ARDOUR::ChanCount*); void reordered (); void report_failed_reorder (); void route_processors_changed (ARDOUR::RouteProcessorChange); void processor_menu_unmapped (); + void io_changed_proxy (); void processors_reordered (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&, int*); void compute_processor_sort_keys (); @@ -434,6 +458,7 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD void mixer_strip_delivery_changed (boost::weak_ptr<ARDOUR::Delivery>); XMLNode* entry_gui_object_state (ProcessorEntry *); + PBD::ScopedConnection amp_config_connection; }; #endif /* __ardour_gtk_processor_box__ */ |