/* 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. */ #include "pbd/error.h" #include "ardour/amp.h" #include "ardour/audioengine.h" #include "ardour/audiofilesource.h" #include "ardour/audioregion.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/io_processor.h" #include "ardour/meter.h" #include "ardour/midi_region.h" #include "ardour/monitor_control.h" #include "ardour/playlist.h" #include "ardour/playlist_factory.h" #include "ardour/port.h" #include "ardour/processor.h" #include "ardour/profile.h" #include "ardour/region_factory.h" #include "ardour/record_enable_control.h" #include "ardour/record_safe_control.h" #include "ardour/route_group_specialized.h" #include "ardour/session.h" #include "ardour/session_playlists.h" #include "ardour/smf_source.h" #include "ardour/track.h" #include "ardour/types_convert.h" #include "ardour/utils.h" #include "pbd/i18n.h" using namespace std; using namespace ARDOUR; 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) , _alignment_choice (Automatic) { _freeze_record.state = NoFreeze; _declickable = true; } Track::~Track () { DEBUG_TRACE (DEBUG::Destruction, string_compose ("track %1 destructor\n", _name)); if (_disk_reader) { _disk_reader->set_route (boost::shared_ptr()); _disk_reader.reset (); } if (_disk_writer) { _disk_writer->set_route (boost::shared_ptr()); _disk_writer.reset (); } } int Track::init () { if (Route::init ()) { return -1; } 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); } _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_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())); 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)); add_control (_record_enable_control); _record_safe_control.reset (new RecordSafeControl (_session, EventTypeMap::instance().to_symbol (RecSafeAutomation), *this)); add_control (_record_safe_control); _monitoring_control.reset (new MonitorControl (_session, EventTypeMap::instance().to_symbol (MonitoringAutomation), *this)); add_control (_monitoring_control); _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)); _input->changed.connect_same_thread (*this, boost::bind (&Track::input_changed, this)); return 0; } void Track::input_changed () { if (_disk_writer && _alignment_choice == Automatic) { set_align_choice_from_io (); } } XMLNode& Track::get_state () { return state (true); } XMLNode& Track::state (bool full) { XMLNode& root (Route::state (full)); if (_playlists[DataType::AUDIO]) { root.set_property (X_("audio-playlist"), _playlists[DataType::AUDIO]->id().to_s()); } if (_playlists[DataType::MIDI]) { root.set_property (X_("midi-playlist"), _playlists[DataType::MIDI]->id().to_s()); } root.add_child_nocopy (_monitoring_control->get_state ()); root.add_child_nocopy (_record_safe_control->get_state ()); root.add_child_nocopy (_record_enable_control->get_state ()); root.set_property (X_("saved-meter-point"), _saved_meter_point); root.set_property (X_("alignment-choice"), _alignment_choice); return root; } int Track::set_state (const XMLNode& node, int version) { if (Route::set_state (node, version)) { return -1; } XMLNode* child; 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. */ } } std::string playlist_id; if (node.get_property (X_("audio-playlist"), playlist_id)) { find_and_use_playlist (DataType::AUDIO, PBD::ID (playlist_id)); } if (node.get_property (X_("midi-playlist"), playlist_id)) { find_and_use_playlist (DataType::MIDI, PBD::ID (playlist_id)); } XMLNodeList nlist = node.children(); for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) { child = *niter; if (child->name() == Controllable::xml_node_name) { std::string name; if (!child->get_property ("name", name)) { continue; } if (name == _record_enable_control->name()) { _record_enable_control->set_state (*child, version); } else if (name == _record_safe_control->name()) { _record_safe_control->set_state (*child, version); } else if (name == _monitoring_control->name()) { _monitoring_control->set_state (*child, version); } } } if (!node.get_property (X_("saved-meter-point"), _saved_meter_point)) { _saved_meter_point = _meter_point; } AlignChoice ac; if (node.get_property (X_("alignment-choice"), ac)) { set_align_choice (ac, true); } return 0; } XMLNode& Track::get_template () { return state (false); } Track::FreezeRecord::~FreezeRecord () { for (vector::iterator i = processor_info.begin(); i != processor_info.end(); ++i) { delete *i; } } Track::FreezeState Track::freeze_state() const { return _freeze_record.state; } bool Track::can_record() { bool will_record = true; for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end() && will_record; ++i) { if (!i->connected()) will_record = false; } return will_record; } int Track::prep_record_enabled (bool yn) { if (yn && _record_safe_control->get_value()) { return -1; } if (!can_be_record_enabled()) { return -1; } /* keep track of the meter point as it was before we rec-enabled */ if (!_disk_writer->record_enabled()) { _saved_meter_point = _meter_point; } bool will_follow; if (yn) { will_follow = _disk_writer->prep_record_enable (); } else { will_follow = _disk_writer->prep_record_disable (); } if (will_follow) { if (yn) { if (_meter_point != MeterCustom) { set_meter_point (MeterInput); } } else { set_meter_point (_saved_meter_point); } } return 0; } void Track::record_enable_changed (bool, Controllable::GroupControlDisposition) { _disk_writer->set_record_enabled (_record_enable_control->get_value()); } void Track::record_safe_changed (bool, Controllable::GroupControlDisposition) { _disk_writer->set_record_safe (_record_safe_control->get_value()); } bool Track::can_be_record_safe () { return !_record_enable_control->get_value() && _disk_writer && _session.writable() && (_freeze_record.state != Frozen); } bool Track::can_be_record_enabled () { return !_record_safe_control->get_value() && _disk_writer && !_disk_writer->record_safe() && _session.writable() && (_freeze_record.state != Frozen); } void Track::parameter_changed (string const & p) { if (p == "track-name-number") { resync_track_name (); } else if (p == "track-name-take") { resync_track_name (); } else if (p == "take-name") { if (_session.config.get_track_name_take()) { resync_track_name (); } } } void Track::resync_track_name () { set_name(name()); } bool Track::set_name (const string& str) { bool ret; if (str.empty ()) { return false; } if (_record_enable_control->get_value()) { /* when re-arm'ed the file (named after the track) is already ready to rolll */ return false; } string diskstream_name = ""; if (_session.config.get_track_name_take () && !_session.config.get_take_name ().empty()) { // Note: any text is fine, legalize_for_path() fixes this later diskstream_name += _session.config.get_take_name (); diskstream_name += "_"; } const int64_t tracknumber = track_number(); if (tracknumber > 0 && _session.config.get_track_name_number()) { char num[64], fmt[10]; snprintf(fmt, sizeof(fmt), "%%0%d" PRId64, _session.track_number_decimals()); snprintf(num, sizeof(num), fmt, tracknumber); diskstream_name += num; diskstream_name += "_"; } diskstream_name += str; if (diskstream_name == _diskstream_name) { return true; } _diskstream_name = diskstream_name; _disk_writer->set_write_source_name (diskstream_name); 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) { /* 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). */ _disk_reader->set_name (str); _disk_writer->set_name (str); } for (uint32_t n = 0; n < DataType::num_types; ++n) { if (_playlists[n]) { _playlists[n]->set_name (str); } } /* save state so that the statefile fully reflects any filename changes */ if ((ret = Route::set_name (str)) == 0) { _session.save_state (""); } return ret; } void Track::set_latency_compensation (framecnt_t longest_session_latency) { Route::set_latency_compensation (longest_session_latency); _disk_reader->set_roll_delay (_roll_delay); } int Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, 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_frame, end_frame, 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_frame, 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; } _amp->apply_gain_automation (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_frame, end_frame, 1.0, nframes, true); _input->process_input (boost::shared_ptr(), start_frame, end_frame, _session.transport_speed(), nframes); } else { _input->process_input (_meter, start_frame, end_frame, _session.transport_speed(), nframes); } } passthru_silence (start_frame, end_frame, 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_frame, end_frame, _session.transport_speed(), nframes, true); } passthru (bufs, start_frame, end_frame, nframes, false); } flush_processor_buffers_locked (nframes); return 0; } boost::shared_ptr Track::playlist () { return _playlists[data_type()]; } void Track::request_input_monitoring (bool m) { for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end(); ++i) { AudioEngine::instance()->request_input_monitoring ((*i)->name(), m); } } void Track::ensure_input_monitoring (bool m) { for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end(); ++i) { AudioEngine::instance()->ensure_input_monitoring ((*i)->name(), m); } } bool Track::destructive () const { return _disk_writer->destructive (); } list > & Track::last_capture_sources () { return _disk_writer->last_capture_sources (); } void Track::update_latency_information () { Glib::Threads::RWLock::ReaderLock lr (_processor_lock); framecnt_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() { return _disk_writer->steal_write_source_name (); } void Track::reset_write_sources (bool r, bool force) { _disk_writer->reset_write_sources (r, force); } float Track::playback_buffer_load () const { return _disk_reader->buffer_load (); } float Track::capture_buffer_load () const { return _disk_writer->buffer_load (); } int Track::do_refill () { return _disk_reader->do_refill (); } int Track::do_flush (RunContext c, bool force) { return _disk_writer->do_flush (c, force); } void Track::set_pending_overwrite (bool o) { _disk_reader->set_pending_overwrite (o); } int Track::seek (framepos_t p, bool complete_refill) { if (_disk_reader->seek (p, complete_refill)) { return -1; } return _disk_writer->seek (p, complete_refill); } int Track::can_internal_playback_seek (framecnt_t p) { return _disk_reader->can_internal_playback_seek (p); } int Track::internal_playback_seek (framecnt_t p) { return _disk_reader->internal_playback_seek (p); } void Track::non_realtime_locate (framepos_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 Track::overwrite_existing_buffers () { return _disk_reader->overwrite_existing_buffers (); } framecnt_t Track::get_captured_frames (uint32_t n) const { return _disk_writer->get_captured_frames (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 (framepos_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) { _disk_writer->transport_stopped_wallclock (n, t, g); } bool Track::pending_overwrite () const { return _disk_reader->pending_overwrite (); } void Track::prepare_to_stop (framepos_t t, framepos_t a) { _disk_writer->prepare_to_stop (t, a); } void Track::set_slaved (bool s) { _disk_reader->set_slaved (s); _disk_writer->set_slaved (s); } ChanCount Track::n_channels () { return _disk_reader->output_streams(); } framepos_t Track::get_capture_start_frame (uint32_t n) const { return _disk_writer->get_capture_start_frame (n); } AlignStyle Track::alignment_style () const { return _disk_writer->alignment_style (); } AlignChoice Track::alignment_choice () const { return _disk_writer->alignment_choice (); } framepos_t Track::current_capture_start () const { return _disk_writer->current_capture_start (); } framepos_t Track::current_capture_end () const { return _disk_writer->current_capture_end (); } void Track::playlist_modified () { _disk_reader->playlist_modified (); } int Track::find_and_use_playlist (DataType dt, PBD::ID const & id) { boost::shared_ptr playlist; if ((playlist = _session.playlists->by_id (id)) == 0) { return -1; } if (!playlist) { error << string_compose(_("DiskIOProcessor: \"%1\" isn't an playlist"), id.to_s()) << endmsg; return -1; } return use_playlist (dt, playlist); } int Track::use_playlist (DataType dt, boost::shared_ptr p) { int ret; if ((ret = _disk_reader->use_playlist (dt, p)) == 0) { if ((ret = _disk_writer->use_playlist (dt, p)) == 0) { p->set_orig_track_id (id()); } } if (ret == 0) { _playlists[dt] = p; } return ret; } int Track::use_copy_playlist () { assert (_playlists[data_type()]); if (_playlists[data_type()] == 0) { error << string_compose(_("DiskIOProcessor %1: there is no existing playlist to make a copy of!"), _name) << endmsg; return -1; } string newname; boost::shared_ptr playlist; newname = Playlist::bump_name (_playlists[data_type()]->name(), _session); if ((playlist = PlaylistFactory::create (_playlists[data_type()], newname)) == 0) { return -1; } playlist->reset_shares(); return use_playlist (data_type(), playlist); } int Track::use_new_playlist (DataType dt) { string newname; boost::shared_ptr playlist = _playlists[dt]; if (playlist) { newname = Playlist::bump_name (playlist->name(), _session); } else { newname = Playlist::bump_name (_name, _session); } playlist = PlaylistFactory::create (dt, _session, newname, is_private_route()); if (!playlist) { return -1; } return use_playlist (dt, playlist); } void Track::set_align_choice (AlignChoice ac, bool force) { switch (ac) { case Automatic: _alignment_choice = Automatic; set_align_choice_from_io (); return; default: break; } _disk_writer->set_align_choice (ac, force); _alignment_choice = ac; } void Track::set_align_style (AlignStyle s, bool force) { _disk_writer->set_align_style (s, force); } void Track::set_align_choice_from_io () { bool have_physical = false; if (_input) { uint32_t n = 0; vector connections; boost::shared_ptr p; while (true) { p = _input->nth (n++); if (!p) { break; } if (p->get_connections (connections) != 0) { if (AudioEngine::instance()->port_is_physical (connections[0])) { have_physical = true; break; } } connections.clear (); } } #ifdef MIXBUS // compensate for latency when bouncing from master or mixbus. // we need to use "ExistingMaterial" to pick up the master bus' latency // see also Route::direct_feeds_according_to_reality IOVector ios; ios.push_back (_input); if (_session.master_out() && ios.fed_by (_session.master_out()->output())) { have_physical = true; } for (uint32_t n = 0; n < NUM_MIXBUSES && !have_physical; ++n) { if (_session.get_mixbus (n) && ios.fed_by (_session.get_mixbus(n)->output())) { have_physical = true; } } #endif if (have_physical) { _disk_writer->set_align_style (ExistingMaterial); } else { _disk_writer->set_align_style (CaptureTime); } } void Track::set_block_size (pframes_t n) { Route::set_block_size (n); _disk_reader->set_block_size (n); _disk_writer->set_block_size (n); } void Track::adjust_playback_buffering () { if (_disk_reader) { _disk_reader->adjust_buffering (); } } void Track::adjust_capture_buffering () { if (_disk_writer) { _disk_writer->adjust_buffering (); } } #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 Track::monitoring_state () const { /* Explicit requests */ if (_monitoring != MonitorInput) { return MonitoringInput; } if (_monitoring & MonitorDisk) { return MonitoringDisk; } /* This is an implementation of the truth table in doc/monitor_modes.pdf; I don't think it's ever going to be too pretty too look at. */ // GZ: NOT USED IN TRACKS //bool const auto_input = _session.config.get_auto_input (); //bool const software_monitor = Config->get_monitoring_model() == SoftwareMonitoring; //bool const tape_machine_mode = Config->get_tape_machine_mode (); bool const roll = _session.transport_rolling (); bool const track_rec = _diskstream->record_enabled (); bool session_rec = _session.actively_recording (); if (track_rec) { if (!session_rec && roll) { return MonitoringDisk; } else { return MonitoringInput; } } else { if (roll) { return MonitoringDisk; } } return MonitoringSilence; } #else /* This is the Ardour/Mixbus version of Track::monitoring_state(). * * Tracks developers: do NOT modify this method under any circumstances. */ MonitorState Track::monitoring_state () const { /* Explicit requests */ MonitorChoice m (_monitoring_control->monitoring_choice()); if (m != MonitorAuto) { MonitorState ms ((MonitorState) 0); if (m & MonitorInput) { ms = MonitoringInput; } if (m & MonitorDisk) { ms = MonitorState (ms | MonitoringDisk); } return ms; } switch (_session.config.get_session_monitoring ()) { case MonitorDisk: return MonitoringDisk; break; case MonitorInput: return MonitoringInput; break; default: break; } /* This is an implementation of the truth table in doc/monitor_modes.pdf; I don't think it's ever going to be too pretty too look at. */ bool const roll = _session.transport_rolling (); bool const track_rec = _disk_writer->record_enabled (); bool const auto_input = _session.config.get_auto_input (); bool const software_monitor = Config->get_monitoring_model() == SoftwareMonitoring; bool const tape_machine_mode = Config->get_tape_machine_mode (); bool session_rec; /* I suspect that just use actively_recording() is good enough all the * time, but just to keep the semantics the same as they were before * sept 26th 2012, we differentiate between the cases where punch is * enabled and those where it is not. * * rg: I suspect this is not the case: monitoring may differ */ if (_session.config.get_punch_in() || _session.config.get_punch_out() || _session.preroll_record_punch_enabled ()) { session_rec = _session.actively_recording (); } else { session_rec = _session.get_record_enabled(); } if (track_rec) { if (!session_rec && roll && auto_input) { return MonitoringDisk; } else { return software_monitor ? MonitoringInput : MonitoringSilence; } } else { if (tape_machine_mode) { return MonitoringDisk; } else { if (!roll && auto_input) { return software_monitor ? MonitoringInput : MonitoringSilence; } else { return MonitoringDisk; } } } abort(); /* NOTREACHED */ return MonitoringSilence; } #endif void Track::maybe_declick (BufferSet& bufs, framecnt_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); } } framecnt_t Track::check_initial_delay (framecnt_t nframes, framepos_t& transport_frame) { if (_roll_delay > nframes) { _roll_delay -= nframes; silence_unlocked (nframes); /* transport frame is not legal for caller to use */ return 0; } else if (_roll_delay > 0) { nframes -= _roll_delay; silence_unlocked (_roll_delay); transport_frame += _roll_delay; /* shuffle all the port buffers for things that lead "out" of this Route to reflect that we just wrote _roll_delay frames of silence. */ 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) { iop->increment_port_buffer_offset (_roll_delay); } } _output->increment_port_buffer_offset (_roll_delay); _roll_delay = 0; } return nframes; } void Track::monitoring_changed (bool, Controllable::GroupControlDisposition) { for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->monitoring_changed (); } } 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_control->monitoring_choice() & MonitorInput) || _disk_writer->record_enabled()); } else { // track no_roll() always metering if rv = _meter_point == MeterInput; } return rv ? MeteringInput : MeteringRoute; } bool Track::set_processor_state (XMLNode const & node, XMLProperty const* prop, ProcessorList& new_order, bool& must_configure) { if (Route::set_processor_state (node, prop, new_order, must_configure)) { return true; } cerr << name() << " looking for state for track procs, DR = " << _disk_reader << endl; if (prop->value() == "diskreader") { if (_disk_reader) { _disk_reader->set_state (node, Stateful::current_state_version); new_order.push_back (_disk_reader); return true; } } else if (prop->value() == "diskwriter") { if (_disk_writer) { _disk_writer->set_state (node, Stateful::current_state_version); new_order.push_back (_disk_writer); return true; } } error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; return false; } void Track::use_captured_sources (SourceList& srcs, CaptureInfos const & capture_info) { if (srcs.empty()) { return; } boost::shared_ptr afs = boost::dynamic_pointer_cast (srcs.front()); boost::shared_ptr mfs = boost::dynamic_pointer_cast (srcs.front()); if (afs) { use_captured_audio_sources (srcs, capture_info); } if (mfs) { use_captured_midi_sources (srcs, capture_info); } } void Track::use_captured_midi_sources (SourceList& srcs, CaptureInfos const & capture_info) { if (srcs.empty() || data_type() != DataType::MIDI) { return; } boost::shared_ptr mfs = boost::dynamic_pointer_cast (srcs.front()); boost::shared_ptr pl = _playlists[DataType::MIDI]; boost::shared_ptr midi_region; CaptureInfos::const_iterator ci; if (!mfs || !pl) { return; } framecnt_t total_capture = 0; for (total_capture = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) { total_capture += (*ci)->frames; } /* we will want to be able to keep (over)writing the source but we don't want it to be removable. this also differs from the audio situation, where the source at this point must be considered immutable. luckily, we can rely on MidiSource::mark_streaming_write_completed() to have already done the necessary work for that. */ string whole_file_region_name; whole_file_region_name = region_name_from_path (mfs->name(), true); /* Register a new region with the Session that describes the entire source. Do this first so that any sub-regions will obviously be children of this one (later!) */ try { PropertyList plist; plist.add (Properties::name, whole_file_region_name); plist.add (Properties::whole_file, true); plist.add (Properties::automatic, true); plist.add (Properties::start, 0); plist.add (Properties::length, total_capture); plist.add (Properties::layer, 0); boost::shared_ptr rx (RegionFactory::create (srcs, plist)); midi_region = boost::dynamic_pointer_cast (rx); midi_region->special_set_position (capture_info.front()->start); } catch (failed_constructor& err) { error << string_compose(_("%1: could not create region for complete midi file"), _name) << endmsg; /* XXX what now? */ } pl->clear_changes (); pl->freeze (); /* Session frame time of the initial capture in this pass, which is where the source starts */ framepos_t initial_capture = 0; if (!capture_info.empty()) { initial_capture = capture_info.front()->start; } BeatsFramesConverter converter (_session.tempo_map(), capture_info.front()->start); const framepos_t preroll_off = _session.preroll_record_trim_len (); for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { string region_name; RegionFactory::region_name (region_name, mfs->name(), false); DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n", _name, (*ci)->start, (*ci)->frames, region_name)); // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n"; try { PropertyList plist; /* start of this region is the offset between the start of its capture and the start of the whole pass */ plist.add (Properties::start, (*ci)->start - initial_capture); plist.add (Properties::length, (*ci)->frames); plist.add (Properties::length_beats, converter.from((*ci)->frames).to_double()); plist.add (Properties::name, region_name); boost::shared_ptr rx (RegionFactory::create (srcs, plist)); midi_region = boost::dynamic_pointer_cast (rx); if (preroll_off > 0) { midi_region->trim_front ((*ci)->start - initial_capture + preroll_off); } } catch (failed_constructor& err) { error << _("MidiDiskstream: could not create region for captured midi!") << endmsg; continue; /* XXX is this OK? */ } // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl; pl->add_region (midi_region, (*ci)->start + preroll_off, _disk_writer->non_layered()); } pl->thaw (); _session.add_command (new StatefulDiffCommand (pl)); } void Track::use_captured_audio_sources (SourceList& srcs, CaptureInfos const & capture_info) { if (srcs.empty() || data_type() != DataType::AUDIO) { return; } boost::shared_ptr afs = boost::dynamic_pointer_cast (srcs.front()); boost::shared_ptr pl = _playlists[DataType::AUDIO]; boost::shared_ptr region; if (!afs || !pl) { return; } /* destructive tracks have a single, never changing region */ if (destructive()) { /* send a signal that any UI can pick up to do the right thing. there is a small problem here in that a UI may need the peak data to be ready for the data that was recorded and this isn't interlocked with that process. this problem is deferred to the UI. */ pl->LayeringChanged(); // XXX this may not get the UI to do the right thing return; } string whole_file_region_name; whole_file_region_name = region_name_from_path (afs->name(), true); /* Register a new region with the Session that describes the entire source. Do this first so that any sub-regions will obviously be children of this one (later!) */ try { PropertyList plist; plist.add (Properties::start, afs->last_capture_start_frame()); plist.add (Properties::length, afs->length(0)); plist.add (Properties::name, whole_file_region_name); boost::shared_ptr rx (RegionFactory::create (srcs, plist)); rx->set_automatic (true); rx->set_whole_file (true); region = boost::dynamic_pointer_cast (rx); region->special_set_position (afs->natural_position()); } catch (failed_constructor& err) { error << string_compose(_("%1: could not create region for complete audio file"), _name) << endmsg; /* XXX what now? */ } pl->clear_changes (); pl->set_capture_insertion_in_progress (true); pl->freeze (); const framepos_t preroll_off = _session.preroll_record_trim_len (); framecnt_t buffer_position = afs->last_capture_start_frame (); CaptureInfos::const_iterator ci; for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { string region_name; RegionFactory::region_name (region_name, whole_file_region_name, false); DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture bufpos %5 start @ %2 length %3 add new region %4\n", _name, (*ci)->start, (*ci)->frames, region_name, buffer_position)); try { PropertyList plist; plist.add (Properties::start, buffer_position); plist.add (Properties::length, (*ci)->frames); plist.add (Properties::name, region_name); boost::shared_ptr rx (RegionFactory::create (srcs, plist)); region = boost::dynamic_pointer_cast (rx); if (preroll_off > 0) { region->trim_front (buffer_position + preroll_off); } } catch (failed_constructor& err) { error << _("AudioDiskstream: could not create region for captured audio!") << endmsg; continue; /* XXX is this OK? */ } pl->add_region (region, (*ci)->start + preroll_off, 1, _disk_writer->non_layered()); pl->set_layer (region, DBL_MAX); buffer_position += (*ci)->frames; } pl->thaw (); pl->set_capture_insertion_in_progress (false); _session.add_command (new StatefulDiffCommand (pl)); }