_solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
_mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ()));
+ _phase_control.reset (new PhaseControllable (X_("phase"), shared_from_this ()));
_solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
_mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
+ _phase_control->set_flags (Controllable::Flag (_phase_control->flags() | Controllable::Toggle));
add_control (_solo_control);
add_control (_mute_control);
+ add_control (_phase_control);
/* panning */
_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);
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 */
}
{
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
_self_solo = yn;
+ set_mute_master_solo ();
}
void
if ((*p)->can_support_io_configuration(in, out)) {
DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
configuration.push_back(make_pair(in, out));
+
+ if (is_monitor()) {
+ // restriction for Monitor Section Processors
+ if (in.n_audio() != out.n_audio() || out.n_midi() > 0) {
+ /* do not allow to add/remove channels (for now)
+ * The Monitor follows the master-bus and has no panner (unpan)
+ * but do allow processors with midi-in to be added (e.g VSTs with control that
+ * will remain unconnected)
+ */
+ DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration not allowed.\n");
+ return list<pair<ChanCount, ChanCount> > ();
+ }
+ if (boost::dynamic_pointer_cast<InternalSend> (*p)) {
+ // internal sends make no sense, only feedback
+ DEBUG_TRACE (DEBUG::Processors, "Monitor: No Sends allowed.\n");
+ return list<pair<ChanCount, ChanCount> > ();
+ }
+ if (boost::dynamic_pointer_cast<PortInsert> (*p)) {
+ /* External Sends can be problematic. one can add/remove ports
+ * there signal leaves the DAW to external monitors anyway, so there's
+ * no real use for allowing them here anyway.
+ */
+ DEBUG_TRACE (DEBUG::Processors, "Monitor: No External Sends allowed.\n");
+ return list<pair<ChanCount, ChanCount> > ();
+ }
+ if (boost::dynamic_pointer_cast<Send> (*p)) {
+ // ditto
+ DEBUG_TRACE (DEBUG::Processors, "Monitor: No Sends allowed.\n");
+ return list<pair<ChanCount, ChanCount> > ();
+ }
+ }
in = out;
} else {
if (err) {
XMLNode&
Route::state(bool full_state)
{
+ if (!_session._template_state_dir.empty()) {
+ assert (!full_state); // only for templates
+ foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), _session._template_state_dir));
+ }
+
XMLNode *node = new XMLNode("Route");
ProcessorList::iterator i;
char buf[32];
}
}
+ if (!_session._template_state_dir.empty()) {
+ foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), ""));
+ }
+
return *node;
}
void
Route::SoloControllable::set_value (double val)
+{
+ if (writable()) {
+ set_value_unchecked (val);
+ }
+}
+
+void
+Route::SoloControllable::set_value_unchecked (double val)
{
const bool bval = ((val >= 0.5) ? true : false);
/* Note we can not use AutomationControl::set_value here since it will emit
Changed(), but the value will not be correct to the observer. */
- bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
+ const bool to_list = _list && ((AutomationList*)_list.get ())->automation_write ();
+ const double where = _session.audible_frame ();
+ if (to_list) {
+ /* Note that we really need this:
+ * if (as == Touch && _list->in_new_write_pass ()) {
+ * alist->start_write_pass (_session.audible_frame ());
+ * }
+ * here in the case of the user calling from a GUI or whatever.
+ * Without the ability to distinguish between user and
+ * automation-initiated changes, we lose the "touch mute"
+ * behaviour we have in AutomationController::toggled ().
+ */
+ _list->set_in_write_pass (true, false, where);
+ }
- Control::set_double (muted, _session.transport_frame(), to_list);
+ Control::set_double (muted, where, to_list);
}
void
Route::MuteControllable::set_value (double val)
+{
+ if (writable()) {
+ set_value_unchecked (val);
+ }
+}
+
+void
+Route::MuteControllable::set_value_unchecked (double val)
{
const bool bval = ((val >= 0.5) ? true : false);
}
if (_list && ((AutomationList*)_list.get())->automation_playback()) {
+ // Set superficial/automation value to drive controller (and possibly record)
+ set_superficial_value (bval);
// Playing back automation, set route mute directly
r->set_mute (bval, this);
} else {
rl->push_back (r);
_session.set_mute (rl, bval, Session::rt_cleanup);
}
-
- // Set superficial/automation value to drive controller (and possibly record)
- set_superficial_value(bval);
}
double
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)
+ , _route (r)
+{
+ boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(PhaseAutomation)));
+ gl->set_interpolation(Evoral::ControlList::Discrete);
+ set_list (gl);
+}
+
+void
+Route::PhaseControllable::set_value (double v)
+{
+ boost::shared_ptr<Route> r = _route.lock ();
+ if (r->phase_invert().size()) {
+ if (v == 0 || (v < 1 && v > 0.9) ) {
+ r->set_phase_invert (_current_phase, false);
+ } else {
+ r->set_phase_invert (_current_phase, true);
+ }
+ }
+}
+
+double
+Route::PhaseControllable::get_value () const
+{
+ boost::shared_ptr<Route> r = _route.lock ();
+ return (double) r->phase_invert (_current_phase);
+}
+
+void
+Route::PhaseControllable::set_channel (uint32_t c)
+{
+ _current_phase = c;
+}
+
+uint32_t
+Route::PhaseControllable::channel () const
+{
+ return _current_phase;
+}
+
void
Route::set_block_size (pframes_t nframes)
{
}
}
+void
+Route::set_plugin_state_dir (boost::weak_ptr<Processor> p, const std::string& d)
+{
+ boost::shared_ptr<Processor> processor (p.lock ());
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
+ if (!pi) {
+ return;
+ }
+ pi->set_state_dir (d);
+}
int
Route::save_as_template (const string& path, const string& name)
{
+ std::string state_dir = path.substr (0, path.find_last_of ('.')); // strip template_suffix
+ PBD::Unwinder<std::string> uw (_session._template_state_dir, state_dir);
+
XMLNode& node (state (false));
+
XMLTree tree;
IO::set_name_in_state (*node.children().front(), name);
tree.set_root (&node);
- return tree.write (path.c_str());
+
+ /* return zero on success, non-zero otherwise */
+ return !tree.write (path.c_str());
}
* @param name New name.
*/
void
-Route::set_name_in_state (XMLNode& node, string const & name)
+Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playlist)
{
node.add_property (X_("name"), name);
} else if ((*i)->name() == X_("Diskstream")) {
- (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
+ if (rename_playlist) {
+ (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
+ }
(*i)->add_property (X_("name"), name);
}
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
+}