diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/ardour/ardour/disk_reader.h | 126 | ||||
-rw-r--r-- | libs/ardour/ardour/disk_writer.h | 168 | ||||
-rw-r--r-- | libs/ardour/disk_io.cc | 205 | ||||
-rw-r--r-- | libs/ardour/disk_reader.cc | 177 | ||||
-rw-r--r-- | libs/ardour/disk_writer.cc | 349 |
5 files changed, 1025 insertions, 0 deletions
diff --git a/libs/ardour/ardour/disk_reader.h b/libs/ardour/ardour/disk_reader.h new file mode 100644 index 0000000000..47dd1d87cc --- /dev/null +++ b/libs/ardour/ardour/disk_reader.h @@ -0,0 +1,126 @@ +/* + Copyright (C) 2009-2016 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. + +*/ + +#ifndef __ardour_disk_reader_h__ +#define __ardour_disk_reader_h__ + +#include "ardour/disk_io.h" + +namespace ARDOUR +{ + +class Playlist; +class AudioPlaylist; +class MidiPlaylist; + +class LIBARDOUR_API DiskReader : public DiskIOProcessor +{ + public: + DiskReader (Session&, std::string const & name, DiskIOProcessor::Flag f = DiskIOProcessor::Flag (0)); + ~DiskReader (); + + bool set_name (std::string const & str); + + static framecnt_t chunk_frames() { return _chunk_frames; } + static framecnt_t default_chunk_frames (); + static void set_chunk_frames (framecnt_t n) { _chunk_frames = n; } + + void run (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framepos_t /*end_frame*/, double speed, pframes_t /*nframes*/, bool /*result_required*/); + void silence (framecnt_t /*nframes*/, framepos_t /*start_frame*/); + bool configure_io (ChanCount in, ChanCount out); + bool can_support_io_configuration (const ChanCount& in, ChanCount& out) = 0; + ChanCount input_streams () const; + ChanCount output_streams() const; + void realtime_handle_transport_stopped (); + void realtime_locate (); + + framecnt_t roll_delay() const { return _roll_delay; } + void set_roll_delay (framecnt_t); + + virtual XMLNode& state (bool full); + int set_state (const XMLNode&, int version); + + boost::shared_ptr<Playlist> playlist(); + boost::shared_ptr<Playlist> get_playlist (DataType); + boost::shared_ptr<MidiPlaylist> midi_playlist(); + boost::shared_ptr<AudioPlaylist> audio_playlist(); + + virtual void playlist_modified (); + virtual int use_playlist (boost::shared_ptr<Playlist>); + virtual int use_new_playlist () = 0; + virtual int use_copy_playlist () = 0; + + PBD::Signal0<void> PlaylistChanged; + PBD::Signal0<void> AlignmentStyleChanged; + + float buffer_load() const; + + void move_processor_automation (boost::weak_ptr<Processor>, std::list<Evoral::RangeMove<framepos_t> > const &); + + /** For non-butler contexts (allocates temporary working buffers) + * + * This accessible method has a default argument; derived classes + * must inherit the virtual method that we call which does NOT + * have a default argument, to avoid complications with inheritance + */ + int do_refill_with_alloc(bool partial_fill = true) { + return _do_refill_with_alloc (partial_fill); + } + + bool pending_overwrite () const { return _pending_overwrite; } + + virtual int find_and_use_playlist (std::string const &) = 0; + + protected: + virtual int do_refill () = 0; + + boost::shared_ptr<Playlist> _playlist; + + virtual void playlist_changed (const PBD::PropertyChange&); + virtual void playlist_deleted (boost::weak_ptr<Playlist>); + virtual void playlist_ranges_moved (std::list< Evoral::RangeMove<framepos_t> > const &, bool); + + private: + typedef std::map<DataType,boost::shared_ptr<Playlist> > Playlists; + + /** The number of frames by which this diskstream's output should be delayed + with respect to the transport frame. This is used for latency compensation. + */ + framecnt_t _roll_delay; + Playlists _playlists; + framepos_t overwrite_frame; + off_t overwrite_offset; + bool _pending_overwrite; + bool overwrite_queued; + IOChange input_change_pending; + framecnt_t wrap_buffer_size; + framecnt_t speed_buffer_size; + framepos_t file_frame; + framepos_t playback_sample; + + PBD::ScopedConnectionList playlist_connections; + + virtual int _do_refill_with_alloc (bool partial_fill); + + static framecnt_t _chunk_frames; +}; + +} // namespace + +#endif /* __ardour_disk_reader_h__ */ diff --git a/libs/ardour/ardour/disk_writer.h b/libs/ardour/ardour/disk_writer.h new file mode 100644 index 0000000000..984a84a938 --- /dev/null +++ b/libs/ardour/ardour/disk_writer.h @@ -0,0 +1,168 @@ +/* + Copyright (C) 2009-2016 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. + +*/ + +#ifndef __ardour_disk_writer_h__ +#define __ardour_disk_writer_h__ + +#include <list> + +#include "ardour/disk_io.h" + +namespace ARDOUR +{ + +class LIBARDOUR_API DiskWriter : public DiskIOProcessor +{ + public: + DiskWriter (Session&, std::string const & name, DiskIOProcessor::Flag f = DiskIOProcessor::Flag (0)); + + virtual bool set_write_source_name (const std::string& str); + + static framecnt_t chunk_frames() { return _chunk_frames; } + static framecnt_t default_chunk_frames (); + static void set_chunk_frames (framecnt_t n) { _chunk_frames = n; } + + void run (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framepos_t /*end_frame*/, double speed, pframes_t /*nframes*/, bool /*result_required*/); + void silence (framecnt_t /*nframes*/, framepos_t /*start_frame*/); + bool configure_io (ChanCount in, ChanCount out); + bool can_support_io_configuration (const ChanCount& in, ChanCount& out) = 0; + ChanCount input_streams () const; + ChanCount output_streams() const; + void realtime_handle_transport_stopped (); + void realtime_locate (); + + virtual XMLNode& state (bool full); + int set_state (const XMLNode&, int version); + + virtual int use_new_write_source (uint32_t n=0) = 0; + + std::string write_source_name () const { + if (_write_source_name.empty()) { + return name(); + } else { + return _write_source_name; + } + } + + virtual std::string steal_write_source_name () { return std::string(); } + + AlignStyle alignment_style() const { return _alignment_style; } + AlignChoice alignment_choice() const { return _alignment_choice; } + void set_align_style (AlignStyle, bool force=false); + void set_align_choice (AlignChoice a, bool force=false); + + PBD::Signal0<void> AlignmentStyleChanged; + + void set_input_latency (framecnt_t); + framecnt_t input_latency () const { return _input_latency; } + + std::list<boost::shared_ptr<Source> >& last_capture_sources () { return _last_capture_sources; } + + bool record_enabled() const { return g_atomic_int_get (const_cast<gint*>(&_record_enabled)); } + bool record_safe () const { return g_atomic_int_get (const_cast<gint*>(&_record_safe)); } + virtual void set_record_enabled (bool yn) = 0; + virtual void set_record_safe (bool yn) = 0; + + bool destructive() const { return _flags & Destructive; } + virtual int set_destructive (bool /*yn*/) { return -1; } + virtual int set_non_layered (bool /*yn*/) { return -1; } + virtual bool can_become_destructive (bool& /*requires_bounce*/) const { return false; } + + /** @return Start position of currently-running capture (in session frames) */ + framepos_t current_capture_start() const { return capture_start_frame; } + framepos_t current_capture_end() const { return capture_start_frame + capture_captured; } + framepos_t get_capture_start_frame (uint32_t n = 0) const; + framecnt_t get_captured_frames (uint32_t n = 0) const; + + float buffer_load() const; + + virtual void request_input_monitoring (bool) {} + virtual void ensure_input_monitoring (bool) {} + + framecnt_t capture_offset() const { return _capture_offset; } + virtual void set_capture_offset (); + + protected: + virtual int do_flush (RunContext context, bool force = false) = 0; + + virtual void check_record_status (framepos_t transport_frame, bool can_record); + virtual void prepare_record_status (framepos_t /*capture_start_frame*/) {} + virtual void set_align_style_from_io() {} + virtual void setup_destructive_playlist () {} + virtual void use_destructive_playlist () {} + virtual void prepare_to_stop (framepos_t transport_pos, framepos_t audible_frame); + + void engage_record_enable (); + void disengage_record_enable (); + void engage_record_safe (); + void disengage_record_safe (); + + virtual bool prep_record_enable () = 0; + virtual bool prep_record_disable () = 0; + + void calculate_record_range ( + Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes, + framecnt_t& rec_nframes, framecnt_t& rec_offset + ); + + static framecnt_t disk_read_chunk_frames; + static framecnt_t disk_write_chunk_frames; + + struct CaptureInfo { + framepos_t start; + framecnt_t frames; + }; + + std::vector<CaptureInfo*> capture_info; + mutable Glib::Threads::Mutex capture_info_lock; + + private: + enum TransitionType { + CaptureStart = 0, + CaptureEnd + }; + + struct CaptureTransition { + TransitionType type; + framepos_t capture_val; ///< The start or end file frame position + }; + + framecnt_t _input_latency; + gint _record_enabled; + gint _record_safe; + framepos_t capture_start_frame; + framecnt_t capture_captured; + bool was_recording; + framecnt_t adjust_capture_position; + framecnt_t _capture_offset; + framepos_t first_recordable_frame; + framepos_t last_recordable_frame; + int last_possibly_recording; + AlignStyle _alignment_style; + AlignChoice _alignment_choice; + std::string _write_source_name; + + std::list<boost::shared_ptr<Source> > _last_capture_sources; + + static framecnt_t _chunk_frames; +}; + +} // namespace + +#endif /* __ardour_disk_writer_h__ */ diff --git a/libs/ardour/disk_io.cc b/libs/ardour/disk_io.cc new file mode 100644 index 0000000000..f75178a5bd --- /dev/null +++ b/libs/ardour/disk_io.cc @@ -0,0 +1,205 @@ +/* + Copyright (C) 2009-2016 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 "pbd/i18n.h" + +#include "ardour/rc_configuration.h" +#include "ardour/disk_io.h" +#include "ardour/disk_reader.h" +#include "ardour/disk_writer.h" +#include "ardour/location.h" +#include "ardour/session.h" + +using namespace ARDOUR; +using namespace PBD; +using namespace std; + +const string DiskIOProcessor::state_node_name = X_("DiskIOProcessor"); + +// PBD::Signal0<void> DiskIOProcessor::DiskOverrun; +// PBD::Signal0<void> DiskIOProcessor::DiskUnderrun; + +DiskIOProcessor::DiskIOProcessor (Session& s, string const & str, Flag f) + : Processor (s, str) + , _flags (f) + , i_am_the_modifier (false) + , _visible_speed (0.0) + , _actual_speed (0.0) + , _speed (0.0) + , _target_speed (0.0) + , _buffer_reallocation_required (false) + , _seek_required (false) + , _slaved (false) + , loop_location (0) + , in_set_state (false) + , wrap_buffer_size (0) + , speed_buffer_size (0) +{ +} + +void +DiskIOProcessor::set_buffering_parameters (BufferingPreset bp) +{ + framecnt_t read_chunk_size; + framecnt_t read_buffer_size; + framecnt_t write_chunk_size; + framecnt_t write_buffer_size; + + if (!get_buffering_presets (bp, read_chunk_size, read_buffer_size, write_chunk_size, write_buffer_size)) { + return; + } + + DiskReader::set_chunk_frames (read_chunk_size); + DiskWriter::set_chunk_frames (write_chunk_size); + + Config->set_audio_capture_buffer_seconds (write_buffer_size); + Config->set_audio_playback_buffer_seconds (read_buffer_size); +} + +bool +DiskIOProcessor::get_buffering_presets (BufferingPreset bp, + framecnt_t& read_chunk_size, + framecnt_t& read_buffer_size, + framecnt_t& write_chunk_size, + framecnt_t& write_buffer_size) +{ + switch (bp) { + case Small: + read_chunk_size = 65536; /* samples */ + write_chunk_size = 65536; /* samples */ + read_buffer_size = 5; /* seconds */ + write_buffer_size = 5; /* seconds */ + break; + + case Medium: + read_chunk_size = 262144; /* samples */ + write_chunk_size = 131072; /* samples */ + read_buffer_size = 10; /* seconds */ + write_buffer_size = 10; /* seconds */ + break; + + case Large: + read_chunk_size = 524288; /* samples */ + write_chunk_size = 131072; /* samples */ + read_buffer_size = 20; /* seconds */ + write_buffer_size = 20; /* seconds */ + break; + + default: + return false; + } + + return true; +} + + +int +DiskIOProcessor::set_loop (Location *location) +{ + if (location) { + if (location->start() >= location->end()) { + error << string_compose(_("Location \"%1\" not valid for track loop (start >= end)"), location->name()) << endl; + return -1; + } + } + + loop_location = location; + + LoopSet (location); /* EMIT SIGNAL */ + return 0; +} + +void +DiskIOProcessor::non_realtime_set_speed () +{ + if (_buffer_reallocation_required) + { + Glib::Threads::Mutex::Lock lm (state_lock); + allocate_temporary_buffers (); + + _buffer_reallocation_required = false; + } + + if (_seek_required) { + if (speed() != 1.0f || speed() != -1.0f) { + seek ((framepos_t) (_session.transport_frame() * (double) speed()), true); + } + else { + seek (_session.transport_frame(), true); + } + + _seek_required = false; + } +} + +bool +DiskIOProcessor::realtime_set_speed (double sp, bool global) +{ + bool changed = false; + double new_speed = sp * _session.transport_speed(); + + if (_visible_speed != sp) { + _visible_speed = sp; + changed = true; + } + + if (new_speed != _actual_speed) { + + framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() * + fabs (new_speed)) + 2; + + if (required_wrap_size > wrap_buffer_size) { + _buffer_reallocation_required = true; + } + + _actual_speed = new_speed; + _target_speed = fabs(_actual_speed); + } + + if (changed) { + if (!global) { + _seek_required = true; + } + SpeedChanged (); /* EMIT SIGNAL */ + } + + return _buffer_reallocation_required || _seek_required; +} + +int +DiskIOProcessor::set_state (const XMLNode& node, int version) +{ + XMLProperty const * prop; + + Processor::set_state (node, version); + + if ((prop = node.property ("flags")) != 0) { + _flags = Flag (string_2_enum (prop->value(), _flags)); + } + + if ((prop = node.property ("speed")) != 0) { + double sp = atof (prop->value().c_str()); + + if (realtime_set_speed (sp, false)) { + non_realtime_set_speed (); + } + } + return 0; +} diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc new file mode 100644 index 0000000000..951a1aa632 --- /dev/null +++ b/libs/ardour/disk_reader.cc @@ -0,0 +1,177 @@ +/* + Copyright (C) 2009-2016 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/i18n.h" + +#include "ardour/debug.h" +#include "ardour/disk_reader.h" +#include "ardour/playlist.h" +#include "ardour/session.h" + +using namespace ARDOUR; +using namespace PBD; +using namespace std; + +ARDOUR::framecnt_t DiskReader::_chunk_frames = default_chunk_frames (); + +DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f) + : DiskIOProcessor (s, str, f) + , _roll_delay (0) + , overwrite_frame (0) + , overwrite_offset (0) + , _pending_overwrite (false) + , overwrite_queued (false) + , file_frame (0) + , playback_sample (0) +{ +} + +DiskReader::~DiskReader () +{ + DEBUG_TRACE (DEBUG::Destruction, string_compose ("DiskReader %1 deleted\n", _name)); + + if (_playlist) { + _playlist->release (); + } +} + +framecnt_t +DiskReader::default_chunk_frames() +{ + return 65536; +} + +bool +DiskReader::set_name (string const & str) +{ + if (_name != str) { + assert (_playlist); + _playlist->set_name (str); + SessionObject::set_name(str); + } + + return true; +} + +void +DiskReader::playlist_changed (const PropertyChange&) +{ + playlist_modified (); +} + +void +DiskReader::playlist_modified () +{ + if (!i_am_the_modifier && !overwrite_queued) { + // !!!! _session.request_overwrite_buffer (this); + overwrite_queued = true; + } +} + +void +DiskReader::playlist_deleted (boost::weak_ptr<Playlist> wpl) +{ + boost::shared_ptr<Playlist> pl (wpl.lock()); + + if (pl == _playlist) { + + /* this catches an ordering issue with session destruction. playlists + are destroyed before disk readers. we have to invalidate any handles + we have to the playlist. + */ + + if (_playlist) { + _playlist.reset (); + } + } +} + +int +DiskReader::use_playlist (boost::shared_ptr<Playlist> playlist) +{ + if (!playlist) { + return 0; + } + + bool prior_playlist = false; + + { + Glib::Threads::Mutex::Lock lm (state_lock); + + if (playlist == _playlist) { + return 0; + } + + playlist_connections.drop_connections (); + + if (_playlist) { + _playlist->release(); + prior_playlist = true; + } + + _playlist = playlist; + _playlist->use(); + + _playlist->ContentsChanged.connect_same_thread (playlist_connections, boost::bind (&DiskReader::playlist_modified, this)); + _playlist->LayeringChanged.connect_same_thread (playlist_connections, boost::bind (&DiskReader::playlist_modified, this)); + _playlist->DropReferences.connect_same_thread (playlist_connections, boost::bind (&DiskReader::playlist_deleted, this, boost::weak_ptr<Playlist>(_playlist))); + _playlist->RangesMoved.connect_same_thread (playlist_connections, boost::bind (&DiskReader::playlist_ranges_moved, this, _1, _2)); + } + + /* don't do this if we've already asked for it *or* if we are setting up + the diskstream for the very first time - the input changed handling will + take care of the buffer refill. + */ + + if (!overwrite_queued && prior_playlist) { + // !!! _session.request_overwrite_buffer (this); + overwrite_queued = true; + } + + PlaylistChanged (); /* EMIT SIGNAL */ + _session.set_dirty (); + + return 0; +} + +void +DiskReader::set_roll_delay (ARDOUR::framecnt_t nframes) +{ + _roll_delay = nframes; +} + +int +DiskReader::set_state (const XMLNode& node, int version) +{ + XMLProperty const * prop; + + if (DiskIOProcessor::set_state (node, version)) { + return -1; + } + + if ((prop = node.property ("playlist")) == 0) { + return -1; + } + + if (find_and_use_playlist (prop->value())) { + return -1; + } + + return 0; +} diff --git a/libs/ardour/disk_writer.cc b/libs/ardour/disk_writer.cc new file mode 100644 index 0000000000..a436324320 --- /dev/null +++ b/libs/ardour/disk_writer.cc @@ -0,0 +1,349 @@ +/* + Copyright (C) 2009-2016 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/i18n.h" + +#include "ardour/debug.h" +#include "ardour/disk_writer.h" +#include "ardour/session.h" + +using namespace ARDOUR; +using namespace PBD; +using namespace std; + +ARDOUR::framecnt_t DiskWriter::_chunk_frames = DiskWriter::default_chunk_frames (); + +DiskWriter::DiskWriter (Session& s, string const & str, DiskIOProcessor::Flag f) + : DiskIOProcessor (s, str, f) + , capture_start_frame (0) + , capture_captured (0) + , was_recording (false) + , adjust_capture_position (0) + , _capture_offset (0) + , first_recordable_frame (max_framepos) + , last_recordable_frame (max_framepos) + , last_possibly_recording (0) + , _alignment_style (ExistingMaterial) + , _alignment_choice (Automatic) +{ +} + +framecnt_t +DiskWriter::default_chunk_frames () +{ + return 65536; +} + +bool +DiskWriter::set_write_source_name (string const & str) +{ + _write_source_name = str; + return true; +} + +void +DiskWriter::check_record_status (framepos_t transport_frame, bool can_record) +{ + int possibly_recording; + int rolling; + int change; + const int transport_rolling = 0x4; + const int track_rec_enabled = 0x2; + const int global_rec_enabled = 0x1; + const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled); + + /* merge together the 3 factors that affect record status, and compute + * what has changed. + */ + + rolling = _session.transport_speed() != 0.0f; + possibly_recording = (rolling << 2) | ((int)record_enabled() << 1) | (int)can_record; + change = possibly_recording ^ last_possibly_recording; + + if (possibly_recording == last_possibly_recording) { + return; + } + + const framecnt_t existing_material_offset = _session.worst_playback_latency(); + + if (possibly_recording == fully_rec_enabled) { + + if (last_possibly_recording == fully_rec_enabled) { + return; + } + + capture_start_frame = _session.transport_frame(); + first_recordable_frame = capture_start_frame + _capture_offset; + last_recordable_frame = max_framepos; + + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %7 (%9) FRF = %2 CSF = %4 CO = %5, EMO = %6 RD = %8 WOL %10 WTL %11\n", + name(), first_recordable_frame, last_recordable_frame, capture_start_frame, + _capture_offset, + existing_material_offset, + transport_frame, + _session.transport_frame(), + _session.worst_output_latency(), + _session.worst_track_latency())); + + + if (_alignment_style == ExistingMaterial) { + first_recordable_frame += existing_material_offset; + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tshift FRF by EMO %1\n", + first_recordable_frame)); + } + + prepare_record_status (capture_start_frame); + + } else { + + if (last_possibly_recording == fully_rec_enabled) { + + /* we were recording last time */ + + if (change & transport_rolling) { + + /* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop(). We + * had to set it there because we likely rolled past the stopping point to declick out, + * and then backed up. + */ + + } else { + /* punch out */ + + last_recordable_frame = _session.transport_frame() + _capture_offset; + + if (_alignment_style == ExistingMaterial) { + last_recordable_frame += existing_material_offset; + } + } + } + } + + last_possibly_recording = possibly_recording; +} + +void +DiskWriter::calculate_record_range (Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes, + framecnt_t & rec_nframes, framecnt_t & rec_offset) +{ + switch (ot) { + case Evoral::OverlapNone: + rec_nframes = 0; + break; + + case Evoral::OverlapInternal: + /* ---------- recrange + * |---| transrange + */ + rec_nframes = nframes; + rec_offset = 0; + break; + + case Evoral::OverlapStart: + /* |--------| recrange + * -----| transrange + */ + rec_nframes = transport_frame + nframes - first_recordable_frame; + if (rec_nframes) { + rec_offset = first_recordable_frame - transport_frame; + } + break; + + case Evoral::OverlapEnd: + /* |--------| recrange + * |-------- transrange + */ + rec_nframes = last_recordable_frame - transport_frame; + rec_offset = 0; + break; + + case Evoral::OverlapExternal: + /* |--------| recrange + * -------------- transrange + */ + rec_nframes = last_recordable_frame - first_recordable_frame; + rec_offset = first_recordable_frame - transport_frame; + break; + } + + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 rec? %2 @ %3 (for %4) FRF %5 LRF %6 : rf %7 @ %8\n", + _name, enum_2_string (ot), transport_frame, nframes, + first_recordable_frame, last_recordable_frame, rec_nframes, rec_offset)); +} + +void +DiskWriter::prepare_to_stop (framepos_t transport_frame, framepos_t audible_frame) +{ + switch (_alignment_style) { + case ExistingMaterial: + last_recordable_frame = transport_frame + _capture_offset; + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to %2 + %3 = %4\n", _name, transport_frame, _capture_offset, last_recordable_frame)); + break; + + case CaptureTime: + last_recordable_frame = audible_frame; // note that capture_offset is zero + /* we may already have captured audio before the last_recordable_frame (audible frame), + so deal with this. + */ + if (last_recordable_frame > capture_start_frame) { + capture_captured = min (capture_captured, last_recordable_frame - capture_start_frame); + } + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to audible frame @ %2\n", _name, audible_frame)); + break; + } + +} + +void +DiskWriter::engage_record_enable () +{ + g_atomic_int_set (&_record_enabled, 1); +} + +void +DiskWriter::disengage_record_enable () +{ + g_atomic_int_set (&_record_enabled, 0); +} + +void +DiskWriter::engage_record_safe () +{ + g_atomic_int_set (&_record_safe, 1); +} + +void +DiskWriter::disengage_record_safe () +{ + g_atomic_int_set (&_record_safe, 0); +} + +/** Get the start position (in session frames) of the nth capture in the current pass */ +ARDOUR::framepos_t +DiskWriter::get_capture_start_frame (uint32_t n) const +{ + Glib::Threads::Mutex::Lock lm (capture_info_lock); + + if (capture_info.size() > n) { + /* this is a completed capture */ + return capture_info[n]->start; + } else { + /* this is the currently in-progress capture */ + return capture_start_frame; + } +} + +ARDOUR::framecnt_t +DiskWriter::get_captured_frames (uint32_t n) const +{ + Glib::Threads::Mutex::Lock lm (capture_info_lock); + + if (capture_info.size() > n) { + /* this is a completed capture */ + return capture_info[n]->frames; + } else { + /* this is the currently in-progress capture */ + return capture_captured; + } +} + +void +DiskWriter::set_input_latency (framecnt_t l) +{ + _input_latency = l; +} + +void +DiskWriter::set_capture_offset () +{ + switch (_alignment_style) { + case ExistingMaterial: + _capture_offset = _input_latency; + break; + + case CaptureTime: + default: + _capture_offset = 0; + break; + } + + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: using IO latency, capture offset set to %2 with style = %3\n", name(), _capture_offset, enum_2_string (_alignment_style))); +} + + +void +DiskWriter::set_align_style (AlignStyle a, bool force) +{ + if (record_enabled() && _session.actively_recording()) { + return; + } + + if ((a != _alignment_style) || force) { + _alignment_style = a; + set_capture_offset (); + AlignmentStyleChanged (); + } +} + +void +DiskWriter::set_align_choice (AlignChoice a, bool force) +{ + if (record_enabled() && _session.actively_recording()) { + return; + } + + if ((a != _alignment_choice) || force) { + _alignment_choice = a; + + switch (_alignment_choice) { + case Automatic: + set_align_style_from_io (); + break; + case UseExistingMaterial: + set_align_style (ExistingMaterial); + break; + case UseCaptureTime: + set_align_style (CaptureTime); + break; + } + } +} + +int +DiskWriter::set_state (const XMLNode& node, int version) +{ + XMLProperty const * prop; + + if (DiskIOProcessor::set_state (node, version)) { + return -1; + } + + if ((prop = node.property (X_("capture-alignment"))) != 0) { + set_align_choice (AlignChoice (string_2_enum (prop->value(), _alignment_choice)), true); + } else { + set_align_choice (Automatic, true); + } + + + if ((prop = node.property ("record-safe")) != 0) { + _record_safe = PBD::string_is_affirmative (prop->value()) ? 1 : 0; + } + + return 0; +} |