#include "pbd/xml++.h"
#include "pbd/enumwriter.h"
+#include "pbd/locale_guard.h"
#include "pbd/memento_command.h"
#include "pbd/stacktrace.h"
-#include "pbd/convert.h"
-#include "pbd/boost_debug.h"
+#include "pbd/types_convert.h"
#include "pbd/unwind.h"
#include "ardour/amp.h"
#include "ardour/audio_track.h"
#include "ardour/audio_port.h"
#include "ardour/audioengine.h"
+#include "ardour/boost_debug.h"
#include "ardour/buffer.h"
#include "ardour/buffer_set.h"
#include "ardour/capturing_processor.h"
#include "ardour/debug.h"
#include "ardour/delivery.h"
+#include "ardour/disk_reader.h"
+#include "ardour/disk_writer.h"
+#include "ardour/event_type_map.h"
#include "ardour/gain_control.h"
#include "ardour/internal_return.h"
#include "ardour/internal_send.h"
#include "ardour/delayline.h"
#include "ardour/midi_buffer.h"
#include "ardour/midi_port.h"
+#include "ardour/monitor_control.h"
#include "ardour/monitor_processor.h"
#include "ardour/pannable.h"
#include "ardour/panner.h"
#include "ardour/panner_shell.h"
#include "ardour/parameter_descriptor.h"
+#include "ardour/phase_control.h"
#include "ardour/plugin_insert.h"
#include "ardour/port.h"
#include "ardour/port_insert.h"
#include "ardour/route_group.h"
#include "ardour/send.h"
#include "ardour/session.h"
+#include "ardour/solo_control.h"
+#include "ardour/solo_isolate_control.h"
+#include "ardour/types_convert.h"
#include "ardour/unknown_processor.h"
#include "ardour/utils.h"
+#include "ardour/vca.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
-PBD::Signal0<void> Route::SyncOrderKeys;
-PBD::Signal0<void> Route::RemoteControlIDChange;
+PBD::Signal3<int,boost::shared_ptr<Route>, boost::shared_ptr<PluginInsert>, Route::PluginSetupOptions > Route::PluginSetup;
/** Base class for all routable/mixable objects (tracks and busses) */
-Route::Route (Session& sess, string name, Flag flg, DataType default_type)
- : SessionObject (sess, name)
- , Automatable (sess)
+Route::Route (Session& sess, string name, PresentationInfo::Flag flag, DataType default_type)
+ : Stripable (sess, name, PresentationInfo (flag))
, GraphNode (sess._process_graph)
+ , Muteable (sess, name)
, _active (true)
, _signal_latency (0)
- , _signal_latency_at_amp_position (0)
- , _signal_latency_at_trim_position (0)
, _initial_delay (0)
- , _roll_delay (0)
+ , _disk_io_point (DiskIOPreFader)
, _pending_process_reorder (0)
, _pending_signals (0)
- , _flags (flg)
, _pending_declick (true)
, _meter_point (MeterPostFader)
, _pending_meter_point (MeterPostFader)
, _meter_type (MeterPeak)
- , _self_solo (false)
- , _soloed_by_others_upstream (0)
- , _soloed_by_others_downstream (0)
- , _solo_isolated (false)
- , _solo_isolated_by_upstream (0)
, _denormal_protection (false)
, _recordable (true)
, _silent (false)
, _declickable (false)
- , _mute_master (new MuteMaster (sess, name))
, _have_internal_generator (false)
- , _solo_safe (false)
, _default_type (default_type)
- , _order_key (0)
- , _has_order_key (false)
- , _remote_control_id (0)
, _track_number (0)
, _in_configure_processors (false)
, _initial_io_setup (false)
, _in_sidechain_setup (false)
, _strict_io (false)
, _custom_meter_position_noted (false)
+ , _pinmgr_proxy (0)
+ , _patch_selector_dialog (0)
{
processor_max_streams.reset();
}
+boost::weak_ptr<Route>
+Route::weakroute () {
+ return boost::weak_ptr<Route> (boost::dynamic_pointer_cast<Route> (shared_from_this ()));
+}
+
int
Route::init ()
{
/* add standard controls */
- _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_isolate_control.reset (new SoloIsolateControllable (X_("solo-iso"), shared_from_this ()));
- _solo_safe_control.reset (new SoloSafeControllable (X_("solo-safe"), 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));
+ _gain_control.reset (new GainControl (_session, GainAutomation));
+ _trim_control.reset (new GainControl (_session, TrimAutomation));
+ /* While the route has-a gain-control for consistency with Stripable and VCA
+ * ownership is handed over to the Amp Processor which manages the
+ * state of the Control and AutomationList as part of its
+ * Automatable API. -- Don't call add_control () here.
+ */
+ _solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this));
add_control (_solo_control);
+ _solo_control->Changed.connect_same_thread (*this, boost::bind (&Route::solo_control_changed, this, _1, _2));
+
+ _mute_control.reset (new MuteControl (_session, X_("mute"), *this));
add_control (_mute_control);
+
+ _phase_control.reset (new PhaseControl (_session, X_("phase")));
add_control (_phase_control);
+ _solo_isolate_control.reset (new SoloIsolateControl (_session, X_("solo-iso"), *this, *this));
+ add_control (_solo_isolate_control);
+
+ _solo_safe_control.reset (new SoloSafeControl (_session, X_("solo-safe")));
+ add_control (_solo_safe_control);
+
/* panning */
- if (!(_flags & Route::MonitorOut)) {
+ if (!(_presentation_info.flags() & PresentationInfo::MonitorOut)) {
_pannable.reset (new Pannable (_session));
}
_output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
_output->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::output_port_count_changing, this, _1));
-#if 0 // not used - just yet
- if (!is_master() && !is_monitor() && !is_auditioner()) {
- _delayline.reset (new DelayLine (_session, _name));
- add_processor (_delayline, PreFader);
- }
-#endif
-
- /* add amp processor */
-
- _gain_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, GainAutomation, shared_from_this ()));
- add_control (_gain_control);
+ /* add the amp/fader processor.
+ * it should be the first processor to be added on every route.
+ */
_amp.reset (new Amp (_session, X_("Fader"), _gain_control, true));
add_processor (_amp, PostFader);
_amp->set_display_name (_("Monitor"));
}
- /* and input trim */
+#if 0 // not used - just yet
+ if (!is_master() && !is_monitor() && !is_auditioner()) {
+ _delayline.reset (new DelayLine (_session, _name));
+ add_processor (_delayline, PreFader);
+ }
+#endif
- _trim_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, TrimAutomation, shared_from_this ()));
- add_control (_trim_control);
+ /* and input trim */
_trim.reset (new Amp (_session, X_("Trim"), _trim_control, false));
_trim->set_display_to_user (false);
_monitor_control->activate ();
}
- if (is_master() || is_monitor() || is_auditioner()) {
- _mute_master->set_solo_ignore (true);
- }
-
/* now that we have _meter, its safe to connect to this */
{
_processors.clear ();
}
-void
-Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
-{
- if (Config->get_remote_model() != UserOrdered) {
- return;
- }
-
- set_remote_control_id_internal (id, notify_class_listeners);
-}
-
-void
-Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
-{
- /* force IDs for master/monitor busses and prevent
- any other route from accidentally getting these IDs
- (i.e. legacy sessions)
- */
-
- if (is_master() && id != MasterBusRemoteControlID) {
- id = MasterBusRemoteControlID;
- }
-
- if (is_monitor() && id != MonitorBusRemoteControlID) {
- id = MonitorBusRemoteControlID;
- }
-
- if (id < 1) {
- return;
- }
-
- /* don't allow it to collide */
-
- if (!is_master () && !is_monitor() &&
- (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) {
- id += MonitorBusRemoteControlID;
- }
-
- if (id != remote_control_id()) {
- _remote_control_id = id;
- RemoteControlIDChanged ();
-
- if (notify_class_listeners) {
- RemoteControlIDChange ();
- }
- }
-}
-
-uint32_t
-Route::remote_control_id() const
-{
- if (is_master()) {
- return MasterBusRemoteControlID;
- }
-
- if (is_monitor()) {
- return MonitorBusRemoteControlID;
- }
-
- return _remote_control_id;
-}
-
-bool
-Route::has_order_key () const
-{
- return _has_order_key;
-}
-
-uint32_t
-Route::order_key () const
-{
- return _order_key;
-}
-
-void
-Route::set_remote_control_id_explicit (uint32_t rid)
-{
- if (is_master() || is_monitor() || is_auditioner()) {
- /* hard-coded remote IDs, or no remote ID */
- return;
- }
-
- if (_remote_control_id != rid) {
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1: set edit-based RID to %2\n", name(), rid));
- _remote_control_id = rid;
- RemoteControlIDChanged (); /* EMIT SIGNAL (per-route) */
- }
-
- /* don't emit the class-level RID signal RemoteControlIDChange here,
- leave that to the entity that changed the order key, so that we
- don't get lots of emissions for no good reasons (e.g. when changing
- all route order keys).
-
- See Session::sync_remote_id_from_order_keys() for the (primary|only)
- spot where that is emitted.
- */
-}
-
-void
-Route::set_order_key (uint32_t n)
-{
- _has_order_key = true;
-
- if (_order_key == n) {
- return;
- }
-
- _order_key = n;
-
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 order key set to %2\n",
- name(), order_key ()));
-
- _session.set_dirty ();
-}
-
string
Route::ensure_track_or_route_name(string name, Session &session)
{
return newname;
}
-void
-Route::inc_gain (gain_t factor)
-{
- /* To be used ONLY when doing group-relative gain adjustment, from
- * ::set_gain()
- */
-
- float desired_gain = _gain_control->user_double();
-
- if (fabsf (desired_gain) < GAIN_COEFF_SMALL) {
- // really?! what's the idea here?
- _gain_control->route_set_value (0.000001f + (0.000001f * factor));
- } else {
- _gain_control->route_set_value (desired_gain + (desired_gain * factor));
- }
-}
-
-void
-Route::set_gain (gain_t val, Controllable::GroupControlDisposition group_override)
-{
- if (use_group (group_override, &RouteGroup::is_gain)) {
-
- if (_route_group->is_relative()) {
-
- gain_t usable_gain = _gain_control->get_value();
- if (usable_gain < 0.000001f) {
- usable_gain = 0.000001f;
- }
-
- gain_t delta = val;
- if (delta < 0.000001f) {
- delta = 0.000001f;
- }
-
- delta -= usable_gain;
-
- if (delta == 0.0f)
- return;
-
- gain_t factor = delta / usable_gain;
-
- if (factor > 0.0f) {
- factor = _route_group->get_max_factor(factor);
- if (factor == 0.0f) {
- _amp->gain_control()->Changed(); /* EMIT SIGNAL */
- return;
- }
- } else {
- factor = _route_group->get_min_factor(factor);
- if (factor == 0.0f) {
- _amp->gain_control()->Changed(); /* EMIT SIGNAL */
- return;
- }
- }
-
- _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor));
-
- } else {
-
- _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, Controllable::NoGroup));
- }
-
- return;
- }
-
- if (val == _gain_control->get_value()) {
- return;
- }
-
- _gain_control->route_set_value (val);
-}
-
void
Route::set_trim (gain_t val, Controllable::GroupControlDisposition /* group override */)
{
// TODO route group, see set_gain()
- _trim_control->route_set_value (val);
+ // _trim_control->route_set_value (val);
}
void
-Route::maybe_declick (BufferSet&, framecnt_t, int)
+Route::maybe_declick (BufferSet&, samplecnt_t, int)
{
/* this is the "bus" implementation and they never declick.
*/
/** Process this route for one (sub) cycle (process thread)
*
* @param bufs Scratch buffers to use for the signal path
- * @param start_frame Initial transport frame
- * @param end_frame Final transport frame
- * @param nframes Number of frames to output (to ports)
+ * @param start_sample Initial transport sample
+ * @param end_sample Final transport sample
+ * @param nframes Number of samples to output (to ports)
*
- * Note that (end_frame - start_frame) may not be equal to nframes when the
+ * Note that (end_sample - start_sample) may not be equal to nframes when the
* transport speed isn't 1.0 (eg varispeed).
*/
void
Route::process_output_buffers (BufferSet& bufs,
- framepos_t start_frame, framepos_t end_frame, pframes_t nframes,
+ samplepos_t start_sample, samplepos_t end_sample, pframes_t nframes,
int declick, bool gain_automation_ok)
{
/* Caller must hold process lock */
return;
}
+ automation_run (start_sample, nframes);
+
/* figure out if we're going to use gain automation */
if (gain_automation_ok) {
_amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
_amp->setup_gain_automation (
- start_frame + _signal_latency_at_amp_position,
- end_frame + _signal_latency_at_amp_position,
+ start_sample + _amp->output_latency (),
+ end_sample + _amp->output_latency (),
nframes);
_trim->set_gain_automation_buffer (_session.trim_automation_buffer ());
_trim->setup_gain_automation (
- start_frame + _signal_latency_at_trim_position,
- end_frame + _signal_latency_at_trim_position,
+ start_sample + _trim->output_latency (),
+ end_sample + _trim->output_latency (),
nframes);
- } else {
- _amp->apply_gain_automation (false);
- _trim->apply_gain_automation (false);
}
/* Tell main outs what to do about monitoring. We do this so that
DENORMAL CONTROL/PHASE INVERT
----------------------------------------------------------------------------------------- */
- if (_phase_invert.any ()) {
+ if (!_phase_control->none()) {
int chn = 0;
for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
Sample* const sp = i->data();
- if (_phase_invert[chn]) {
+ if (_phase_control->inverted (chn)) {
for (pframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] = -sp[nx];
sp[nx] += 1.0e-27f;
for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
Sample* const sp = i->data();
- if (_phase_invert[chn]) {
+ if (_phase_control->inverted (chn)) {
for (pframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] = -sp[nx];
}
/* set this to be true if the meter will already have been ::run() earlier */
bool const meter_already_run = metering_state() == MeteringInput;
- framecnt_t latency = 0;
+ samplecnt_t latency = 0;
+ const double speed = (is_auditioner() ? 1.0 : _session.transport_speed ());
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (boost::dynamic_pointer_cast<Send>(*i) != 0) {
boost::dynamic_pointer_cast<Send>(*i)->set_delay_in(_signal_latency - latency);
}
+ if (boost::dynamic_pointer_cast<PluginInsert>(*i) != 0) {
+ const samplecnt_t longest_session_latency = _initial_delay + _signal_latency;
+ boost::dynamic_pointer_cast<PluginInsert>(*i)->set_sidechain_latency (
+ _initial_delay + latency, longest_session_latency - latency);
+ }
- (*i)->run (bufs, start_frame - latency, end_frame - latency, nframes, *i != _processors.back());
+ //cerr << name() << " run " << (*i)->name() << endl;
+ (*i)->run (bufs, start_sample - latency, end_sample - latency, speed, nframes, *i != _processors.back());
bufs.set_count ((*i)->output_streams());
if ((*i)->active ()) {
}
void
-Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
+Route::bounce_process (BufferSet& buffers, samplepos_t start, samplecnt_t nframes,
boost::shared_ptr<Processor> endpoint,
bool include_endpoint, bool for_export, bool for_freeze)
{
return;
}
- framecnt_t latency = bounce_get_latency(_amp, false, for_export, for_freeze);
+ samplecnt_t latency = bounce_get_latency(_amp, false, for_export, for_freeze);
_amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
_amp->setup_gain_automation (start - latency, start - latency + nframes, nframes);
_trim->setup_gain_automation (start, start + nframes, nframes);
latency = 0;
+ const double speed = _session.transport_speed ();
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (!include_endpoint && (*i) == endpoint) {
*/
if ((*i) == _main_outs) {
assert ((*i)->does_routing());
- (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
+ (*i)->run (buffers, start - latency, start - latency + nframes, speed, nframes, true);
buffers.set_count ((*i)->output_streams());
}
* Also don't bother with metering.
*/
if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
- (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
+ (*i)->run (buffers, start - latency, start - latency + nframes, 1.0, nframes, true);
buffers.set_count ((*i)->output_streams());
latency += (*i)->signal_latency ();
}
}
}
-framecnt_t
+samplecnt_t
Route::bounce_get_latency (boost::shared_ptr<Processor> endpoint,
bool include_endpoint, bool for_export, bool for_freeze) const
{
- framecnt_t latency = 0;
+ samplecnt_t latency = 0;
if (!endpoint && !include_endpoint) {
return latency;
}
}
void
-Route::monitor_run (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
+Route::monitor_run (samplepos_t start_sample, samplepos_t end_sample, pframes_t nframes, int declick)
{
assert (is_monitor());
BufferSet& bufs (_session.get_route_buffers (n_process_buffers()));
fill_buffers_with_input (bufs, _input, nframes);
- passthru (bufs, start_frame, end_frame, nframes, declick);
+ passthru (bufs, start_sample, end_sample, nframes, declick, true);
}
void
-Route::passthru (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
+Route::passthru (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, pframes_t nframes, int declick, bool gain_automation_ok)
{
_silent = false;
bufs.silence (nframes, 0);
}
- write_out_of_band_data (bufs, start_frame, end_frame, nframes);
- process_output_buffers (bufs, start_frame, end_frame, nframes, declick, true);
+ /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */
+
+ write_out_of_band_data (bufs, start_sample, end_sample, nframes);
+
+ /* run processor chain */
+
+ process_output_buffers (bufs, start_sample, end_sample, nframes, declick, gain_automation_ok);
}
void
-Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
+Route::passthru_silence (samplepos_t start_sample, samplepos_t end_sample, pframes_t nframes, int declick)
{
BufferSet& bufs (_session.get_route_buffers (n_process_buffers(), true));
bufs.set_count (_input->n_ports());
- write_out_of_band_data (bufs, start_frame, end_frame, nframes);
- process_output_buffers (bufs, start_frame, end_frame, nframes, declick, false);
+ write_out_of_band_data (bufs, start_sample, end_sample, nframes);
+ process_output_buffers (bufs, start_sample, end_sample, nframes, declick, false);
}
void
-Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override)
+Route::set_listen (bool yn)
{
- if (_solo_safe) {
- return;
- }
-
- if (use_group (group_override, &RouteGroup::is_solo)) {
- _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup));
- return;
- }
-
if (_monitor_send) {
- if (yn != _monitor_send->active()) {
- if (yn) {
- _monitor_send->activate ();
- _mute_master->set_soloed_by_self (true);
- } else {
- _monitor_send->deactivate ();
- _mute_master->set_soloed_by_self (false);
- }
- _mute_master->set_soloed_by_others (false);
-
- listen_changed (group_override); /* EMIT SIGNAL */
+ if (_monitor_send->active() == yn) {
+ return;
+ }
+ if (yn) {
+ _monitor_send->activate ();
+ } else {
+ _monitor_send->deactivate ();
}
- }
-}
-
-bool
-Route::listening_via_monitor () const
-{
- if (_monitor_send) {
- return _monitor_send->active ();
- } else {
- return false;
}
}
void
-Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition /* group_override */)
+Route::solo_control_changed (bool, Controllable::GroupControlDisposition)
{
- if (_solo_safe != yn) {
- _solo_safe = yn;
- solo_safe_changed (); /* EMIT SIGNAL */
- _solo_safe_control->Changed(); /* EMIT SIGNAL */
- }
-}
+ /* nothing to do if we're not using AFL/PFL. But if we are, we need
+ to alter the active state of the monitor send.
+ */
-bool
-Route::solo_safe() const
-{
- return _solo_safe;
+ if (Config->get_solo_control_is_listen_control ()) {
+ set_listen (_solo_control->self_soloed() || _solo_control->get_masters_value());
+ }
}
void
-Route::clear_all_solo_state ()
+Route::push_solo_isolate_upstream (int32_t delta)
{
- // ideally this function will never do anything, it only exists to forestall Murphy
- bool emit_changed = false;
-
-#ifndef NDEBUG
- // these are really debug messages, but of possible interest.
- if (_self_solo) {
- PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name());
- }
- if (_soloed_by_others_upstream || _soloed_by_others_downstream) {
- PBD::info << string_compose (_("Cleared Implicit solo: %1 up:%2 down:%3\n"),
- name(), _soloed_by_others_upstream, _soloed_by_others_downstream);
- }
-#endif
+ /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
- if (!_self_solo && (_soloed_by_others_upstream || _soloed_by_others_downstream)) {
- // if self-soled, set_solo() will do signal emission
- emit_changed = true;
- }
+ boost::shared_ptr<RouteList> routes = _session.get_routes ();
+ for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
- _soloed_by_others_upstream = 0;
- _soloed_by_others_downstream = 0;
+ if ((*i).get() == this || !(*i)->can_solo()) {
+ continue;
+ }
- {
- PBD::Unwinder<bool> uw (_solo_safe, false);
- set_solo (false, Controllable::NoGroup);
- }
+ bool sends_only;
+ bool does_feed = feeds (*i, &sends_only);
- if (emit_changed) {
- set_mute_master_solo ();
- solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
+ if (does_feed && !sends_only) {
+ (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (delta);
+ }
}
}
void
-Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override)
+Route::push_solo_upstream (int delta)
{
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n",
- name(), yn, enum_2_string(group_override), self_soloed()));
-
- if (_solo_safe) {
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name()));
- return;
- }
-
- if (is_master() || is_monitor() || is_auditioner()) {
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change (master, monitor or auditioner)\n", name()));
- return;
- }
-
- if (use_group (group_override, &RouteGroup::is_solo)) {
- _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup));
- return;
- }
-
- if (self_soloed() != yn) {
- set_self_solo (yn);
- solo_changed (true, group_override); /* EMIT SIGNAL */
- _solo_control->Changed (); /* EMIT SIGNAL */
- }
-
- assert (Config->get_solo_control_is_listen_control() || !_monitor_send || !_monitor_send->active());
-
- /* XXX TRACKS DEVELOPERS: THIS LOGIC SUGGESTS THAT YOU ARE NOT AWARE OF
- Config->get_solo_mute_overrride().
- */
-
- if (yn && Profile->get_trx()) {
- set_mute (false, Controllable::UseGroup);
+ DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
+ for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+ if (i->sends_only) {
+ continue;
+ }
+ boost::shared_ptr<Route> sr (i->r.lock());
+ if (sr) {
+ sr->solo_control()->mod_solo_by_others_downstream (-delta);
+ }
}
}
-void
-Route::set_self_solo (bool yn)
+#if 0
+static void
+dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
{
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
- _self_solo = yn;
- set_mute_master_solo ();
+ cerr << name << " {" << endl;
+ for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin();
+ p != procs.end(); ++p) {
+ cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << " @ " << (*p) << endl;
+ }
+ cerr << "}" << endl;
}
+#endif
-void
-Route::mod_solo_by_others_upstream (int32_t delta)
+/** Supposing that we want to insert a Processor at a given Placement, return
+ * the processor to add the new one before (or 0 to add at the end).
+ */
+boost::shared_ptr<Processor>
+Route::before_processor_for_placement (Placement p)
{
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n",
- name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
- uint32_t old_sbu = _soloed_by_others_upstream;
+ ProcessorList::iterator loc;
- if (delta < 0) {
- if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) {
- _soloed_by_others_upstream += delta;
- } else {
- _soloed_by_others_upstream = 0;
- }
+ if (p == PreFader) {
+ /* generic pre-fader: insert immediately before the amp */
+ loc = find (_processors.begin(), _processors.end(), _amp);
} else {
- _soloed_by_others_upstream += delta;
+ /* generic post-fader: insert right before the main outs */
+ loc = find (_processors.begin(), _processors.end(), _main_outs);
}
- DEBUG_TRACE (DEBUG::Solo, string_compose (
- "%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n",
- name(), delta, _soloed_by_others_upstream, old_sbu,
- _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo()));
-
- /* push the inverse solo change to everything that feeds us.
-
- This is important for solo-within-group. When we solo 1 track out of N that
- feed a bus, that track will cause mod_solo_by_upstream (+1) to be called
- on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all
- tracks that feed it. This will silence them if they were audible because
- of a bus solo, but the newly soloed track will still be audible (because
- it is self-soloed).
+ return loc != _processors.end() ? *loc : boost::shared_ptr<Processor> ();
+}
- but .. do this only when we are being told to solo-by-upstream (i.e delta = +1),
- not in reverse.
- */
+/** Supposing that we want to insert a Processor at a given index, return
+ * the processor to add the new one before (or 0 to add at the end).
+ */
+boost::shared_ptr<Processor>
+Route::before_processor_for_index (int index)
+{
+ if (index == -1) {
+ return boost::shared_ptr<Processor> ();
+ }
- if ((_self_solo || _soloed_by_others_downstream) &&
- ((old_sbu == 0 && _soloed_by_others_upstream > 0) ||
- (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
- if (delta > 0 || !Config->get_exclusive_solo()) {
- DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
- for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
- if (i->sends_only) {
- continue;
- }
- boost::shared_ptr<Route> sr = i->r.lock();
- if (sr) {
- sr->mod_solo_by_others_downstream (-delta);
- }
- }
+ ProcessorList::iterator i = _processors.begin ();
+ int j = 0;
+ while (i != _processors.end() && j < index) {
+ if ((*i)->display_to_user()) {
+ ++j;
}
+
+ ++i;
}
- set_mute_master_solo ();
- solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
-}
-
-void
-Route::mod_solo_by_others_downstream (int32_t delta)
-{
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n",
- name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
-
- if (delta < 0) {
- if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) {
- _soloed_by_others_downstream += delta;
- } else {
- _soloed_by_others_downstream = 0;
- }
- } else {
- _soloed_by_others_downstream += delta;
- }
-
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
-
- set_mute_master_solo ();
- solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
-}
-
-void
-Route::set_mute_master_solo ()
-{
- _mute_master->set_soloed_by_self (self_soloed());
- _mute_master->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream());
-}
-
-void
-Route::mod_solo_isolated_by_upstream (bool yn)
-{
- bool old = solo_isolated ();
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod_solo_isolated_by_upstream cur: %2 d: %3\n",
- name(), _solo_isolated_by_upstream, yn ? "+1" : "-1"));
-
- if (!yn) {
- if (_solo_isolated_by_upstream >= 1) {
- _solo_isolated_by_upstream--;
- } else {
- _solo_isolated_by_upstream = 0;
- }
- } else {
- _solo_isolated_by_upstream++;
- }
-
- if (solo_isolated() != old) {
- /* solo isolated status changed */
- _mute_master->set_solo_ignore (solo_isolated());
- solo_isolated_changed (); /* EMIT SIGNAL */
- }
-}
-
-void
-Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override)
-{
- if (is_master() || is_monitor() || is_auditioner()) {
- return;
- }
-
- if (use_group (group_override, &RouteGroup::is_solo)) {
- _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::ForGroup));
- return;
- }
-
- bool changed = false;
-
- if (yn) {
- if (_solo_isolated == false) {
- _mute_master->set_solo_ignore (true);
- changed = true;
- }
- _solo_isolated = true;
- } else {
- if (_solo_isolated == true) {
- _solo_isolated = false;
- _mute_master->set_solo_ignore (false);
- changed = true;
- }
- }
-
-
- if (!changed) {
- return;
- }
-
- /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
-
- boost::shared_ptr<RouteList> routes = _session.get_routes ();
- for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
-
- if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
- continue;
- }
-
- bool sends_only;
- bool does_feed = feeds (*i, &sends_only);
-
- if (does_feed && !sends_only) {
- (*i)->mod_solo_isolated_by_upstream (yn);
- }
- }
-
- /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
-
- solo_isolated_changed (); /* EMIT SIGNAL */
- _solo_isolate_control->Changed(); /* EMIT SIGNAL */
-}
-
-bool
-Route::solo_isolated () const
-{
- return (_solo_isolated == true) || (_solo_isolated_by_upstream > 0);
-}
-
-void
-Route::set_mute_points (MuteMaster::MutePoint mp)
-{
- _mute_master->set_mute_points (mp);
- mute_points_changed (); /* EMIT SIGNAL */
-
- if (_mute_master->muted_by_self()) {
- mute_changed (); /* EMIT SIGNAL */
- _mute_control->Changed (); /* EMIT SIGNAL */
- }
-}
-
-void
-Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override)
-{
- if (use_group (group_override, &RouteGroup::is_mute)) {
- _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::ForGroup));
- return;
- }
-
- if (muted() != yn) {
- _mute_master->set_muted_by_self (yn);
- /* allow any derived classes to respond to the mute change
- before anybody else knows about it.
- */
- act_on_mute ();
- /* tell everyone else */
- mute_changed (); /* EMIT SIGNAL */
- _mute_control->Changed (); /* EMIT SIGNAL */
- }
-}
-
-bool
-Route::muted () const
-{
- return _mute_master->muted_by_self();
-}
-
-bool
-Route::muted_by_others () const
-{
- // This method is only used by route_ui for display state.
- // The real thing is MuteMaster::muted_by_others_at()
-
- //master is never muted by others
- if (is_master())
- return false;
-
- //now check to see if something is soloed (and I am not)
- //see also MuteMaster::mute_gain_at()
- return (_session.soloing() && !soloed() && !solo_isolated());
-}
-
-#if 0
-static void
-dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
-{
- cerr << name << " {" << endl;
- for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin();
- p != procs.end(); ++p) {
- cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << " @ " << (*p) << endl;
- }
- cerr << "}" << endl;
-}
-#endif
-
-/** Supposing that we want to insert a Processor at a given Placement, return
- * the processor to add the new one before (or 0 to add at the end).
- */
-boost::shared_ptr<Processor>
-Route::before_processor_for_placement (Placement p)
-{
- Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-
- ProcessorList::iterator loc;
-
- if (p == PreFader) {
- /* generic pre-fader: insert immediately before the amp */
- loc = find (_processors.begin(), _processors.end(), _amp);
- } else {
- /* generic post-fader: insert right before the main outs */
- loc = find (_processors.begin(), _processors.end(), _main_outs);
- }
-
- return loc != _processors.end() ? *loc : boost::shared_ptr<Processor> ();
-}
-
-/** Supposing that we want to insert a Processor at a given index, return
- * the processor to add the new one before (or 0 to add at the end).
- */
-boost::shared_ptr<Processor>
-Route::before_processor_for_index (int index)
-{
- if (index == -1) {
- return boost::shared_ptr<Processor> ();
- }
-
- Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-
- ProcessorList::iterator i = _processors.begin ();
- int j = 0;
- while (i != _processors.end() && j < index) {
- if ((*i)->display_to_user()) {
- ++j;
- }
-
- ++i;
- }
-
- return (i != _processors.end() ? *i : boost::shared_ptr<Processor> ());
+ return (i != _processors.end() ? *i : boost::shared_ptr<Processor> ());
}
/** Add a processor either pre- or post-fader
DEBUG_TRACE (DEBUG::Processors, string_compose (
"%1 adding processor %2\n", name(), processor->name()));
- if (!AudioEngine::instance()->connected() || !processor) {
- return 1;
- }
-
- if (_strict_io) {
- boost::shared_ptr<PluginInsert> pi;
- if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
- pi->set_strict_io (true);
- }
- }
+ ProcessorList pl;
- {
- Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
- Glib::Threads::RWLock::WriterLock lm (_processor_lock);
- ProcessorState pstate (this);
+ pl.push_back (processor);
+ int rv = add_processors (pl, before, err);
- boost::shared_ptr<PluginInsert> pi;
- boost::shared_ptr<PortInsert> porti;
-
- if (processor == _amp) {
- /* Ensure that only one amp is in the list at any time */
- ProcessorList::iterator check = find (_processors.begin(), _processors.end(), processor);
- if (check != _processors.end()) {
- if (before == _amp) {
- /* Already in position; all is well */
- return 0;
- } else {
- _processors.erase (check);
- }
- }
- }
-
- assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ());
-
- ProcessorList::iterator loc;
- if (before) {
- /* inserting before a processor; find it */
- loc = find (_processors.begin(), _processors.end(), before);
- if (loc == _processors.end ()) {
- /* Not found */
- return 1;
- }
- } else {
- /* inserting at end */
- loc = _processors.end ();
- }
-
- _processors.insert (loc, processor);
- processor->set_owner (this);
-
- // Set up processor list channels. This will set processor->[input|output]_streams(),
- // configure redirect ports properly, etc.
-
- {
- if (configure_processors_unlocked (err)) {
- pstate.restore ();
- configure_processors_unlocked (0); // it worked before we tried to add it ...
- return -1;
- }
- }
-
- if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-
- if (pi->has_no_inputs ()) {
- /* generator plugin */
- _have_internal_generator = true;
- }
-
- }
-
- if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
- processor->activate ();
- }
-
- processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
-
- _output->set_user_latency (0);
+ if (rv) {
+ return rv;
}
- reset_instrument_info ();
- processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
- set_processor_positions ();
+ if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
+ processor->activate ();
+ }
return 0;
}
+void
+Route::processor_selfdestruct (boost::weak_ptr<Processor> wp)
+{
+ /* We cannot destruct the processor here (usually RT-thread
+ * with various locks held - in case of sends also io_locks).
+ * Queue for deletion in low-priority thread.
+ */
+ Glib::Threads::Mutex::Lock lx (selfdestruct_lock);
+ selfdestruct_sequence.push_back (wp);
+}
+
bool
Route::add_processor_from_xml_2X (const XMLNode& node, int version)
{
- const XMLProperty *prop;
+ XMLProperty const * prop;
try {
boost::shared_ptr<Processor> processor;
if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
prop->value() == "lv2" ||
prop->value() == "windows-vst" ||
+ prop->value() == "mac-vst" ||
prop->value() == "lxvst" ||
prop->value() == "audiounit") {
//A2 uses the "active" flag in the toplevel redirect node, not in the child plugin/IO
if (i != children.end()) {
if ((prop = (*i)->property (X_("active"))) != 0) {
- if ( string_is_affirmative (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) )
+ if ( string_to<bool> (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) )
processor->activate();
else
processor->deactivate();
}
}
+
+inline Route::PluginSetupOptions operator|= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) {
+ return a = static_cast<Route::PluginSetupOptions> (static_cast <int>(a) | static_cast<int> (b));
+}
+
+inline Route::PluginSetupOptions operator&= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) {
+ return a = static_cast<Route::PluginSetupOptions> (static_cast <int>(a) & static_cast<int> (b));
+}
+
int
Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
{
- /* NOTE: this is intended to be used ONLY when copying
- processors from another Route. Hence the subtle
- differences between this and ::add_processor()
- */
-
ProcessorList::iterator loc;
+ boost::shared_ptr <PluginInsert> fanout;
+
+ if (g_atomic_int_get (&_pending_process_reorder)) {
+ /* we need to flush any pending re-order changes */
+ Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+ apply_processor_changes_rt ();
+ }
if (before) {
loc = find(_processors.begin(), _processors.end(), before);
+ if (loc == _processors.end ()) {
+ return 1;
+ }
} else {
/* nothing specified - at end */
loc = _processors.end ();
}
- if (!_session.engine().connected()) {
- return 1;
- }
-
if (others.empty()) {
return 0;
}
+ ProcessorList to_skip;
+
+ // check if there's an instrument to replace or configure
+ for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
+ boost::shared_ptr<PluginInsert> pi;
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) == 0) {
+ continue;
+ }
+ if (!pi->plugin ()->get_info ()->is_instrument ()) {
+ continue;
+ }
+ boost::shared_ptr<Processor> instrument = the_instrument ();
+ ChanCount in (DataType::MIDI, 1);
+ ChanCount out (DataType::AUDIO, 2); // XXX route's out?!
+
+ PluginSetupOptions flags = None;
+ if (instrument) {
+ flags |= CanReplace;
+ in = instrument->input_streams ();
+ out = instrument->output_streams ();
+ }
+ if (pi->has_output_presets (in, out)) {
+ flags |= MultiOut;
+ }
+
+ pi->set_strict_io (_strict_io);
+
+ PluginSetupOptions mask = None;
+ if (Config->get_ask_replace_instrument ()) {
+ mask |= CanReplace;
+ }
+ if (Config->get_ask_setup_instrument ()) {
+ mask |= MultiOut;
+ }
+
+ flags &= mask;
+
+ if (flags != None) {
+ boost::optional<int> rv = PluginSetup (boost::dynamic_pointer_cast<Route>(shared_from_this ()), pi, flags); /* EMIT SIGNAL */
+ int mode = rv.get_value_or (0);
+ switch (mode & 3) {
+ case 1:
+ to_skip.push_back (*i); // don't add this one;
+ break;
+ case 2:
+ replace_processor (instrument, *i, err);
+ to_skip.push_back (*i);
+ break;
+ default:
+ break;
+ }
+ if ((mode & 5) == 4) {
+ fanout = pi;
+ }
+ }
+ }
+
{
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
if (*i == _meter) {
continue;
}
+ ProcessorList::iterator check = find (to_skip.begin(), to_skip.end(), *i);
+ if (check != to_skip.end()) {
+ continue;
+ }
boost::shared_ptr<PluginInsert> pi;
pi->set_strict_io (_strict_io);
}
+ if (*i == _amp) {
+ /* Ensure that only one amp is in the list at any time */
+ ProcessorList::iterator check = find (_processors.begin(), _processors.end(), *i);
+ if (check != _processors.end()) {
+ if (before == _amp) {
+ /* Already in position; all is well */
+ continue;
+ } else {
+ _processors.erase (check);
+ }
+ }
+ }
+
+ assert (find (_processors.begin(), _processors.end(), *i) == _processors.end ());
+
_processors.insert (loc, *i);
(*i)->set_owner (this);
- if ((*i)->active()) {
- (*i)->activate ();
- }
-
- /* Think: does this really need to be called for every processor in the loop? */
{
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
- configure_processors_unlocked (0); // it worked before we tried to add it ...
+ configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
return -1;
}
}
+ if (pi && pi->has_sidechain ()) {
+ pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2));
+ }
+
+ if ((*i)->active()) {
+ // emit ActiveChanged() and latency_changed() if needed
+ (*i)->activate ();
+ }
+
(*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
+
+ boost::shared_ptr<Send> send;
+ if ((send = boost::dynamic_pointer_cast<Send> (*i))) {
+ send->SelfDestruct.connect_same_thread (*this,
+ boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (*i)));
+ }
}
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
set_processor_positions ();
+ if (fanout && fanout->configured ()
+ && fanout->output_streams().n_audio() > 2
+ && boost::dynamic_pointer_cast<PluginInsert> (the_instrument ()) == fanout) {
+ fan_out (); /* EMIT SIGNAL */
+ }
return 0;
}
placement_range(p, start, end);
for (ProcessorList::iterator i = start; i != end; ++i) {
- (*i)->deactivate ();
+ (*i)->enable (false);
}
_session.set_dirty ();
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- (*i)->deactivate ();
+ (*i)->enable (false);
}
_session.set_dirty ();
for (ProcessorList::iterator i = start; i != end; ++i) {
if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
- (*i)->deactivate ();
+ (*i)->enable (false);
}
}
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
- (*i)->deactivate ();
+ (*i)->enable (false);
}
}
if (!boost::dynamic_pointer_cast<PluginInsert> (*i)) {
continue;
}
+ if (!(*i)->display_to_user ()) {
+ continue;
+ }
+#ifdef MIXBUS
+ if (boost::dynamic_pointer_cast<PluginInsert> (*i)->is_channelstrip()) {
+ continue;
+ }
+#endif
- if ((*i)->active()) {
- (*i)->deactivate ();
+ if ((*i)->enabled ()) {
+ (*i)->enable (false);
(*i)->set_next_ab_is_active (true);
} else {
(*i)->set_next_ab_is_active (false);
/* backward = if the redirect was marked to go active on the next ab, do so */
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
if (!boost::dynamic_pointer_cast<PluginInsert> (*i)) {
continue;
}
-
- if ((*i)->get_next_ab_is_active()) {
- (*i)->activate ();
- } else {
- (*i)->deactivate ();
+ if (!(*i)->display_to_user ()) {
+ continue;
+ }
+#ifdef MIXBUS
+ if (boost::dynamic_pointer_cast<PluginInsert> (*i)->is_channelstrip()) {
+ continue;
}
+#endif
+
+ (*i)->enable ((*i)->get_next_ab_is_active ());
}
}
_session.set_deletion_in_progress();
}
+ ProcessorList old_list = _processors;
{
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
}
_processors = new_list;
- configure_processors_unlocked (&err); // this can't fail
+ configure_processors_unlocked (&err, &lm); // this can't fail
}
+ /* drop references w/o process-lock (I/O procs may re-take it in ~IO() */
+ old_list.clear ();
processor_max_streams.reset();
_have_internal_generator = false;
- processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+ reset_instrument_info ();
set_processor_positions ();
- reset_instrument_info ();
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
if (!already_deleting) {
_session.clear_deletion_in_progress();
return 1;
}
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
/* we know this will work, because it worked before :) */
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
return -1;
}
ProcessorList::iterator i;
bool replaced = false;
- bool enable = old->active ();
+ bool enable = old->enabled ();
for (i = _processors.begin(); i != _processors.end(); ) {
if (*i == old) {
}
}
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
return -1;
}
}
if (enable) {
- sub->activate ();
+ sub->enable (true);
}
sub->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
_output->set_user_latency (0);
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
/* we know this will work, because it worked before :) */
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
return -1;
}
//lx.unlock();
if (!_in_configure_processors) {
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
- return configure_processors_unlocked (err);
+ return configure_processors_unlocked (err, &lm);
}
return 0;
if (boost::dynamic_pointer_cast<Delivery> (*p)
&& boost::dynamic_pointer_cast<Delivery> (*p)->role() == Delivery::Main
- && ( _strict_io || Profile->get_mixbus ())) {
+ && !is_auditioner()
+ && (is_monitor() || _strict_io || Profile->get_mixbus ())) {
/* with strict I/O the panner + output are forced to
* follow the last processor's output.
*
* Delivery::configure_io() will do the actual removal
* by calling _output->ensure_io()
*/
- if (!is_master() && _session.master_out ()) {
- /* ..but at least as many as there are master-inputs */
+ if (!is_master() && _session.master_out () && in.n_audio() > 0) {
+ /* ..but at least as many as there are master-inputs, if
+ * the delivery is dealing with audio */
// XXX this may need special-casing for mixbus (master-outputs)
// and should maybe be a preference anyway ?!
out = ChanCount::max (in, _session.master_out ()->n_inputs ());
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)
+ /* Note: The Monitor follows the master-bus and has no panner.
+ *
+ * The general idea is to only allow plugins that retain the channel-count
+ * and plugins with MIDI in (e.g VSTs with control that will remain unconnected).
+ * Then again 5.1 in, monitor stereo is a valid use-case.
+ *
+ * and worse: we only refuse adding plugins *here*.
+ *
+ * 1) stereo-master, stereo-mon, add a stereo-plugin, OK
+ * 2) change master-bus, add a channel
+ * 2a) monitor-secion follows
+ * 3) monitor processors fail to re-reconfigure (stereo plugin)
+ * 4) re-load session, monitor-processor remains unconfigured, crash.
*/
- DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration not allowed.\n");
- return list<pair<ChanCount, ChanCount> > ();
+ DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration change.\n");
}
if (boost::dynamic_pointer_cast<InternalSend> (*p)) {
// internal sends make no sense, only feedback
* Return 0 on success, otherwise configuration is impossible.
*/
int
-Route::configure_processors_unlocked (ProcessorStreams* err)
+Route::configure_processors_unlocked (ProcessorStreams* err, Glib::Threads::RWLock::WriterLock* lm)
{
#ifndef PLATFORM_WINDOWS
assert (!AudioEngine::instance()->process_lock().trylock());
processor_out_streams = _input->n_ports();
processor_max_streams.reset();
+ /* processor configure_io() may result in adding ports
+ * e.g. Delivery::configure_io -> ARDOUR::IO::ensure_io ()
+ *
+ * with jack2 adding ports results in a graph-order callback,
+ * which calls Session::resort_routes() and eventually
+ * Route::direct_feeds_according_to_reality()
+ * which takes a ReaderLock (_processor_lock).
+ *
+ * so we can't hold a WriterLock here until jack2 threading
+ * is fixed.
+ *
+ * NB. we still hold the process lock
+ *
+ * (ardour's own engines do call graph-order from the
+ * process-thread and hence do not have this issue; besides
+ * merely adding ports won't trigger a graph-order, only
+ * making connections does)
+ */
+ lm->release ();
+
+ // TODO check for a potential ReaderLock after ReaderLock ??
+ Glib::Threads::RWLock::ReaderLock lr (_processor_lock);
+
list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
if (!(*p)->configure_io(c->first, c->second)) {
DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name));
_in_configure_processors = false;
+ lr.release ();
+ lm->acquire ();
return -1;
}
+
processor_max_streams = ChanCount::max(processor_max_streams, c->first);
processor_max_streams = ChanCount::max(processor_max_streams, c->second);
}
}
+ lr.release ();
+ lm->acquire ();
+
if (_meter) {
_meter->set_max_channels (processor_max_streams);
if (!(*i)->display_to_user() || boost::dynamic_pointer_cast<Amp> (*i)) {
continue;
}
-
- if (state) {
- (*i)->activate ();
- } else {
- (*i)->deactivate ();
+#ifdef MIXBUS
+ boost::shared_ptr<PluginInsert> pi;
+ if (0 != (pi = boost::dynamic_pointer_cast<PluginInsert>(*i))) {
+ if (pi->is_channelstrip ()) {
+ continue;
+ }
}
+#endif
+ (*i)->enable (state);
}
_session.set_dirty ();
maybe_note_meter_position ();
}
+void
+Route::move_instrument_down (bool postfader)
+{
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+ ProcessorList new_order;
+ boost::shared_ptr<Processor> instrument;
+ 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->plugin ()->get_info ()->is_instrument ()) {
+ instrument = *i;
+ } else if (instrument && *i == _amp) {
+ if (postfader) {
+ new_order.push_back (*i);
+ new_order.push_back (instrument);
+ } else {
+ new_order.push_back (instrument);
+ new_order.push_back (*i);
+ }
+ } else {
+ new_order.push_back (*i);
+ }
+ }
+ if (!instrument) {
+ return;
+ }
+ lm.release ();
+ reorder_processors (new_order, 0);
+}
+
int
Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
{
apply_processor_order (new_order);
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
return -1;
}
g_atomic_int_set (&_pending_process_reorder, 1);
}
+ /* update processor input/output latency
+ * (total signal_latency does not change)
+ */
+ update_signal_latency (true);
+
return 0;
}
return false;
}
lx.acquire ();
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
}
if (pi->has_sidechain ()) {
return true;
}
+bool
+Route::plugin_preset_output (boost::shared_ptr<Processor> proc, ChanCount outs)
+{
+ boost::shared_ptr<PluginInsert> pi;
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+ return false;
+ }
+
+ {
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+ ProcessorList::iterator i = find (_processors.begin(), _processors.end(), proc);
+ if (i == _processors.end ()) {
+ return false;
+ }
+ }
+
+ {
+ Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+ Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+
+ const ChanCount& old (pi->preset_out ());
+ if (!pi->set_preset_out (outs)) {
+ return true; // no change, OK
+ }
+
+ list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+ if (c.empty()) {
+ /* not possible */
+ pi->set_preset_out (old);
+ return false;
+ }
+ configure_processors_unlocked (0, &lm);
+ }
+
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+ _session.set_dirty ();
+ return true;
+}
+
bool
Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
{
ChanCount unused;
- return customize_plugin_insert (proc, 0, unused);
+ return customize_plugin_insert (proc, 0, unused, unused);
}
bool
-Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs)
+Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs, ChanCount sinks)
{
boost::shared_ptr<PluginInsert> pi;
if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
- bool old_cust = pi->custom_cfg ();
- uint32_t old_cnt = pi->get_count ();
- ChanCount old_chan = pi->output_streams ();
+ bool old_cust = pi->custom_cfg ();
+ uint32_t old_cnt = pi->get_count ();
+ ChanCount old_chan = pi->output_streams ();
+ ChanCount old_sinks = pi->natural_input_streams ();
if (count == 0) {
pi->set_custom_cfg (false);
pi->set_custom_cfg (true);
pi->set_count (count);
pi->set_outputs (outs);
+ pi->set_sinks (sinks);
}
list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
/* not possible */
pi->set_count (old_cnt);
+ pi->set_sinks (old_sinks);
pi->set_outputs (old_chan);
pi->set_custom_cfg (old_cust);
return false;
}
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
}
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
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];
- id().print (buf, sizeof (buf));
- node->add_property("id", buf);
- node->add_property ("name", _name);
- node->add_property("default-type", _default_type.to_string());
- node->add_property ("strict-io", _strict_io);
+ node->set_property (X_("id"), id ());
+ node->set_property (X_("name"), name());
+ node->set_property (X_("default-type"), _default_type);
+ node->set_property (X_("strict-io"), _strict_io);
- if (_flags) {
- node->add_property("flags", enum_2_string (_flags));
- }
+ node->add_child_nocopy (_presentation_info.get_state());
- node->add_property("active", _active?"yes":"no");
- string p;
- boost::to_string (_phase_invert, p);
- node->add_property("phase-invert", p);
- node->add_property("denormal-protection", _denormal_protection?"yes":"no");
- node->add_property("meter-point", enum_2_string (_meter_point));
+ node->set_property (X_("active"), _active);
+ node->set_property (X_("denormal-protection"), _denormal_protection);
+ node->set_property (X_("meter-point"), _meter_point);
+ node->set_property (X_("disk-io-point"), _disk_io_point);
- node->add_property("meter-type", enum_2_string (_meter_type));
+ node->set_property (X_("meter-type"), _meter_type);
if (_route_group) {
- node->add_property("route-group", _route_group->name());
+ node->set_property (X_("route-group"), _route_group->name());
}
- snprintf (buf, sizeof (buf), "%d", _order_key);
- node->add_property ("order-key", buf);
- node->add_property ("self-solo", (_self_solo ? "yes" : "no"));
- snprintf (buf, sizeof (buf), "%d", _soloed_by_others_upstream);
- node->add_property ("soloed-by-upstream", buf);
- snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream);
- node->add_property ("soloed-by-downstream", buf);
- node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no");
- node->add_property ("solo-safe", _solo_safe ? "yes" : "no");
+ node->add_child_nocopy (_solo_control->get_state ());
+ node->add_child_nocopy (_solo_isolate_control->get_state ());
+ node->add_child_nocopy (_solo_safe_control->get_state ());
node->add_child_nocopy (_input->state (full_state));
node->add_child_nocopy (_output->state (full_state));
- node->add_child_nocopy (_solo_control->get_state ());
- node->add_child_nocopy (_mute_control->get_state ());
node->add_child_nocopy (_mute_master->get_state ());
+ node->add_child_nocopy (_mute_control->get_state ());
+ node->add_child_nocopy (_phase_control->get_state ());
+
if (full_state) {
node->add_child_nocopy (Automatable::get_automation_xml_state ());
}
- XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
- snprintf (buf, sizeof (buf), "%d", _remote_control_id);
- remote_control_node->add_property (X_("id"), buf);
- node->add_child_nocopy (*remote_control_node);
-
if (_comment.length()) {
XMLNode *cmt = node->add_child ("Comment");
cmt->add_content (_comment);
node->add_child_nocopy (_pannable->state (full_state));
}
- for (i = _processors.begin(); i != _processors.end(); ++i) {
- if (!full_state) {
- /* template save: do not include internal sends functioning as
- aux sends because the chance of the target ID
- in the session where this template is used
- is not very likely.
-
- similarly, do not save listen sends which connect to
- the monitor section, because these will always be
- added if necessary.
- */
- boost::shared_ptr<InternalSend> is;
+ {
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ if (!full_state) {
+ /* template save: do not include internal sends functioning as
+ aux sends because the chance of the target ID
+ in the session where this template is used
+ is not very likely.
+
+ similarly, do not save listen sends which connect to
+ the monitor section, because these will always be
+ added if necessary.
+ */
+ boost::shared_ptr<InternalSend> is;
- if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
- if (is->role() == Delivery::Listen) {
- continue;
+ if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
+ if (is->role() == Delivery::Listen) {
+ continue;
+ }
}
}
+ node->add_child_nocopy((*i)->state (full_state));
}
- node->add_child_nocopy((*i)->state (full_state));
}
if (_extra_xml) {
if (_custom_meter_position_noted) {
boost::shared_ptr<Processor> after = _processor_after_last_custom_meter.lock ();
if (after) {
- after->id().print (buf, sizeof (buf));
- node->add_property (X_("processor-after-last-custom-meter"), buf);
+ node->set_property (X_("processor-after-last-custom-meter"), after->id());
}
}
foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), ""));
}
+ node->add_child_copy (Slavable::get_state());
+
return *node;
}
XMLNodeList nlist;
XMLNodeConstIterator niter;
XMLNode *child;
- const XMLProperty *prop;
if (node.name() != "Route"){
error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
return -1;
}
- if ((prop = node.property (X_("name"))) != 0) {
- Route::set_name (prop->value());
+ std::string route_name;
+ if (node.get_property (X_("name"), route_name)) {
+ Route::set_name (route_name);
}
set_id (node);
_initial_io_setup = true;
- if ((prop = node.property (X_("flags"))) != 0) {
- _flags = Flag (string_2_enum (prop->value(), _flags));
- } else {
- _flags = Flag (0);
- }
-
- if ((prop = node.property (X_("strict-io"))) != 0) {
- _strict_io = string_is_affirmative (prop->value());
- }
+ Stripable::set_state (node, version);
- if (is_master() || is_monitor() || is_auditioner()) {
- _mute_master->set_solo_ignore (true);
- }
+ node.get_property (X_("strict-io"), _strict_io);
if (is_monitor()) {
/* monitor bus does not get a panner, but if (re)created
child = *niter;
if (child->name() == IO::state_node_name) {
- if ((prop = child->property (X_("direction"))) == 0) {
+ std::string direction;
+ if (!child->get_property (X_("direction"), direction)) {
continue;
}
- if (prop->value() == "Input") {
+ if (direction == "Input") {
_input->set_state (*child, version);
- } else if (prop->value() == "Output") {
+ } else if (direction == "Output") {
_output->set_state (*child, version);
}
- }
- if (child->name() == X_("Processor")) {
+ } else if (child->name() == X_("Processor")) {
processor_state.add_child_copy (*child);
- }
-
- if (child->name() == X_("Pannable")) {
+ } else if (child->name() == X_("Pannable")) {
if (_pannable) {
_pannable->set_state (*child, version);
} else {
warning << string_compose (_("Pannable state found for route (%1) without a panner!"), name()) << endmsg;
}
+ } else if (child->name() == Slavable::xml_node_name) {
+ Slavable::set_state (*child, version);
}
}
- if ((prop = node.property (X_("meter-point"))) != 0) {
- MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point));
+ MeterPoint mp;
+ if (node.get_property (X_("meter-point"), mp)) {
set_meter_point (mp, true);
if (_meter) {
_meter->set_display_to_user (_meter_point == MeterCustom);
}
}
- if ((prop = node.property (X_("meter-type"))) != 0) {
- _meter_type = MeterType (string_2_enum (prop->value (), _meter_type));
+ DiskIOPoint diop;
+ if (node.get_property (X_("disk-io-point"), diop)) {
+ if (_disk_writer) {
+ _disk_writer->set_display_to_user (diop == DiskIOCustom);
+ }
+ if (_disk_reader) {
+ _disk_reader->set_display_to_user (diop == DiskIOCustom);
+ }
+ set_disk_io_point (diop);
}
+ node.get_property (X_("meter-type"), _meter_type);
+
_initial_io_setup = false;
set_processor_state (processor_state);
// this looks up the internal instrument in processors
reset_instrument_info();
- if ((prop = node.property ("self-solo")) != 0) {
- set_self_solo (string_is_affirmative (prop->value()));
+ bool denormal_protection;
+ if (node.get_property (X_("denormal-protection"), denormal_protection)) {
+ set_denormal_protection (denormal_protection);
}
- if ((prop = node.property ("soloed-by-upstream")) != 0) {
- _soloed_by_others_upstream = 0; // needed for mod_.... () to work
- mod_solo_by_others_upstream (atoi (prop->value()));
+ /* convert old 3001 state */
+ std::string phase_invert_str;
+ if (node.get_property (X_("phase-invert"), phase_invert_str)) {
+ _phase_control->set_phase_invert (boost::dynamic_bitset<> (phase_invert_str));
}
- if ((prop = node.property ("soloed-by-downstream")) != 0) {
- _soloed_by_others_downstream = 0; // needed for mod_.... () to work
- mod_solo_by_others_downstream (atoi (prop->value()));
- }
-
- if ((prop = node.property ("solo-isolated")) != 0) {
- set_solo_isolated (string_is_affirmative (prop->value()), Controllable::NoGroup);
- }
-
- if ((prop = node.property ("solo-safe")) != 0) {
- set_solo_safe (string_is_affirmative (prop->value()), Controllable::NoGroup);
- }
-
- if ((prop = node.property (X_("phase-invert"))) != 0) {
- set_phase_invert (boost::dynamic_bitset<> (prop->value ()));
- }
-
- if ((prop = node.property (X_("denormal-protection"))) != 0) {
- set_denormal_protection (string_is_affirmative (prop->value()));
- }
-
- if ((prop = node.property (X_("active"))) != 0) {
- bool yn = string_is_affirmative (prop->value());
- _active = !yn; // force switch
- set_active (yn, this);
- }
-
- if ((prop = node.property (X_("order-key"))) != 0) { // New order key (no separate mixer/editor ordering)
- set_order_key (atoi(prop->value()));
- }
-
- if ((prop = node.property (X_("order-keys"))) != 0) { // Deprecated order keys
-
- int32_t n;
-
- string::size_type colon, equal;
- string remaining = prop->value();
-
- while (remaining.length()) {
-
- if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
- error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
- << endmsg;
- } else {
- if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
- error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
- << endmsg;
- } else {
- string keyname = remaining.substr (0, equal);
-
- if ((keyname == "EditorSort") || (keyname == "editor")) {
- cerr << "Setting " << name() << " order key to " << n << " using saved Editor order." << endl;
- set_order_key (n);
- }
- }
- }
-
- colon = remaining.find_first_of (':');
-
- if (colon != string::npos) {
- remaining = remaining.substr (colon+1);
- } else {
- break;
- }
- }
+ bool is_active;
+ if (node.get_property (X_("active"), is_active)) {
+ set_active (is_active, this);
}
- if ((prop = node.property (X_("processor-after-last-custom-meter"))) != 0) {
- PBD::ID id (prop->value ());
+ std::string id_string;
+ if (node.get_property (X_("processor-after-last-custom-meter"), id_string)) {
+ PBD::ID id (id_string);
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
ProcessorList::const_iterator i = _processors.begin ();
while (i != _processors.end() && (*i)->id() != id) {
XMLNode *cmt = *(child->children().begin());
_comment = cmt->content();
- } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
- if (prop->value() == "solo") {
- _solo_control->set_state (*child, version);
- } else if (prop->value() == "mute") {
- _mute_control->set_state (*child, version);
+ } else if (child->name() == Controllable::xml_node_name) {
+ std::string control_name;
+ if (!child->get_property (X_("name"), control_name)) {
+ continue;
}
- } else if (child->name() == X_("RemoteControl")) {
- if ((prop = child->property (X_("id"))) != 0) {
- int32_t x;
- sscanf (prop->value().c_str(), "%d", &x);
- set_remote_control_id_internal (x);
+ if (control_name == _solo_control->name()) {
+ _solo_control->set_state (*child, version);
+ } else if (control_name == _solo_safe_control->name()) {
+ _solo_safe_control->set_state (*child, version);
+ } else if (control_name == _solo_isolate_control->name()) {
+ _solo_isolate_control->set_state (*child, version);
+ } else if (control_name == _mute_control->name()) {
+ _mute_control->set_state (*child, version);
+ } else if (control_name == _phase_control->name()) {
+ _phase_control->set_state (*child, version);
+ } else {
+ Evoral::Parameter p = EventTypeMap::instance().from_symbol (control_name);
+ if (p.type () >= MidiCCAutomation && p.type () < MidiSystemExclusiveAutomation) {
+ boost::shared_ptr<AutomationControl> ac = automation_control (p, true);
+ if (ac) {
+ ac->set_state (*child, version);
+ }
+ }
}
-
- } else if (child->name() == X_("MuteMaster")) {
+ } else if (child->name() == MuteMaster::xml_node_name) {
_mute_master->set_state (*child, version);
} else if (child->name() == Automatable::xml_node_name) {
int
Route::set_state_2X (const XMLNode& node, int version)
{
- LocaleGuard lg (X_("C"));
+ LocaleGuard lg;
XMLNodeList nlist;
XMLNodeConstIterator niter;
XMLNode *child;
- const XMLProperty *prop;
+ XMLProperty const * prop;
/* 2X things which still remain to be handled:
* default-type
return -1;
}
- if ((prop = node.property (X_("flags"))) != 0) {
- string f = prop->value ();
- boost::replace_all (f, "ControlOut", "MonitorOut");
- _flags = Flag (string_2_enum (f, _flags));
- } else {
- _flags = Flag (0);
- }
-
- if (is_master() || is_monitor() || is_auditioner()) {
- _mute_master->set_solo_ignore (true);
- }
-
- if ((prop = node.property (X_("phase-invert"))) != 0) {
- boost::dynamic_bitset<> p (_input->n_ports().n_audio ());
- if (string_is_affirmative (prop->value ())) {
- p.set ();
- }
- set_phase_invert (p);
- }
+ Stripable::set_state (node, version);
if ((prop = node.property (X_("denormal-protection"))) != 0) {
- set_denormal_protection (string_is_affirmative (prop->value()));
- }
-
- if ((prop = node.property (X_("soloed"))) != 0) {
- bool yn = string_is_affirmative (prop->value());
-
- /* XXX force reset of solo status */
-
- set_solo (yn);
+ set_denormal_protection (string_to<bool> (prop->value()));
}
if ((prop = node.property (X_("muted"))) != 0) {
bool first = true;
- bool muted = string_is_affirmative (prop->value());
+ bool muted = string_to<bool> (prop->value());
if (muted) {
if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) {
- if (string_is_affirmative (prop->value())){
+ if (string_to<bool> (prop->value())){
mute_point = mute_point + "PreFader";
first = false;
}
if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) {
- if (string_is_affirmative (prop->value())){
+ if (string_to<bool> (prop->value())){
if (!first) {
mute_point = mute_point + ",";
if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) {
- if (string_is_affirmative (prop->value())){
+ if (string_to<bool> (prop->value())){
if (!first) {
mute_point = mute_point + ",";
if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) {
- if (string_is_affirmative (prop->value())){
+ if (string_to<bool> (prop->value())){
if (!first) {
mute_point = mute_point + ",";
_meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
}
- /* do not carry over edit/mix groups from 2.X because (a) its hard (b) they
- don't mean the same thing.
- */
-
- if ((prop = node.property (X_("order-keys"))) != 0) {
-
- int32_t n;
-
- string::size_type colon, equal;
- string remaining = prop->value();
-
- while (remaining.length()) {
-
- if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
- error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
- << endmsg;
- } else {
- if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
- error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
- << endmsg;
- } else {
- string keyname = remaining.substr (0, equal);
-
- if (keyname == "EditorSort" || keyname == "editor") {
- info << string_compose(_("Converting deprecated order key for %1 using Editor order %2"), name (), n) << endmsg;
- set_order_key (n);
- }
- }
- }
-
- colon = remaining.find_first_of (':');
-
- if (colon != string::npos) {
- remaining = remaining.substr (colon+1);
- } else {
- break;
- }
- }
- }
-
/* IOs */
nlist = node.children ();
set_id (*child);
if ((prop = child->property (X_("active"))) != 0) {
- bool yn = string_is_affirmative (prop->value());
+ bool yn = string_to<bool> (prop->value());
_active = !yn; // force switch
set_active (yn, this);
}
_mute_control->set_state (*child, version);
}
- } else if (child->name() == X_("RemoteControl")) {
- if ((prop = child->property (X_("id"))) != 0) {
- int32_t x;
- sscanf (prop->value().c_str(), "%d", &x);
- set_remote_control_id_internal (x);
- }
-
}
}
ProcessorList new_order;
bool must_configure = false;
- for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-
- XMLProperty* prop = (*niter)->property ("type");
-
- if (prop->value() == "amp") {
- _amp->set_state (**niter, Stateful::current_state_version);
- new_order.push_back (_amp);
- } else if (prop->value() == "trim") {
- _trim->set_state (**niter, Stateful::current_state_version);
- new_order.push_back (_trim);
- } else if (prop->value() == "meter") {
- _meter->set_state (**niter, Stateful::current_state_version);
- new_order.push_back (_meter);
- } else if (prop->value() == "delay") {
- if (_delayline) {
- _delayline->set_state (**niter, Stateful::current_state_version);
- new_order.push_back (_delayline);
- }
- } else if (prop->value() == "main-outs") {
- _main_outs->set_state (**niter, Stateful::current_state_version);
- } else if (prop->value() == "intreturn") {
- if (!_intreturn) {
- _intreturn.reset (new InternalReturn (_session));
- must_configure = true;
- }
- _intreturn->set_state (**niter, Stateful::current_state_version);
- } else if (is_monitor() && prop->value() == "monitor") {
- if (!_monitor_control) {
- _monitor_control.reset (new MonitorProcessor (_session));
- must_configure = true;
- }
- _monitor_control->set_state (**niter, Stateful::current_state_version);
- } else if (prop->value() == "capture") {
- /* CapturingProcessor should never be restored, it's always
- added explicitly when needed */
- } else {
- ProcessorList::iterator o;
-
- for (o = _processors.begin(); o != _processors.end(); ++o) {
- XMLProperty* id_prop = (*niter)->property(X_("id"));
- if (id_prop && (*o)->id() == id_prop->value()) {
- (*o)->set_state (**niter, Stateful::current_state_version);
- new_order.push_back (*o);
- break;
- }
- }
-
- // If the processor (*niter) is not on the route then create it
-
- if (o == _processors.end()) {
-
- boost::shared_ptr<Processor> processor;
-
- if (prop->value() == "intsend") {
-
- processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), boost::shared_ptr<Route>(), Delivery::Aux, true));
-
- } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
- prop->value() == "lv2" ||
- prop->value() == "windows-vst" ||
- prop->value() == "lxvst" ||
- prop->value() == "luaproc" ||
- prop->value() == "audiounit") {
-
- if (_session.get_disable_all_loaded_plugins ()) {
- processor.reset (new UnknownProcessor (_session, **niter));
- } else {
- processor.reset (new PluginInsert (_session));
- processor->set_owner (this);
- if (_strict_io) {
- boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(processor);
- pi->set_strict_io (true);
- }
-
- }
- } else if (prop->value() == "port") {
-
- processor.reset (new PortInsert (_session, _pannable, _mute_master));
-
- } else if (prop->value() == "send") {
-
- processor.reset (new Send (_session, _pannable, _mute_master, Delivery::Send, true));
-
- } else {
- error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
- continue;
- }
-
- if (processor->set_state (**niter, Stateful::current_state_version) != 0) {
- /* This processor could not be configured. Turn it into a UnknownProcessor */
- processor.reset (new UnknownProcessor (_session, **niter));
- }
-
- /* subscribe to Sidechain IO changes */
- boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
- if (pi && pi->has_sidechain ()) {
- pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2));
- }
-
- /* we have to note the monitor send here, otherwise a new one will be created
- and the state of this one will be lost.
- */
- boost::shared_ptr<InternalSend> isend = boost::dynamic_pointer_cast<InternalSend> (processor);
- if (isend && isend->role() == Delivery::Listen) {
- _monitor_send = isend;
- }
-
- /* it doesn't matter if invisible processors are added here, as they
- will be sorted out by setup_invisible_processors () shortly.
- */
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ XMLProperty* prop = (*niter)->property ("type");
- new_order.push_back (processor);
+ if (prop->value() == "amp") {
+ _amp->set_state (**niter, Stateful::current_state_version);
+ new_order.push_back (_amp);
+ } else if (prop->value() == "trim") {
+ _trim->set_state (**niter, Stateful::current_state_version);
+ new_order.push_back (_trim);
+ } else if (prop->value() == "meter") {
+ _meter->set_state (**niter, Stateful::current_state_version);
+ new_order.push_back (_meter);
+ } else if (prop->value() == "delay") {
+ if (_delayline) {
+ _delayline->set_state (**niter, Stateful::current_state_version);
+ new_order.push_back (_delayline);
+ }
+ } else if (prop->value() == "main-outs") {
+ _main_outs->set_state (**niter, Stateful::current_state_version);
+ } else if (prop->value() == "intreturn") {
+ if (!_intreturn) {
+ _intreturn.reset (new InternalReturn (_session));
+ must_configure = true;
+ }
+ _intreturn->set_state (**niter, Stateful::current_state_version);
+ } else if (is_monitor() && prop->value() == "monitor") {
+ if (!_monitor_control) {
+ _monitor_control.reset (new MonitorProcessor (_session));
must_configure = true;
}
+ _monitor_control->set_state (**niter, Stateful::current_state_version);
+ } else if (prop->value() == "capture") {
+ /* CapturingProcessor should never be restored, it's always
+ added explicitly when needed */
+ } else if (prop->value() == "diskreader" && _disk_reader) {
+ _disk_reader->set_state (**niter, Stateful::current_state_version);
+ new_order.push_back (_disk_reader);
+ } else if (prop->value() == "diskwriter" && _disk_writer) {
+ _disk_writer->set_state (**niter, Stateful::current_state_version);
+ new_order.push_back (_disk_writer);
+ } else {
+ set_processor_state (**niter, prop, new_order, must_configure);
}
}
+ ProcessorList old_list = _processors; // keep a copy
{
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+ /* re-assign _processors w/o process-lock.
+ * if there's an IO-processor present in _processors but
+ * not in new_order, it will be deleted and ~IO takes
+ * a process lock.
+ */
_processors = new_order;
if (must_configure) {
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lm);
}
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
}
}
}
+ /* drop references w/o process-lock (I/O procs may re-take it in ~IO() */
+ old_list.clear ();
reset_instrument_info ();
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
set_processor_positions ();
}
+bool
+Route::set_processor_state (XMLNode const & node, XMLProperty const* prop, ProcessorList& new_order, bool& must_configure)
+{
+ ProcessorList::iterator o;
+
+ for (o = _processors.begin(); o != _processors.end(); ++o) {
+ XMLProperty const * id_prop = node.property(X_("id"));
+ if (id_prop && (*o)->id() == id_prop->value()) {
+ (*o)->set_state (node, Stateful::current_state_version);
+ new_order.push_back (*o);
+ break;
+ }
+ }
+
+ // If the processor (node) is not on the route then create it
+
+ if (o == _processors.end()) {
+
+ boost::shared_ptr<Processor> processor;
+
+ if (prop->value() == "intsend") {
+
+ processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), boost::shared_ptr<Route>(), Delivery::Aux, true));
+
+ } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
+ prop->value() == "lv2" ||
+ prop->value() == "windows-vst" ||
+ prop->value() == "mac-vst" ||
+ prop->value() == "lxvst" ||
+ prop->value() == "luaproc" ||
+ prop->value() == "audiounit") {
+
+ if (_session.get_disable_all_loaded_plugins ()) {
+ processor.reset (new UnknownProcessor (_session, node));
+ } else {
+ processor.reset (new PluginInsert (_session));
+ processor->set_owner (this);
+ if (_strict_io) {
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(processor);
+ pi->set_strict_io (true);
+ }
+
+ }
+ } else if (prop->value() == "port") {
+
+ processor.reset (new PortInsert (_session, _pannable, _mute_master));
+
+ } else if (prop->value() == "send") {
+
+ processor.reset (new Send (_session, _pannable, _mute_master, Delivery::Send, true));
+ boost::shared_ptr<Send> send = boost::dynamic_pointer_cast<Send> (processor);
+ send->SelfDestruct.connect_same_thread (*this,
+ boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
+
+ } else {
+ return false;
+ }
+
+ if (processor->set_state (node, Stateful::current_state_version) != 0) {
+ /* This processor could not be configured. Turn it into a UnknownProcessor */
+ processor.reset (new UnknownProcessor (_session, node));
+ }
+
+ /* subscribe to Sidechain IO changes */
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
+ if (pi && pi->has_sidechain ()) {
+ pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2));
+ }
+
+ /* we have to note the monitor send here, otherwise a new one will be created
+ and the state of this one will be lost.
+ */
+ boost::shared_ptr<InternalSend> isend = boost::dynamic_pointer_cast<InternalSend> (processor);
+ if (isend && isend->role() == Delivery::Listen) {
+ _monitor_send = isend;
+ }
+
+ /* it doesn't matter if invisible processors are added here, as they
+ will be sorted out by setup_invisible_processors () shortly.
+ */
+
+ new_order.push_back (processor);
+ must_configure = true;
+ }
+ return true;
+}
+
void
Route::curve_reallocate ()
{
}
void
-Route::silence (framecnt_t nframes)
+Route::silence (samplecnt_t nframes)
{
Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
if (!lm.locked()) {
}
void
-Route::silence_unlocked (framecnt_t nframes)
+Route::silence_unlocked (samplecnt_t nframes)
{
/* Must be called with the processor lock held */
+ const samplepos_t now = _session.transport_sample ();
+
if (!_silent) {
_output->silence (nframes);
+ // update owned automated controllables
+ automation_run (now, nframes);
+
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
boost::shared_ptr<PluginInsert> pi;
if (!_active && (pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
- // skip plugins, they don't need anything when we're not active
+ /* evaluate automated automation controls */
+ pi->automation_run (now, nframes);
+ /* skip plugins, they don't need anything when we're not active */
continue;
}
- (*i)->silence (nframes);
+ (*i)->silence (nframes, now);
}
if (nframes == _session.get_block_size()) {
{
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- boost::shared_ptr<Pannable> sendpan (new Pannable (_session));
- listener.reset (new InternalSend (_session, sendpan, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), route, Delivery::Aux));
+ listener.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), route, Delivery::Aux));
}
add_processor (listener, before);
return true;
}
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
- Glib::Threads::RWLock::ReaderLock lm (_processor_lock); // XXX
for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(*r);
if (iop != 0) {
boost::shared_ptr<const IO> iop_out = iop->output();
+ if (other.get() == this && iop_out && iop->input() && iop_out->connected_to (iop->input())) {
+ // TODO this needs a delaylines in the Insert to align connections (!)
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does feed its own return (%2)\n", iop->name(), other->name()));
+ continue;
+ }
if ((iop_out && other->all_inputs().fed_by (iop_out)) || iop->feeds (other)) {
DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name()));
if (via_send_only) {
bool
Route::direct_feeds_according_to_graph (boost::shared_ptr<Route> other, bool* via_send_only)
{
- return _session._current_route_graph.has (shared_from_this (), other, via_send_only);
+ return _session._current_route_graph.has (boost::dynamic_pointer_cast<Route> (shared_from_this ()), other, via_send_only);
}
bool
Route::feeds_according_to_graph (boost::shared_ptr<Route> other)
{
- return _session._current_route_graph.feeds (shared_from_this (), other);
+ return _session._current_route_graph.feeds (boost::dynamic_pointer_cast<Route> (shared_from_this ()), other);
}
/** Called from the (non-realtime) butler thread when the transport is stopped */
void
-Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool /*did_locate*/, bool can_flush_processors)
+Route::non_realtime_transport_stop (samplepos_t now, bool flush)
{
- framepos_t now = _session.transport_frame();
-
{
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
- Automatable::transport_stopped (now);
+ Automatable::non_realtime_transport_stop (now, flush);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- if (!_have_internal_generator && (Config->get_plugins_stop_with_transport() && can_flush_processors)) {
+ if (!_have_internal_generator && (Config->get_plugins_stop_with_transport() && flush)) {
(*i)->flush ();
}
- (*i)->transport_stopped (now);
+ (*i)->non_realtime_transport_stop (now, flush);
}
}
- _roll_delay = _initial_delay;
+ if (_disk_reader) {
+ _disk_reader->set_roll_delay (_initial_delay);
+ }
}
void
contains ConfigurationChanged
*/
configure_processors (0);
- _phase_invert.resize (_input->n_ports().n_audio ());
+ _phase_control->resize (_input->n_ports().n_audio ());
io_changed (); /* EMIT SIGNAL */
}
- if (_soloed_by_others_upstream || _solo_isolated_by_upstream) {
+ if (_solo_control->soloed_by_others_upstream() || _solo_isolate_control->solo_isolated_by_upstream()) {
int sbou = 0;
int ibou = 0;
boost::shared_ptr<RouteList> routes = _session.get_routes ();
continue;
}
bool sends_only;
- bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
+ bool does_feed = (*i)->direct_feeds_according_to_reality (boost::dynamic_pointer_cast<Route> (shared_from_this()), &sends_only);
if (does_feed && !sends_only) {
if ((*i)->soloed()) {
++sbou;
}
- if ((*i)->solo_isolated()) {
+ if ((*i)->solo_isolate_control()->solo_isolated()) {
++ibou;
}
}
}
}
- int delta = sbou - _soloed_by_others_upstream;
- int idelta = ibou - _solo_isolated_by_upstream;
+ int delta = sbou - _solo_control->soloed_by_others_upstream();
+ int idelta = ibou - _solo_isolate_control->solo_isolated_by_upstream();
if (idelta < -1) {
PBD::warning << string_compose (
_("Invalid Solo-Isolate propagation: from:%1 new:%2 - old:%3 = delta:%4"),
- _name, ibou, _solo_isolated_by_upstream, idelta)
+ _name, ibou, _solo_isolate_control->solo_isolated_by_upstream(), idelta)
<< endmsg;
}
- if (_soloed_by_others_upstream) {
+ if (_solo_control->soloed_by_others_upstream()) {
// ignore new connections (they're not propagated)
if (delta <= 0) {
- mod_solo_by_others_upstream (delta);
+ _solo_control->mod_solo_by_others_upstream (delta);
}
}
- if (_solo_isolated_by_upstream) {
+ if (_solo_isolate_control->solo_isolated_by_upstream()) {
// solo-isolate currently only propagates downstream
if (idelta < 0) {
- mod_solo_isolated_by_upstream (false);
+ _solo_isolate_control->mod_solo_isolated_by_upstream (1);
}
- // TODO think: mod_solo_isolated_by_upstream() does not take delta arg,
- // but idelta can't be smaller than -1, can it?
//_solo_isolated_by_upstream = ibou;
}
bool sends_only;
bool does_feed = feeds (*i, &sends_only);
if (delta <= 0 && does_feed && !sends_only) {
- (*i)->mod_solo_by_others_upstream (delta);
+ (*i)->solo_control()->mod_solo_by_others_upstream (delta);
}
if (idelta < 0 && does_feed && !sends_only) {
- (*i)->mod_solo_isolated_by_upstream (false);
+ (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (-1);
}
}
}
io_changed (); /* EMIT SIGNAL */
}
- if (_soloed_by_others_downstream) {
- int sbod = 0;
- /* checking all all downstream routes for
- * explicit of implict solo is a rather drastic measure,
- * ideally the input_change_handler() of the other route
- * would propagate the change to us.
+ if ((change.type & IOChange::ConnectionsChanged)) {
+
+ /* do this ONLY if connections have changed. Configuration
+ * changes do not, by themselves alter solo upstream or
+ * downstream status.
*/
- boost::shared_ptr<RouteList> routes = _session.get_routes ();
- if (_output->connected()) {
- for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
- if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
- continue;
- }
- bool sends_only;
- bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
- if (does_feed && !sends_only) {
- if ((*i)->soloed()) {
- ++sbod;
- break;
+
+ if (_solo_control->soloed_by_others_downstream()) {
+ int sbod = 0;
+ /* checking all all downstream routes for
+ * explicit of implict solo is a rather drastic measure,
+ * ideally the input_change_handler() of the other route
+ * would propagate the change to us.
+ */
+ boost::shared_ptr<RouteList> routes = _session.get_routes ();
+ if (_output->connected()) {
+ for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+ if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ continue;
+ }
+ bool sends_only;
+ bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
+ if (does_feed && !sends_only) {
+ if ((*i)->soloed()) {
+ ++sbod;
+ break;
+ }
}
}
}
- }
- int delta = sbod - _soloed_by_others_downstream;
- if (delta <= 0) {
- // do not allow new connections to change implicit solo (no propagation)
- mod_solo_by_others_downstream (delta);
- // Session::route_solo_changed() does not propagate indirect solo-changes
- // propagate upstream to tracks
- for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
- if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
- continue;
- }
- bool sends_only;
- bool does_feed = (*i)->feeds (shared_from_this(), &sends_only);
- if (delta != 0 && does_feed && !sends_only) {
- (*i)->mod_solo_by_others_downstream (delta);
+
+ int delta = sbod - _solo_control->soloed_by_others_downstream();
+ if (delta <= 0) {
+ // do not allow new connections to change implicit solo (no propagation)
+ _solo_control->mod_solo_by_others_downstream (delta);
+ // Session::route_solo_changed() does not propagate indirect solo-changes
+ // propagate upstream to tracks
+ boost::shared_ptr<Route> shared_this = boost::dynamic_pointer_cast<Route> (shared_from_this());
+ for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+ if ((*i).get() == this || !can_solo()) {
+ continue;
+ }
+ bool sends_only;
+ bool does_feed = (*i)->feeds (shared_this, &sends_only);
+ if (delta != 0 && does_feed && !sends_only) {
+ (*i)->solo_control()->mod_solo_by_others_downstream (delta);
+ }
}
- }
+ }
}
}
}
void
-Route::sidechain_change_handler (IOChange change, void * /*src*/)
+Route::sidechain_change_handler (IOChange change, void* src)
{
if (_initial_io_setup || _in_sidechain_setup) {
return;
}
- if ((change.type & IOChange::ConfigurationChanged)) {
- /* This is called with the process lock held if change
- contains ConfigurationChanged
- */
- configure_processors (0);
- }
+ input_change_handler (change, src);
}
uint32_t
return max (n_inputs ().n_audio(), processor_max_streams.n_audio());
}
+void
+Route::flush_processor_buffers_locked (samplecnt_t nframes)
+{
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i);
+ if (d) {
+ d->flush_buffers (nframes);
+ } else {
+ boost::shared_ptr<PortInsert> p = boost::dynamic_pointer_cast<PortInsert> (*i);
+ if (p) {
+ p->flush_buffers (nframes);
+ }
+ }
+ }
+}
+
int
-Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing)
+Route::no_roll (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample, bool session_state_changing)
{
Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
return 0;
}
- if (n_outputs().n_total() == 0) {
- return 0;
- }
-
- if (!_active || n_inputs() == ChanCount::ZERO) {
+ if (!_active) {
silence_unlocked (nframes);
return 0;
}
fill_buffers_with_input (bufs, _input, nframes);
if (_meter_point == MeterInput) {
- _meter->run (bufs, start_frame, end_frame, nframes, true);
+ _meter->run (bufs, start_sample, end_sample, 0.0, nframes, true);
}
- _amp->apply_gain_automation (false);
- _trim->apply_gain_automation (false);
- passthru (bufs, start_frame, end_frame, nframes, 0);
+ passthru (bufs, start_sample, end_sample, nframes, 0, true);
+
+ flush_processor_buffers_locked (nframes);
return 0;
}
int
-Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& /* need_butler */)
+Route::roll (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample, int declick, bool& need_butler)
{
Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
- if (!lm.locked()) {
- return 0;
- }
- if (n_outputs().n_total() == 0) {
+ if (!lm.locked()) {
return 0;
}
- if (!_active || n_inputs().n_total() == 0) {
+ if (!_active) {
silence_unlocked (nframes);
- return 0;
- }
-
- framepos_t unused = 0;
-
- if ((nframes = check_initial_delay (nframes, unused)) == 0) {
+ if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || (!_disk_writer || _disk_writer->record_enabled()))) {
+ _meter->reset();
+ }
return 0;
}
_silent = false;
- BufferSet& bufs = _session.get_route_buffers (n_process_buffers());
+ BufferSet& bufs = _session.get_route_buffers (n_process_buffers ());
fill_buffers_with_input (bufs, _input, nframes);
- if (_meter_point == MeterInput) {
- _meter->run (bufs, start_frame, end_frame, nframes, true);
+ /* filter captured data before meter sees it */
+ filter_input (bufs);
+
+ if (_meter_point == MeterInput &&
+ ((_monitoring_control->monitoring_choice() & MonitorInput) || (_disk_writer && _disk_writer->record_enabled()))) {
+ _meter->run (bufs, start_sample, end_sample, 1.0 /*speed()*/, nframes, true);
}
- passthru (bufs, start_frame, end_frame, nframes, declick);
+ passthru (bufs, start_sample, end_sample, nframes, declick, (!_disk_writer || !_disk_writer->record_enabled()) && _session.transport_rolling());
+
+ if ((_disk_reader && _disk_reader->need_butler()) || (_disk_writer && _disk_writer->need_butler())) {
+ need_butler = true;
+ }
+
+ flush_processor_buffers_locked (nframes);
return 0;
}
int
-Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, bool& /* need_butler */)
+Route::silent_roll (pframes_t nframes, samplepos_t /*start_sample*/, samplepos_t /*end_sample*/, bool& /* need_butler */)
{
silence (nframes);
+ flush_processor_buffers_locked (nframes);
return 0;
}
void
Route::flush_processors ()
{
- /* XXX shouldn't really try to take this lock, since
- this is called from the RT audio thread.
- */
-
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
}
if (changed) {
set_processor_positions ();
+ /* update processor input/output latency
+ * (total signal_latency does not change)
+ */
+ update_signal_latency (true);
}
if (emissions != 0) {
g_atomic_int_set (&_pending_signals, emissions);
return true;
}
- return false;
+ return (!selfdestruct_sequence.empty ());
}
void
Route::emit_pending_signals ()
{
-
int sig = g_atomic_int_and (&_pending_signals, 0);
if (sig & EmitMeterChanged) {
_meter->emit_configuration_changed();
if (sig & EmitRtProcessorChange) {
processors_changed (RouteProcessorChange (RouteProcessorChange::RealTimeChange)); /* EMIT SIGNAL */
}
+
+ /* this would be a job for the butler.
+ * Conceptually we should not take processe/processor locks here.
+ * OTOH its more efficient (less overhead for summoning the butler and
+ * telling her what do do) and signal emission is called
+ * directly after the process callback, which decreases the chance
+ * of x-runs when taking the locks.
+ */
+ while (!selfdestruct_sequence.empty ()) {
+ Glib::Threads::Mutex::Lock lx (selfdestruct_lock);
+ if (selfdestruct_sequence.empty ()) { break; } // re-check with lock
+ boost::shared_ptr<Processor> proc = selfdestruct_sequence.back ().lock ();
+ selfdestruct_sequence.pop_back ();
+ lx.release ();
+ if (proc) {
+ remove_processor (proc);
+ }
+ }
}
void
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
ProcessorState pstate (this);
- if (configure_processors_unlocked (0)) {
+ if (configure_processors_unlocked (0, &lm)) {
DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
pstate.restore ();
- configure_processors_unlocked (0); // it worked before we tried to add it ...
+ configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
return;
}
}
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lw (_processor_lock);
- _capturing_processor.reset (new CapturingProcessor (_session));
+ // this aligns all tracks; but not tracks + busses
+ samplecnt_t latency = _session.worst_track_roll_delay ();
+ assert (latency >= _initial_delay);
+ _capturing_processor.reset (new CapturingProcessor (_session, latency - _initial_delay));
_capturing_processor->activate ();
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lw);
}
return _capturing_processor;
}
-framecnt_t
-Route::update_signal_latency ()
+samplecnt_t
+Route::update_signal_latency (bool set_initial_delay)
{
- framecnt_t l = _output->user_latency();
- framecnt_t lamp = 0;
- bool before_amp = true;
- framecnt_t ltrim = 0;
- bool before_trim = true;
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+
+ samplecnt_t l_in = _input->latency ();
+ samplecnt_t l_out = _output->user_latency();
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((*i)->active ()) {
- l += (*i)->signal_latency ();
- }
- if ((*i) == _amp) {
- before_amp = false;
+ l_in += (*i)->signal_latency ();
}
- if ((*i) == _trim) {
- before_amp = false;
- }
- if (before_amp) {
- lamp = l;
- }
- if (before_trim) {
- lamp = l;
+ (*i)->set_input_latency (l_in);
+ }
+
+ for (ProcessorList::reverse_iterator i = _processors.rbegin(); i != _processors.rend(); ++i) {
+ (*i)->set_output_latency (l_out);
+ if ((*i)->active ()) {
+ l_out += (*i)->signal_latency ();
}
}
- DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l));
+ DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l_out));
+
+ _signal_latency = l_out;
+
+ lm.release ();
- // TODO: (lamp - _signal_latency) to sync to output (read-ahed), currently _roll_delay shifts this around
- _signal_latency_at_amp_position = lamp;
- _signal_latency_at_trim_position = ltrim;
+ if (set_initial_delay) {
+ /* see also Session::post_playback_latency() */
+ set_latency_compensation (_session.worst_track_latency () + _session.worst_track_out_latency () - output ()->latency ());
+ }
- if (_signal_latency != l) {
- _signal_latency = l;
+ if (_signal_latency != l_out) {
signal_latency_changed (); /* EMIT SIGNAL */
}
}
void
-Route::set_user_latency (framecnt_t nframes)
+Route::set_user_latency (samplecnt_t nframes)
{
_output->set_user_latency (nframes);
_session.update_latency_compensation ();
}
void
-Route::set_latency_compensation (framecnt_t longest_session_latency)
+Route::set_latency_compensation (samplecnt_t longest_session_latency)
{
- framecnt_t old = _initial_delay;
+ samplecnt_t old = _initial_delay;
+ assert (!_disk_reader || _disk_reader->output_latency () <= _signal_latency);
- if (_signal_latency < longest_session_latency) {
- _initial_delay = longest_session_latency - _signal_latency;
+ if (_disk_reader && _disk_reader->output_latency () < longest_session_latency) {
+ _initial_delay = longest_session_latency - _disk_reader->output_latency ();
} else {
_initial_delay = 0;
}
}
if (_session.transport_stopped()) {
- _roll_delay = _initial_delay;
+ if (_disk_reader) {
+ _disk_reader->set_roll_delay (_initial_delay);
+ }
}
}
* Adds undo commands for any shifts that are performed.
*
* @param pos Position to start shifting from.
- * @param frames Amount to shift forwards by.
+ * @param samples Amount to shift forwards by.
*/
void
-Route::shift (framepos_t pos, framecnt_t frames)
+Route::shift (samplepos_t pos, samplecnt_t samples)
{
/* gain automation */
{
boost::shared_ptr<AutomationControl> gc = _amp->gain_control();
XMLNode &before = gc->alist()->get_state ();
- gc->alist()->shift (pos, frames);
+ gc->alist()->shift (pos, samples);
XMLNode &after = gc->alist()->get_state ();
_session.add_command (new MementoCommand<AutomationList> (*gc->alist().get(), &before, &after));
}
boost::shared_ptr<AutomationControl> gc = _trim->gain_control();
XMLNode &before = gc->alist()->get_state ();
- gc->alist()->shift (pos, frames);
+ gc->alist()->shift (pos, samples);
XMLNode &after = gc->alist()->get_state ();
_session.add_command (new MementoCommand<AutomationList> (*gc->alist().get(), &before, &after));
}
if (pc) {
boost::shared_ptr<AutomationList> al = pc->alist();
XMLNode& before = al->get_state ();
- al->shift (pos, frames);
+ al->shift (pos, samples);
XMLNode& after = al->get_state ();
_session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
}
if (ac) {
boost::shared_ptr<AutomationList> al = ac->alist();
XMLNode &before = al->get_state ();
- al->shift (pos, frames);
+ al->shift (pos, samples);
XMLNode &after = al->get_state ();
_session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
}
}
int
-Route::save_as_template (const string& path, const string& name)
+Route::save_as_template (const string& path, const string& name, const string& description)
{
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));
+ node.set_property (X_("name"), name);
+
+ node.remove_nodes (X_("description"));
+ if (!description.empty()) {
+ XMLNode* desc = new XMLNode(X_("description"));
+ XMLNode* desc_cont = new XMLNode(X_("content"), description);
+ desc->add_child_nocopy (*desc_cont);
+
+ node.add_child_nocopy (*desc);
+ }
XMLTree tree;
bool
Route::set_name (const string& str)
{
+ if (str.empty ()) {
+ return false;
+ }
+
if (str == name()) {
return true;
}
void
Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playlist)
{
- node.add_property (X_("name"), name);
+ node.set_property (X_("name"), name);
XMLNodeList children = node.children();
for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
} else if ((*i)->name() == X_("Processor")) {
- XMLProperty* role = (*i)->property (X_("role"));
- if (role && role->value() == X_("Main")) {
- (*i)->add_property (X_("name"), name);
+ std::string str;
+ if ((*i)->get_property (X_("role"), str) && str == X_("Main")) {
+ (*i)->set_property (X_("name"), name);
}
} else if ((*i)->name() == X_("Diskstream")) {
if (rename_playlist) {
- (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
+ (*i)->set_property (X_("playlist"), name + ".1");
}
- (*i)->add_property (X_("name"), name);
+ (*i)->set_property (X_("name"), name);
}
}
return boost::shared_ptr<Send>();
}
-/** @param c Audio channel index.
- * @param yn true to invert phase, otherwise false.
- */
-void
-Route::set_phase_invert (uint32_t c, bool yn)
-{
- if (_phase_invert[c] != yn) {
- _phase_invert[c] = yn;
- phase_invert_changed (); /* EMIT SIGNAL */
- _phase_control->Changed(); /* EMIT SIGNAL */
- _session.set_dirty ();
- }
-}
-
-void
-Route::set_phase_invert (boost::dynamic_bitset<> p)
-{
- if (_phase_invert != p) {
- _phase_invert = p;
- phase_invert_changed (); /* EMIT SIGNAL */
- _session.set_dirty ();
- }
-}
-
-bool
-Route::phase_invert (uint32_t c) const
-{
- return _phase_invert[c];
-}
-
-boost::dynamic_bitset<>
-Route::phase_invert () const
-{
- return _phase_invert;
-}
-
void
Route::set_denormal_protection (bool yn)
{
_active = yn;
_input->set_active (yn);
_output->set_active (yn);
+ flush_processors ();
active_changed (); // EMIT SIGNAL
_session.set_dirty ();
}
return _trim_control;
}
-boost::shared_ptr<Route::PhaseControllable>
+boost::shared_ptr<PhaseControl>
Route::phase_control() const
{
- if (phase_invert().size()) {
- return _phase_control;
- } else {
- return boost::shared_ptr<PhaseControllable>();
- }
+ return _phase_control;
}
boost::shared_ptr<AutomationControl>
return false;
}
-MuteMaster::MutePoint
-Route::mute_points () const
-{
- return _mute_master->mute_points ();
-}
-
void
Route::set_processor_positions ()
{
}
-framecnt_t
-Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecnt_t our_latency) const
+samplecnt_t
+Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, samplecnt_t our_latency) const
{
/* we assume that all our input ports feed all our output ports. its not
universally true, but the alternative is way too corner-case to worry about.
return all_connections.max;
}
-framecnt_t
+samplecnt_t
Route::set_private_port_latencies (bool playback) const
{
- framecnt_t own_latency = 0;
+ samplecnt_t own_latency = 0;
/* Processor list not protected by lock: MUST BE CALLED FROM PROCESS THREAD
OR LATENCY CALLBACK.
}
void
-Route::set_public_port_latencies (framecnt_t value, bool playback) const
+Route::set_public_port_latencies (samplecnt_t value, bool playback) const
{
/* this is called to set the JACK-visible port latencies, which take
latency compensation into account.
*/
ProcessorList new_processors;
+ ProcessorList::iterator dr;
+ ProcessorList::iterator dw;
/* find visible processors */
/* find the amp */
- ProcessorList::iterator amp = new_processors.begin ();
- while (amp != new_processors.end() && *amp != _amp) {
- ++amp;
- }
+ ProcessorList::iterator amp = find (new_processors.begin(), new_processors.end(), _amp);
- assert (amp != new_processors.end ());
+ if (amp == new_processors.end ()) {
+ error << string_compose (_("Amp/Fader on Route '%1' went AWOL. Re-added."), name()) << endmsg;
+ new_processors.push_front (_amp);
+ amp = find (new_processors.begin(), new_processors.end(), _amp);
+ }
/* and the processor after the amp */
new_processors.insert (amp, _monitor_control);
}
+ /* TRIMÂ CONTROL */
+
+ ProcessorList::iterator trim = new_processors.end();
+
+ if (_trim->active()) {
+ assert (!_trim->display_to_user ());
+ new_processors.push_front (_trim);
+ trim = new_processors.begin();
+ }
+
/* INTERNAL RETURN */
- /* doing this here means that any monitor control will come just after
- the return.
+ /* doing this here means that any monitor control will come after
+ the return and trim.
*/
if (_intreturn) {
new_processors.push_front (_intreturn);
}
- if (_trim && _trim->active()) {
- assert (!_trim->display_to_user ());
- new_processors.push_front (_trim);
- }
/* EXPORT PROCESSOR */
+ /* DISK READER & WRITER (for Track objects) */
+
+ if (_disk_reader || _disk_writer) {
+ switch (_disk_io_point) {
+ case DiskIOPreFader:
+ if (trim != new_processors.end()) {
+ /* insert BEFORE TRIM */
+ if (_disk_writer) {
+ new_processors.insert (trim, _disk_writer);
+ }
+ if (_disk_reader) {
+ new_processors.insert (trim, _disk_reader);
+ }
+ } else {
+ if (_disk_writer) {
+ new_processors.push_front (_disk_writer);
+ }
+ if (_disk_reader) {
+ new_processors.push_front (_disk_reader);
+ }
+ }
+ break;
+ case DiskIOPostFader:
+ /* insert BEFORE main outs */
+ if (_disk_writer) {
+ new_processors.insert (main, _disk_writer);
+ }
+ if (_disk_reader) {
+ new_processors.insert (main, _disk_reader);
+ }
+ break;
+ case DiskIOCustom:
+ /* reader and writer are visible under this condition, so they
+ * are not invisible and thus not handled here.
+ */
+ break;
+ }
+ }
+
if (_capturing_processor) {
assert (!_capturing_processor->display_to_user ());
- new_processors.push_front (_capturing_processor);
+ ProcessorList::iterator reader_pos = find (new_processors.begin(), new_processors.end(), _disk_reader);
+ if (reader_pos != new_processors.end()) {
+ /* insert after disk-reader */
+ new_processors.insert (++reader_pos, _capturing_processor);
+ } else {
+ new_processors.push_front (_capturing_processor);
+ }
}
+
_processors = new_processors;
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- if (!(*i)->display_to_user () && !(*i)->active () && (*i) != _monitor_send) {
- (*i)->activate ();
+ if (!(*i)->display_to_user () && !(*i)->enabled () && (*i) != _monitor_send) {
+ (*i)->enable (true);
}
}
return boost::shared_ptr<Processor> ();
}
-/** @return the monitoring state, or in other words what data we are pushing
- * into the route (data from the inputs, data from disk or silence)
- */
-MonitorState
-Route::monitoring_state () const
-{
- return MonitoringInput;
-}
-
/** @return what we should be metering; either the data coming from the input
* IO or the data that is flowing through the route.
*/
Route::the_instrument_unlocked () const
{
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
- if (boost::dynamic_pointer_cast<PluginInsert>(*i)) {
- if ((*i)->input_streams().n_midi() > 0 &&
- (*i)->output_streams().n_audio() > 0) {
- return (*i);
- }
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*i);
+ if (pi && pi->plugin ()->get_info ()->is_instrument ()) {
+ return (*i);
}
}
return boost::shared_ptr<Processor>();
void
-Route::non_realtime_locate (framepos_t pos)
+Route::non_realtime_locate (samplepos_t pos)
{
+ Automatable::non_realtime_locate (pos);
+
if (_pannable) {
- _pannable->transport_located (pos);
+ _pannable->non_realtime_locate (pos);
}
if (_delayline.get()) {
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- (*i)->transport_located (pos);
+ (*i)->non_realtime_locate (pos);
}
}
- _roll_delay = _initial_delay;
+
+ if (_disk_reader) {
+ _disk_reader->set_roll_delay (_initial_delay);
+ }
}
void
boost::shared_ptr<AudioPort> source_port = io->audio (i);
AudioBuffer& buf (bufs.get_audio (i%n_buffers));
-
if (i < n_buffers) {
/* first time through just copy a channel into
Route::pan_azimuth_control() const
{
#ifdef MIXBUS
+# undef MIXBUS_PORTS_H
+# include "../../gtk2_ardour/mixbus_ports.h"
boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
if (!plug) {
return boost::shared_ptr<AutomationControl>();
}
- 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()) {
boost::shared_ptr<AutomationControl>
Route::pan_width_control() const
{
+#ifdef MIXBUS
+ if (mixbus() && _ch_pre) {
+ //mono blend
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(_ch_pre->control(Evoral::Parameter(PluginAutomation, 0, 5)));
+ }
+#endif
if (Profile->get_mixbus() || !_pannable || !panner()) {
return boost::shared_ptr<AutomationControl>();
}
Route::eq_band_cnt () const
{
if (Profile->get_mixbus()) {
+#ifdef MIXBUS32C
+ if (is_master() || mixbus()) {
+ return 3;
+ } else {
+ return 4;
+ }
+#else
return 3;
+#endif
} else {
/* Ardour has no well-known EQ object */
return 0;
}
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;
+ if (is_master() || mixbus()) {
+ switch (band) {
+ case 0: port_number = 4; break;
+ case 1: port_number = 3; break;
+ case 2: port_number = 2; break;
+ default:
+ return boost::shared_ptr<AutomationControl>();
}
- break;
- case 2:
- if (is_master() || mixbus()) {
- port_number = 2;
- } else {
- port_number = 4;
+ } else {
+#ifdef MIXBUS32C
+ switch (band) {
+ case 0: port_number = 14; break;
+ case 1: port_number = 12; break;
+ case 2: port_number = 10; break;
+ case 3: port_number = 8; break;
+ default:
+ return boost::shared_ptr<AutomationControl>();
}
- break;
- default:
- return boost::shared_ptr<AutomationControl>();
+#else
+ switch (band) {
+ case 0: port_number = 8; break;
+ case 1: port_number = 6; break;
+ case 2: port_number = 4; break;
+ default:
+ return boost::shared_ptr<AutomationControl>();
+ }
+#endif
}
return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number)));
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>();
}
uint32_t port_number;
+#ifdef MIXBUS32C
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>();
+ case 0: port_number = 13; break; // lo
+ case 1: port_number = 11; break; // lo mid
+ case 2: port_number = 9; break; // hi mid
+ case 3: port_number = 7; break; // hi
+ default:
+ return boost::shared_ptr<AutomationControl>();
+ }
+#else
+ 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>();
}
+#endif
return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number)));
#else
boost::shared_ptr<AutomationControl>
Route::eq_shape_controllable (uint32_t band) const
{
+#ifdef MIXBUS32C
+ boost::shared_ptr<PluginInsert> eq = ch_eq();
+ if (is_master() || mixbus() || !eq) {
+ return boost::shared_ptr<AutomationControl>();
+ }
+ switch (band) {
+ case 0:
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 4))); // lo bell
+ break;
+ case 3:
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 3))); // hi bell
+ break;
+ default:
+ break;
+ }
+#endif
return boost::shared_ptr<AutomationControl>();
}
}
boost::shared_ptr<AutomationControl>
-Route::eq_hpf_controllable () const
+Route::filter_freq_controllable (bool hpf) const
{
#ifdef MIXBUS
boost::shared_ptr<PluginInsert> eq = ch_eq();
- if (!eq) {
+ if (is_master() || mixbus() || !eq) {
+ return boost::shared_ptr<AutomationControl>();
+ }
+ if (hpf) {
+#ifdef MIXBUS32C
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 5))); // HPF freq
+#else
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2)));
+#endif
+ } else {
+#ifdef MIXBUS32C
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6))); // LPF freq
+#else
+ return boost::shared_ptr<AutomationControl>();
+#endif
+ }
+
+#else
+ return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::filter_slope_controllable (bool) const
+{
+ return boost::shared_ptr<AutomationControl>();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::filter_enable_controllable (bool) const
+{
+#ifdef MIXBUS32C
+ boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+ if (is_master() || mixbus() || !eq) {
return boost::shared_ptr<AutomationControl>();
}
string
Route::eq_band_name (uint32_t band) const
{
+#ifdef MIXBUS32C
+ if (is_master() || mixbus()) {
+#endif
if (Profile->get_mixbus()) {
switch (band) {
- case 0:
- return _("lo");
- case 1:
- return _("mid");
- case 2:
- return _("hi");
- default:
- return string();
+ case 0: return _("lo");
+ case 1: return _("mid");
+ case 2: return _("hi");
+ default: return string();
}
} else {
return string ();
}
+#ifdef MIXBUS32C
+ } else {
+ switch (band) {
+ case 0: return _("lo");
+ case 1: return _("lo mid");
+ case 2: return _("hi mid");
+ case 3: return _("hi");
+ default: return string();
+ }
+ }
+#endif
}
boost::shared_ptr<AutomationControl>
return boost::shared_ptr<AutomationControl>();
#endif
}
-boost::shared_ptr<AutomationControl>
+boost::shared_ptr<ReadOnlyControl>
Route::comp_redux_controllable () const
{
#ifdef MIXBUS
boost::shared_ptr<PluginInsert> comp = ch_comp();
if (!comp) {
- return boost::shared_ptr<AutomationControl>();
+ return boost::shared_ptr<ReadOnlyControl>();
+ }
+ if (is_master()) {
+ return comp->control_output (2);
+ } else {
+ return comp->control_output (6);
}
- return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6)));
#else
- return boost::shared_ptr<AutomationControl>();
+ return boost::shared_ptr<ReadOnlyControl>();
#endif
}
Route::send_level_controllable (uint32_t n) const
{
#ifdef MIXBUS
+# undef MIXBUS_PORTS_H
+# include "../../gtk2_ardour/mixbus_ports.h"
boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
- if (!plug) {
- return boost::shared_ptr<AutomationControl>();
- }
+ if (plug && !mixbus()) {
+ uint32_t port_id = 0;
+ switch (n) {
+ case 0: port_id = port_channel_post_aux1_level; break;
+ case 1: port_id = port_channel_post_aux2_level; break;
+ case 2: port_id = port_channel_post_aux3_level; break;
+ case 3: port_id = port_channel_post_aux4_level; break;
+ case 4: port_id = port_channel_post_aux5_level; break;
+ case 5: port_id = port_channel_post_aux6_level; break;
+ case 6: port_id = port_channel_post_aux7_level; break;
+ case 7: port_id = port_channel_post_aux8_level; break;
+# ifdef MIXBUS32C
+ case 8: port_id = port_channel_post_aux9_level; break;
+ case 9: port_id = port_channel_post_aux10_level; break;
+ case 10: port_id = port_channel_post_aux11_level; break;
+ case 11: port_id = port_channel_post_aux12_level; break;
+# endif
+ default:
+ break;
+ }
- if (n >= 8) {
- /* no such bus */
- return boost::shared_ptr<AutomationControl>();
+ if (port_id > 0) {
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
+ }
+# ifdef MIXBUS32C
+ assert (n > 11);
+ n -= 12;
+# else
+ assert (n > 7);
+ n -= 8;
+# endif
}
-
- const uint32_t port_id = port_channel_post_aux1_level + (2*n); // gtk2_ardour/mixbus_ports.h
- return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
-#else
+#endif
boost::shared_ptr<Send> s = boost::dynamic_pointer_cast<Send>(nth_send (n));
if (!s) {
return boost::shared_ptr<AutomationControl>();
}
return s->gain_control ();
-#endif
}
boost::shared_ptr<AutomationControl>
Route::send_enable_controllable (uint32_t n) const
{
#ifdef MIXBUS
+# undef MIXBUS_PORTS_H
+# include "../../gtk2_ardour/mixbus_ports.h"
boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
- if (!plug) {
- return boost::shared_ptr<AutomationControl>();
- }
+ if (plug && !mixbus()) {
+ uint32_t port_id = 0;
+ switch (n) {
+ case 0: port_id = port_channel_post_aux1_asgn; break;
+ case 1: port_id = port_channel_post_aux2_asgn; break;
+ case 2: port_id = port_channel_post_aux3_asgn; break;
+ case 3: port_id = port_channel_post_aux4_asgn; break;
+ case 4: port_id = port_channel_post_aux5_asgn; break;
+ case 5: port_id = port_channel_post_aux6_asgn; break;
+ case 6: port_id = port_channel_post_aux7_asgn; break;
+ case 7: port_id = port_channel_post_aux8_asgn; break;
+# ifdef MIXBUS32C
+ case 8: port_id = port_channel_post_aux9_asgn; break;
+ case 9: port_id = port_channel_post_aux10_asgn; break;
+ case 10: port_id = port_channel_post_aux11_asgn; break;
+ case 11: port_id = port_channel_post_aux12_asgn; break;
+# endif
+ default:
+ break;
+ }
- if (n >= 8) {
- /* no such bus */
- return boost::shared_ptr<AutomationControl>();
+ if (port_id > 0) {
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
+ }
+# ifdef MIXBUS32C
+ assert (n > 11);
+ n -= 12;
+# else
+ assert (n > 7);
+ n -= 8;
+# endif
}
-
- const uint32_t port_id = port_channel_post_aux1_asgn + (2*n); // gtk2_ardour/mixbus_ports.h
- return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
-#else
+#endif
/* although Ardour sends have enable/disable as part of the Processor
- API, it is not exposed as a controllable.
-
- XXX: we should fix this.
- */
+ * API, it is not exposed as a controllable.
+ *
+ * XXX: we should fix this (make it click-free, automatable enable-control)
+ */
return boost::shared_ptr<AutomationControl>();
-#endif
}
string
Route::send_name (uint32_t n) const
{
#ifdef MIXBUS
- if (n >= 8) {
- return string();
- }
- boost::shared_ptr<Route> r = _session.get_mixbus (n);
- assert (r);
- return r->name();
+ boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+ if (plug && !mixbus()) {
+# ifdef MIXBUS32C
+ if (n < 12) {
+ return _session.get_mixbus (n)->name();
+ }
+ n -= 12;
#else
+ if (n < 8) {
+ return _session.get_mixbus (n)->name();
+ }
+ n -= 8;
+# endif
+ }
+#endif
boost::shared_ptr<Processor> p = nth_send (n);
if (p) {
return p->name();
} else {
return string();
}
-#endif
}
boost::shared_ptr<AutomationControl>
if (!plug) {
return boost::shared_ptr<AutomationControl>();
}
+# undef MIXBUS_PORTS_H
+# include "../../gtk2_ardour/mixbus_ports.h"
return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_mstr_assign)));
#else
return boost::shared_ptr<AutomationControl>();
#endif
}
+
+bool
+Route::slaved () const
+{
+ if (!_gain_control) {
+ return false;
+ }
+ /* just test one particular control, not all of them */
+ return _gain_control->slaved ();
+}
+
+bool
+Route::slaved_to (boost::shared_ptr<VCA> vca) const
+{
+ if (!vca || !_gain_control) {
+ return false;
+ }
+
+ /* just test one particular control, not all of them */
+
+ return _gain_control->slaved_to (vca->gain_control());
+}
+
+bool
+Route::muted_by_others_soloing () const
+{
+ if (!can_be_muted_by_others ()) {
+ return false;
+ }
+
+ return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated();
+}
+
+void
+Route::clear_all_solo_state ()
+{
+ _solo_control->clear_all_solo_state ();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::automation_control_recurse (PBD::ID const & id) const
+{
+ boost::shared_ptr<AutomationControl> ac = Automatable::automation_control (id);
+
+ if (ac) {
+ return ac;
+ }
+
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((ac = (*i)->automation_control (id))) {
+ return ac;
+ }
+ }
+
+ return boost::shared_ptr<AutomationControl> ();
+}
+
+SlavableControlList
+Route::slavables () const
+{
+ SlavableControlList rv;
+ rv.push_back (_gain_control);
+ rv.push_back (_mute_control);
+ rv.push_back (_solo_control);
+ return rv;
+}
+
+void
+Route::set_disk_io_point (DiskIOPoint diop)
+{
+ bool display = false;
+
+ cerr << "set disk io to " << enum_2_string (diop) << endl;
+
+ switch (diop) {
+ case DiskIOCustom:
+ display = true;
+ break;
+ default:
+ display = false;
+ }
+
+ if (_disk_writer) {
+ _disk_writer->set_display_to_user (display);
+ }
+
+ if (_disk_reader) {
+ _disk_reader->set_display_to_user (display);
+ }
+
+ const bool changed = (diop != _disk_io_point);
+
+ _disk_io_point = diop;
+
+ if (changed) {
+ Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+ configure_processors (0);
+ }
+
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+}
+
+#ifdef USE_TRACKS_CODE_FEATURES
+
+/* This is the Tracks version of Track::monitoring_state().
+ *
+ * Ardour developers: try to flag or fix issues if parts of the libardour API
+ * change in ways that invalidate this
+ */
+
+MonitorState
+Route::monitoring_state () const
+{
+ /* Explicit requests */
+
+ if (_monitoring != MonitorInput) {
+ return MonitoringInput;
+ }
+
+ if (_monitoring & MonitorDisk) {
+ return MonitoringDisk;
+ }
+
+ /* This is an implementation of the truth table in doc/monitor_modes.pdf;
+ I don't think it's ever going to be too pretty too look at.
+ */
+
+ // GZ: NOT USED IN TRACKS
+ //bool const auto_input = _session.config.get_auto_input ();
+ //bool const software_monitor = Config->get_monitoring_model() == SoftwareMonitoring;
+ //bool const tape_machine_mode = Config->get_tape_machine_mode ();
+
+ bool const roll = _session.transport_rolling ();
+ bool const track_rec = _diskstream->record_enabled ();
+ bool session_rec = _session.actively_recording ();
+
+ if (track_rec) {
+
+ if (!session_rec && roll) {
+ return MonitoringDisk;
+ } else {
+ return MonitoringInput;
+ }
+
+ } else {
+
+ if (roll) {
+ return MonitoringDisk;
+ }
+ }
+
+ return MonitoringSilence;
+}
+
+#else
+
+/* This is the Ardour/Mixbus version of Track::monitoring_state().
+ *
+ * Tracks developers: do NOT modify this method under any circumstances.
+ */
+
+MonitorState
+Route::monitoring_state () const
+{
+ if (!_disk_reader) {
+ return MonitoringInput;
+ }
+
+ /* Explicit requests */
+ MonitorChoice m (_monitoring_control->monitoring_choice());
+
+ if (m != MonitorAuto) {
+
+ MonitorState ms ((MonitorState) 0);
+
+ if (m & MonitorInput) {
+ ms = MonitoringInput;
+ }
+
+ if (m & MonitorDisk) {
+ ms = MonitorState (ms | MonitoringDisk);
+ }
+
+ return ms;
+ }
+
+ switch (_session.config.get_session_monitoring ()) {
+ case MonitorDisk:
+ return MonitoringDisk;
+ break;
+ case MonitorInput:
+ return MonitoringInput;
+ break;
+ default:
+ break;
+ }
+
+ /* This is an implementation of the truth table in doc/monitor_modes.pdf;
+ I don't think it's ever going to be too pretty too look at.
+ */
+
+ bool const roll = _session.transport_rolling ();
+ bool const track_rec = _disk_writer->record_enabled ();
+ bool const auto_input = _session.config.get_auto_input ();
+ bool const software_monitor = Config->get_monitoring_model() == SoftwareMonitoring;
+ bool const tape_machine_mode = Config->get_tape_machine_mode ();
+ bool session_rec;
+
+ /* I suspect that just use actively_recording() is good enough all the
+ * time, but just to keep the semantics the same as they were before
+ * sept 26th 2012, we differentiate between the cases where punch is
+ * enabled and those where it is not.
+ *
+ * rg: I suspect this is not the case: monitoring may differ
+ */
+
+ if (_session.config.get_punch_in() || _session.config.get_punch_out() || _session.preroll_record_punch_enabled ()) {
+ session_rec = _session.actively_recording ();
+ } else {
+ session_rec = _session.get_record_enabled();
+ }
+
+ if (track_rec) {
+
+ if (!session_rec && roll && auto_input) {
+ return MonitoringDisk;
+ } else {
+ return software_monitor ? MonitoringInput : MonitoringSilence;
+ }
+
+ } else {
+
+ if (tape_machine_mode) {
+
+ return MonitoringDisk;
+
+ } else {
+
+ if (!roll && auto_input) {
+ return software_monitor ? MonitoringInput : MonitoringSilence;
+ } else {
+ return MonitoringDisk;
+ }
+
+ }
+ }
+
+ abort(); /* NOTREACHED */
+ return MonitoringSilence;
+}
+
+#endif