X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Ftrack.cc;h=e80026742a8908fb02f6556f615775120e674e2e;hb=f08b90f36870249cc3e9ea089dd6594269c67148;hp=f451a3ed51cb55ed51ddc8cf2e9f701e3254925d;hpb=48d11000e5c13ebc831b98c56bc18329e6fc7505;p=ardour.git diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index f451a3ed51..e80026742a 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -1,25 +1,31 @@ /* - Copyright (C) 2006 Paul Davis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + * Copyright (C) 2006-2014 David Robillard + * Copyright (C) 2007-2012 Carl Hetherington + * Copyright (C) 2007-2019 Paul Davis + * Copyright (C) 2013-2019 Robin Gareus + * Copyright (C) 2014-2018 Ben Loftis + * Copyright (C) 2016 Julien "_FrnchFrgg_" RIVAUD + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ #include "pbd/error.h" #include "ardour/amp.h" #include "ardour/audioengine.h" #include "ardour/audiofilesource.h" +#include "ardour/audioplaylist.h" #include "ardour/audioregion.h" #include "ardour/debug.h" #include "ardour/delivery.h" @@ -28,6 +34,7 @@ #include "ardour/event_type_map.h" #include "ardour/io_processor.h" #include "ardour/meter.h" +#include "ardour/midi_playlist.h" #include "ardour/midi_region.h" #include "ardour/monitor_control.h" #include "ardour/playlist.h" @@ -54,13 +61,11 @@ using namespace PBD; Track::Track (Session& sess, string name, PresentationInfo::Flag flag, TrackMode mode, DataType default_type) : Route (sess, name, flag, default_type) - , _saved_meter_point (_meter_point) - , _mode (mode) + , _saved_meter_point (_meter_point) + , _mode (mode) , _alignment_choice (Automatic) { _freeze_record.state = NoFreeze; - _declickable = true; - } Track::~Track () @@ -81,29 +86,37 @@ Track::~Track () int Track::init () { - if (Route::init ()) { - return -1; - } + if (Route::init ()) { + return -1; + } - DiskIOProcessor::Flag dflags = DiskIOProcessor::Recordable; + DiskIOProcessor::Flag dflags = DiskIOProcessor::Recordable; - if (_mode == Destructive && !Profile->get_trx()) { - dflags = DiskIOProcessor::Flag (dflags | DiskIOProcessor::Destructive); - } else if (_mode == NonLayered){ - dflags = DiskIOProcessor::Flag(dflags | DiskIOProcessor::NonLayered); - } + if (_mode == Destructive && !Profile->get_trx()) { + dflags = DiskIOProcessor::Flag (dflags | DiskIOProcessor::Destructive); + } - _disk_reader.reset (new DiskReader (_session, name(), dflags)); - _disk_reader->set_block_size (_session.get_block_size ()); - _disk_reader->set_route (boost::dynamic_pointer_cast (shared_from_this())); + _disk_reader.reset (new DiskReader (_session, name(), dflags)); + _disk_reader->set_block_size (_session.get_block_size ()); + _disk_reader->set_route (boost::dynamic_pointer_cast (shared_from_this())); + _disk_reader->set_owner (this); - _disk_writer.reset (new DiskWriter (_session, name(), dflags)); - _disk_writer->set_block_size (_session.get_block_size ()); - _disk_writer->set_route (boost::dynamic_pointer_cast (shared_from_this())); + _disk_writer.reset (new DiskWriter (_session, name(), dflags)); + _disk_writer->set_block_size (_session.get_block_size ()); + _disk_writer->set_route (boost::dynamic_pointer_cast (shared_from_this())); + _disk_writer->set_owner (this); - use_new_playlist (data_type()); + set_align_choice_from_io (); - boost::shared_ptr rp (boost::dynamic_pointer_cast (shared_from_this())); + if (!name().empty()) { + /* an empty name means that we are being constructed via + serialized state (XML). Don't create a playlist, because one + will be created or discovered during ::set_state(). + */ + use_new_playlist (data_type()); + } + + boost::shared_ptr rp (boost::dynamic_pointer_cast (shared_from_this())); boost::shared_ptr rt = boost::dynamic_pointer_cast (rp); _record_enable_control.reset (new RecordEnableControl (_session, EventTypeMap::instance().to_symbol (RecEnableAutomation), *this)); @@ -117,13 +130,13 @@ Track::init () _session.config.ParameterChanged.connect_same_thread (*this, boost::bind (&Track::parameter_changed, this, _1)); - _monitoring_control->Changed.connect_same_thread (*this, boost::bind (&Track::monitoring_changed, this, _1, _2)); - _record_safe_control->Changed.connect_same_thread (*this, boost::bind (&Track::record_safe_changed, this, _1, _2)); - _record_enable_control->Changed.connect_same_thread (*this, boost::bind (&Track::record_enable_changed, this, _1, _2)); + _monitoring_control->Changed.connect_same_thread (*this, boost::bind (&Track::monitoring_changed, this, _1, _2)); + _record_safe_control->Changed.connect_same_thread (*this, boost::bind (&Track::record_safe_changed, this, _1, _2)); + _record_enable_control->Changed.connect_same_thread (*this, boost::bind (&Track::record_enable_changed, this, _1, _2)); - _input->changed.connect_same_thread (*this, boost::bind (&Track::input_changed, this)); + _input->changed.connect_same_thread (*this, boost::bind (&Track::input_changed, this)); - return 0; + return 0; } void @@ -135,15 +148,9 @@ Track::input_changed () } XMLNode& -Track::get_state () +Track::state (bool save_template) { - return state (true); -} - -XMLNode& -Track::state (bool full) -{ - XMLNode& root (Route::state (full)); + XMLNode& root (Route::state (save_template)); if (_playlists[DataType::AUDIO]) { root.set_property (X_("audio-playlist"), _playlists[DataType::AUDIO]->id().to_s()); @@ -170,22 +177,33 @@ Track::set_state (const XMLNode& node, int version) return -1; } - XMLNode* child; + if (version >= 3000 && version < 6000) { + if (XMLNode* ds_node = find_named_node (node, "Diskstream")) { + std::string name; + if (ds_node->get_property ("playlist", name)) { + + ds_node->set_property ("active", true); + + _disk_writer->set_state (*ds_node, version); + _disk_reader->set_state (*ds_node, version); + + AlignChoice ac; + if (ds_node->get_property (X_("capture-alignment"), ac)) { + set_align_choice (ac, true); + } + + if (boost::shared_ptr pl = boost::dynamic_pointer_cast (_session.playlists()->by_name (name))) { + use_playlist (DataType::AUDIO, pl); + } - if (version >= 3000 && version < 4000) { - if ((child = find_named_node (node, X_("Diskstream"))) != 0) { - /* XXX if we remember anything from stored DiskStream - state (older Ardour versions) that is needed by a - DiskReader or DiskWriter, we should cook up a new - XMLNode here, populate it with that information - (child nodes, properties, etc.) and then call - ::set_state() on the writer/reader. - - But at present (June 2017), there's no such state. - */ + if (boost::shared_ptr pl = boost::dynamic_pointer_cast (_session.playlists()->by_name (name))) { + use_playlist (DataType::MIDI, pl); + } + } } } + XMLNode* child; std::string playlist_id; if (node.get_property (X_("audio-playlist"), playlist_id)) { @@ -230,12 +248,6 @@ Track::set_state (const XMLNode& node, int version) return 0; } -XMLNode& -Track::get_template () -{ - return state (false); -} - Track::FreezeRecord::~FreezeRecord () { for (vector::iterator i = processor_info.begin(); i != processor_info.end(); ++i) { @@ -249,6 +261,12 @@ Track::freeze_state() const return _freeze_record.state; } +bool +Track::declick_in_progress () const +{ + return _disk_reader->declick_in_progress (); +} + bool Track::can_record() { @@ -383,7 +401,7 @@ Track::set_name (const string& str) boost::shared_ptr me = boost::dynamic_pointer_cast (shared_from_this ()); - if (_playlists[data_type()]->all_regions_empty () && _session.playlists->playlists_for_track (me).size() == 1) { + if (_playlists[data_type()]->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. @@ -414,139 +432,6 @@ Track::set_name (const string& str) return ret; } -int -Track::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); - - 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 && !ARDOUR::Profile->get_mixbus()) { - //Note: Mixbus has its own output mechanism, so we should operate even if no explicit outputs are assigned - return 0; - } - - /* not active ... do the minimum possible by just outputting silence */ - - if (!_active) { - silence (nframes); - if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _disk_writer->record_enabled())) { - _meter->reset(); - } - return 0; - } - - if (session_state_changing) { - if (_session.transport_speed() != 0.0f) { - /* we're rolling but some state is changing (e.g. our - disk reader contents) so we cannot use them. Be - silent till this is over. Don't declick. - - XXX note the absurdity of ::no_roll() being called when we ARE rolling! - */ - passthru_silence (start_sample, end_sample, nframes, 0); - return 0; - } - /* we're really not rolling, so we're either delivery silence or actually - monitoring, both of which are safe to do while session_state_changing is true. - */ - } - - _disk_writer->check_record_status (start_sample, can_record); - - bool be_silent; - - 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: - be_silent = true; - break; - case MonitoringDisk: - be_silent = true; - break; - case MonitoringInput: - be_silent = false; - break; - default: - be_silent = false; - break; - } - - //if we have an internal generator, let it play regardless of monitoring state - if (_have_internal_generator) { - be_silent = false; - } - - /* if have_internal_generator, or .. */ - - if (be_silent) { - - if (_meter_point == MeterInput) { - /* still need input monitoring and metering */ - - 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 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 (no_meter) { - BufferSet& bufs (_session.get_silent_buffers (n_process_buffers())); - _meter->run (bufs, start_sample, end_sample, 1.0, nframes, true); - _input->process_input (boost::shared_ptr(), start_sample, end_sample, _session.transport_speed(), nframes); - } else { - _input->process_input (_meter, start_sample, end_sample, _session.transport_speed(), nframes); - } - } - - passthru_silence (start_sample, end_sample, nframes, 0); - - } else { - - BufferSet& bufs = _session.get_route_buffers (n_process_buffers()); - - fill_buffers_with_input (bufs, _input, nframes); - - if (_meter_point == MeterInput) { - _meter->run (bufs, start_sample, end_sample, _session.transport_speed(), nframes, true); - } - - passthru (bufs, start_sample, end_sample, nframes, false, true); - } - - flush_processor_buffers_locked (nframes); - - return 0; -} - boost::shared_ptr Track::playlist () { @@ -581,18 +466,6 @@ Track::last_capture_sources () return _disk_writer->last_capture_sources (); } -void -Track::update_latency_information () -{ - Glib::Threads::RWLock::ReaderLock lr (_processor_lock); - samplecnt_t chain_latency = _input->latency (); - - for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) { - (*p)->set_input_latency (chain_latency); - chain_latency += (*p)->signal_latency (); - } -} - std::string Track::steal_write_source_name() { @@ -630,9 +503,9 @@ Track::do_flush (RunContext c, bool force) } void -Track::set_pending_overwrite (bool o) +Track::set_pending_overwrite () { - _disk_reader->set_pending_overwrite (o); + _disk_reader->set_pending_overwrite (); } int @@ -644,13 +517,13 @@ Track::seek (samplepos_t p, bool complete_refill) return _disk_writer->seek (p, complete_refill); } -int +bool Track::can_internal_playback_seek (samplecnt_t p) { return _disk_reader->can_internal_playback_seek (p); } -int +void Track::internal_playback_seek (samplecnt_t p) { return _disk_reader->internal_playback_seek (p); @@ -660,23 +533,9 @@ void Track::non_realtime_locate (samplepos_t p) { Route::non_realtime_locate (p); - - if (!is_private_route()) { - /* don't waste i/o cycles and butler calls - for private tracks (e.g.auditioner) - */ - _disk_reader->non_realtime_locate (p); - _disk_writer->non_realtime_locate (p); - } } -void -Track::non_realtime_speed_change () -{ - _disk_reader->non_realtime_speed_change (); -} - -int +bool Track::overwrite_existing_buffers () { return _disk_reader->overwrite_existing_buffers (); @@ -688,44 +547,12 @@ Track::get_captured_samples (uint32_t n) const return _disk_writer->get_captured_samples (n); } -int -Track::set_loop (Location* l) -{ - if (_disk_reader->set_loop (l)) { - return -1; - } - return _disk_writer->set_loop (l); -} - void Track::transport_looped (samplepos_t p) { return _disk_writer->transport_looped (p); } -bool -Track::realtime_speed_change () -{ - if (_disk_reader->realtime_speed_change ()) { - return -1; - } - return _disk_writer->realtime_speed_change (); -} - -void -Track::realtime_handle_transport_stopped () -{ - Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); - - if (!lm.locked ()) { - return; - } - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (*i)->realtime_handle_transport_stopped (); - } -} - void Track::transport_stopped_wallclock (struct tm & n, time_t t, bool g) { @@ -738,12 +565,6 @@ Track::pending_overwrite () const return _disk_reader->pending_overwrite (); } -void -Track::prepare_to_stop (samplepos_t t, samplepos_t a) -{ - _disk_writer->prepare_to_stop (t, a); -} - void Track::set_slaved (bool s) { @@ -772,7 +593,7 @@ Track::alignment_style () const AlignChoice Track::alignment_choice () const { - return _disk_writer->alignment_choice (); + return _alignment_choice; } samplepos_t @@ -798,7 +619,7 @@ Track::find_and_use_playlist (DataType dt, PBD::ID const & id) { boost::shared_ptr playlist; - if ((playlist = _session.playlists->by_id (id)) == 0) { + if ((playlist = _session.playlists()->by_id (id)) == 0) { return -1; } @@ -810,6 +631,13 @@ Track::find_and_use_playlist (DataType dt, PBD::ID const & id) return use_playlist (dt, playlist); } +void +update_region_visibility(boost::shared_ptr r) +{ + Region::RegionPropertyChanged(r, Properties::hidden); +} + + int Track::use_playlist (DataType dt, boost::shared_ptr p) { @@ -821,9 +649,18 @@ Track::use_playlist (DataType dt, boost::shared_ptr p) } } + boost::shared_ptr old = _playlists[dt]; + if (ret == 0) { _playlists[dt] = p; } + + //allow all regions of prior and new playlists to update their visibility? + if (old) old->foreach_region(update_region_visibility); + if (p) p->foreach_region(update_region_visibility); + + _session.set_dirty (); + PlaylistChanged (); /* EMIT SIGNAL */ return ret; } @@ -876,17 +713,18 @@ Track::use_new_playlist (DataType dt) void Track::set_align_choice (AlignChoice ac, bool force) { + _alignment_choice = ac; switch (ac) { - case Automatic: - _alignment_choice = Automatic; - set_align_choice_from_io (); - return; - default: - break; + case Automatic: + set_align_choice_from_io (); + break; + case UseCaptureTime: + _disk_writer->set_align_style (CaptureTime, force); + break; + case UseExistingMaterial: + _disk_writer->set_align_style (ExistingMaterial, force); + break; } - - _disk_writer->set_align_choice (ac, force); - _alignment_choice = ac; } void @@ -922,8 +760,20 @@ Track::set_align_choice_from_io () connections.clear (); } + + /* Special case bounding the Metronome. + * Click-out is aligned to output and hence + * equivalent to a physical round-trip alike + * ExistingMaterial. + */ + if (!have_physical && _session.click_io ()) { + if (_session.click_io ()->connected_to (_input)) { + have_physical = true; + } + } } + #ifdef MIXBUS // compensate for latency when bouncing from master or mixbus. // we need to use "ExistingMaterial" to pick up the master bus' latency @@ -971,29 +821,6 @@ Track::adjust_capture_buffering () } } - -void -Track::maybe_declick (BufferSet& bufs, samplecnt_t nframes, int declick) -{ - /* never declick if there is an internal generator - we just want it to - keep generating sound without interruption. - - ditto if we are monitoring inputs. - */ - - if (_have_internal_generator || (_monitoring_control->monitoring_choice() == MonitorInput)) { - return; - } - - if (!declick) { - declick = _pending_declick; - } - - if (declick != 0) { - Amp::declick (bufs, nframes, declick); - } -} - void Track::monitoring_changed (bool, Controllable::GroupControlDisposition) { @@ -1167,9 +994,9 @@ Track::use_captured_midi_sources (SourceList& srcs, CaptureInfos const & capture continue; /* XXX is this OK? */ } - // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl; + cerr << "add new region, len = " << (*ci)->samples << " @ " << (*ci)->start << endl; - pl->add_region (midi_region, (*ci)->start + preroll_off, _disk_writer->non_layered()); + pl->add_region (midi_region, (*ci)->start + preroll_off, 1, _session.config.get_layered_record_mode ()); } pl->thaw (); @@ -1271,7 +1098,7 @@ Track::use_captured_audio_sources (SourceList& srcs, CaptureInfos const & captur continue; /* XXX is this OK? */ } - pl->add_region (region, (*ci)->start + preroll_off, 1, _disk_writer->non_layered()); + pl->add_region (region, (*ci)->start + preroll_off, 1, _session.config.get_layered_record_mode()); pl->set_layer (region, DBL_MAX); buffer_position += (*ci)->samples;