mirror of
https://github.com/Ardour/ardour.git
synced 2026-01-18 03:15:52 +01:00
merge almost all audio & midi diskstream code, redistribute between DiskIOProcessor, DiskReader,DiskWriter; compile and link
This commit is contained in:
parent
286af12156
commit
38c8aef47c
6 changed files with 861 additions and 502 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
static PBD::Signal0<void> Underrun;
|
||||
|
||||
void playlist_modified ();
|
||||
|
||||
protected:
|
||||
boost::shared_ptr<Playlist> _playlists[DataType::num_types];
|
||||
|
||||
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 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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,9 +60,18 @@ 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)
|
||||
{
|
||||
|
|
@ -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)
|
||||
|
|
@ -129,14 +196,24 @@ DiskIOProcessor::set_loop (Location *location)
|
|||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (_playlists[dt]) {
|
||||
prior_playlist = true;
|
||||
}
|
||||
|
||||
if (playlist == _playlists[dt]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
playlist_connections.drop_connections ();
|
||||
|
||||
if (_playlists[dt]) {
|
||||
_playlists[dt]->release();
|
||||
prior_playlist = true;
|
||||
}
|
||||
|
||||
_playlists[dt] = 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));
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* we're doing playback */
|
||||
if (!need_disk_signal) {
|
||||
|
||||
framecnt_t necessary_samples;
|
||||
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
||||
(*chan)->buf->increment_read_ptr (playback_distance);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we're doing playback */
|
||||
|
||||
size_t n_buffers = bufs.count().n_audio();
|
||||
size_t n_chans = c->size();
|
||||
gain_t scaling;
|
||||
|
||||
if (n_chans > n_buffers) {
|
||||
scaling = ((float) n_buffers)/n_chans;
|
||||
} else {
|
||||
scaling = 1.0;
|
||||
}
|
||||
|
||||
for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
|
||||
|
||||
AudioBuffer& buf (bufs.get_audio (n%n_buffers));
|
||||
Sample* outgoing = buf.data ();
|
||||
|
||||
ChannelInfo* chaninfo (*chan);
|
||||
|
||||
chaninfo->buf->get_read_vector (&(*chan)->rw_vector);
|
||||
|
||||
if (playback_distance <= (framecnt_t) chaninfo->rw_vector.len[0]) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (_actual_speed != 1.0) {
|
||||
necessary_samples = (framecnt_t) ceil ((nframes * fabs (_actual_speed))) + 2;
|
||||
} else {
|
||||
necessary_samples = nframes;
|
||||
}
|
||||
|
||||
for (chan = c->begin(); chan != c->end(); ++chan) {
|
||||
(*chan)->buf->get_read_vector (&(*chan)->read_vector);
|
||||
}
|
||||
const framecnt_t total = chaninfo->rw_vector.len[0] + chaninfo->rw_vector.len[1];
|
||||
|
||||
n = 0;
|
||||
if (playback_distance <= total) {
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
/* We have enough samples, but not in one lump.
|
||||
*/
|
||||
|
||||
for (chan = c->begin(); chan != c->end(); ++chan, ++n) {
|
||||
|
||||
ChannelInfo* chaninfo (*chan);
|
||||
|
||||
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];
|
||||
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));
|
||||
}
|
||||
|
||||
} else {
|
||||
framecnt_t total = chaninfo->read_vector.len[0] + chaninfo->read_vector.len[1];
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
} 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.
|
||||
*/
|
||||
|
||||
assert(wrap_buffer_size >= necessary_samples);
|
||||
|
||||
/* Copy buf[0] from buf */
|
||||
memcpy ((char *) chaninfo->wrap_buffer,
|
||||
chaninfo->read_vector.buf[0],
|
||||
chaninfo->read_vector.len[0] * sizeof (Sample));
|
||||
|
||||
/* 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));
|
||||
|
||||
chaninfo->current_buffer = chaninfo->wrap_buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_actual_speed != 1.0f && _actual_speed != -1.0f) {
|
||||
|
||||
interpolation.set_speed (_target_speed);
|
||||
|
||||
int channel = 0;
|
||||
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) {
|
||||
ChannelInfo* chaninfo (*chan);
|
||||
|
||||
playback_distance = interpolation.interpolate (
|
||||
channel, nframes, chaninfo->current_buffer, chaninfo->speed_buffer);
|
||||
|
||||
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) {
|
||||
/* MIDI data handling */
|
||||
|
||||
/* copy data over to buffer set */
|
||||
if (!_session.declick_out_pending()) {
|
||||
|
||||
size_t n_buffers = bufs.count().n_audio();
|
||||
size_t n_chans = c->size();
|
||||
gain_t scaling = 1.0f;
|
||||
/* copy the diskstream data to all output buffers */
|
||||
|
||||
if (n_chans > n_buffers) {
|
||||
scaling = ((float) n_buffers)/n_chans;
|
||||
}
|
||||
MidiBuffer& mbuf (bufs.get_midi (0));
|
||||
get_playback (mbuf, playback_distance);
|
||||
|
||||
for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
|
||||
|
||||
AudioBuffer& buf (bufs.get_audio (n%n_buffers));
|
||||
ChannelInfo* chaninfo (*chan);
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -301,6 +308,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)
|
||||
{
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue