crawling towards the APIs for separate disk i/o

This commit is contained in:
Paul Davis 2017-03-03 16:14:07 +01:00
parent 50fc5e5d69
commit c200871529
5 changed files with 1025 additions and 0 deletions

View file

@ -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__ */

View file

@ -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__ */

205
libs/ardour/disk_io.cc Normal file
View file

@ -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;
}

177
libs/ardour/disk_reader.cc Normal file
View file

@ -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;
}

349
libs/ardour/disk_writer.cc Normal file
View file

@ -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;
}