save/restore plugin state with track-template
[ardour.git] / libs / ardour / route.cc
index babec9ea741c394e441159b16f64f822fb8f0b4e..e24919a3b3198beef8c4b58affa5ef5833949c7b 100644 (file)
@@ -174,6 +174,11 @@ Route::init ()
        _amp.reset (new Amp (_session));
        add_processor (_amp, PostFader);
 
+       // amp should exist before amp controls
+       _group_gain_control.reset (new GroupGainControllable (X_("groupgain"), shared_from_this ()));
+       _group_gain_control->set_flags (Controllable::Flag (_group_gain_control->flags() | Controllable::GainLike));
+       add_control (_group_gain_control);
+
        /* and input trim */
        _trim.reset (new Amp (_session, "trim"));
        _trim->set_display_to_user (false);
@@ -887,7 +892,6 @@ Route::set_solo (bool yn, void *src, bool group_override)
 
        if (self_soloed() != yn) {
                set_self_solo (yn);
-               set_mute_master_solo ();
                solo_changed (true, src, group_override); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
@@ -908,6 +912,7 @@ Route::set_self_solo (bool yn)
 {
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
        _self_solo = yn;
+       set_mute_master_solo ();
 }
 
 void
@@ -3973,12 +3978,41 @@ Route::MuteControllable::get_value () const
        return (r && r->muted()) ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
 }
 
+Route::GroupGainControllable::GroupGainControllable (std::string name, boost::shared_ptr<Route> r)
+       : AutomationControl (r->session(),
+                                               Evoral::Parameter (GainAutomation),
+                                               ParameterDescriptor (Evoral::Parameter (GainAutomation)),
+                                               boost::shared_ptr<AutomationList>(),
+                                               name)
+       , _route (r)
+{
+       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(GainAutomation)));
+       gl->set_interpolation(Evoral::ControlList::Discrete);
+       set_list (gl);
+}
+
+void
+Route::GroupGainControllable::set_value (double val)
+{
+       boost::shared_ptr<Route> r = _route.lock ();
+       // I am not sure why I need the * .5 to make this work
+       float normalized_position = r->gain_control()->interface_to_internal (val * 0.5);
+       r->set_gain ((gain_t)normalized_position, this);
+}
+
+double
+Route::GroupGainControllable::get_value () const
+{
+       boost::shared_ptr<Route> r = _route.lock ();
+       return 2.0 * r->gain_control()->internal_to_interface (r->gain_control()->get_value ());
+}
+
 Route::PhaseControllable::PhaseControllable (std::string name, boost::shared_ptr<Route> r)
        : AutomationControl (r->session(),
-                            Evoral::Parameter (PhaseAutomation),
-                            ParameterDescriptor (Evoral::Parameter (PhaseAutomation)),
-                            boost::shared_ptr<AutomationList>(),
-                            name)
+                                               Evoral::Parameter (PhaseAutomation),
+                                               ParameterDescriptor (Evoral::Parameter (PhaseAutomation)),
+                                               boost::shared_ptr<AutomationList>(),
+                                               name)
        , _route (r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(PhaseAutomation)));
@@ -4124,12 +4158,39 @@ Route::shift (framepos_t pos, framecnt_t frames)
 int
 Route::save_as_template (const string& path, const string& name)
 {
+       {
+               // would be nice to use foreach_processor()
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               std::string state_dir = path.substr (0, path.find_last_of ('.')); // strip template_suffix
+               for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi  = boost::dynamic_pointer_cast<PluginInsert> (*i);
+                       if (pi) {
+                               pi->set_state_dir (state_dir);
+                       }
+               }
+       }
+
        XMLNode& node (state (false));
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi  = boost::dynamic_pointer_cast<PluginInsert> (*i);
+                       if (pi) {
+                               pi->set_state_dir ();
+                       }
+               }
+       }
+
        XMLTree tree;
 
        IO::set_name_in_state (*node.children().front(), name);
 
        tree.set_root (&node);
