mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-27 00:47:43 +01:00
crawling towards the APIs for separate disk i/o
This commit is contained in:
parent
50fc5e5d69
commit
c200871529
5 changed files with 1025 additions and 0 deletions
126
libs/ardour/ardour/disk_reader.h
Normal file
126
libs/ardour/ardour/disk_reader.h
Normal 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__ */
|
||||
168
libs/ardour/ardour/disk_writer.h
Normal file
168
libs/ardour/ardour/disk_writer.h
Normal 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
205
libs/ardour/disk_io.cc
Normal 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
177
libs/ardour/disk_reader.cc
Normal 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
349
libs/ardour/disk_writer.cc
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue