#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/types_convert.h"
#include "pbd/unwind.h"
#include "ardour/amp.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/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)
- : Stripable (sess, name)
- , Muteable (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)
, _declickable (false)
, _have_internal_generator (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)
, _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 */
_gain_control.reset (new GainControl (_session, GainAutomation));
- add_control (_gain_control);
-
_trim_control.reset (new GainControl (_session, TrimAutomation));
- add_control (_trim_control);
+ /* 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));
- _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
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));
- _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
add_control (_mute_control);
_phase_control.reset (new PhaseControl (_session, X_("phase")));
/* panning */
- if (!(_flags & Route::MonitorOut)) {
+ if (!(_presentation_info.flags() & PresentationInfo::MonitorOut)) {
_pannable.reset (new Pannable (_session));
}
_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)
{
}
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
/* 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)
{
if (_monitor_send) {
+ if (_monitor_send->active() == yn) {
+ return;
+ }
if (yn) {
_monitor_send->activate ();
} else {
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();
Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
{
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);
loc = _processors.end ();
}
- if (!AudioEngine::instance()->connected()) {
- return 1;
- }
-
if (others.empty()) {
return 0;
}
flags &= mask;
if (flags != None) {
- boost::optional<int> rv = PluginSetup (shared_from_this (), pi, flags); /* EMIT SIGNAL */
- switch (rv.get_value_or (0)) {
+ 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;
default:
break;
}
+ if ((mode & 5) == 4) {
+ fanout = pi;
+ }
}
}
}
}
+ 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 ();
}
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, &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();
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 (enable) {
- sub->activate ();
+ sub->enable (true);
}
sub->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
if (boost::dynamic_pointer_cast<Delivery> (*p)
&& boost::dynamic_pointer_cast<Delivery> (*p)->role() == Delivery::Main
- && !(is_monitor() || is_auditioner())
- && ( _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
lm->acquire ();
return -1;
}
+
processor_max_streams = ChanCount::max(processor_max_streams, c->first);
processor_max_streams = ChanCount::max(processor_max_streams, c->second);
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)
{
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;
}
XMLNode&
Route::state(bool full_state)
{
- LocaleGuard lg;
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;
- 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_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 (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);
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;
- XMLProperty const * 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 (!can_solo()) {
- _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;
}
- }
-
- if (child->name() == Controllable::xml_node_name) {
- if ((prop = child->property (X_("name"))) == 0) {
- continue;
- }
-
- if (prop->value() == _gain_control->name()) {
- _gain_control->set_state (*child, version);
- } else if (prop->value() == _solo_control->name()) {
- _solo_control->set_state (*child, version);
- } else if (prop->value() == _solo_safe_control->name()) {
- _solo_safe_control->set_state (*child, version);
- } else if (prop->value() == _solo_isolate_control->name()) {
- _solo_isolate_control->set_state (*child, version);
- } else if (prop->value() == _solo_control->name()) {
- _mute_control->set_state (*child, version);
- }
+ } 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 (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());
- set_active (yn, this);
+ bool denormal_protection;
+ if (node.get_property (X_("denormal-protection"), denormal_protection)) {
+ set_denormal_protection (denormal_protection);
}
- if ((prop = node.property (X_("order-key"))) != 0) { // New order key (no separate mixer/editor ordering)
- set_order_key (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 (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() == MuteMaster::xml_node_name) {
_mute_master->set_state (*child, version);
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);
- }
+ Stripable::set_state (node, version);
if ((prop = node.property (X_("denormal-protection"))) != 0) {
- set_denormal_protection (string_is_affirmative (prop->value()));
+ 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);
- }
-
}
}
} 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 {
- ProcessorList::iterator o;
+ set_processor_state (**niter, prop, new_order, must_configure);
+ }
+ }
- for (o = _processors.begin(); o != _processors.end(); ++o) {
- XMLProperty const * 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;
- }
- }
+ 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 the processor (*niter) is not on the route then create it
+ if (must_configure) {
+ configure_processors_unlocked (0, &lm);
+ }
- if (o == _processors.end()) {
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
- boost::shared_ptr<Processor> processor;
+ (*i)->set_owner (this);
+ (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
- if (prop->value() == "intsend") {
+ boost::shared_ptr<PluginInsert> pi;
- processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), boost::shared_ptr<Route>(), Delivery::Aux, true));
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+ if (pi->has_no_inputs ()) {
+ _have_internal_generator = true;
+ break;
+ }
+ }
+ }
+ }
+ /* drop references w/o process-lock (I/O procs may re-take it in ~IO() */
+ old_list.clear ();
- } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
- prop->value() == "lv2" ||
- prop->value() == "windows-vst" ||
- prop->value() == "lxvst" ||
- prop->value() == "luaproc" ||
- prop->value() == "audiounit") {
+ reset_instrument_info ();
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+ set_processor_positions ();
+}
- 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);
- }
+bool
+Route::set_processor_state (XMLNode const & node, XMLProperty const* prop, ProcessorList& new_order, bool& must_configure)
+{
+ ProcessorList::iterator o;
- }
- } else if (prop->value() == "port") {
+ 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;
+ }
+ }
- processor.reset (new PortInsert (_session, _pannable, _mute_master));
+ // If the processor (node) is not on the route then create it
- } else if (prop->value() == "send") {
+ if (o == _processors.end()) {
- 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)));
+ boost::shared_ptr<Processor> processor;
- } else {
- error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
- continue;
- }
+ if (prop->value() == "intsend") {
- 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));
- }
+ processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), boost::shared_ptr<Route>(), Delivery::Aux, true));
- /* 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));
- }
+ } 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") {
- /* 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;
+ 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);
}
- /* 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;
}
- }
- }
+ } else if (prop->value() == "port") {
- {
- Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
- Glib::Threads::RWLock::WriterLock lm (_processor_lock);
- _processors = new_order;
+ processor.reset (new PortInsert (_session, _pannable, _mute_master));
- if (must_configure) {
- configure_processors_unlocked (0, &lm);
- }
+ } else if (prop->value() == "send") {
- for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ 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)));
- (*i)->set_owner (this);
- (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
+ } else {
+ return false;
+ }
- boost::shared_ptr<PluginInsert> pi;
+ 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));
+ }
- if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
- if (pi->has_no_inputs ()) {
- _have_internal_generator = true;
- break;
- }
- }
+ /* 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));
}
- }
- reset_instrument_info ();
- processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
- set_processor_positions ();
+ /* 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
}
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);
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);
}
}
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;
io_changed (); /* EMIT SIGNAL */
}
- 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.
+ 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 - _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
- 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_from_this(), &sends_only);
- if (delta != 0 && does_feed && !sends_only) {
- (*i)->solo_control()->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);
+ }
}
- }
+ }
}
}
}
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);
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, &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;
- }
- if ((*i) == _trim) {
- before_amp = false;
+ l_in += (*i)->signal_latency ();
}
- 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;
}
* 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 const * 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);
}
}
_active = yn;
_input->set_active (yn);
_output->set_active (yn);
+ flush_processors ();
active_changed (); // EMIT SIGNAL
_session.set_dirty ();
}
}
-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 */
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.
*/
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;
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;
+ 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 1:
- if (is_master() || mixbus()) {
- port_number = 3;
- } else {
- port_number = 6;
+ } 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;
- case 2:
- if (is_master() || mixbus()) {
- port_number = 2;
- } else {
- port_number = 4;
+#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>();
}
- 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
{
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::vca_assign (boost::shared_ptr<VCA> vca)
+Route::clear_all_solo_state ()
{
- _gain_control->add_master (vca->gain_control());
- _solo_control->add_master (vca->solo_control());
- _mute_control->add_master (vca->mute_control());
+ _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::vca_unassign (boost::shared_ptr<VCA> vca)
+Route::set_disk_io_point (DiskIOPoint diop)
{
- if (!vca) {
- /* unassign from all */
- _gain_control->clear_masters ();
- _solo_control->clear_masters ();
- _mute_control->clear_masters ();
- } else {
- _gain_control->remove_master (vca->gain_control());
- _solo_control->remove_master (vca->solo_control());
- _mute_control->remove_master (vca->mute_control());
+ 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 */
}
-bool
-Route::muted_by_others_soloing () const
+#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
{
- if (!can_be_muted_by_others ()) {
- return false;
+ /* Explicit requests */
+
+ if (_monitoring != MonitorInput) {
+ return MonitoringInput;
+ }
+
+ if (_monitoring & MonitorDisk) {
+ return MonitoringDisk;
}
- return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated();
+ /* 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;
}
-void
-Route::clear_all_solo_state ()
+#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
{
- double v = _solo_safe_control->get_value ();
+ if (!_disk_reader) {
+ return MonitoringInput;
+ }
- _solo_control->clear_all_solo_state ();
+ /* Explicit requests */
+ MonitorChoice m (_monitoring_control->monitoring_choice());
- if (v != 0.0) {
- _solo_safe_control->set_value (v, Controllable::NoGroup);
+ 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