+       // TODO: special case LV2 plugin state
+       // copy of serialize it. Alternatively:
+       // create a plugin-preset (which can be loaded separately)
+
        /* return zero on success, non-zero otherwise */
        return !tree.write (path.c_str());
 }
@@ -4998,3 +5059,362 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
                bufs.set_count (io->n_ports());
        }
 }
+
+boost::shared_ptr<AutomationControl>
+Route::pan_azimuth_control() const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+       assert (plug);
+       const uint32_t port_channel_post_pan = 2; // gtk2_ardour/mixbus_ports.h
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_pan)));
+#else
+       if (!_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+       return _pannable->pan_azimuth_control;
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::pan_elevation_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanElevationAutomation) != c.end()) {
+               return _pannable->pan_elevation_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+boost::shared_ptr<AutomationControl>
+Route::pan_width_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanWidthAutomation) != c.end()) {
+               return _pannable->pan_width_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+boost::shared_ptr<AutomationControl>
+Route::pan_frontback_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanFrontBackAutomation) != c.end()) {
+               return _pannable->pan_frontback_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+boost::shared_ptr<AutomationControl>
+Route::pan_lfe_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanLFEAutomation) != c.end()) {
+               return _pannable->pan_lfe_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+
+uint32_t
+Route::eq_band_cnt () const
+{
+       if (Profile->get_mixbus()) {
+               return 3;
+       } else {
+               /* Ardour has no well-known EQ object */
+               return 0;
+       }
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_gain_controllable (uint32_t band) const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       uint32_t port_number;
+       switch (band) {
+       case 0:
+               if (is_master() || mixbus()) {
+                       port_number = 4;
+               } else {
+                       port_number = 8;
+               }
+               break;
+       case 1:
+               if (is_master() || mixbus()) {
+                       port_number = 3;
+               } else {
+                       port_number = 6;
+               }
+               break;
+       case 2:
+               if (is_master() || mixbus()) {
+                       port_number = 2;
+               } else {
+                       port_number = 4;
+               }
+               break;
+       default:
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::eq_freq_controllable (uint32_t band) const
+{
+#ifdef MIXBUS
+
+       if (mixbus() || is_master()) {
+               /* no frequency controls for mixbusses or master */
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       uint32_t port_number;
+       switch (band) {
+       case 0:
+               port_number = 7;
+               break;
+       case 1:
+               port_number = 5;
+               break;
+       case 2:
+               port_number = 3;
+               break;
+       default:
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_q_controllable (uint32_t band) const
+{
+       return boost::shared_ptr<AutomationControl>();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_shape_controllable (uint32_t band) const
+{
+       return boost::shared_ptr<AutomationControl>();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_enable_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 1)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_hpf_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+string
+Route::eq_band_name (uint32_t band) const
+{
+       if (Profile->get_mixbus()) {
+               switch (band) {
+               case 0:
+                       return _("lo");
+               case 1:
+                       return _("mid");
+               case 2:
+                       return _("hi");
+               default:
+                       return string();
+               }
+       } else {
+               return string ();
+       }
+}
+
+boost::shared_ptr<AutomationControl>
+Route::comp_enable_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 1)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_threshold_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2)));
+
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_speed_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 3)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_mode_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 4)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_makeup_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 5)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_redux_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+string
+Route::comp_mode_name (uint32_t mode) const
+{
+#ifdef MIXBUS
+       switch (mode) {
+       case 0:
+               return _("Leveler");
+       case 1:
+               return _("Compressor");
+       case 2:
+               return _("Limiter");
+       }
+
+       return _("???");
+#else
+       return _("???");
+#endif
+}
+
+string
+Route::comp_speed_name (uint32_t mode) const
+{
+#ifdef MIXBUS
+       switch (mode) {
+       case 0:
+               return _("Attk");
+       case 1:
+               return _("Ratio");
+       case 2:
+               return _("Rels");
+       }
+       return _("???");
+#else
+       return _("???");
+#endif
+}