X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Ftrack.cc;h=57239cb841a35201f9d9438bdee416e984978e3f;hb=97a537cd2138dea1c64d01c5a0fa0f9335893c19;hp=549a54e010473905c209edfa55b89e43c93af65b;hpb=60978b5bc49906abae490383a609f907c85a36fa;p=ardour.git diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 549a54e010..57239cb841 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -18,18 +18,17 @@ #include "pbd/error.h" #include "ardour/amp.h" -#include "ardour/audioplaylist.h" -#include "ardour/audioregion.h" -#include "ardour/audiosource.h" #include "ardour/debug.h" #include "ardour/delivery.h" #include "ardour/diskstream.h" #include "ardour/io_processor.h" #include "ardour/meter.h" +#include "ardour/playlist.h" #include "ardour/port.h" #include "ardour/processor.h" #include "ardour/route_group_specialized.h" #include "ardour/session.h" +#include "ardour/session_playlists.h" #include "ardour/track.h" #include "ardour/utils.h" @@ -44,7 +43,6 @@ Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, Data , _saved_meter_point (_meter_point) , _mode (mode) , _monitoring (MonitorAuto) - , _rec_enable_control (new RecEnableControllable(*this)) { _freeze_record.state = NoFreeze; _declickable = true; @@ -62,9 +60,30 @@ Track::init () return -1; } + boost::shared_ptr rp (shared_from_this()); + boost::shared_ptr rt = boost::dynamic_pointer_cast (rp); + _rec_enable_control = boost::shared_ptr (new RecEnableControl(rt)); + _rec_enable_control->set_flags (Controllable::Toggle); + + /* don't add rec_enable_control to controls because we don't want it to + * appear as an automatable parameter + */ + return 0; } +void +Track::use_new_diskstream () +{ + boost::shared_ptr ds = create_diskstream (); + + ds->do_refill_with_alloc (); + ds->set_block_size (_session.get_block_size ()); + ds->playlist()->set_orig_track_id (id()); + + set_diskstream (ds); +} + XMLNode& Track::get_state () { @@ -76,21 +95,17 @@ Track::state (bool full) { XMLNode& root (Route::state (full)); root.add_property (X_("monitoring"), enum_2_string (_monitoring)); + root.add_property (X_("saved-meter-point"), enum_2_string (_saved_meter_point)); root.add_child_nocopy (_rec_enable_control->get_state()); root.add_child_nocopy (_diskstream->get_state ()); + return root; } int Track::set_state (const XMLNode& node, int version) { - return _set_state (node, version); -} - -int -Track::_set_state (const XMLNode& node, int version) -{ - if (Route::_set_state (node, version)) { + if (Route::set_state (node, version)) { return -1; } @@ -104,6 +119,10 @@ Track::_set_state (const XMLNode& node, int version) } } + if (_diskstream) { + _diskstream->playlist()->set_orig_track_id (id()); + } + /* set rec-enable control *AFTER* setting up diskstream, because it may want to operate on the diskstream as it sets its own state */ @@ -128,6 +147,12 @@ Track::_set_state (const XMLNode& node, int version) _monitoring = MonitorAuto; } + if ((prop = node.property (X_("saved-meter-point"))) != 0) { + _saved_meter_point = MeterPoint (string_2_enum (prop->value(), _saved_meter_point)); + } else { + _saved_meter_point = _meter_point; + } + return 0; } @@ -137,14 +162,6 @@ Track::get_template () return state (false); } -void -Track::toggle_monitor_input () -{ - for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end(); ++i) { - i->ensure_monitor_input(!i->monitoring_input()); - } -} - Track::FreezeRecord::~FreezeRecord () { for (vector::iterator i = processor_info.begin(); i != processor_info.end(); ++i) { @@ -158,23 +175,34 @@ Track::freeze_state() const return _freeze_record.state; } -Track::RecEnableControllable::RecEnableControllable (Track& s) - : Controllable (X_("recenable")), track (s) +Track::RecEnableControl::RecEnableControl (boost::shared_ptr t) + : AutomationControl (t->session(), RecEnableAutomation, boost::shared_ptr(), X_("recenable")) + , track (t) { + boost::shared_ptr gl(new AutomationList(Evoral::Parameter(RecEnableAutomation))); + set_list (gl); } void -Track::RecEnableControllable::set_value (double val) +Track::RecEnableControl::set_value (double val) { - bool bval = ((val >= 0.5) ? true: false); - track.set_record_enabled (bval, this); + boost::shared_ptr t = track.lock (); + if (!t) { + return; + } + + t->set_record_enabled (val >= 0.5 ? true : false, this); } double -Track::RecEnableControllable::get_value (void) const +Track::RecEnableControl::get_value () const { - if (track.record_enabled()) { return 1.0; } - return 0.0; + boost::shared_ptr t = track.lock (); + if (!t) { + return 0; + } + + return (t->record_enabled() ? 1.0 : 0.0); } bool @@ -196,7 +224,7 @@ Track::can_record() } void -Track::set_record_enabled (bool yn, void *src) +Track::prep_record_enabled (bool yn, void *src) { if (!_session.writable()) { return; @@ -207,7 +235,7 @@ Track::set_record_enabled (bool yn, void *src) } if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_recenable()) { - _route_group->apply (&Track::set_record_enabled, yn, _route_group); + _route_group->apply (&Track::prep_record_enabled, yn, _route_group); return; } @@ -216,20 +244,46 @@ Track::set_record_enabled (bool yn, void *src) _saved_meter_point = _meter_point; } - _diskstream->set_record_enabled (yn); + bool will_follow; + + if (yn) { + will_follow = _diskstream->prep_record_enable (); + } else { + will_follow = _diskstream->prep_record_disable (); + } - if (_diskstream->record_enabled()) { - if (_meter_point != MeterCustom) { - set_meter_point (MeterInput); + if (will_follow) { + if (yn) { + if (_meter_point != MeterCustom) { + set_meter_point (MeterInput); + } + } else { + set_meter_point (_saved_meter_point); } - } else { - set_meter_point (_saved_meter_point); } +} + +void +Track::set_record_enabled (bool yn, void *src) +{ + if (!_session.writable()) { + return; + } + + if (_freeze_record.state == Frozen) { + return; + } + + if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_recenable()) { + _route_group->apply (&Track::set_record_enabled, yn, _route_group); + return; + } + + _diskstream->set_record_enabled (yn); _rec_enable_control->Changed (); } - bool Track::set_name (const string& str) { @@ -240,7 +294,21 @@ Track::set_name (const string& str) return false; } - _diskstream->set_name (str); + boost::shared_ptr me = boost::dynamic_pointer_cast (shared_from_this ()); + if (_diskstream->playlist()->all_regions_empty () && _session.playlists->playlists_for_track (me).size() == 1) { + /* Only rename the diskstream (and therefore the playlist) if + a) the playlist has never had a region added to it and + b) there is only one playlist for this track. + + If (a) is not followed, people can get confused if, say, + they have notes about a playlist with a given name and then + it changes (see mantis #4759). + + If (b) is not followed, we rename the current playlist and not + the other ones, which is a bit confusing (see mantis #4977). + */ + _diskstream->set_name (str); + } /* save state so that the statefile fully reflects any filename changes */ @@ -258,30 +326,30 @@ Track::set_latency_compensation (framecnt_t longest_session_latency) _diskstream->set_roll_delay (_roll_delay); } -void -Track::zero_diskstream_id_in_xml (XMLNode& node) -{ - if (node.property ("diskstream-id")) { - node.add_property ("diskstream-id", "0"); - } -} - int Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing) { - Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); + if (!lm.locked()) { return 0; } bool can_record = _session.actively_recording (); + /* no outputs? nothing to do ... what happens if we have sends etc. ? */ + if (n_outputs().n_total() == 0) { return 0; } + /* not active ... do the minimum possible by just outputting silence */ + if (!_active) { silence (nframes); + if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + _meter->reset(); + } return 0; } @@ -310,39 +378,94 @@ Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, into the route. */ be_silent = true; + } else { - be_silent = send_silence (); + + MonitorState const s = monitoring_state (); + /* we are not rolling, so be silent even if we are monitoring disk, as there + will be no disk data coming in. + */ + switch (s) { + case MonitoringSilence: + /* if there is an instrument, be_silent should always + be false + */ + be_silent = (the_instrument_unlocked() == 0); + break; + case MonitoringDisk: + be_silent = true; + break; + case MonitoringInput: + be_silent = false; + break; + default: + be_silent = false; + break; + } } - _amp->apply_gain_automation(false); + _amp->apply_gain_automation (false); + + /* if have_internal_generator, or .. */ if (be_silent) { - /* if we're sending silence, but we want the meters to show levels for the signal, - meter right here. - */ + if (_meter_point == MeterInput) { + /* still need input monitoring and metering */ + + bool const track_rec = _diskstream->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 no_meter = false; + + /* this needs a proper K-map + * and should be separated into a function similar to monitoring_state() + * that also handles roll() states in audio_track.cc, midi_track.cc and route.cc + * + * see http://www.oofus.co.uk/ardour/Ardour3MonitorModesV3.pdf + */ + if (!auto_input && !track_rec) { + no_meter=true; + } + else if (tape_machine_mode && !track_rec && auto_input) { + no_meter=true; + } + else if (!software_monitor && tape_machine_mode && !track_rec) { + no_meter=true; + } + else if (!software_monitor && !tape_machine_mode && !track_rec && !auto_input) { + no_meter=true; + } - if (_have_internal_generator) { - passthru_silence (start_frame, end_frame, nframes, 0); - } else { - if (_meter_point == MeterInput) { + if (no_meter) { + BufferSet& bufs (_session.get_silent_buffers (n_process_buffers())); + _meter->run (bufs, 0, 0, nframes, true); + _input->process_input (boost::shared_ptr(), start_frame, end_frame, nframes); + } else { _input->process_input (_meter, start_frame, end_frame, nframes); } - passthru_silence (start_frame, end_frame, nframes, 0); } + passthru_silence (start_frame, end_frame, nframes, 0); + } else { - /* we're sending signal, but we may still want to meter the input. - */ + BufferSet& bufs = _session.get_route_buffers (n_process_buffers()); + + fill_buffers_with_input (bufs, _input, nframes); - passthru (start_frame, end_frame, nframes, false); + if (_meter_point == MeterInput) { + _meter->run (bufs, start_frame, end_frame, nframes, true); + } + + passthru (bufs, start_frame, end_frame, nframes, false); } for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr d = boost::dynamic_pointer_cast (*i); if (d) { - d->flush_buffers (nframes, end_frame - start_frame - 1); + d->flush_buffers (nframes); } } @@ -352,8 +475,12 @@ Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int Track::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, bool& need_butler) { - Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); if (!lm.locked()) { + framecnt_t playback_distance = _diskstream->calculate_playback_distance(nframes); + if (can_internal_playback_seek(playback_distance)) { + internal_playback_seek(playback_distance); + } return 0; } @@ -371,7 +498,13 @@ Track::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /* silence (nframes); - return _diskstream->process (_session.transport_frame(), nframes, need_butler); + framecnt_t playback_distance; + + BufferSet& bufs (_session.get_route_buffers (n_process_buffers(), true)); + + int const dret = _diskstream->process (bufs, _session.transport_frame(), nframes, playback_distance, false); + need_butler = _diskstream->commit (playback_distance); + return dret; } void @@ -417,9 +550,15 @@ Track::playlist () } void -Track::monitor_input (bool m) +Track::request_input_monitoring (bool m) +{ + _diskstream->request_input_monitoring (m); +} + +void +Track::ensure_input_monitoring (bool m) { - _diskstream->monitor_input (m); + _diskstream->ensure_input_monitoring (m); } bool @@ -495,13 +634,13 @@ Track::hidden () const } int -Track::can_internal_playback_seek (framepos_t p) +Track::can_internal_playback_seek (framecnt_t p) { return _diskstream->can_internal_playback_seek (p); } int -Track::internal_playback_seek (framepos_t p) +Track::internal_playback_seek (framecnt_t p) { return _diskstream->internal_playback_seek (p); } @@ -515,7 +654,14 @@ Track::non_realtime_input_change () void Track::non_realtime_locate (framepos_t p) { - _diskstream->non_realtime_locate (p); + Route::non_realtime_locate (p); + + if (!hidden()) { + /* don't waste i/o cycles and butler calls + for hidden (secret) tracks + */ + _diskstream->non_realtime_locate (p); + } } void @@ -629,19 +775,35 @@ Track::playlist_modified () int Track::use_playlist (boost::shared_ptr p) { - return _diskstream->use_playlist (p); + int ret = _diskstream->use_playlist (p); + if (ret == 0) { + p->set_orig_track_id (id()); + } + return ret; } int Track::use_copy_playlist () { - return _diskstream->use_copy_playlist (); + int ret = _diskstream->use_copy_playlist (); + + if (ret == 0) { + _diskstream->playlist()->set_orig_track_id (id()); + } + + return ret; } int Track::use_new_playlist () { - return _diskstream->use_new_playlist (); + int ret = _diskstream->use_new_playlist (); + + if (ret == 0) { + _diskstream->playlist()->set_orig_track_id (id()); + } + + return ret; } void @@ -656,10 +818,10 @@ Track::set_align_choice (AlignChoice s, bool force) _diskstream->set_align_choice (s, force); } -PBD::ID const & -Track::diskstream_id () const +bool +Track::using_diskstream_id (PBD::ID id) const { - return _diskstream->id (); + return (id == _diskstream->id ()); } void @@ -685,101 +847,69 @@ Track::adjust_capture_buffering () } } -bool -Track::send_silence () const -{ - bool send_silence; - - if (Config->get_tape_machine_mode()) { - - /* ADATs work in a strange way.. - they monitor input always when stopped.and auto-input is engaged. - */ - - if ((Config->get_monitoring_model() == SoftwareMonitoring) - && ((_monitoring & MonitorInput) || (_diskstream->record_enabled()))) { - send_silence = false; - } else { - send_silence = true; - } - - - } else { - - /* Other machines switch to input on stop if the track is record enabled, - regardless of the auto input setting (auto input only changes the - monitoring state when the transport is rolling) - */ - - if ((Config->get_monitoring_model() == SoftwareMonitoring) - && ((_monitoring & MonitorInput) || - (!(_monitoring & MonitorDisk) && (_session.config.get_auto_input () || _diskstream->record_enabled())))){ - - DEBUG_TRACE (DEBUG::Monitor, - string_compose ("%1: no roll, use silence = FALSE, monitoring choice %2 recenable %3 sRA %4 autoinput %5\n", - name(), enum_2_string (_monitoring), - _diskstream->record_enabled(), _session.actively_recording(), - _session.config.get_auto_input())); - - send_silence = false; - } else { - DEBUG_TRACE (DEBUG::Monitor, - string_compose ("%1: no roll, use silence = TRUE, monitoring choice %2 recenable %3 sRA %4 autoinput %5\n", - name(), enum_2_string (_monitoring), - _diskstream->record_enabled(), _session.actively_recording(), - _session.config.get_auto_input())); - send_silence = true; - } - } - - return send_silence; -} - MonitorState -Track::monitoring_state () +Track::monitoring_state () const { - MonitorState ms = MonitoringSilence; - - if (_session.transport_rolling()) { - - /* roll case */ + /* Explicit requests */ + + if (_monitoring & MonitorInput) { + return MonitoringInput; + } - if (_monitoring & MonitorInput) { // explicitly requested input monitoring - - ms = MonitoringInput; - - } else if (_monitoring & MonitorDisk) { // explicitly requested disk monitoring - - ms = MonitoringDisk; + if (_monitoring & MonitorDisk) { + return MonitoringDisk; + } - } else if (_diskstream->record_enabled() && _session.actively_recording()) { // Track actually recording - - ms = MonitoringInput; + /* 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. + */ - } else if (_diskstream->record_enabled() && !_session.actively_recording() && _session.config.get_auto_input()) { // Track armed but not recording, with auto input enabled - - ms = MonitoringInput; + bool const roll = _session.transport_rolling (); + bool const track_rec = _diskstream->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. + */ + + if (_session.config.get_punch_in() || _session.config.get_punch_out()) { + session_rec = _session.actively_recording (); + } else { + session_rec = _session.get_record_enabled(); + } - } else { // Every other state - - ms = MonitoringDisk; + if (track_rec) { + if (!session_rec && roll && auto_input) { + return MonitoringDisk; + } else { + return software_monitor ? MonitoringInput : MonitoringSilence; } } else { - /* no-roll case */ + if (tape_machine_mode) { + + return MonitoringDisk; - if (send_silence()) { - - ms = MonitoringSilence; } else { + + if (!roll && auto_input) { + return software_monitor ? MonitoringInput : MonitoringSilence; + } else { + return MonitoringDisk; + } - ms = MonitoringInput; } } - return ms; + /* NOTREACHED */ + return MonitoringSilence; } void @@ -805,7 +935,7 @@ Track::maybe_declick (BufferSet& bufs, framecnt_t nframes, int declick) } framecnt_t -Track::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame) +Track::check_initial_delay (framecnt_t nframes, framepos_t& transport_frame) { if (_roll_delay > nframes) { @@ -824,7 +954,7 @@ Track::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame) to reflect that we just wrote _roll_delay frames of silence. */ - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr iop = boost::dynamic_pointer_cast (*i); if (iop) { @@ -845,7 +975,26 @@ Track::set_monitoring (MonitorChoice mc) { if (mc != _monitoring) { _monitoring = mc; + + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + (*i)->monitoring_changed (); + } + MonitoringChanged (); /* EMIT SIGNAL */ } } +MeterState +Track::metering_state () const +{ + bool rv; + if (_session.transport_rolling ()) { + // audio_track.cc || midi_track.cc roll() runs meter IFF: + rv = _meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled()); + } else { + // track no_roll() always metering if + rv = _meter_point == MeterInput; + } + return rv ? MeteringInput : MeteringRoute; +} +