summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/ardour/ardour/disk_io.h76
-rw-r--r--libs/ardour/ardour/disk_reader.h39
-rw-r--r--libs/ardour/ardour/disk_writer.h74
-rw-r--r--libs/ardour/disk_io.cc260
-rw-r--r--libs/ardour/disk_reader.cc506
-rw-r--r--libs/ardour/disk_writer.cc390
6 files changed, 852 insertions, 493 deletions
diff --git a/libs/ardour/ardour/disk_io.h b/libs/ardour/ardour/disk_io.h
index d1fa1e7d66..dd775cbe84 100644
--- a/libs/ardour/ardour/disk_io.h
+++ b/libs/ardour/ardour/disk_io.h
@@ -32,9 +32,16 @@
namespace ARDOUR {
-class Session;
-class Route;
+class AudioFileSource;
+class AudioPlaylist;
class Location;
+class MidiPlaylist;
+class Playlist;
+class Route;
+class Route;
+class Session;
+
+template<typename T> class MidiRingBuffer;
class LIBARDOUR_API DiskIOProcessor : public Processor
{
@@ -50,8 +57,14 @@ class LIBARDOUR_API DiskIOProcessor : public Processor
DiskIOProcessor (Session&, const std::string& name, Flag f);
+ void set_route (boost::shared_ptr<Route>);
+
static void set_buffering_parameters (BufferingPreset bp);
+ int set_block_size (pframes_t);
+ bool configure_io (ChanCount in, ChanCount out);
+ bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
+
/** @return A number between 0 and 1, where 0 indicates that the playback buffer
* is dry (ie the disk subsystem could not keep up) and 1 indicates that the
* buffer is full.
@@ -68,7 +81,7 @@ class LIBARDOUR_API DiskIOProcessor : public Processor
bool reversed() const { return _actual_speed < 0.0f; }
double speed() const { return _visible_speed; }
- ChanCount n_channels() { return _n_channels; }
+ virtual void non_realtime_locate (framepos_t);
void non_realtime_set_speed ();
bool realtime_set_speed (double sp, bool global);
@@ -94,14 +107,24 @@ class LIBARDOUR_API DiskIOProcessor : public Processor
bool need_butler() const { return _need_butler; }
+ boost::shared_ptr<Playlist> get_playlist (DataType dt) const { return _playlists[dt]; }
+ boost::shared_ptr<MidiPlaylist> midi_playlist() const;
+ boost::shared_ptr<AudioPlaylist> audio_playlist() const;
+
+ virtual void playlist_modified () {}
+ virtual int use_playlist (DataType, boost::shared_ptr<Playlist>);
+ virtual int use_new_playlist (DataType);
+ virtual int use_copy_playlist (DataType);
+
+ PBD::Signal1<void,DataType> PlaylistChanged;
+
protected:
friend class Auditioner;
virtual int seek (framepos_t which_sample, bool complete_refill = false) = 0;
protected:
Flag _flags;
- uint32_t i_am_the_modifier;
- ChanCount _n_channels;
+ uint32_t i_am_the_modifier;
double _visible_speed;
double _actual_speed;
double _speed;
@@ -115,6 +138,9 @@ class LIBARDOUR_API DiskIOProcessor : public Processor
framecnt_t wrap_buffer_size;
framecnt_t speed_buffer_size;
bool _need_butler;
+ boost::shared_ptr<Route> _route;
+
+ void init ();
Glib::Threads::Mutex state_lock;
@@ -124,22 +150,24 @@ class LIBARDOUR_API DiskIOProcessor : public Processor
framecnt_t& write_chunk_size,
framecnt_t& write_buffer_size);
- virtual void allocate_temporary_buffers () = 0;
+ enum TransitionType {
+ CaptureStart = 0,
+ CaptureEnd
+ };
+
+ struct CaptureTransition {
+ TransitionType type;
+ framepos_t capture_val; ///< The start or end file frame position
+ };
/** Information about one audio channel, playback or capture
* (depending on the derived class)
*/
struct ChannelInfo : public boost::noncopyable {
- ChannelInfo (framecnt_t buffer_size,
- framecnt_t speed_buffer_size,
- framecnt_t wrap_buffer_size);
+ ChannelInfo (framecnt_t buffer_size);
~ChannelInfo ();
- Sample *wrap_buffer;
- Sample *speed_buffer;
- Sample *current_buffer;
-
/** A ringbuffer for data to be played back, written to in the
butler thread, read from in the process thread.
*/
@@ -149,7 +177,13 @@ class LIBARDOUR_API DiskIOProcessor : public Processor
Sample* scrub_forward_buffer;
Sample* scrub_reverse_buffer;
- PBD::RingBufferNPT<Sample>::rw_vector read_vector;
+ PBD::RingBufferNPT<Sample>::rw_vector rw_vector;
+
+ /* used only by capture */
+ boost::shared_ptr<AudioFileSource> write_source;
+ PBD::RingBufferNPT<CaptureTransition> * capture_transition_buf;
+ // the following are used in the butler thread only
+ framecnt_t curr_capture_cnt;
void resize (framecnt_t);
};
@@ -162,6 +196,20 @@ class LIBARDOUR_API DiskIOProcessor : public Processor
CubicInterpolation interpolation;
+ boost::shared_ptr<Playlist> _playlists[DataType::num_types];
+ PBD::ScopedConnectionList playlist_connections;
+
+ 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) {}
+ int find_and_use_playlist (DataType, std::string const &);
+
+ /* The MIDI stuff */
+
+ MidiRingBuffer<framepos_t>* _midi_buf;
+ gint _frames_written_to_ringbuffer;
+ gint _frames_read_from_ringbuffer;
+ CubicMidiInterpolation midi_interpolation;
};
} // namespace ARDOUR
diff --git a/libs/ardour/ardour/disk_reader.h b/libs/ardour/ardour/disk_reader.h
index 6a50594dc4..7b076ab23f 100644
--- a/libs/ardour/ardour/disk_reader.h
+++ b/libs/ardour/ardour/disk_reader.h
@@ -44,12 +44,8 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
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*/);
- int set_block_size (pframes_t);
- bool configure_io (ChanCount in, ChanCount out);
- bool can_support_io_configuration (const ChanCount& in, ChanCount& out) = 0;
void realtime_handle_transport_stopped ();
void realtime_locate ();
- void non_realtime_locate (framepos_t);
int overwrite_existing_buffers ();
void set_pending_overwrite (bool yn);
@@ -59,16 +55,6 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
virtual XMLNode& state (bool full);
int set_state (const XMLNode&, int version);
- boost::shared_ptr<Playlist> get_playlist (DataType dt) const { return _playlists[dt]; }
- boost::shared_ptr<MidiPlaylist> midi_playlist() const;
- boost::shared_ptr<AudioPlaylist> audio_playlist() const;
-
- virtual void playlist_modified ();
- virtual int use_playlist (DataType, boost::shared_ptr<Playlist>);
- virtual int use_new_playlist (DataType);
- virtual int use_copy_playlist (DataType);
-
- PBD::Signal1<void,DataType> PlaylistChanged;
PBD::Signal0<void> AlignmentStyleChanged;
float buffer_load() const;
@@ -93,8 +79,6 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
bool pending_overwrite () const { return _pending_overwrite; }
- virtual int find_and_use_playlist (DataType, std::string const &);
-
// Working buffers for do_refill (butler thread)
static void allocate_working_buffers();
static void free_working_buffers();
@@ -104,19 +88,19 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
int can_internal_playback_seek (framecnt_t distance);
int seek (framepos_t frame, bool complete_refill = false);
- PBD::Signal0<void> Underrun;
-
- protected:
- boost::shared_ptr<Playlist> _playlists[DataType::num_types];
+ static PBD::Signal0<void> Underrun;
- 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);
+ void playlist_modified ();
+ protected:
void reset_tracker ();
void resolve_tracker (Evoral::EventSink<framepos_t>& buffer, framepos_t time);
boost::shared_ptr<MidiBuffer> get_gui_feed_buffer () const;
+ void playlist_changed (const PBD::PropertyChange&);
+ int use_playlist (DataType, boost::shared_ptr<Playlist>);
+ void playlist_ranges_moved (std::list< Evoral::RangeMove<framepos_t> > const &, bool);
+
private:
/** 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.
@@ -133,8 +117,6 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
framepos_t playback_sample;
MonitorChoice _monitoring_choice;
- PBD::ScopedConnectionList playlist_connections;
-
int _do_refill_with_alloc (bool partial_fill);
static framecnt_t _chunk_frames;
@@ -142,17 +124,11 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
/* The MIDI stuff */
- MidiRingBuffer<framepos_t>* _midi_buf;
-
/** A buffer that we use to put newly-arrived MIDI data in for
the GUI to read (so that it can update itself).
*/
MidiBuffer _gui_feed_buffer;
mutable Glib::Threads::Mutex _gui_feed_buffer_mutex;
- CubicMidiInterpolation midi_interpolation;
- gint _frames_written_to_ringbuffer;
- gint _frames_read_from_ringbuffer;
-
int audio_read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
framepos_t& start, framecnt_t cnt,
@@ -169,7 +145,6 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
int internal_playback_seek (framecnt_t distance);
frameoffset_t calculate_playback_distance (pframes_t);
- void allocate_temporary_buffers();
void get_playback (MidiBuffer& dst, framecnt_t nframes);
void flush_playback (framepos_t start, framepos_t end);
};
diff --git a/libs/ardour/ardour/disk_writer.h b/libs/ardour/ardour/disk_writer.h
index 984a84a938..3eed3c6d99 100644
--- a/libs/ardour/ardour/disk_writer.h
+++ b/libs/ardour/ardour/disk_writer.h
@@ -21,12 +21,16 @@
#define __ardour_disk_writer_h__
#include <list>
+#include <vector>
#include "ardour/disk_io.h"
namespace ARDOUR
{
+class AudioFileSource;
+class SMFSource;
+
class LIBARDOUR_API DiskWriter : public DiskIOProcessor
{
public:
@@ -34,18 +38,14 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor
virtual bool set_write_source_name (const std::string& str);
+ bool recordable() const { return _flags & Recordable; }
+
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 ();
+ void non_realtime_locate (framepos_t);
virtual XMLNode& state (bool full);
int set_state (const XMLNode&, int version);
@@ -60,6 +60,17 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor
}
}
+ boost::shared_ptr<AudioFileSource> audio_write_source (uint32_t n=0) {
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ if (n < c->size()) {
+ return (*c)[n]->write_source;
+ }
+
+ return boost::shared_ptr<AudioFileSource>();
+ }
+
+ boost::shared_ptr<SMFSource> midi_write_source () { return _midi_write_source; }
+
virtual std::string steal_write_source_name () { return std::string(); }
AlignStyle alignment_style() const { return _alignment_style; }
@@ -80,9 +91,9 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor
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; }
+ int set_destructive (bool yn);
+ int set_non_layered (bool yn);
+ bool can_become_destructive (bool& requires_bounce) const;
/** @return Start position of currently-running capture (in session frames) */
framepos_t current_capture_start() const { return capture_start_frame; }
@@ -98,23 +109,29 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor
framecnt_t capture_offset() const { return _capture_offset; }
virtual void set_capture_offset ();
+ static PBD::Signal0<void> Overrun;
+
+ PBD::Signal0<void> RecordEnableChanged;
+ PBD::Signal0<void> RecordSafeChanged;
+
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 get_input_sources ();
+ void check_record_status (framepos_t transport_frame, bool can_record);
+ void prepare_record_status (framepos_t /*capture_start_frame*/);
+ void set_align_style_from_io();
+ void setup_destructive_playlist ();
+ void use_destructive_playlist ();
+ 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;
+ bool prep_record_enable ();
+ bool prep_record_disable ();
void calculate_record_range (
Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes,
@@ -133,16 +150,6 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor
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;
@@ -157,10 +164,19 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor
AlignStyle _alignment_style;
AlignChoice _alignment_choice;
std::string _write_source_name;
+ boost::shared_ptr<SMFSource> _midi_write_source;
std::list<boost::shared_ptr<Source> > _last_capture_sources;
-
+ std::vector<boost::shared_ptr<AudioFileSource> > capturing_sources;
+
static framecnt_t _chunk_frames;
+
+ NoteMode _note_mode;
+ volatile gint _frames_pending_write;
+ volatile gint _num_captured_loops;
+ framepos_t _accumulated_capture_offset;
+
+ void finish_capture (boost::shared_ptr<ChannelList> c);
};
} // namespace
diff --git a/libs/ardour/disk_io.cc b/libs/ardour/disk_io.cc
index 48b58ca3f1..1516933e32 100644
--- a/libs/ardour/disk_io.cc
+++ b/libs/ardour/disk_io.cc
@@ -20,13 +20,19 @@
#include "pbd/error.h"
#include "pbd/i18n.h"
+#include "ardour/audioplaylist.h"
#include "ardour/butler.h"
#include "ardour/disk_io.h"
#include "ardour/disk_reader.h"
#include "ardour/disk_writer.h"
#include "ardour/location.h"
+#include "ardour/midi_ring_buffer.h"
+#include "ardour/midi_playlist.h"
+#include "ardour/playlist.h"
+#include "ardour/playlist_factory.h"
#include "ardour/rc_configuration.h"
#include "ardour/session.h"
+#include "ardour/session_playlists.h"
using namespace ARDOUR;
using namespace PBD;
@@ -54,10 +60,19 @@ DiskIOProcessor::DiskIOProcessor (Session& s, string const & str, Flag f)
, speed_buffer_size (0)
, _need_butler (false)
, channels (new ChannelList)
+ , _midi_buf (0)
+ , _frames_written_to_ringbuffer (0)
+ , _frames_read_from_ringbuffer (0)
{
}
void
+DiskIOProcessor::init ()
+{
+ set_block_size (_session.get_block_size());
+}
+
+void
DiskIOProcessor::set_buffering_parameters (BufferingPreset bp)
{
framecnt_t read_chunk_size;
@@ -112,6 +127,58 @@ DiskIOProcessor::get_buffering_presets (BufferingPreset bp,
return true;
}
+bool
+DiskIOProcessor::can_support_io_configuration (const ChanCount& in, ChanCount& out)
+{
+ if (in.n_midi() != 0 && in.n_midi() != 1) {
+ /* we only support zero or 1 MIDI stream */
+ return false;
+ }
+
+ if (in != out) {
+ /* currently no way to deliver different channels that we receive */
+ return false;
+ }
+
+ return true;
+}
+
+bool
+DiskIOProcessor::configure_io (ChanCount in, ChanCount out)
+{
+ Glib::Threads::Mutex::Lock lm (state_lock);
+
+ RCUWriter<ChannelList> writer (channels);
+ boost::shared_ptr<ChannelList> c = writer.get_copy();
+
+ uint32_t n_audio = in.n_audio();
+
+ if (n_audio > c->size()) {
+ add_channel_to (c, n_audio - c->size());
+ } else if (n_audio < c->size()) {
+ remove_channel_from (c, c->size() - n_audio);
+ }
+
+ if (in.n_midi() > 0 && !_midi_buf) {
+ const size_t size = _session.butler()->midi_diskstream_buffer_size();
+ _midi_buf = new MidiRingBuffer<framepos_t>(size);
+ midi_interpolation.add_channel_to (0,0);
+ }
+
+ if (speed() != 1.0f || speed() != -1.0f) {
+ seek ((framepos_t) (_session.transport_frame() * (double) speed()));
+ } else {
+ seek (_session.transport_frame());
+ }
+
+ return Processor::configure_io (in, out);
+}
+
+int
+DiskIOProcessor::set_block_size (pframes_t nframes)
+{
+ return 0;
+}
int
DiskIOProcessor::set_loop (Location *location)
@@ -130,13 +197,23 @@ DiskIOProcessor::set_loop (Location *location)
}
void
+DiskIOProcessor::non_realtime_locate (framepos_t location)
+{
+ /* now refill channel buffers */
+
+ if (speed() != 1.0f || speed() != -1.0f) {
+ seek ((framepos_t) (location * (double) speed()), true);
+ } else {
+ seek (location, true);
+ }
+}
+
+void
DiskIOProcessor::non_realtime_set_speed ()
{
if (_buffer_reallocation_required)
{
Glib::Threads::Mutex::Lock lm (state_lock);
- allocate_temporary_buffers ();
-
_buffer_reallocation_required = false;
}
@@ -211,16 +288,10 @@ int
DiskIOProcessor::add_channel_to (boost::shared_ptr<ChannelList> c, uint32_t how_many)
{
while (how_many--) {
- c->push_back (new ChannelInfo(
- _session.butler()->audio_diskstream_playback_buffer_size(),
- speed_buffer_size, wrap_buffer_size));
- interpolation.add_channel_to (
- _session.butler()->audio_diskstream_playback_buffer_size(),
- speed_buffer_size);
+ c->push_back (new ChannelInfo (_session.butler()->audio_diskstream_playback_buffer_size()));
+ interpolation.add_channel_to (_session.butler()->audio_diskstream_playback_buffer_size(), speed_buffer_size);
}
- _n_channels.set (DataType::AUDIO, c->size());
-
return 0;
}
@@ -242,8 +313,6 @@ DiskIOProcessor::remove_channel_from (boost::shared_ptr<ChannelList> c, uint32_t
interpolation.remove_channel_from ();
}
- _n_channels.set(DataType::AUDIO, c->size());
-
return 0;
}
@@ -256,3 +325,170 @@ DiskIOProcessor::remove_channel (uint32_t how_many)
return remove_channel_from (c, how_many);
}
+void
+DiskIOProcessor::playlist_deleted (boost::weak_ptr<Playlist> wpl)
+{
+ boost::shared_ptr<Playlist> pl (wpl.lock());
+
+ if (!pl) {
+ return;
+ }
+
+ for (uint32_t n = 0; n < DataType::num_types; ++n) {
+ if (pl == _playlists[n]) {
+
+ /* 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.
+ */
+ _playlists[n].reset ();
+ break;
+ }
+ }
+}
+
+boost::shared_ptr<AudioPlaylist>
+DiskIOProcessor::audio_playlist () const
+{
+ return boost::dynamic_pointer_cast<AudioPlaylist> (_playlists[DataType::AUDIO]);
+}
+
+boost::shared_ptr<MidiPlaylist>
+DiskIOProcessor::midi_playlist () const
+{
+ return boost::dynamic_pointer_cast<MidiPlaylist> (_playlists[DataType::MIDI]);
+}
+
+int
+DiskIOProcessor::use_playlist (DataType dt, boost::shared_ptr<Playlist> playlist)
+{
+ if (!playlist) {
+ return 0;
+ }
+
+ {
+ Glib::Threads::Mutex::Lock lm (state_lock);
+
+ if (playlist == _playlists[dt]) {
+ return 0;
+ }
+
+ playlist_connections.drop_connections ();
+
+ if (_playlists[dt]) {
+ _playlists[dt]->release();
+ }
+
+ _playlists[dt] = playlist;
+ playlist->use();
+
+ playlist->ContentsChanged.connect_same_thread (playlist_connections, boost::bind (&DiskIOProcessor::playlist_modified, this));
+ playlist->LayeringChanged.connect_same_thread (playlist_connections, boost::bind (&DiskIOProcessor::playlist_modified, this));
+ playlist->DropReferences.connect_same_thread (playlist_connections, boost::bind (&DiskIOProcessor::playlist_deleted, this, boost::weak_ptr<Playlist>(playlist)));
+ playlist->RangesMoved.connect_same_thread (playlist_connections, boost::bind (&DiskIOProcessor::playlist_ranges_moved, this, _1, _2));
+ }
+
+ PlaylistChanged (dt); /* EMIT SIGNAL */
+ _session.set_dirty ();
+
+ return 0;
+}
+
+int
+DiskIOProcessor::find_and_use_playlist (DataType dt, const string& name)
+{
+ boost::shared_ptr<Playlist> playlist;
+
+ if ((playlist = _session.playlists->by_name (name)) == 0) {
+ playlist = PlaylistFactory::create (dt, _session, name);
+ }
+
+ if (!playlist) {
+ error << string_compose(_("DiskIOProcessor: \"%1\" isn't an playlist"), name) << endmsg;
+ return -1;
+ }
+
+ return use_playlist (dt, playlist);
+}
+
+int
+DiskIOProcessor::use_new_playlist (DataType dt)
+{
+ string newname;
+ boost::shared_ptr<Playlist> playlist = _playlists[dt];
+
+ if (playlist) {
+ newname = Playlist::bump_name (playlist->name(), _session);
+ } else {
+ newname = Playlist::bump_name (_name, _session);
+ }
+
+ playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (dt, _session, newname, hidden()));
+
+ if (!playlist) {
+ return -1;
+ }
+
+ return use_playlist (dt, playlist);
+}
+
+int
+DiskIOProcessor::use_copy_playlist (DataType dt)
+{
+ assert (_playlists[dt]);
+
+ if (_playlists[dt] == 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> playlist;
+
+ newname = Playlist::bump_name (_playlists[dt]->name(), _session);
+
+ if ((playlist = PlaylistFactory::create (_playlists[dt], newname)) == 0) {
+ return -1;
+ }
+
+ playlist->reset_shares();
+
+ return use_playlist (dt, playlist);
+}
+
+DiskIOProcessor::ChannelInfo::ChannelInfo (framecnt_t bufsize)
+{
+ buf = new RingBufferNPT<Sample> (bufsize);
+
+ /* touch the ringbuffer buffer, which will cause
+ them to be mapped into locked physical RAM if
+ we're running with mlockall(). this doesn't do
+ much if we're not.
+ */
+
+ memset (buf->buffer(), 0, sizeof (Sample) * buf->bufsize());
+ capture_transition_buf = new RingBufferNPT<CaptureTransition> (256);
+}
+
+void
+DiskIOProcessor::ChannelInfo::resize (framecnt_t bufsize)
+{
+ delete buf;
+ buf = new RingBufferNPT<Sample> (bufsize);
+ memset (buf->buffer(), 0, sizeof (Sample) * buf->bufsize());
+}
+
+DiskIOProcessor::ChannelInfo::~ChannelInfo ()
+{
+ delete buf;
+ buf = 0;
+
+ delete capture_transition_buf;
+ capture_transition_buf = 0;
+}
+
+void
+DiskIOProcessor::set_route (boost::shared_ptr<Route> r)
+{
+ _route = r;
+}
diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc
index 73d905b4c8..aa26f2644f 100644
--- a/libs/ardour/disk_reader.cc
+++ b/libs/ardour/disk_reader.cc
@@ -28,6 +28,7 @@
#include "ardour/disk_reader.h"
#include "ardour/midi_ring_buffer.h"
#include "ardour/midi_playlist.h"
+#include "ardour/pannable.h"
#include "ardour/playlist.h"
#include "ardour/playlist_factory.h"
#include "ardour/session.h"
@@ -38,6 +39,10 @@ using namespace PBD;
using namespace std;
ARDOUR::framecnt_t DiskReader::_chunk_frames = default_chunk_frames ();
+PBD::Signal0<void> DiskReader::Underrun;
+Sample* DiskReader::_mixdown_buffer = 0;
+gain_t* DiskReader::_gain_buffer = 0;
+framecnt_t DiskReader::midi_readahead = 4096;
DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f)
: DiskIOProcessor (s, str, f)
@@ -50,8 +55,6 @@ DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f)
, playback_sample (0)
, _monitoring_choice (MonitorDisk)
, _gui_feed_buffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
- , _frames_written_to_ringbuffer (0)
- , _frames_read_from_ringbuffer (0)
{
}
@@ -130,6 +133,13 @@ DiskReader::set_roll_delay (ARDOUR::framecnt_t nframes)
_roll_delay = nframes;
}
+XMLNode&
+DiskReader::state (bool full)
+{
+ XMLNode& node (DiskIOProcessor::state (full));
+ return node;
+}
+
int
DiskReader::set_state (const XMLNode& node, int version)
{
@@ -158,51 +168,6 @@ DiskReader::set_state (const XMLNode& node, int version)
return 0;
}
-/* Processor interface */
-
-bool
-DiskReader::configure_io (ChanCount in, ChanCount out)
-{
- Glib::Threads::Mutex::Lock lm (state_lock);
-
- RCUWriter<ChannelList> writer (channels);
- boost::shared_ptr<ChannelList> c = writer.get_copy();
-
- uint32_t n_audio = in.n_audio();
-
- if (n_audio > c->size()) {
- add_channel_to (c, n_audio - c->size());
- } else if (n_audio < c->size()) {
- remove_channel_from (c, c->size() - n_audio);
- }
-
- if (in.n_midi() > 0 && !_midi_buf) {
- const size_t size = _session.butler()->midi_diskstream_buffer_size();
- _midi_buf = new MidiRingBuffer<framepos_t>(size);
- midi_interpolation.add_channel_to (0,0);
- }
-
- Processor::configure_io (in, out);
-
- return true;
-}
-
-bool
-DiskReader::can_support_io_configuration (const ChanCount& in, ChanCount& out)
-{
- if (in.n_midi() != 0 && in.n_midi() != 1) {
- /* we only support zero or 1 MIDI stream */
- return false;
- }
-
- if (in != out) {
- /* currently no way to deliver different channels that we receive */
- return false;
- }
-
- return true;
-}
-
void
DiskReader::realtime_handle_transport_stopped ()
{
@@ -247,87 +212,6 @@ DiskReader::adjust_buffering ()
}
}
-DiskReader::ChannelInfo::ChannelInfo (framecnt_t bufsize, framecnt_t speed_size, framecnt_t wrap_size)
-{
- current_buffer = 0;
-
- speed_buffer = new Sample[speed_size];
- wrap_buffer = new Sample[wrap_size];
-
- buf = new RingBufferNPT<Sample> (bufsize);
-
- /* touch the ringbuffer buffer, which will cause
- them to be mapped into locked physical RAM if
- we're running with mlockall(). this doesn't do
- much if we're not.
- */
-
- memset (buf->buffer(), 0, sizeof (Sample) * buf->bufsize());
-}
-
-void
-DiskReader::ChannelInfo::resize (framecnt_t bufsize)
-{
- delete buf;
- buf = new RingBufferNPT<Sample> (bufsize);
- memset (buf->buffer(), 0, sizeof (Sample) * buf->bufsize());
-}
-
-DiskReader::ChannelInfo::~ChannelInfo ()
-{
- delete [] speed_buffer;
- speed_buffer = 0;
-
- delete [] wrap_buffer;
- wrap_buffer = 0;
-
- delete buf;
- buf = 0;
-}
-
-int
-DiskReader::set_block_size (pframes_t /*nframes*/)
-{
- if (_session.get_block_size() > speed_buffer_size) {
- speed_buffer_size = _session.get_block_size();
- boost::shared_ptr<ChannelList> c = channels.reader();
-
- for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
- delete [] (*chan)->speed_buffer;
- (*chan)->speed_buffer = new Sample[speed_buffer_size];
- }
- }
- allocate_temporary_buffers ();
- return 0;
-}
-
-void
-DiskReader::allocate_temporary_buffers ()
-{
- /* make sure the wrap buffer is at least large enough to deal
- with the speeds up to 1.2, to allow for micro-variation
- when slaving to MTC, Timecode etc.
- */
-
- double const sp = max (fabs (_actual_speed), 1.2);
- framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() * sp) + 2;
-
- if (required_wrap_size > wrap_buffer_size) {
-
- boost::shared_ptr<ChannelList> c = channels.reader();
-
- for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
- if ((*chan)->wrap_buffer) {
- delete [] (*chan)->wrap_buffer;
- }
- (*chan)->wrap_buffer = new Sample[required_wrap_size];
- }
-
- wrap_buffer_size = required_wrap_size;
- }
-}
-
-
void
DiskReader::playlist_changed (const PropertyChange&)
{
@@ -343,70 +227,17 @@ DiskReader::playlist_modified ()
}
}
-void
-DiskReader::playlist_deleted (boost::weak_ptr<Playlist> wpl)
-{
- boost::shared_ptr<Playlist> pl (wpl.lock());
-
- if (!pl) {
- return;
- }
-
- for (uint32_t n = 0; n < DataType::num_types; ++n) {
- if (pl == _playlists[n]) {
-
- /* 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.
- */
- _playlists[n].reset ();
- break;
- }
- }
-}
-
-boost::shared_ptr<AudioPlaylist>
-DiskReader::audio_playlist () const
-{
- return boost::dynamic_pointer_cast<AudioPlaylist> (_playlists[DataType::AUDIO]);
-}
-
-boost::shared_ptr<MidiPlaylist>
-DiskReader::midi_playlist () const
-{
- return boost::dynamic_pointer_cast<MidiPlaylist> (_playlists[DataType::MIDI]);
-}
-
int
DiskReader::use_playlist (DataType dt, boost::shared_ptr<Playlist> playlist)
{
- if (!playlist) {
- return 0;
- }
-
bool prior_playlist = false;
- {
- Glib::Threads::Mutex::Lock lm (state_lock);
-
- if (playlist == _playlists[dt]) {
- return 0;
- }
-
- playlist_connections.drop_connections ();
-
- if (_playlists[dt]) {
- _playlists[dt]->release();
- prior_playlist = true;
- }
-
- _playlists[dt] = playlist;
- playlist->use();
+ if (_playlists[dt]) {
+ prior_playlist = true;
+ }
- 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));
+ if (DiskIOProcessor::use_playlist (dt, playlist)) {
+ return -1;
}
/* don't do this if we've already asked for it *or* if we are setting up
@@ -419,83 +250,9 @@ DiskReader::use_playlist (DataType dt, boost::shared_ptr<Playlist> playlist)
overwrite_queued = true;
}
- PlaylistChanged (dt); /* EMIT SIGNAL */
- _session.set_dirty ();
-
return 0;
}
-int
-DiskReader::find_and_use_playlist (DataType dt, const string& name)
-{
- boost::shared_ptr<Playlist> playlist;
-
- if ((playlist = _session.playlists->by_name (name)) == 0) {
- playlist = PlaylistFactory::create (dt, _session, name);
- }
-
- if (!playlist) {
- error << string_compose(_("DiskReader: \"%1\" isn't an playlist"), name) << endmsg;
- return -1;
- }
-
- return use_playlist (dt, playlist);
-}
-
-int
-DiskReader::use_new_playlist (DataType dt)
-{
- string newname;
- boost::shared_ptr<Playlist> playlist = _playlists[dt];
-
- if (playlist) {
- newname = Playlist::bump_name (playlist->name(), _session);
- } else {
- newname = Playlist::bump_name (_name, _session);
- }
-
- playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (dt, _session, newname, hidden()));
-
- if (!playlist) {
- return -1;
- }
-
- return use_playlist (dt, playlist);
-}
-
-int
-DiskReader::use_copy_playlist (DataType dt)
-{
- assert (_playlists[dt]);
-
- if (_playlists[dt] == 0) {
- error << string_compose(_("DiskReader %1: there is no existing playlist to make a copy of!"), _name) << endmsg;
- return -1;
- }
-
- string newname;
- boost::shared_ptr<Playlist> playlist;
-
- newname = Playlist::bump_name (_playlists[dt]->name(), _session);
-
- if ((playlist = PlaylistFactory::create (_playlists[dt], newname)) == 0) {
- return -1;
- }
-
- playlist->reset_shares();
-
- return use_playlist (dt, playlist);
-}
-
-
-/** Do some record stuff [not described in this comment!]
- *
- * Also:
- * - Setup playback_distance with the nframes, or nframes adjusted
- * for current varispeed, if appropriate.
- * - Setup current_buffer in each ChannelInfo to point to data
- * that someone can read playback_distance worth of data from.
- */
void
DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
double speed, pframes_t nframes, bool result_required)
@@ -511,133 +268,122 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
return;
}
- for (chan = c->begin(); chan != c->end(); ++chan) {
- (*chan)->current_buffer = 0;
- }
-
const bool need_disk_signal = result_required || _monitoring_choice == MonitorDisk || _monitoring_choice == MonitorCue;
- if (need_disk_signal) {
-
- /* we're doing playback */
+ if (fabsf (_actual_speed) != 1.0f) {
+ midi_interpolation.set_speed (_target_speed);
+ interpolation.set_speed (_target_speed);
+ playback_distance = midi_interpolation.distance (nframes);
+ } else {
+ playback_distance = nframes;
+ }
- framecnt_t necessary_samples;
+ if (!need_disk_signal) {
- if (_actual_speed != 1.0) {
- necessary_samples = (framecnt_t) ceil ((nframes * fabs (_actual_speed))) + 2;
- } else {
- necessary_samples = nframes;
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ (*chan)->buf->increment_read_ptr (playback_distance);
}
- for (chan = c->begin(); chan != c->end(); ++chan) {
- (*chan)->buf->get_read_vector (&(*chan)->read_vector);
- }
+ return;
+ }
- n = 0;
+ /* we're doing playback */
- /* Setup current_buffer in each ChannelInfo to point to data that someone
- can read necessary_samples (== nframes at a transport speed of 1) worth of data
- from right now.
- */
+ size_t n_buffers = bufs.count().n_audio();
+ size_t n_chans = c->size();
+ gain_t scaling;
- for (chan = c->begin(); chan != c->end(); ++chan, ++n) {
+ if (n_chans > n_buffers) {
+ scaling = ((float) n_buffers)/n_chans;
+ } else {
+ scaling = 1.0;
+ }
- ChannelInfo* chaninfo (*chan);
+ for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
- if (necessary_samples <= (framecnt_t) chaninfo->read_vector.len[0]) {
- /* There are enough samples in the first part of the ringbuffer */
- chaninfo->current_buffer = chaninfo->read_vector.buf[0];
+ AudioBuffer& buf (bufs.get_audio (n%n_buffers));
+ Sample* outgoing = buf.data ();
- } else {
- framecnt_t total = chaninfo->read_vector.len[0] + chaninfo->read_vector.len[1];
+ ChannelInfo* chaninfo (*chan);
- if (necessary_samples > total) {
- cerr << _name << " Need " << necessary_samples << " total = " << total << endl;
- cerr << "underrun for " << _name << endl;
- DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, total space = %3\n",
- DEBUG_THREAD_SELF, name(), total));
- Underrun ();
- return;
+ chaninfo->buf->get_read_vector (&(*chan)->rw_vector);
- } else {
-
- /* We have enough samples, but not in one lump. Coalesce the two parts
- into one in wrap_buffer in our ChannelInfo, and specify that
- as our current_buffer.
- */
+ if (playback_distance <= (framecnt_t) chaninfo->rw_vector.len[0]) {
- assert(wrap_buffer_size >= necessary_samples);
+ if (fabsf (_actual_speed) != 1.0f) {
+ (void) interpolation.interpolate (
+ n, nframes,
+ chaninfo->rw_vector.buf[0],
+ outgoing);
+ } else {
+ memcpy (outgoing, chaninfo->rw_vector.buf[0], sizeof (Sample) * playback_distance);
+ }
- /* Copy buf[0] from buf */
- memcpy ((char *) chaninfo->wrap_buffer,
- chaninfo->read_vector.buf[0],
- chaninfo->read_vector.len[0] * sizeof (Sample));
+ } else {
- /* Copy buf[1] from buf */
- memcpy (chaninfo->wrap_buffer + chaninfo->read_vector.len[0],
- chaninfo->read_vector.buf[1],
- (necessary_samples - chaninfo->read_vector.len[0])
- * sizeof (Sample));
+ const framecnt_t total = chaninfo->rw_vector.len[0] + chaninfo->rw_vector.len[1];
- chaninfo->current_buffer = chaninfo->wrap_buffer;
- }
- }
- }
+ if (playback_distance <= total) {
- if (_actual_speed != 1.0f && _actual_speed != -1.0f) {
+ /* We have enough samples, but not in one lump.
+ */
- interpolation.set_speed (_target_speed);
+ if (fabsf (_actual_speed) != 1.0f) {
+ interpolation.interpolate (n, chaninfo->rw_vector.len[0],
+ chaninfo->rw_vector.buf[0],
+ outgoing);
+ outgoing += chaninfo->rw_vector.len[0];
+ interpolation.interpolate (n, playback_distance - chaninfo->rw_vector.len[0],
+ chaninfo->rw_vector.buf[1],
+ outgoing);
+ } else {
+ memcpy (outgoing,
+ chaninfo->rw_vector.buf[0],
+ chaninfo->rw_vector.len[0] * sizeof (Sample));
+ outgoing += chaninfo->rw_vector.len[0];
+ memcpy (outgoing,
+ chaninfo->rw_vector.buf[1],
+ (playback_distance - chaninfo->rw_vector.len[0]) * sizeof (Sample));
+ }
- int channel = 0;
- for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) {
- ChannelInfo* chaninfo (*chan);
+ } else {
- playback_distance = interpolation.interpolate (
- channel, nframes, chaninfo->current_buffer, chaninfo->speed_buffer);
+ cerr << _name << " Need " << playback_distance << " total = " << total << endl;
+ cerr << "underrun for " << _name << endl;
+ DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, total space = %3\n",
+ DEBUG_THREAD_SELF, name(), total));
+ Underrun ();
+ return;
- chaninfo->current_buffer = chaninfo->speed_buffer;
}
- } else {
- playback_distance = nframes;
}
+ if (scaling != 1.0f) {
+ apply_gain_to_buffer (outgoing, nframes, scaling);
+ }
+
+ chaninfo->buf->increment_read_ptr (playback_distance);
_speed = _target_speed;
}
- if (need_disk_signal) {
-
- /* copy data over to buffer set */
-
- size_t n_buffers = bufs.count().n_audio();
- size_t n_chans = c->size();
- gain_t scaling = 1.0f;
+ /* MIDI data handling */
- if (n_chans > n_buffers) {
- scaling = ((float) n_buffers)/n_chans;
- }
+ if (!_session.declick_out_pending()) {
- for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
+ /* copy the diskstream data to all output buffers */
- AudioBuffer& buf (bufs.get_audio (n%n_buffers));
- ChannelInfo* chaninfo (*chan);
+ MidiBuffer& mbuf (bufs.get_midi (0));
+ get_playback (mbuf, playback_distance);
- if (n < n_chans) {
- if (scaling != 1.0f) {
- buf.read_from_with_gain (chaninfo->current_buffer, nframes, scaling);
- } else {
- buf.read_from (chaninfo->current_buffer, nframes);
- }
- } else {
- if (scaling != 1.0f) {
- buf.accumulate_with_gain_from (chaninfo->current_buffer, nframes, scaling);
- } else {
- buf.accumulate_from (chaninfo->current_buffer, nframes);
- }
+ /* vari-speed */
+ if (_target_speed > 0 && _actual_speed != 1.0f) {
+ MidiBuffer& mbuf (bufs.get_midi (0));
+ for (MidiBuffer::iterator i = mbuf.begin(); i != mbuf.end(); ++i) {
+ MidiBuffer::TimeType *tme = i.timeptr();
+ *tme = (*tme) * nframes / playback_distance;
}
}
-
- /* extra buffers will already be silent, so leave them alone */
}
_need_butler = false;
@@ -648,10 +394,6 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
playback_sample += playback_distance;
}
- for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
- (*chan)->buf->increment_read_ptr (playback_distance);
- }
-
if (!c->empty()) {
if (_slaved) {
if (c->front()->buf->write_space() >= c->front()->buf->bufsize() / 2) {
@@ -664,40 +406,6 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
}
}
- /* MIDI data handling */
-
- if (_actual_speed != 1.0f && _target_speed > 0) {
-
- interpolation.set_speed (_target_speed);
-
- playback_distance = midi_interpolation.distance (nframes);
-
- } else {
- playback_distance = nframes;
- }
-
- if (need_disk_signal && !_session.declick_out_pending()) {
-
- /* copy the diskstream data to all output buffers */
-
- MidiBuffer& mbuf (bufs.get_midi (0));
- get_playback (mbuf, playback_distance);
-
- /* leave the audio count alone */
- ChanCount cnt (DataType::MIDI, 1);
- cnt.set (DataType::AUDIO, bufs.count().n_audio());
- bufs.set_count (cnt);
-
- /* vari-speed */
- if (_target_speed > 0 && _actual_speed != 1.0f) {
- MidiBuffer& mbuf (bufs.get_midi (0));
- for (MidiBuffer::iterator i = mbuf.begin(); i != mbuf.end(); ++i) {
- MidiBuffer::TimeType *tme = i.timeptr();
- *tme = (*tme) * nframes / playback_distance;
- }
- }
- }
-
/* MIDI butler needed part */
uint32_t frames_read = g_atomic_int_get(const_cast<gint*>(&_frames_read_from_ringbuffer));
@@ -890,18 +598,6 @@ DiskReader::overwrite_existing_buffers ()
return ret;
}
-void
-DiskReader::non_realtime_locate (framepos_t location)
-{
- /* now refill channel buffers */
-
- if (speed() != 1.0f || speed() != -1.0f) {
- seek ((framepos_t) (location * (double) speed()), true);
- } else {
- seek (location, true);
- }
-}
-
int
DiskReader::seek (framepos_t frame, bool complete_refill)
{
@@ -1411,8 +1107,7 @@ DiskReader::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const &
return;
}
-#if 0
- if (!_track || Config->get_automation_follows_regions () == false) {
+ if (!_route || Config->get_automation_follows_regions () == false) {
return;
}
@@ -1426,7 +1121,7 @@ DiskReader::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const &
}
/* move panner automation */
- boost::shared_ptr<Pannable> pannable = _track->pannable();
+ boost::shared_ptr<Pannable> pannable = _route->pannable();
Evoral::ControlSet::Controls& c (pannable->controls());
for (Evoral::ControlSet::Controls::iterator ci = c.begin(); ci != c.end(); ++ci) {
@@ -1446,8 +1141,7 @@ DiskReader::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const &
}
}
/* move processor automation */
- _track->foreach_processor (boost::bind (&Diskstream::move_processor_automation, this, _1, movements_frames));
-#endif
+ _route->foreach_processor (boost::bind (&DiskReader::move_processor_automation, this, _1, movements_frames));
}
void
diff --git a/libs/ardour/disk_writer.cc b/libs/ardour/disk_writer.cc
index a436324320..c6f5c92c76 100644
--- a/libs/ardour/disk_writer.cc
+++ b/libs/ardour/disk_writer.cc
@@ -19,15 +19,21 @@
#include "pbd/i18n.h"
+#include "ardour/audioengine.h"
+#include "ardour/audio_buffer.h"
+#include "ardour/audiofilesource.h"
#include "ardour/debug.h"
#include "ardour/disk_writer.h"
+#include "ardour/port.h"
#include "ardour/session.h"
+#include "ardour/smf_source.h"
using namespace ARDOUR;
using namespace PBD;
using namespace std;
ARDOUR::framecnt_t DiskWriter::_chunk_frames = DiskWriter::default_chunk_frames ();
+PBD::Signal0<void> DiskWriter::Overrun;
DiskWriter::DiskWriter (Session& s, string const & str, DiskIOProcessor::Flag f)
: DiskIOProcessor (s, str, f)
@@ -42,6 +48,7 @@ DiskWriter::DiskWriter (Session& s, string const & str, DiskIOProcessor::Flag f)
, _alignment_style (ExistingMaterial)
, _alignment_choice (Automatic)
{
+ DiskIOProcessor::init ();
}
framecnt_t
@@ -302,6 +309,62 @@ DiskWriter::set_align_style (AlignStyle a, bool force)
}
void
+DiskWriter::set_align_style_from_io ()
+{
+ bool have_physical = false;
+
+ if (_alignment_choice != Automatic) {
+ return;
+ }
+
+ if (!_route) {
+ return;
+ }
+
+ boost::shared_ptr<IO> input = _route->input ();
+
+ if (input) {
+ uint32_t n = 0;
+ vector<string> connections;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++n) {
+
+ if ((input->nth (n).get()) && (input->nth (n)->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 (_io);
+ 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) {
+ set_align_style (ExistingMaterial);
+ } else {
+ set_align_style (CaptureTime);
+ }
+}
+
+void
DiskWriter::set_align_choice (AlignChoice a, bool force)
{
if (record_enabled() && _session.actively_recording()) {
@@ -325,6 +388,15 @@ DiskWriter::set_align_choice (AlignChoice a, bool force)
}
}
+XMLNode&
+DiskWriter::state (bool full)
+{
+ XMLNode& node (DiskIOProcessor::state (full));
+ node.add_property (X_("capture-alignment"), enum_2_string (_alignment_choice));
+ node.add_property (X_("record-safe"), (_record_safe ? X_("yes" : "no")));
+ return node;
+}
+
int
DiskWriter::set_state (const XMLNode& node, int version)
{
@@ -347,3 +419,321 @@ DiskWriter::set_state (const XMLNode& node, int version)
return 0;
}
+
+void
+DiskWriter::non_realtime_locate (framepos_t position)
+{
+ if (_midi_write_source) {
+ _midi_write_source->set_timeline_position (position);
+ }
+
+ DiskIOProcessor::non_realtime_locate (position);
+}
+
+
+void
+DiskWriter::prepare_record_status(framepos_t capture_start_frame)
+{
+ if (recordable() && destructive()) {
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+ RingBufferNPT<CaptureTransition>::rw_vector transitions;
+ (*chan)->capture_transition_buf->get_write_vector (&transitions);
+
+ if (transitions.len[0] > 0) {
+ transitions.buf[0]->type = CaptureStart;
+ transitions.buf[0]->capture_val = capture_start_frame;
+ (*chan)->capture_transition_buf->increment_write_ptr(1);
+ } else {
+ // bad!
+ fatal << X_("programming error: capture_transition_buf is full on rec start! inconceivable!")
+ << endmsg;
+ }
+ }
+ }
+}
+
+
+/** Do some record stuff [not described in this comment!]
+ *
+ * Also:
+ * - Setup playback_distance with the nframes, or nframes adjusted
+ * for current varispeed, if appropriate.
+ * - Setup current_playback_buffer in each ChannelInfo to point to data
+ * that someone can read playback_distance worth of data from.
+ */
+void
+DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
+ double speed, pframes_t nframes, bool result_required)
+
+/* (BufferSet& bufs, framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance, bool need_disk_signal)
+ */
+{
+ uint32_t n;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ ChannelList::iterator chan;
+ framecnt_t rec_offset = 0;
+ framecnt_t rec_nframes = 0;
+ bool can_record = _session.actively_recording ();
+
+ check_record_status (start_frame, can_record);
+
+ if (nframes == 0) {
+ return;
+ }
+
+ Glib::Threads::Mutex::Lock sm (state_lock, Glib::Threads::TRY_LOCK);
+
+ if (!sm.locked()) {
+ return;
+ }
+
+ // Safeguard against situations where process() goes haywire when autopunching
+ // and last_recordable_frame < first_recordable_frame
+
+ if (last_recordable_frame < first_recordable_frame) {
+ last_recordable_frame = max_framepos;
+ }
+
+ if (record_enabled()) {
+
+ Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, start_frame, end_frame);
+ // XXX should this be transport_frame + nframes - 1 ? coverage() expects its parameter ranges to include their end points
+ // XXX also, first_recordable_frame & last_recordable_frame may both be == max_framepos: coverage() will return OverlapNone in that case. Is thak OK?
+ calculate_record_range (ot, start_frame, nframes, rec_nframes, rec_offset);
+
+ DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: this time record %2 of %3 frames, offset %4\n", _name, rec_nframes, nframes, rec_offset));
+
+ if (rec_nframes && !was_recording) {
+ capture_captured = 0;
+ was_recording = true;
+ }
+ }
+
+ if (can_record && !_last_capture_sources.empty()) {
+ _last_capture_sources.clear ();
+ }
+
+ if (rec_nframes) {
+
+ const size_t n_buffers = bufs.count().n_audio();
+
+ for (n = 0; chan != c->end(); ++chan, ++n) {
+
+ ChannelInfo* chaninfo (*chan);
+ AudioBuffer& buf (bufs.get_audio (n%n_buffers));
+
+ chaninfo->buf->get_write_vector (&chaninfo->rw_vector);
+
+ if (rec_nframes <= (framecnt_t) chaninfo->rw_vector.len[0]) {
+
+ Sample *incoming = buf.data (rec_offset);
+ memcpy (chaninfo->rw_vector.buf[0], incoming, sizeof (Sample) * rec_nframes);
+
+ } else {
+
+ framecnt_t total = chaninfo->rw_vector.len[0] + chaninfo->rw_vector.len[1];
+
+ if (rec_nframes > total) {
+ DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 overrun in %2, rec_nframes = %3 total space = %4\n",
+ DEBUG_THREAD_SELF, name(), rec_nframes, total));
+ Overrun ();
+ return;
+ }
+
+ Sample *incoming = buf.data (rec_offset);
+ framecnt_t first = chaninfo->rw_vector.len[0];
+
+ memcpy (chaninfo->rw_vector.buf[0], incoming, sizeof (Sample) * first);
+ memcpy (chaninfo->rw_vector.buf[1], incoming + first, sizeof (Sample) * (rec_nframes - first));
+ }
+ }
+
+ } else {
+
+ if (was_recording) {
+ finish_capture (c);
+ }
+
+ }
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ if (rec_nframes) {
+ (*chan)->buf->increment_write_ptr (rec_nframes);
+ }
+ }
+
+ if (rec_nframes != 0) {
+ capture_captured += rec_nframes;
+ DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 now captured %2 (by %3)\n", name(), capture_captured, rec_nframes));
+ }
+
+ if (!c->empty()) {
+ if (_slaved) {
+ if (c->front()->buf->write_space() >= c->front()->buf->bufsize() / 2) {
+ _need_butler = true;
+ }
+ } else {
+ if (((framecnt_t) c->front()->buf->read_space() >= _chunk_frames)) {
+ _need_butler = true;
+ }
+ }
+ }
+}
+
+void
+DiskWriter::finish_capture (boost::shared_ptr<ChannelList> c)
+{
+ was_recording = false;
+ first_recordable_frame = max_framepos;
+ last_recordable_frame = max_framepos;
+
+ if (capture_captured == 0) {
+ return;
+ }
+
+ if (recordable() && destructive()) {
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+ RingBufferNPT<CaptureTransition>::rw_vector transvec;
+ (*chan)->capture_transition_buf->get_write_vector(&transvec);
+
+ if (transvec.len[0] > 0) {
+ transvec.buf[0]->type = CaptureEnd;
+ transvec.buf[0]->capture_val = capture_captured;
+ (*chan)->capture_transition_buf->increment_write_ptr(1);
+ }
+ else {
+ // bad!
+ fatal << string_compose (_("programmer error: %1"), X_("capture_transition_buf is full when stopping record! inconceivable!")) << endmsg;
+ }
+ }
+ }
+
+
+ CaptureInfo* ci = new CaptureInfo;
+
+ ci->start = capture_start_frame;
+ ci->frames = capture_captured;
+
+ /* XXX theoretical race condition here. Need atomic exchange ?
+ However, the circumstances when this is called right
+ now (either on record-disable or transport_stopped)
+ mean that no actual race exists. I think ...
+ We now have a capture_info_lock, but it is only to be used
+ to synchronize in the transport_stop and the capture info
+ accessors, so that invalidation will not occur (both non-realtime).
+ */
+
+ DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("Finish capture, add new CI, %1 + %2\n", ci->start, ci->frames));
+
+ capture_info.push_back (ci);
+ capture_captured = 0;
+
+ /* now we've finished a capture, reset first_recordable_frame for next time */
+ first_recordable_frame = max_framepos;
+}
+
+void
+DiskWriter::set_record_enabled (bool yn)
+{
+ if (!recordable() || !_session.record_enabling_legal() || record_safe ()) {
+ return;
+ }
+
+ /* can't rec-enable in destructive mode if transport is before start */
+
+ if (destructive() && yn && _session.transport_frame() < _session.current_start_frame()) {
+ return;
+ }
+
+ /* yes, i know that this not proof against race conditions, but its
+ good enough. i think.
+ */
+
+ if (record_enabled() != yn) {
+ if (yn) {
+ engage_record_enable ();
+ } else {
+ disengage_record_enable ();
+ }
+
+ RecordEnableChanged (); /* EMIT SIGNAL */
+ }
+}
+
+void
+DiskWriter::set_record_safe (bool yn)
+{
+ if (!recordable() || !_session.record_enabling_legal() || channels.reader()->empty()) {
+ return;
+ }
+
+ /* can't rec-safe in destructive mode if transport is before start ????
+ REQUIRES REVIEW */
+
+ if (destructive() && yn && _session.transport_frame() < _session.current_start_frame()) {
+ return;
+ }
+
+ /* yes, i know that this not proof against race conditions, but its
+ good enough. i think.
+ */
+
+ if (record_safe () != yn) {
+ if (yn) {
+ engage_record_safe ();
+ } else {
+ disengage_record_safe ();
+ }
+
+ RecordSafeChanged (); /* EMIT SIGNAL */
+ }
+}
+
+bool
+DiskWriter::prep_record_enable ()
+{
+ if (!recordable() || !_session.record_enabling_legal() || channels.reader()->empty() || record_safe ()) { // REQUIRES REVIEW "|| record_safe ()"
+ return false;
+ }
+
+ /* can't rec-enable in destructive mode if transport is before start */
+
+ if (destructive() && _session.transport_frame() < _session.current_start_frame()) {
+ return false;
+ }
+
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ capturing_sources.clear ();
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ capturing_sources.push_back ((*chan)->write_source);
+ Source::Lock lock((*chan)->write_source->mutex());
+ (*chan)->write_source->mark_streaming_write_started (lock);
+ }
+
+ return true;
+}
+
+bool
+DiskWriter::prep_record_disable ()
+{
+ capturing_sources.clear ();
+ return true;
+}
+
+float
+DiskWriter::buffer_load () const
+{
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ if (c->empty ()) {
+ return 1.0;
+ }
+
+ return (float) ((double) c->front()->buf->write_space()/
+ (double) c->front()->buf->bufsize());
+}