From a4a87f56e9dc8e2351101439aeea7a87064fa146 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 31 Mar 2017 17:28:14 +0200 Subject: mega-commit to save state of first "it compilesand links" state for separated disk i/o changes. THIS WILL NOT RUN. THIS REQUIRES MANY CHANGES --- libs/ardour/ardour/audio_track.h | 11 - libs/ardour/ardour/auditioner.h | 6 - libs/ardour/ardour/disk_io.h | 14 +- libs/ardour/ardour/disk_reader.h | 9 +- libs/ardour/ardour/disk_writer.h | 40 +- libs/ardour/ardour/midi_track.h | 6 - libs/ardour/ardour/public_diskstream.h | 2 +- libs/ardour/ardour/route.h | 7 + libs/ardour/ardour/track.h | 32 +- libs/ardour/audio_track.cc | 170 ++---- libs/ardour/auditioner.cc | 162 +----- libs/ardour/disk_io.cc | 85 +-- libs/ardour/disk_reader.cc | 40 -- libs/ardour/disk_writer.cc | 1000 +++++++++++++++++++++++++++++++- libs/ardour/midi_track.cc | 129 +--- libs/ardour/session.cc | 9 - libs/ardour/session_state.cc | 2 +- libs/ardour/track.cc | 257 ++++---- 18 files changed, 1292 insertions(+), 689 deletions(-) (limited to 'libs') diff --git a/libs/ardour/ardour/audio_track.h b/libs/ardour/ardour/audio_track.h index 7103825daf..2779e6a4f8 100644 --- a/libs/ardour/ardour/audio_track.h +++ b/libs/ardour/ardour/audio_track.h @@ -40,13 +40,6 @@ class LIBARDOUR_API AudioTrack : public Track int roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& need_butler); - boost::shared_ptr create_diskstream (); - void set_diskstream (boost::shared_ptr); - - DataType data_type () const { - return DataType::AUDIO; - } - void freeze_me (InterThreadInfo&); void unfreeze (); @@ -62,13 +55,9 @@ class LIBARDOUR_API AudioTrack : public Track boost::shared_ptr write_source (uint32_t n = 0); protected: - boost::shared_ptr audio_diskstream () const; XMLNode& state (bool full); private: - - boost::shared_ptr diskstream_factory (XMLNode const &); - int deprecated_use_diskstream_connections (); void set_state_part_two (); void set_state_part_three (); diff --git a/libs/ardour/ardour/auditioner.h b/libs/ardour/ardour/auditioner.h index 48d889344a..1dbe0cd81e 100644 --- a/libs/ardour/ardour/auditioner.h +++ b/libs/ardour/ardour/auditioner.h @@ -80,9 +80,6 @@ class LIBARDOUR_API Auditioner : public Track int roll_audio (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& need_butler); int roll_midi (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& need_butler); - boost::shared_ptr create_diskstream (); - void set_diskstream (boost::shared_ptr ds); - /* fake track */ void set_state_part_two () {} int set_state (const XMLNode&, int) { return 0; } @@ -102,9 +99,6 @@ class LIBARDOUR_API Auditioner : public Track boost::shared_ptr diskstream_factory (XMLNode const &) { return boost::shared_ptr (); } - boost::shared_ptr audio_diskstream() const; - boost::shared_ptr midi_diskstream() const; - private: boost::shared_ptr the_region; boost::shared_ptr midi_region; diff --git a/libs/ardour/ardour/disk_io.h b/libs/ardour/ardour/disk_io.h index dd775cbe84..c10dad54a1 100644 --- a/libs/ardour/ardour/disk_io.h +++ b/libs/ardour/ardour/disk_io.h @@ -65,12 +65,11 @@ class LIBARDOUR_API DiskIOProcessor : public Processor 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 + /** @return A number between 0 and 1, where 0 indicates that the playback/capture buffer * is dry (ie the disk subsystem could not keep up) and 1 indicates that the * buffer is full. */ - virtual float playback_buffer_load() const = 0; - virtual float capture_buffer_load() const = 0; + virtual float buffer_load() const = 0; void set_flag (Flag f) { _flags = Flag (_flags | f); } void unset_flag (Flag f) { _flags = Flag (_flags & ~f); } @@ -89,8 +88,6 @@ class LIBARDOUR_API DiskIOProcessor : public Processor virtual void punch_in() {} virtual void punch_out() {} - virtual float buffer_load() const = 0; - bool slaved() const { return _slaved; } void set_slaved(bool yn) { _slaved = yn; } @@ -113,8 +110,6 @@ class LIBARDOUR_API DiskIOProcessor : public Processor virtual void playlist_modified () {} virtual int use_playlist (DataType, boost::shared_ptr); - virtual int use_new_playlist (DataType); - virtual int use_copy_playlist (DataType); PBD::Signal1 PlaylistChanged; @@ -135,6 +130,8 @@ class LIBARDOUR_API DiskIOProcessor : public Processor bool _slaved; Location* loop_location; bool in_set_state; + framepos_t file_frame; + framepos_t playback_sample; framecnt_t wrap_buffer_size; framecnt_t speed_buffer_size; bool _need_butler; @@ -202,7 +199,6 @@ class LIBARDOUR_API DiskIOProcessor : public Processor virtual void playlist_changed (const PBD::PropertyChange&) {} virtual void playlist_deleted (boost::weak_ptr); virtual void playlist_ranges_moved (std::list< Evoral::RangeMove > const &, bool) {} - int find_and_use_playlist (DataType, std::string const &); /* The MIDI stuff */ @@ -210,6 +206,8 @@ class LIBARDOUR_API DiskIOProcessor : public Processor gint _frames_written_to_ringbuffer; gint _frames_read_from_ringbuffer; CubicMidiInterpolation midi_interpolation; + + static void get_location_times (const Location* location, framepos_t* start, framepos_t* end, framepos_t* length); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/disk_reader.h b/libs/ardour/ardour/disk_reader.h index 7b076ab23f..e09904ad34 100644 --- a/libs/ardour/ardour/disk_reader.h +++ b/libs/ardour/ardour/disk_reader.h @@ -86,14 +86,18 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor void adjust_buffering (); int can_internal_playback_seek (framecnt_t distance); + int internal_playback_seek (framecnt_t distance); int seek (framepos_t frame, bool complete_refill = false); static PBD::Signal0 Underrun; void playlist_modified (); + void reset_tracker (); protected: - void reset_tracker (); + friend class Track; + friend class MidiTrack; + void resolve_tracker (Evoral::EventSink& buffer, framepos_t time); boost::shared_ptr get_gui_feed_buffer () const; @@ -113,8 +117,6 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor IOChange input_change_pending; framecnt_t wrap_buffer_size; framecnt_t speed_buffer_size; - framepos_t file_frame; - framepos_t playback_sample; MonitorChoice _monitoring_choice; int _do_refill_with_alloc (bool partial_fill); @@ -142,7 +144,6 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor int refill_audio (Sample *mixdown_buffer, float *gain_buffer, framecnt_t fill_level); int refill_midi (); - int internal_playback_seek (framecnt_t distance); frameoffset_t calculate_playback_distance (pframes_t); void get_playback (MidiBuffer& dst, framecnt_t nframes); diff --git a/libs/ardour/ardour/disk_writer.h b/libs/ardour/ardour/disk_writer.h index 3eed3c6d99..d0f3672cbd 100644 --- a/libs/ardour/ardour/disk_writer.h +++ b/libs/ardour/ardour/disk_writer.h @@ -24,12 +24,14 @@ #include #include "ardour/disk_io.h" +#include "ardour/midi_buffer.h" namespace ARDOUR { class AudioFileSource; class SMFSource; +class MidiSource; class LIBARDOUR_API DiskWriter : public DiskIOProcessor { @@ -50,8 +52,6 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor virtual XMLNode& state (bool full); int set_state (const XMLNode&, int version); - virtual int use_new_write_source (uint32_t n=0) = 0; - std::string write_source_name () const { if (_write_source_name.empty()) { return name(); @@ -72,6 +72,8 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor boost::shared_ptr midi_write_source () { return _midi_write_source; } virtual std::string steal_write_source_name () { return std::string(); } + int use_new_write_source (DataType, uint32_t n = 0); + void reset_write_sources (bool, bool force = false); AlignStyle alignment_style() const { return _alignment_style; } AlignChoice alignment_choice() const { return _alignment_choice; } @@ -87,8 +89,8 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor bool record_enabled() const { return g_atomic_int_get (const_cast(&_record_enabled)); } bool record_safe () const { return g_atomic_int_get (const_cast(&_record_safe)); } - virtual void set_record_enabled (bool yn) = 0; - virtual void set_record_safe (bool yn) = 0; + virtual void set_record_enabled (bool yn); + virtual void set_record_safe (bool yn); bool destructive() const { return _flags & Destructive; } int set_destructive (bool yn); @@ -109,16 +111,31 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor framecnt_t capture_offset() const { return _capture_offset; } virtual void set_capture_offset (); + int seek (framepos_t frame, bool complete_refill); + static PBD::Signal0 Overrun; + void set_note_mode (NoteMode m); + + /** Emitted when some MIDI data has been received for recording. + * Parameter is the source that it is destined for. + * A caller can get a copy of the data with get_gui_feed_buffer () + */ + PBD::Signal1 > DataRecorded; + PBD::Signal0 RecordEnableChanged; PBD::Signal0 RecordSafeChanged; + void check_record_status (framepos_t transport_frame, bool can_record); + + void transport_looped (framepos_t transport_frame); + void transport_stopped_wallclock (struct tm&, time_t, bool abort); + protected: - virtual int do_flush (RunContext context, bool force = false) = 0; + friend class Track; + int do_flush (RunContext context, bool force = false); 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 (); @@ -138,9 +155,6 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor framecnt_t& rec_nframes, framecnt_t& rec_offset ); - static framecnt_t disk_read_chunk_frames; - static framecnt_t disk_write_chunk_frames; - struct CaptureInfo { framepos_t start; framecnt_t frames; @@ -168,7 +182,7 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor std::list > _last_capture_sources; std::vector > capturing_sources; - + static framecnt_t _chunk_frames; NoteMode _note_mode; @@ -176,6 +190,12 @@ class LIBARDOUR_API DiskWriter : public DiskIOProcessor volatile gint _num_captured_loops; framepos_t _accumulated_capture_offset; + /** 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; + void finish_capture (boost::shared_ptr c); }; diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index dc2d4ec2cf..ea6e4e3636 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -54,10 +54,6 @@ public: bool can_be_record_enabled (); bool can_be_record_safe (); - DataType data_type () const { - return DataType::MIDI; - } - void freeze_me (InterThreadInfo&); void unfreeze (); @@ -156,8 +152,6 @@ private: virtual boost::shared_ptr diskstream_factory (XMLNode const &); - boost::shared_ptr midi_diskstream () const; - void write_out_of_band_data (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, framecnt_t nframes); void set_state_part_two (); diff --git a/libs/ardour/ardour/public_diskstream.h b/libs/ardour/ardour/public_diskstream.h index 1fc93a6674..a80219ff80 100644 --- a/libs/ardour/ardour/public_diskstream.h +++ b/libs/ardour/ardour/public_diskstream.h @@ -68,7 +68,7 @@ public: virtual framepos_t current_capture_start () const = 0; virtual framepos_t current_capture_end () const = 0; virtual void playlist_modified () = 0; - virtual int use_playlist (boost::shared_ptr) = 0; + // XXX DISK removed virtual int use_playlist (boost::shared_ptr) = 0; virtual void set_align_style (AlignStyle, bool force=false) = 0; virtual void set_align_choice (AlignChoice, bool force=false) = 0; virtual int use_copy_playlist () = 0; diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 2c668f6ed7..619e79fafc 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -100,6 +100,13 @@ public: virtual int init (); + DataType data_type () const { + /* XXX ultimately nice to do away with this concept, but it is + quite useful for coders and for users too. + */ + return _default_type; + } + boost::shared_ptr input() const { return _input; } boost::shared_ptr output() const { return _output; } IOVector all_inputs () const; diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index b67e9d970a..33c967f088 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -36,15 +36,19 @@ class RouteGroup; class Source; class Region; class Diskstream; +class DiskReader; +class DiskWriter; class IO; class MonitorControl; class RecordEnableControl; class RecordSafeControl; /** A track is an route (bus) with a recordable diskstream and - * related objects relevant to tracking, playback and editing. + * related objects relevant to recording, playback and editing. * - * Specifically a track has regions and playlist objects. + * Specifically a track has a playlist object that describes material + * to be played from disk, and modifies that object during recording and + * editing. */ class LIBARDOUR_API Track : public Route, public Recordable, public PublicDiskstream { @@ -75,14 +79,8 @@ class LIBARDOUR_API Track : public Route, public Recordable, public PublicDiskst bool needs_butler() const { return _needs_butler; } - virtual DataType data_type () const = 0; - bool can_record(); - void use_new_diskstream (); - virtual boost::shared_ptr create_diskstream() = 0; - virtual void set_diskstream (boost::shared_ptr); - void set_latency_compensation (framecnt_t); enum FreezeState { @@ -179,18 +177,17 @@ class LIBARDOUR_API Track : public Route, public Recordable, public PublicDiskst AlignChoice alignment_choice () const; framepos_t current_capture_start () const; framepos_t current_capture_end () const; - void playlist_modified (); - int use_playlist (boost::shared_ptr); void set_align_style (AlignStyle, bool force=false); void set_align_choice (AlignChoice, bool force=false); + void playlist_modified (); + int use_playlist (DataType, boost::shared_ptr); + int find_and_use_playlist (DataType, std::string const & name); int use_copy_playlist (); int use_new_playlist (); void adjust_playback_buffering (); void adjust_capture_buffering (); - PBD::Signal0 DiskstreamChanged; PBD::Signal0 FreezeChange; - /* Emitted when our diskstream is set to use a different playlist */ PBD::Signal0 PlaylistChanged; PBD::Signal0 SpeedChanged; PBD::Signal0 AlignmentStyleChanged; @@ -199,6 +196,11 @@ class LIBARDOUR_API Track : public Route, public Recordable, public PublicDiskst XMLNode& state (bool full); boost::shared_ptr _diskstream; + + boost::shared_ptr _disk_reader; + boost::shared_ptr _disk_writer; + boost::shared_ptr _playlists[DataType::num_types]; + MeterPoint _saved_meter_point; TrackMode _mode; bool _needs_butler; @@ -245,12 +247,6 @@ class LIBARDOUR_API Track : public Route, public Recordable, public PublicDiskst virtual void monitoring_changed (bool, PBD::Controllable::GroupControlDisposition); private: - - virtual boost::shared_ptr diskstream_factory (XMLNode const &) = 0; - - void diskstream_playlist_changed (); - void diskstream_speed_changed (); - void diskstream_alignment_style_changed (); void parameter_changed (std::string const & p); std::string _diskstream_name; diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index ef0f3f0dab..4076ed5056 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -32,6 +32,8 @@ #include "ardour/boost_debug.h" #include "ardour/buffer_set.h" #include "ardour/delivery.h" +#include "ardour/disk_reader.h" +#include "ardour/disk_writer.h" #include "ardour/meter.h" #include "ardour/monitor_control.h" #include "ardour/playlist_factory.h" @@ -63,96 +65,47 @@ AudioTrack::~AudioTrack () } } -boost::shared_ptr -AudioTrack::create_diskstream () -{ - AudioDiskstream::Flag dflags = AudioDiskstream::Flag (AudioDiskstream::Recordable); - - if (_mode == Destructive && !Profile->get_trx()) { - dflags = AudioDiskstream::Flag (dflags | AudioDiskstream::Destructive); - } else if (_mode == NonLayered){ - dflags = AudioDiskstream::Flag(dflags | AudioDiskstream::NonLayered); - } - - return boost::shared_ptr (new AudioDiskstream (_session, name(), dflags)); -} - -void -AudioTrack::set_diskstream (boost::shared_ptr ds) +#ifdef XXX_OLD_DESTRUCTIVE_API_XXX +int +AudioTrack::set_mode (TrackMode m) { - Track::set_diskstream (ds); + if (m != _mode) { - _diskstream->set_track (this); - - if (audio_diskstream()->deprecated_io_node) { - - if (!IO::connecting_legal) { - IO::ConnectingLegal.connect_same_thread (*this, boost::bind (&AudioTrack::deprecated_use_diskstream_connections, this)); - } else { - deprecated_use_diskstream_connections (); + if (!Profile->get_trx() && _diskstream->set_destructive (m == Destructive)) { + return -1; } - } - _diskstream->set_record_enabled (false); - _diskstream->request_input_monitoring (false); + _diskstream->set_non_layered (m == NonLayered); + _mode = m; - DiskstreamChanged (); /* EMIT SIGNAL */ -} + TrackModeChanged (); /* EMIT SIGNAL */ + } -boost::shared_ptr -AudioTrack::audio_diskstream() const -{ - return boost::dynamic_pointer_cast(_diskstream); + return 0; } -int -AudioTrack::deprecated_use_diskstream_connections () +bool +AudioTrack::can_use_mode (TrackMode m, bool& bounce_required) { - boost::shared_ptr diskstream = audio_diskstream(); - - if (diskstream->deprecated_io_node == 0) { - return 0; - } - - XMLNode& node (*diskstream->deprecated_io_node); - - /* don't do this more than once. */ - - diskstream->deprecated_io_node = 0; - - float val; - if (node.get_property ("gain", val)) { - _amp->gain_control()->set_value (val, PBD::Controllable::NoGroup); - } - - std::string str; - if (node.get_property ("input-connection", str)) { - boost::shared_ptr c = _session.bundle_by_name (str); - - if (c == 0) { - error << string_compose(_("Unknown bundle \"%1\" listed for input of %2"), str, _name) << endmsg; + switch (m) { + case NonLayered: + case Normal: + bounce_required = false; + return true; - if ((c = _session.bundle_by_name (_("in 1"))) == 0) { - error << _("No input bundles available as a replacement") - << endmsg; - return -1; - } else { - info << string_compose (_("Bundle %1 was not available - \"in 1\" used instead"), str) - << endmsg; - } + case Destructive: + if (Profile->get_trx()) { + return false; + } else { + return _diskstream->can_become_destructive (bounce_required); } + break; - _input->connect_ports_to_bundle (c, true, this); - - } else if (node.get_property ("inputs", str)) { - if (_input->set_ports (str)) { - error << string_compose(_("improper input channel list in XML node (%1)"), str) << endmsg; - return -1; - } + default: + return false; } - - return 0; } +#endif int AudioTrack::set_state (const XMLNode& node, int version) @@ -279,49 +232,21 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); if (!lm.locked()) { - boost::shared_ptr diskstream = audio_diskstream(); - framecnt_t playback_distance = diskstream->calculate_playback_distance(nframes); - if (can_internal_playback_seek(::llabs(playback_distance))) { - /* TODO should declick */ - internal_playback_seek(playback_distance); - } return 0; } - framepos_t transport_frame; - boost::shared_ptr diskstream = audio_diskstream(); - if (n_outputs().n_total() == 0 && _processors.empty()) { return 0; } if (!_active) { silence (nframes); - if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _disk_writer->record_enabled())) { _meter->reset(); } return 0; } - transport_frame = _session.transport_frame(); - - int dret; - framecnt_t playback_distance; - - if ((nframes = check_initial_delay (nframes, transport_frame)) == 0) { - - /* need to do this so that the diskstream sets its - playback distance to zero, thus causing diskstream::commit - to do nothing. - */ - - BufferSet bufs; /* empty set, no matter - nothing will happen */ - - dret = diskstream->process (bufs, transport_frame, 0, playback_distance, false); - need_butler = diskstream->commit (playback_distance); - return dret; - } - _silent = false; _amp->apply_gain_automation(false); @@ -333,18 +258,10 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram _meter->run (bufs, start_frame, end_frame, 1.0 /*speed()*/, nframes, true); } - if ((dret = diskstream->process (bufs, transport_frame, nframes, playback_distance, (monitoring_state() == MonitoringDisk))) != 0) { - need_butler = diskstream->commit (playback_distance); - silence (nframes); - return dret; - } - - process_output_buffers (bufs, start_frame, end_frame, nframes, declick, (!diskstream->record_enabled() && _session.transport_rolling())); + process_output_buffers (bufs, start_frame, end_frame, nframes, declick, (!_disk_writer->record_enabled() && _session.transport_rolling())); flush_processor_buffers_locked (nframes); - need_butler = diskstream->commit (playback_distance); - return 0; } @@ -354,11 +271,10 @@ AudioTrack::export_stuff (BufferSet& buffers, framepos_t start, framecnt_t nfram { boost::scoped_array gain_buffer (new gain_t[nframes]); boost::scoped_array mix_buffer (new Sample[nframes]); - boost::shared_ptr diskstream = audio_diskstream(); Glib::Threads::RWLock::ReaderLock rlock (_processor_lock); - boost::shared_ptr apl = boost::dynamic_pointer_cast(diskstream->playlist()); + boost::shared_ptr apl = boost::dynamic_pointer_cast(playlist()); assert(apl); assert(buffers.count().n_audio() >= 1); @@ -373,7 +289,7 @@ AudioTrack::export_stuff (BufferSet& buffers, framepos_t start, framecnt_t nfram BufferSet::audio_iterator bi = buffers.audio_begin(); ++bi; for ( ; bi != buffers.audio_end(); ++bi, ++n) { - if (n < diskstream->n_channels().n_audio()) { + if (n < _disk_reader->output_streams().n_audio()) { if (apl->read (bi->data(), mix_buffer.get(), gain_buffer.get(), start, nframes, n) != nframes) { return -1; } @@ -468,9 +384,8 @@ AudioTrack::freeze_me (InterThreadInfo& itt) boost::shared_ptr new_playlist; string dir; string region_name; - boost::shared_ptr diskstream = audio_diskstream(); - if ((_freeze_record.playlist = boost::dynamic_pointer_cast(diskstream->playlist())) == 0) { + if ((_freeze_record.playlist = boost::dynamic_pointer_cast(playlist())) == 0) { return; } @@ -555,8 +470,8 @@ AudioTrack::freeze_me (InterThreadInfo& itt) new_playlist->set_frozen (true); region->set_locked (true); - diskstream->use_playlist (boost::dynamic_pointer_cast(new_playlist)); - diskstream->set_record_enabled (false); + use_playlist (DataType::AUDIO, boost::dynamic_pointer_cast(new_playlist)); + _disk_writer->set_record_enabled (false); _freeze_record.playlist->use(); // prevent deletion @@ -576,7 +491,7 @@ AudioTrack::unfreeze () { if (_freeze_record.playlist) { _freeze_record.playlist->release(); - audio_diskstream()->use_playlist (_freeze_record.playlist); + use_playlist (DataType::AUDIO, _freeze_record.playlist); { Glib::Threads::RWLock::ReaderLock lm (_processor_lock); // should this be a write lock? jlc @@ -601,13 +516,6 @@ AudioTrack::unfreeze () boost::shared_ptr AudioTrack::write_source (uint32_t n) { - boost::shared_ptr ds = boost::dynamic_pointer_cast (_diskstream); - assert (ds); - return ds->write_source (n); -} - -boost::shared_ptr -AudioTrack::diskstream_factory (XMLNode const & node) -{ - return boost::shared_ptr (new AudioDiskstream (_session, node)); + assert (_disk_writer); + return _disk_writer->audio_write_source (n); } diff --git a/libs/ardour/auditioner.cc b/libs/ardour/auditioner.cc index 85fa4cce31..4426464adc 100644 --- a/libs/ardour/auditioner.cc +++ b/libs/ardour/auditioner.cc @@ -22,7 +22,6 @@ #include "pbd/error.h" #include "ardour/amp.h" -#include "ardour/audio_diskstream.h" #include "ardour/audio_port.h" #include "ardour/audioengine.h" #include "ardour/audioplaylist.h" @@ -30,7 +29,8 @@ #include "ardour/auditioner.h" #include "ardour/data_type.h" #include "ardour/delivery.h" -#include "ardour/midi_diskstream.h" +#include "ardour/disk_reader.h" +#include "ardour/midi_playlist.h" #include "ardour/midi_region.h" #include "ardour/plugin.h" #include "ardour/plugin_insert.h" @@ -215,37 +215,8 @@ Auditioner::data_type () const { } } -boost::shared_ptr -Auditioner::create_diskstream () { - - { - AudioDiskstream::Flag dflags = AudioDiskstream::Flag (0); - dflags = AudioDiskstream::Flag (dflags | AudioDiskstream::Hidden); - _diskstream_audio = boost::shared_ptr (new AudioDiskstream (_session, name(), dflags)); - } - - { - MidiDiskstream::Flag dflags = MidiDiskstream::Flag (0); - dflags = MidiDiskstream::Flag (dflags | MidiDiskstream::Hidden); - _diskstream_midi = boost::shared_ptr (new MidiDiskstream (_session, name(), dflags)); - _diskstream_midi->do_refill_with_alloc (); - _diskstream_midi->playlist()->set_orig_track_id (id()); - } - - return _diskstream_audio; -} - int -Auditioner::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& need_butler) { - if (_midi_audition) { - return roll_midi(nframes, start_frame, end_frame, declick, need_butler); - } else { - return roll_audio(nframes, start_frame, end_frame, declick, need_butler); - } -} - -int -Auditioner::roll_midi (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& need_butler) +Auditioner::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& need_butler) { Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); if (!lm.locked()) { @@ -254,17 +225,13 @@ Auditioner::roll_midi (pframes_t nframes, framepos_t start_frame, framepos_t end assert(_active); - framecnt_t playback_distance = nframes; - boost::shared_ptr diskstream = midi_diskstream(); BufferSet& bufs = _session.get_route_buffers (n_process_buffers()); - MidiBuffer& mbuf (bufs.get_midi (0)); - _silent = false; - ChanCount cnt (DataType::MIDI, 1); - cnt.set (DataType::AUDIO, bufs.count().n_audio()); - bufs.set_count (cnt); + _silent = false; + _amp->apply_gain_automation(false); if (_queue_panic) { + MidiBuffer& mbuf (bufs.get_midi (0)); _queue_panic = false; for (uint8_t chn = 0; chn < 0xf; ++chn) { uint8_t buf[3] = { ((uint8_t) (MIDI_CMD_CONTROL | chn)), ((uint8_t) MIDI_CTL_SUSTAIN), 0 }; @@ -274,20 +241,9 @@ Auditioner::roll_midi (pframes_t nframes, framepos_t start_frame, framepos_t end buf[1] = MIDI_CTL_RESET_CONTROLLERS; mbuf.push_back(0, 3, buf); } - process_output_buffers (bufs, start_frame, start_frame+1, 1, false, false); - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - boost::shared_ptr d = boost::dynamic_pointer_cast (*i); - if (d) { - d->flush_buffers (nframes); - } - } } - diskstream->get_playback (mbuf, nframes); - - process_output_buffers (bufs, start_frame, end_frame, nframes, - declick, (!diskstream->record_enabled() && !_session.transport_stopped())); + process_output_buffers (bufs, start_frame, end_frame, nframes, declick, !_session.transport_stopped()); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr d = boost::dynamic_pointer_cast (*i); @@ -296,67 +252,23 @@ Auditioner::roll_midi (pframes_t nframes, framepos_t start_frame, framepos_t end } } - need_butler = diskstream->commit (playback_distance); - return 0; -} - - -int -Auditioner::roll_audio (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& need_butler) { - Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); - if (!lm.locked()) { - return 0; - } - - assert(n_outputs().n_total() > 0); - assert(_active); - - int dret; - framecnt_t playback_distance; - framepos_t transport_frame = _session.transport_frame(); - boost::shared_ptr diskstream = audio_diskstream(); - BufferSet& bufs = _session.get_route_buffers (n_process_buffers ()); - - _silent = false; - _amp->apply_gain_automation(false); - - if ((dret = diskstream->process (bufs, transport_frame, nframes, playback_distance, (monitoring_state() == MonitoringDisk))) != 0) { - need_butler = diskstream->commit (playback_distance); - silence (nframes); - return dret; - } - - process_output_buffers (bufs, start_frame, end_frame, nframes, declick, (!diskstream->record_enabled() && _session.transport_rolling())); - need_butler = diskstream->commit (playback_distance); return 0; } -void -Auditioner::set_diskstream (boost::shared_ptr ds) -{ - Track::set_diskstream (ds); - - _diskstream->set_track (this); - _diskstream->set_record_enabled (false); - _diskstream->request_input_monitoring (false); - - DiskstreamChanged (); /* EMIT SIGNAL */ -} - AudioPlaylist& Auditioner::prepare_playlist () { // used by CrossfadeEditor::audition() _midi_audition = false; - set_diskstream(_diskstream_audio); + if (_synth_added) { remove_processor(asynth); _synth_added = false; } // FIXME auditioner is still audio-only - boost::shared_ptr apl = boost::dynamic_pointer_cast(_diskstream->playlist()); + boost::shared_ptr apl = boost::dynamic_pointer_cast(playlist()); assert(apl); apl->clear (); @@ -378,7 +290,7 @@ Auditioner::audition_region (boost::shared_ptr region) if (boost::dynamic_pointer_cast(region) != 0) { _midi_audition = false; - set_diskstream(_diskstream_audio); + if (_synth_added) { remove_processor(asynth); _synth_added = false; @@ -390,14 +302,8 @@ Auditioner::audition_region (boost::shared_ptr region) the_region = boost::dynamic_pointer_cast (RegionFactory::create (region)); the_region->set_position (0); - _diskstream->playlist()->drop_regions (); - _diskstream->playlist()->add_region (the_region, 0, 1); - - if (_diskstream->n_channels().n_audio() < the_region->n_channels()) { - audio_diskstream()->add_channel (the_region->n_channels() - _diskstream->n_channels().n_audio()); - } else if (_diskstream->n_channels().n_audio() > the_region->n_channels()) { - audio_diskstream()->remove_channel (_diskstream->n_channels().n_audio() - the_region->n_channels()); - } + _disk_reader->audio_playlist()->drop_regions (); + _disk_reader->audio_playlist()->add_region (the_region, 0, 1); ProcessorStreams ps; { @@ -405,14 +311,14 @@ Auditioner::audition_region (boost::shared_ptr region) if (configure_processors (&ps)) { error << string_compose (_("Cannot setup auditioner processing flow for %1 channels"), - _diskstream->n_channels()) << endmsg; + region->n_channels()) << endmsg; return; } } } else if (boost::dynamic_pointer_cast(region)) { _midi_audition = true; - set_diskstream(_diskstream_midi); + the_region.reset(); _import_position = region->position(); @@ -420,9 +326,9 @@ Auditioner::audition_region (boost::shared_ptr region) midi_region = (boost::dynamic_pointer_cast (RegionFactory::create (region))); midi_region->set_position (_import_position); - _diskstream->playlist()->drop_regions (); - _diskstream->playlist()->add_region (midi_region, _import_position, 1); - midi_diskstream()->reset_tracker(); + _disk_reader->midi_playlist()->drop_regions (); + _disk_reader->midi_playlist()->add_region (midi_region, _import_position, 1); + _disk_reader->reset_tracker(); ProcessorStreams ps; @@ -435,7 +341,6 @@ Auditioner::audition_region (boost::shared_ptr region) lookup_synth(); } - if (!_synth_added && asynth) { int rv = add_processor (asynth, PreFader, &ps, true); if (rv) { @@ -452,7 +357,7 @@ Auditioner::audition_region (boost::shared_ptr region) if (configure_processors (&ps)) { error << string_compose (_("Cannot setup auditioner processing flow for %1 channels"), - _diskstream->n_channels()) << endmsg; + region->n_channels()) << endmsg; return; } } @@ -485,7 +390,7 @@ Auditioner::audition_region (boost::shared_ptr region) offset = 0; } - _diskstream->seek (offset, true); + _disk_reader->seek (offset, true); current_frame = offset; g_atomic_int_set (&_auditioning, 1); @@ -515,9 +420,7 @@ Auditioner::play_audition (framecnt_t nframes) _seek_complete = false; _seeking = false; _seek_frame = -1; - if (_midi_audition && midi_diskstream()) { - midi_diskstream()->reset_tracker(); - } + _disk_reader->reset_tracker(); } if(!_seeking) { @@ -604,18 +507,10 @@ ChanCount Auditioner::input_streams () const { /* auditioner never has any inputs - its channel configuration - depends solely on the region we are auditioning. - */ + depends solely on the region we are auditioning. + */ - if (!_midi_audition && audio_diskstream()) { - return audio_diskstream()->n_channels(); - } - if (_midi_audition && midi_diskstream()) { - ChanCount cnt (DataType::MIDI, 1); - return cnt; - } - - return ChanCount (); + return _disk_reader->input_streams (); } MonitorState @@ -624,14 +519,3 @@ Auditioner::monitoring_state () const return MonitoringDisk; } -boost::shared_ptr -Auditioner::audio_diskstream() const -{ - return boost::dynamic_pointer_cast (_diskstream); -} - -boost::shared_ptr -Auditioner::midi_diskstream() const -{ - return boost::dynamic_pointer_cast (_diskstream); -} diff --git a/libs/ardour/disk_io.cc b/libs/ardour/disk_io.cc index 1516933e32..89aba80322 100644 --- a/libs/ardour/disk_io.cc +++ b/libs/ardour/disk_io.cc @@ -56,6 +56,8 @@ DiskIOProcessor::DiskIOProcessor (Session& s, string const & str, Flag f) , _slaved (false) , loop_location (0) , in_set_state (false) + , file_frame (0) + , playback_sample (0) , wrap_buffer_size (0) , speed_buffer_size (0) , _need_butler (false) @@ -394,68 +396,6 @@ DiskIOProcessor::use_playlist (DataType dt, boost::shared_ptr playlist return 0; } -int -DiskIOProcessor::find_and_use_playlist (DataType dt, const string& name) -{ - boost::shared_ptr 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 = _playlists[dt]; - - if (playlist) { - newname = Playlist::bump_name (playlist->name(), _session); - } else { - newname = Playlist::bump_name (_name, _session); - } - - playlist = boost::dynamic_pointer_cast (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; - - 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 (bufsize); @@ -492,3 +432,24 @@ DiskIOProcessor::set_route (boost::shared_ptr r) { _route = r; } + +/** Get the start, end, and length of a location "atomically". + * + * Note: Locations don't get deleted, so all we care about when I say "atomic" + * is that we are always pointing to the same one and using start/length values + * obtained just once. Use this function to achieve this since location being + * a parameter achieves this. + */ +void +DiskIOProcessor::get_location_times(const Location* location, + framepos_t* start, + framepos_t* end, + framepos_t* length) +{ + if (location) { + *start = location->start(); + *end = location->end(); + *length = *end - *start; + } +} + diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc index aa26f2644f..7cdbaf1a0f 100644 --- a/libs/ardour/disk_reader.cc +++ b/libs/ardour/disk_reader.cc @@ -51,8 +51,6 @@ DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f) , overwrite_offset (0) , _pending_overwrite (false) , overwrite_queued (false) - , file_frame (0) - , playback_sample (0) , _monitoring_choice (MonitorDisk) , _gui_feed_buffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) { @@ -143,28 +141,10 @@ DiskReader::state (bool full) int DiskReader::set_state (const XMLNode& node, int version) { - XMLProperty const * prop; - if (DiskIOProcessor::set_state (node, version)) { return -1; } - if ((prop = node.property ("audio-playlist")) == 0) { - return -1; - } - - if (find_and_use_playlist (DataType::AUDIO, prop->value())) { - return -1; - } - - if ((prop = node.property ("midi-playlist")) == 0) { - return -1; - } - - if (find_and_use_playlist (DataType::MIDI, prop->value())) { - return -1; - } - return 0; } @@ -1310,26 +1290,6 @@ DiskReader::get_playback (MidiBuffer& dst, framecnt_t nframes) //cerr << "----------------\n"; } -/** Get the start, end, and length of a location "atomically". - * - * Note: Locations don't get deleted, so all we care about when I say "atomic" - * is that we are always pointing to the same one and using start/length values - * obtained just once. Use this function to achieve this since location being - * a parameter achieves this. - */ -static void -get_location_times(const Location* location, - framepos_t* start, - framepos_t* end, - framepos_t* length) -{ - if (location) { - *start = location->start(); - *end = location->end(); - *length = *end - *start; - } -} - /** @a start is set to the new frame position (TIME) read up to */ int DiskReader::midi_read (framepos_t& start, framecnt_t dur, bool reversed) diff --git a/libs/ardour/disk_writer.cc b/libs/ardour/disk_writer.cc index c6f5c92c76..32d22fb1ae 100644 --- a/libs/ardour/disk_writer.cc +++ b/libs/ardour/disk_writer.cc @@ -19,12 +19,19 @@ #include "pbd/i18n.h" +#include "ardour/analyser.h" #include "ardour/audioengine.h" -#include "ardour/audio_buffer.h" #include "ardour/audiofilesource.h" +#include "ardour/audio_buffer.h" +#include "ardour/audioplaylist.h" +#include "ardour/audioregion.h" #include "ardour/debug.h" #include "ardour/disk_writer.h" +#include "ardour/midi_playlist.h" +#include "ardour/midi_source.h" +#include "ardour/midi_track.h" #include "ardour/port.h" +#include "ardour/region_factory.h" #include "ardour/session.h" #include "ardour/smf_source.h" @@ -47,6 +54,9 @@ DiskWriter::DiskWriter (Session& s, string const & str, DiskIOProcessor::Flag f) , last_possibly_recording (0) , _alignment_style (ExistingMaterial) , _alignment_choice (Automatic) + , _num_captured_loops (0) + , _accumulated_capture_offset (0) + , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) { DiskIOProcessor::init (); } @@ -475,6 +485,8 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, ChannelList::iterator chan; framecnt_t rec_offset = 0; framecnt_t rec_nframes = 0; + bool nominally_recording; + bool re = record_enabled (); bool can_record = _session.actively_recording (); check_record_status (start_frame, can_record); @@ -483,6 +495,8 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, return; } + nominally_recording = (can_record && re); + Glib::Threads::Mutex::Lock sm (state_lock, Glib::Threads::TRY_LOCK); if (!sm.locked()) { @@ -496,7 +510,18 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, last_recordable_frame = max_framepos; } - if (record_enabled()) { + const Location* const loop_loc = loop_location; + framepos_t loop_start = 0; + framepos_t loop_end = 0; + framepos_t loop_length = 0; + + if (loop_loc) { + get_location_times (loop_loc, &loop_start, &loop_end, &loop_length); + } + + adjust_capture_position = 0; + + if (nominally_recording || (re && was_recording && _session.get_record_enabled() && (_session.config.get_punch_in() || _session.preroll_record_punch_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 @@ -507,8 +532,36 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, if (rec_nframes && !was_recording) { capture_captured = 0; + + if (loop_loc) { + /* Loop recording, so pretend the capture started at the loop + start rgardless of what time it is now, so the source starts + at the loop start and can handle time wrapping around. + Otherwise, start the source right now as usual. + */ + capture_captured = start_frame - loop_start; + capture_start_frame = loop_start; + } + + _midi_write_source->mark_write_starting_now (capture_start_frame, capture_captured, loop_length); + + g_atomic_int_set(const_cast (&_frames_pending_write), 0); + g_atomic_int_set(const_cast (&_num_captured_loops), 0); + was_recording = true; + } + + /* For audio: not writing frames to the capture ringbuffer offsets + * the recording. For midi: we need to keep track of the record range + * and subtract the accumulated difference from the event time. + */ + if (rec_nframes) { + _accumulated_capture_offset += rec_offset; + } else { + _accumulated_capture_offset += nframes; + } + } if (can_record && !_last_capture_sources.empty()) { @@ -517,6 +570,8 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, if (rec_nframes) { + /* AUDIO */ + const size_t n_buffers = bufs.count().n_audio(); for (n = 0; chan != c->end(); ++chan, ++n) { @@ -548,38 +603,106 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, memcpy (chaninfo->rw_vector.buf[0], incoming, sizeof (Sample) * first); memcpy (chaninfo->rw_vector.buf[1], incoming + first, sizeof (Sample) * (rec_nframes - first)); } - } - } else { + chaninfo->buf->increment_write_ptr (rec_nframes); - if (was_recording) { - finish_capture (c); } - } + /* MIDI */ - for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - if (rec_nframes) { - (*chan)->buf->increment_write_ptr (rec_nframes); + // Pump entire port buffer into the ring buffer (TODO: split cycles?) + MidiBuffer& buf = bufs.get_midi (0); + boost::shared_ptr mt = boost::dynamic_pointer_cast(_route); + MidiChannelFilter* filter = mt ? &mt->capture_filter() : 0; + + for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { + Evoral::Event ev(*i, false); + if (ev.time() + rec_offset > rec_nframes) { + break; + } +#ifndef NDEBUG + if (DEBUG_ENABLED(DEBUG::MidiIO)) { + const uint8_t* __data = ev.buffer(); + DEBUG_STR_DECL(a); + DEBUG_STR_APPEND(a, string_compose ("mididiskstream %1 capture event @ %2 + %3 sz %4 ", this, ev.time(), start_frame, ev.size())); + for (size_t i=0; i < ev.size(); ++i) { + DEBUG_STR_APPEND(a,hex); + DEBUG_STR_APPEND(a,"0x"); + DEBUG_STR_APPEND(a,(int)__data[i]); + DEBUG_STR_APPEND(a,' '); + } + DEBUG_STR_APPEND(a,'\n'); + DEBUG_TRACE (DEBUG::MidiIO, DEBUG_STR(a).str()); + } +#endif + /* Write events to the capture buffer in frames from session start, + but ignoring looping so event time progresses monotonically. + The source knows the loop length so it knows exactly where the + event occurs in the series of recorded loops and can implement + any desirable behaviour. We don't want to send event with + transport time here since that way the source can not + reconstruct their actual time; future clever MIDI looping should + probably be implemented in the source instead of here. + */ + const framecnt_t loop_offset = _num_captured_loops * loop_length; + const framepos_t event_time = start_frame + loop_offset - _accumulated_capture_offset + ev.time(); + if (event_time < 0 || event_time < first_recordable_frame) { + /* Event out of range, skip */ + continue; + } + + if (!filter || !filter->filter(ev.buffer(), ev.size())) { + _midi_buf->write (event_time, ev.event_type(), ev.size(), ev.buffer()); + } + } + g_atomic_int_add(const_cast(&_frames_pending_write), nframes); + + if (buf.size() != 0) { + Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK); + + if (lm.locked ()) { + /* Copy this data into our GUI feed buffer and tell the GUI + that it can read it if it likes. + */ + _gui_feed_buffer.clear (); + + for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { + /* This may fail if buf is larger than _gui_feed_buffer, but it's not really + the end of the world if it does. + */ + _gui_feed_buffer.push_back ((*i).time() + start_frame, (*i).size(), (*i).buffer()); + } + } + + DataRecorded (_midi_write_source); /* EMIT SIGNAL */ } - } - 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)); + + } else { + + /* not recording this time, but perhaps we were before .. */ + + if (was_recording) { + finish_capture (c); + _accumulated_capture_offset = 0; + } } + /* AUDIO BUTLER REQUIRED CODE */ + 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; - } + if (((framecnt_t) c->front()->buf->read_space() >= _chunk_frames)) { + _need_butler = true; } } + + /* MIDI BUTLER REQUIRED CODE */ + + if (_midi_buf->read_space() < _midi_buf->bufsize() / 2) { + _need_butler = true; + } } void @@ -737,3 +860,840 @@ DiskWriter::buffer_load () const return (float) ((double) c->front()->buf->write_space()/ (double) c->front()->buf->bufsize()); } + +void +DiskWriter::set_note_mode (NoteMode m) +{ + _note_mode = m; + + boost::shared_ptr mp = boost::dynamic_pointer_cast (_playlists[DataType::MIDI]); + + if (mp) { + mp->set_note_mode (m); + } + + if (_midi_write_source && _midi_write_source->model()) + _midi_write_source->model()->set_note_mode(m); +} + +int +DiskWriter::seek (framepos_t frame, bool complete_refill) +{ + uint32_t n; + ChannelList::iterator chan; + boost::shared_ptr c = channels.reader(); + + Glib::Threads::Mutex::Lock lm (state_lock); + + for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { + (*chan)->buf->reset (); + } + + _midi_buf->reset (); + g_atomic_int_set(&_frames_read_from_ringbuffer, 0); + g_atomic_int_set(&_frames_written_to_ringbuffer, 0); + + /* can't rec-enable in destructive mode if transport is before start */ + + if (destructive() && record_enabled() && frame < _session.current_start_frame()) { + disengage_record_enable (); + } + + playback_sample = frame; + file_frame = frame; + + return 0; +} + +int +DiskWriter::do_flush (RunContext ctxt, bool force_flush) +{ + uint32_t to_write; + int32_t ret = 0; + RingBufferNPT::rw_vector vector; + RingBufferNPT::rw_vector transvec; + framecnt_t total; + + transvec.buf[0] = 0; + transvec.buf[1] = 0; + vector.buf[0] = 0; + vector.buf[1] = 0; + + boost::shared_ptr c = channels.reader(); + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { + + (*chan)->buf->get_read_vector (&vector); + + total = vector.len[0] + vector.len[1]; + + if (total == 0 || (total < _chunk_frames && !force_flush && was_recording)) { + goto out; + } + + /* if there are 2+ chunks of disk i/o possible for + this track, let the caller know so that it can arrange + for us to be called again, ASAP. + + if we are forcing a flush, then if there is* any* extra + work, let the caller know. + + if we are no longer recording and there is any extra work, + let the caller know too. + */ + + if (total >= 2 * _chunk_frames || ((force_flush || !was_recording) && total > _chunk_frames)) { + ret = 1; + } + + to_write = min (_chunk_frames, (framecnt_t) vector.len[0]); + + // check the transition buffer when recording destructive + // important that we get this after the capture buf + + if (destructive()) { + (*chan)->capture_transition_buf->get_read_vector(&transvec); + size_t transcount = transvec.len[0] + transvec.len[1]; + size_t ti; + + for (ti=0; ti < transcount; ++ti) { + CaptureTransition & captrans = (ti < transvec.len[0]) ? transvec.buf[0][ti] : transvec.buf[1][ti-transvec.len[0]]; + + if (captrans.type == CaptureStart) { + // by definition, the first data we got above represents the given capture pos + + (*chan)->write_source->mark_capture_start (captrans.capture_val); + (*chan)->curr_capture_cnt = 0; + + } else if (captrans.type == CaptureEnd) { + + // capture end, the capture_val represents total frames in capture + + if (captrans.capture_val <= (*chan)->curr_capture_cnt + to_write) { + + // shorten to make the write a perfect fit + uint32_t nto_write = (captrans.capture_val - (*chan)->curr_capture_cnt); + + if (nto_write < to_write) { + ret = 1; // should we? + } + to_write = nto_write; + + (*chan)->write_source->mark_capture_end (); + + // increment past this transition, but go no further + ++ti; + break; + } + else { + // actually ends just beyond this chunk, so force more work + ret = 1; + break; + } + } + } + + if (ti > 0) { + (*chan)->capture_transition_buf->increment_read_ptr(ti); + } + } + + if ((!(*chan)->write_source) || (*chan)->write_source->write (vector.buf[0], to_write) != to_write) { + error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg; + return -1; + } + + (*chan)->buf->increment_read_ptr (to_write); + (*chan)->curr_capture_cnt += to_write; + + if ((to_write == vector.len[0]) && (total > to_write) && (to_write < _chunk_frames) && !destructive()) { + + /* we wrote all of vector.len[0] but it wasn't an entire + disk_write_chunk_frames of data, so arrange for some part + of vector.len[1] to be flushed to disk as well. + */ + + to_write = min ((framecnt_t)(_chunk_frames - to_write), (framecnt_t) vector.len[1]); + + DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 additional write of %2\n", name(), to_write)); + + if ((*chan)->write_source->write (vector.buf[1], to_write) != to_write) { + error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg; + return -1; + } + + (*chan)->buf->increment_read_ptr (to_write); + (*chan)->curr_capture_cnt += to_write; + } + } + + /* MIDI*/ + + out: + return ret; + +} + +void +DiskWriter::reset_write_sources (bool mark_write_complete, bool /*force*/) +{ + ChannelList::iterator chan; + boost::shared_ptr c = channels.reader(); + uint32_t n; + + if (!_session.writable() || !recordable()) { + return; + } + + capturing_sources.clear (); + + for (chan = c->begin(), n = 0; chan != c->end(); ++chan, ++n) { + + if (!destructive()) { + + if ((*chan)->write_source) { + + if (mark_write_complete) { + Source::Lock lock((*chan)->write_source->mutex()); + (*chan)->write_source->mark_streaming_write_completed (lock); + (*chan)->write_source->done_with_peakfile_writes (); + } + + if ((*chan)->write_source->removable()) { + (*chan)->write_source->mark_for_remove (); + (*chan)->write_source->drop_references (); + } + + (*chan)->write_source.reset (); + } + + use_new_write_source (DataType::AUDIO, n); + + if (record_enabled()) { + capturing_sources.push_back ((*chan)->write_source); + } + + } else { + + if ((*chan)->write_source == 0) { + use_new_write_source (DataType::AUDIO, n); + } + } + } + + if (_midi_write_source) { + if (mark_write_complete) { + Source::Lock lm(_midi_write_source->mutex()); + _midi_write_source->mark_streaming_write_completed (lm); + } + + use_new_write_source (DataType::MIDI); + + if (destructive() && !c->empty ()) { + + /* we now have all our write sources set up, so create the + playlist's single region. + */ + + if (_playlists[DataType::MIDI]->empty()) { + setup_destructive_playlist (); + } + } + } +} + +int +DiskWriter::use_new_write_source (DataType dt, uint32_t n) +{ + if (dt == DataType::MIDI) { + + _accumulated_capture_offset = 0; + _midi_write_source.reset(); + + try { + _midi_write_source = boost::dynamic_pointer_cast( + _session.create_midi_source_for_session (write_source_name ())); + + if (!_midi_write_source) { + throw failed_constructor(); + } + } + + catch (failed_constructor &err) { + error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg; + _midi_write_source.reset(); + return -1; + } + } else { + boost::shared_ptr c = channels.reader(); + + if (!recordable()) { + return 1; + } + + if (n >= c->size()) { + error << string_compose (_("AudioDiskstream: channel %1 out of range"), n) << endmsg; + return -1; + } + + ChannelInfo* chan = (*c)[n]; + + try { + if ((chan->write_source = _session.create_audio_source_for_session ( + c->size(), write_source_name(), n, destructive())) == 0) { + throw failed_constructor(); + } + } + + catch (failed_constructor &err) { + error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg; + chan->write_source.reset (); + return -1; + } + + /* do not remove destructive files even if they are empty */ + + chan->write_source->set_allow_remove_if_empty (!destructive()); + } + + return 0; +} + +void +DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abort_capture) +{ + uint32_t buffer_position; + bool more_work = true; + int err = 0; + boost::shared_ptr region; + framecnt_t total_capture; + SourceList srcs; + SourceList::iterator src; + ChannelList::iterator chan; + vector::iterator ci; + boost::shared_ptr c = channels.reader(); + uint32_t n = 0; + bool mark_write_completed = false; + + finish_capture (c); + + boost::shared_ptr pl = boost::dynamic_pointer_cast (_playlists[DataType::AUDIO]); + + /* butler is already stopped, but there may be work to do + to flush remaining data to disk. + */ + + while (more_work && !err) { + switch (do_flush (TransportContext, true)) { + case 0: + more_work = false; + break; + case 1: + break; + case -1: + error << string_compose(_("AudioDiskstream \"%1\": cannot flush captured data to disk!"), _name) << endmsg; + err++; + } + } + + /* XXX is there anything we can do if err != 0 ? */ + Glib::Threads::Mutex::Lock lm (capture_info_lock); + + if (capture_info.empty()) { + return; + } + + if (abort_capture) { + + if (destructive()) { + goto outout; + } + + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { + + if ((*chan)->write_source) { + + (*chan)->write_source->mark_for_remove (); + (*chan)->write_source->drop_references (); + (*chan)->write_source.reset (); + } + + /* new source set up in "out" below */ + } + + goto out; + } + + for (total_capture = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) { + total_capture += (*ci)->frames; + } + + /* figure out the name for this take */ + + for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { + + boost::shared_ptr s = (*chan)->write_source; + + if (s) { + srcs.push_back (s); + s->update_header (capture_info.front()->start, when, twhen); + s->set_captured_for (_name.val()); + s->mark_immutable (); + + if (Config->get_auto_analyse_audio()) { + Analyser::queue_source_for_analysis (s, true); + } + + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("newly captured source %1 length %2\n", s->path(), s->length (0))); + } + } + + if (!pl) { + goto midi; + } + + /* destructive tracks have a single, never changing region */ + + if (destructive()) { + + /* send a signal that any UI can pick up to do the right thing. there is + a small problem here in that a UI may need the peak data to be ready + for the data that was recorded and this isn't interlocked with that + process. this problem is deferred to the UI. + */ + + pl->LayeringChanged(); // XXX this may not get the UI to do the right thing + + } else { + + string whole_file_region_name; + whole_file_region_name = region_name_from_path (c->front()->write_source->name(), true); + + /* Register a new region with the Session that + describes the entire source. Do this first + so that any sub-regions will obviously be + children of this one (later!) + */ + + try { + PropertyList plist; + + plist.add (Properties::start, c->front()->write_source->last_capture_start_frame()); + plist.add (Properties::length, total_capture); + plist.add (Properties::name, whole_file_region_name); + boost::shared_ptr rx (RegionFactory::create (srcs, plist)); + rx->set_automatic (true); + rx->set_whole_file (true); + + region = boost::dynamic_pointer_cast (rx); + region->special_set_position (capture_info.front()->start); + } + + + catch (failed_constructor& err) { + error << string_compose(_("%1: could not create region for complete audio file"), _name) << endmsg; + /* XXX what now? */ + } + + _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end()); + + pl->clear_changes (); + pl->set_capture_insertion_in_progress (true); + pl->freeze (); + + const framepos_t preroll_off = _session.preroll_record_trim_len (); + for (buffer_position = c->front()->write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) { + + string region_name; + + RegionFactory::region_name (region_name, whole_file_region_name, false); + + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture bufpos %5 start @ %2 length %3 add new region %4\n", + _name, (*ci)->start, (*ci)->frames, region_name, buffer_position)); + + try { + + PropertyList plist; + + plist.add (Properties::start, buffer_position); + plist.add (Properties::length, (*ci)->frames); + plist.add (Properties::name, region_name); + + boost::shared_ptr rx (RegionFactory::create (srcs, plist)); + region = boost::dynamic_pointer_cast (rx); + if (preroll_off > 0) { + region->trim_front (buffer_position + preroll_off); + } + } + + catch (failed_constructor& err) { + error << _("AudioDiskstream: could not create region for captured audio!") << endmsg; + continue; /* XXX is this OK? */ + } + + i_am_the_modifier++; + + pl->add_region (region, (*ci)->start + preroll_off, 1, non_layered()); + pl->set_layer (region, DBL_MAX); + i_am_the_modifier--; + + buffer_position += (*ci)->frames; + } + + pl->thaw (); + pl->set_capture_insertion_in_progress (false); + _session.add_command (new StatefulDiffCommand (pl)); + } + + mark_write_completed = true; + + out: + reset_write_sources (mark_write_completed); + + outout: + + for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { + delete *ci; + } + + capture_info.clear (); + capture_start_frame = 0; + + midi: + return; +} + +#if 0 // MIDI PART +void +DiskWriter::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen*/, bool abort_capture) +{ + bool more_work = true; + int err = 0; + boost::shared_ptr region; + MidiRegion::SourceList srcs; + MidiRegion::SourceList::iterator src; + vector::iterator ci; + + finish_capture (); + + /* butler is already stopped, but there may be work to do + to flush remaining data to disk. + */ + + while (more_work && !err) { + switch (do_flush (TransportContext, true)) { + case 0: + more_work = false; + break; + case 1: + break; + case -1: + error << string_compose(_("MidiDiskstream \"%1\": cannot flush captured data to disk!"), _name) << endmsg; + err++; + } + } + + /* XXX is there anything we can do if err != 0 ? */ + Glib::Threads::Mutex::Lock lm (capture_info_lock); + + if (capture_info.empty()) { + goto no_capture_stuff_to_do; + } + + if (abort_capture) { + + if (_write_source) { + _write_source->mark_for_remove (); + _write_source->drop_references (); + _write_source.reset(); + } + + /* new source set up in "out" below */ + + } else { + + framecnt_t total_capture = 0; + for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { + total_capture += (*ci)->frames; + } + + if (_write_source->length (capture_info.front()->start) != 0) { + + /* phew, we have data */ + + Source::Lock source_lock(_write_source->mutex()); + + /* figure out the name for this take */ + + srcs.push_back (_write_source); + + _write_source->set_timeline_position (capture_info.front()->start); + _write_source->set_captured_for (_name); + + /* set length in beats to entire capture length */ + + BeatsFramesConverter converter (_session.tempo_map(), capture_info.front()->start); + const Evoral::Beats total_capture_beats = converter.from (total_capture); + _write_source->set_length_beats (total_capture_beats); + + /* flush to disk: this step differs from the audio path, + where all the data is already on disk. + */ + + _write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence::ResolveStuckNotes, total_capture_beats); + + /* we will want to be able to keep (over)writing the source + but we don't want it to be removable. this also differs + from the audio situation, where the source at this point + must be considered immutable. luckily, we can rely on + MidiSource::mark_streaming_write_completed() to have + already done the necessary work for that. + */ + + string whole_file_region_name; + whole_file_region_name = region_name_from_path (_write_source->name(), true); + + /* Register a new region with the Session that + describes the entire source. Do this first + so that any sub-regions will obviously be + children of this one (later!) + */ + + try { + PropertyList plist; + + plist.add (Properties::name, whole_file_region_name); + plist.add (Properties::whole_file, true); + plist.add (Properties::automatic, true); + plist.add (Properties::start, 0); + plist.add (Properties::length, total_capture); + plist.add (Properties::layer, 0); + + boost::shared_ptr rx (RegionFactory::create (srcs, plist)); + + region = boost::dynamic_pointer_cast (rx); + region->special_set_position (capture_info.front()->start); + } + + + catch (failed_constructor& err) { + error << string_compose(_("%1: could not create region for complete midi file"), _name) << endmsg; + /* XXX what now? */ + } + + _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end()); + + _playlist->clear_changes (); + _playlist->freeze (); + + /* Session frame time of the initial capture in this pass, which is where the source starts */ + framepos_t initial_capture = 0; + if (!capture_info.empty()) { + initial_capture = capture_info.front()->start; + } + + const framepos_t preroll_off = _session.preroll_record_trim_len (); + for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { + + string region_name; + + RegionFactory::region_name (region_name, _write_source->name(), false); + + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n", + _name, (*ci)->start, (*ci)->frames, region_name)); + + + // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n"; + + try { + PropertyList plist; + + /* start of this region is the offset between the start of its capture and the start of the whole pass */ + plist.add (Properties::start, (*ci)->start - initial_capture); + plist.add (Properties::length, (*ci)->frames); + plist.add (Properties::length_beats, converter.from((*ci)->frames).to_double()); + plist.add (Properties::name, region_name); + + boost::shared_ptr rx (RegionFactory::create (srcs, plist)); + region = boost::dynamic_pointer_cast (rx); + if (preroll_off > 0) { + region->trim_front ((*ci)->start - initial_capture + preroll_off); + } + } + + catch (failed_constructor& err) { + error << _("MidiDiskstream: could not create region for captured midi!") << endmsg; + continue; /* XXX is this OK? */ + } + + // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl; + + i_am_the_modifier++; + _playlist->add_region (region, (*ci)->start + preroll_off); + i_am_the_modifier--; + } + + _playlist->thaw (); + _session.add_command (new StatefulDiffCommand(_playlist)); + + } else { + + /* No data was recorded, so this capture will + effectively be aborted; do the same as we + do for an explicit abort. + */ + + if (_write_source) { + _write_source->mark_for_remove (); + _write_source->drop_references (); + _write_source.reset(); + } + } + + } + + use_new_write_source (0); + + for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { + delete *ci; + } + + capture_info.clear (); + capture_start_frame = 0; + + no_capture_stuff_to_do: + + reset_tracker (); +} +#endif + +void +DiskWriter::transport_looped (framepos_t transport_frame) +{ + if (was_recording) { + // all we need to do is finish this capture, with modified capture length + boost::shared_ptr c = channels.reader(); + + finish_capture (c); + + // the next region will start recording via the normal mechanism + // we'll set the start position to the current transport pos + // no latency adjustment or capture offset needs to be made, as that already happened the first time + capture_start_frame = transport_frame; + first_recordable_frame = transport_frame; // mild lie + last_recordable_frame = max_framepos; + was_recording = true; + + if (recordable() && destructive()) { + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { + + RingBufferNPT::rw_vector transvec; + (*chan)->capture_transition_buf->get_write_vector(&transvec); + + if (transvec.len[0] > 0) { + transvec.buf[0]->type = CaptureStart; + transvec.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 loop! inconceivable!") + << endmsg; + } + } + } + + } + + /* Here we only keep track of the number of captured loops so monotonic + event times can be delivered to the write source in process(). Trying + to be clever here is a world of trouble, it is better to simply record + the input in a straightforward non-destructive way. In the future when + we want to implement more clever MIDI looping modes it should be done in + the Source and/or entirely after the capture is finished. + */ + if (was_recording) { + g_atomic_int_add(const_cast (&_num_captured_loops), 1); + } +} + +void +DiskWriter::setup_destructive_playlist () +{ + SourceList srcs; + boost::shared_ptr c = channels.reader(); + + for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { + srcs.push_back ((*chan)->write_source); + } + + /* a single full-sized region */ + + assert (!srcs.empty ()); + + PropertyList plist; + plist.add (Properties::name, _name.val()); + plist.add (Properties::start, 0); + plist.add (Properties::length, max_framepos - srcs.front()->natural_position()); + + boost::shared_ptr region (RegionFactory::create (srcs, plist)); + _playlists[DataType::AUDIO]->add_region (region, srcs.front()->natural_position()); + + /* apply region properties and update write sources */ + use_destructive_playlist(); +} + +void +DiskWriter::use_destructive_playlist () +{ + /* this is called from the XML-based constructor or ::set_destructive. when called, + we already have a playlist and a region, but we need to + set up our sources for write. we use the sources associated + with the (presumed single, full-extent) region. + */ + + boost::shared_ptr rp; + { + const RegionList& rl (_playlists[DataType::AUDIO]->region_list_property().rlist()); + if (rl.size() > 0) { + /* this can happen when dragging a region onto a tape track */ + assert((rl.size() == 1)); + rp = rl.front(); + } + } + + if (!rp) { + reset_write_sources (false, true); + return; + } + + boost::shared_ptr region = boost::dynamic_pointer_cast (rp); + + if (region == 0) { + throw failed_constructor(); + } + + /* be sure to stretch the region out to the maximum length (non-musical)*/ + + region->set_length (max_framepos - region->position(), 0); + + uint32_t n; + ChannelList::iterator chan; + boost::shared_ptr c = channels.reader(); + + for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { + (*chan)->write_source = boost::dynamic_pointer_cast(region->source (n)); + assert((*chan)->write_source); + (*chan)->write_source->set_allow_remove_if_empty (false); + + /* this might be false if we switched modes, so force it */ + +#ifdef XXX_OLD_DESTRUCTIVE_API_XXX + (*chan)->write_source->set_destructive (true); +#else + // should be set when creating the source or loading the state + assert ((*chan)->write_source->destructive()); +#endif + } + + /* the source list will never be reset for a destructive track */ +} diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 5d315c52a7..6cbf700f03 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -33,10 +33,13 @@ #include "pbd/types_convert.h" #include "evoral/midi_util.h" +#include "ardour/amp.h" #include "ardour/beats_frames_converter.h" #include "ardour/buffer_set.h" #include "ardour/debug.h" #include "ardour/delivery.h" +#include "ardour/disk_reader.h" +#include "ardour/disk_writer.h" #include "ardour/event_type_map.h" #include "ardour/meter.h" #include "ardour/midi_diskstream.h" @@ -72,11 +75,15 @@ MidiTrack::MidiTrack (Session& sess, string name, TrackMode mode) : Track (sess, name, PresentationInfo::MidiTrack, mode, DataType::MIDI) , _immediate_events(6096) // FIXME: size? , _step_edit_ring_buffer(64) // FIXME: size? - , _note_mode(Sustained) + , _note_mode (Sustained) , _step_editing (false) , _input_active (true) { _session.SessionLoaded.connect_same_thread (*this, boost::bind (&MidiTrack::restore_controls, this)); + + _disk_writer->set_note_mode (_note_mode); + _disk_reader->reset_tracker (); + } MidiTrack::~MidiTrack () @@ -126,36 +133,6 @@ MidiTrack::can_be_record_enabled () return Track::can_be_record_enabled (); } -void -MidiTrack::set_diskstream (boost::shared_ptr ds) -{ - /* We have to do this here, as Track::set_diskstream will cause a buffer refill, - and the diskstream must be set up to fill its buffers using the correct _note_mode. - */ - boost::shared_ptr mds = boost::dynamic_pointer_cast (ds); - mds->set_note_mode (_note_mode); - - Track::set_diskstream (ds); - - mds->reset_tracker (); - - _diskstream->set_track (this); - _diskstream->set_record_enabled (false); - - _diskstream_data_recorded_connection.disconnect (); - mds->DataRecorded.connect_same_thread ( - _diskstream_data_recorded_connection, - boost::bind (&MidiTrack::diskstream_data_recorded, this, _1)); - - DiskstreamChanged (); /* EMIT SIGNAL */ -} - -boost::shared_ptr -MidiTrack::midi_diskstream() const -{ - return boost::dynamic_pointer_cast(_diskstream); -} - int MidiTrack::set_state (const XMLNode& node, int version) { @@ -323,10 +300,6 @@ MidiTrack::set_state_part_two () } } - if (midi_diskstream ()) { - midi_diskstream()->set_block_size (_session.get_block_size ()); - } - return; } @@ -364,18 +337,11 @@ int MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& need_butler) { Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); + if (!lm.locked()) { - boost::shared_ptr diskstream = midi_diskstream(); - framecnt_t playback_distance = diskstream->calculate_playback_distance(nframes); - if (can_internal_playback_seek(::llabs(playback_distance))) { - /* TODO should declick, and/or note-off */ - internal_playback_seek(playback_distance); - } return 0; } - boost::shared_ptr diskstream = midi_diskstream(); - if (n_outputs().n_total() == 0 && _processors.empty()) { return 0; } @@ -388,22 +354,8 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame return 0; } - framepos_t transport_frame = _session.transport_frame(); - - int dret; - framecnt_t playback_distance; - - if ((nframes = check_initial_delay (nframes, transport_frame)) == 0) { - /* need to do this so that the diskstream sets its - playback distance to zero, thus causing diskstream::commit - to do nothing. - */ - BufferSet bufs; /* empty set - is OK, since nothing will happen */ - - dret = diskstream->process (bufs, transport_frame, 0, playback_distance, false); - need_butler = diskstream->commit (playback_distance); - return dret; - } + _silent = false; + _amp->apply_gain_automation (false); BufferSet& bufs = _session.get_route_buffers (n_process_buffers()); @@ -416,47 +368,16 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame _meter->run (bufs, start_frame, end_frame, 1.0 /*speed()*/, nframes, true); } - - _silent = false; - - if ((dret = diskstream->process (bufs, transport_frame, nframes, playback_distance, (monitoring_state() == MonitoringDisk))) != 0) { - need_butler = diskstream->commit (playback_distance); - silence (nframes); - return dret; - } - - /* note diskstream uses our filter to filter/map playback channels appropriately. */ - - if (monitoring_state() == MonitoringInput) { - - /* not actually recording, but we want to hear the input material anyway, - at least potentially (depending on monitoring options) - */ - - /* because the playback buffer is event based and not a - * continuous stream, we need to make sure that we empty - * it of events every cycle to avoid it filling up with events - * read from disk, while we are actually monitoring input - */ - - diskstream->flush_playback (start_frame, end_frame); - - } - - /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */ write_out_of_band_data (bufs, start_frame, end_frame, nframes); /* final argument: don't waste time with automation if we're not recording or rolling */ - process_output_buffers (bufs, start_frame, end_frame, nframes, - declick, (!diskstream->record_enabled() && !_session.transport_stopped())); + process_output_buffers (bufs, start_frame, end_frame, nframes, declick, (!_disk_writer->record_enabled() && !_session.transport_stopped())); flush_processor_buffers_locked (nframes); - need_butler = diskstream->commit (playback_distance); - return 0; } @@ -485,7 +406,7 @@ MidiTrack::realtime_locate () (*i)->realtime_locate (); } - midi_diskstream()->reset_tracker (); + _disk_reader->reset_tracker (); } void @@ -507,7 +428,7 @@ MidiTrack::non_realtime_locate (framepos_t pos) { Track::non_realtime_locate(pos); - boost::shared_ptr playlist = midi_diskstream()->midi_playlist(); + boost::shared_ptr playlist = _disk_writer->midi_playlist(); if (!playlist) { return; } @@ -612,11 +533,9 @@ MidiTrack::export_stuff (BufferSet& buffers, return -1; } - boost::shared_ptr diskstream = midi_diskstream(); - Glib::Threads::RWLock::ReaderLock rlock (_processor_lock); - boost::shared_ptr mpl = boost::dynamic_pointer_cast(diskstream->playlist()); + boost::shared_ptr mpl = _disk_writer->midi_playlist(); if (!mpl) { return -2; } @@ -665,7 +584,7 @@ void MidiTrack::set_note_mode (NoteMode m) { _note_mode = m; - midi_diskstream()->set_note_mode(m); + _disk_writer->set_note_mode(m); } std::string @@ -809,7 +728,7 @@ MidiTrack::set_step_editing (bool yn) boost::shared_ptr MidiTrack::write_source (uint32_t) { - return midi_diskstream()->write_source (); + return _disk_writer->midi_write_source (); } void @@ -847,7 +766,7 @@ MidiTrack::set_capture_channel_mask (uint16_t mask) boost::shared_ptr MidiTrack::midi_playlist () { - return midi_diskstream()->midi_playlist (); + return boost::dynamic_pointer_cast (_playlists[DataType::MIDI]); } void @@ -906,7 +825,7 @@ MidiTrack::diskstream_factory (XMLNode const & node) boost::shared_ptr MidiTrack::get_gui_feed_buffer () const { - return midi_diskstream()->get_gui_feed_buffer (); + return _disk_reader->get_gui_feed_buffer (); } void @@ -922,7 +841,7 @@ MidiTrack::act_on_mute () /* If we haven't got a diskstream yet, there's nothing to worry about, and we can't call get_channel_mask() anyway. */ - if (!midi_diskstream()) { + if (!_disk_writer) { return; } @@ -945,7 +864,7 @@ MidiTrack::act_on_mute () } /* Resolve active notes. */ - midi_diskstream()->resolve_tracker(_immediate_events, Port::port_offset()); + _disk_reader->resolve_tracker(_immediate_events, Port::port_offset()); } } @@ -967,11 +886,7 @@ MidiTrack::monitoring_changed (bool self, Controllable::GroupControlDisposition } } - boost::shared_ptr md (midi_diskstream()); - - if (md) { - md->reset_tracker (); - } + _disk_reader->reset_tracker (); } MonitorState diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 8814d01978..28b99fd427 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -1546,7 +1546,6 @@ Session::hookup_io () if (a->init()) { throw failed_constructor (); } - a->use_new_diskstream (); auditioner = a; } @@ -2625,8 +2624,6 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool s track->set_strict_io (true); } - track->use_new_diskstream(); - BOOST_MARK_TRACK (track); { @@ -2648,8 +2645,6 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool s route_group->add (track); } - track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this)); - new_routes.push_back (track); ret.push_back (track); } @@ -3223,8 +3218,6 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r } } - track->use_new_diskstream(); - BOOST_MARK_TRACK (track); { @@ -3253,8 +3246,6 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r track->non_realtime_input_change(); - track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this)); - new_routes.push_back (track); ret.push_back (track); } diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index b454c33367..c818943e1d 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -1845,7 +1845,7 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version) return ret; } - track->set_diskstream (*i); + // XXX DISK NEED TO SET UP DISKSTREAM ??? BOOST_MARK_TRACK (track); ret = track; diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index d7d9a0a9d1..5cc87f8ecd 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -21,11 +21,14 @@ #include "ardour/debug.h" #include "ardour/delivery.h" #include "ardour/diskstream.h" +#include "ardour/disk_reader.h" +#include "ardour/disk_writer.h" #include "ardour/event_type_map.h" #include "ardour/io_processor.h" #include "ardour/meter.h" #include "ardour/monitor_control.h" #include "ardour/playlist.h" +#include "ardour/playlist_factory.h" #include "ardour/port.h" #include "ardour/processor.h" #include "ardour/profile.h" @@ -51,6 +54,25 @@ Track::Track (Session& sess, string name, PresentationInfo::Flag flag, TrackMode { _freeze_record.state = NoFreeze; _declickable = true; + + DiskIOProcessor::Flag dflags = DiskIOProcessor::Recordable; + + if (_mode == Destructive && !Profile->get_trx()) { + dflags = DiskIOProcessor::Flag (dflags | DiskIOProcessor::Destructive); + } else if (_mode == NonLayered){ + dflags = DiskIOProcessor::Flag(dflags | DiskIOProcessor::NonLayered); + } + + _disk_reader.reset (new DiskReader (sess, name, dflags)); + + _disk_reader->set_block_size (_session.get_block_size ()); + _disk_reader->set_route (shared_from_this()); + _disk_reader->do_refill_with_alloc (); + + _disk_writer.reset (new DiskWriter (sess, name, dflags)); + _disk_writer->set_block_size (_session.get_block_size ()); + _disk_writer->set_route (shared_from_this()); + } Track::~Track () @@ -86,18 +108,6 @@ Track::init () return 0; } -void -Track::use_new_diskstream () -{ - boost::shared_ptr ds = create_diskstream (); - - ds->do_refill_with_alloc (); - ds->set_block_size (_session.get_block_size ()); - ds->playlist()->set_orig_track_id (id()); - - set_diskstream (ds); -} - XMLNode& Track::get_state () { @@ -128,18 +138,12 @@ Track::set_state (const XMLNode& node, int version) XMLNode* child; - if (version >= 3000) { + if (version >= 3000 && version < 4000) { if ((child = find_named_node (node, X_("Diskstream"))) != 0) { - boost::shared_ptr ds = diskstream_factory (*child); - ds->do_refill_with_alloc (); - set_diskstream (ds); + /* XXX DISK ... setup reader/writer from XML */ } } - if (_diskstream) { - _diskstream->playlist()->set_orig_track_id (id()); - } - /* set rec-enable control *AFTER* setting up diskstream, because it may want to operate on the diskstream as it sets its own state */ @@ -406,7 +410,7 @@ Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, */ } - _diskstream->check_record_status (start_frame, can_record); + _disk_writer->check_record_status (start_frame, can_record); bool be_silent; @@ -443,7 +447,7 @@ Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, if (_meter_point == MeterInput) { /* still need input monitoring and metering */ - bool const track_rec = _diskstream->record_enabled (); + bool const track_rec = _disk_writer->record_enabled (); bool const auto_input = _session.config.get_auto_input (); bool const software_monitor = Config->get_monitoring_model() == SoftwareMonitoring; bool const tape_machine_mode = Config->get_tape_machine_mode (); @@ -502,10 +506,11 @@ Track::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /* { Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); if (!lm.locked()) { - framecnt_t playback_distance = _diskstream->calculate_playback_distance(nframes); - if (can_internal_playback_seek(playback_distance)) { - internal_playback_seek(playback_distance); - } + // XXX DISK reader needs to seek ahead the correct distance ?? OR DOES IT ? + //framecnt_t playback_distance = _disk_reader->calculate_playback_distance(nframes); + //if (can_internal_playback_seek(playback_distance)) { + // internal_playback_seek(playback_distance); + //} return 0; } @@ -524,42 +529,9 @@ Track::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /* silence (nframes); flush_processor_buffers_locked (nframes); - framecnt_t playback_distance; - - BufferSet& bufs (_session.get_route_buffers (n_process_buffers(), true)); - - int const dret = _diskstream->process (bufs, _session.transport_frame(), nframes, playback_distance, false); - need_butler = _diskstream->commit (playback_distance); - return dret; -} - -void -Track::set_diskstream (boost::shared_ptr ds) -{ - _diskstream = ds; - - ds->PlaylistChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_playlist_changed, this)); - diskstream_playlist_changed (); - ds->SpeedChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_speed_changed, this)); - ds->AlignmentStyleChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_alignment_style_changed, this)); -} - -void -Track::diskstream_playlist_changed () -{ - PlaylistChanged (); /* EMIT SIGNAL */ -} - -void -Track::diskstream_speed_changed () -{ - SpeedChanged (); /* EMIT SIGNAL */ -} - -void -Track::diskstream_alignment_style_changed () -{ - AlignmentStyleChanged (); /* EMIT SIGNAL */ + //BufferSet& bufs (_session.get_route_buffers (n_process_buffers(), true)); + // XXXX DISKWRITER/READER ADVANCE, SET need_butler + return 0; } boost::shared_ptr @@ -571,103 +543,100 @@ Track::playlist () void Track::request_input_monitoring (bool m) { - _diskstream->request_input_monitoring (m); + // XXX DISK } void Track::ensure_input_monitoring (bool m) { - _diskstream->ensure_input_monitoring (m); + // XXX DISK } bool Track::destructive () const { - return _diskstream->destructive (); + return _disk_writer->destructive (); } list > & Track::last_capture_sources () { - return _diskstream->last_capture_sources (); + return _disk_writer->last_capture_sources (); } void Track::set_capture_offset () { - _diskstream->set_capture_offset (); + _disk_writer->set_capture_offset (); } std::string Track::steal_write_source_name() { - return _diskstream->steal_write_source_name (); + return _disk_writer->steal_write_source_name (); } void Track::reset_write_sources (bool r, bool force) { - _diskstream->reset_write_sources (r, force); + _disk_writer->reset_write_sources (r, force); } float Track::playback_buffer_load () const { - return _diskstream->playback_buffer_load (); + return _disk_reader->buffer_load (); } float Track::capture_buffer_load () const { - return _diskstream->capture_buffer_load (); + return _disk_writer->buffer_load (); } int Track::do_refill () { - return _diskstream->do_refill (); + return _disk_reader->do_refill (); } int Track::do_flush (RunContext c, bool force) { - return _diskstream->do_flush (c, force); + return _disk_writer->do_flush (c, force); } void Track::set_pending_overwrite (bool o) { - _diskstream->set_pending_overwrite (o); + _disk_reader->set_pending_overwrite (o); } int Track::seek (framepos_t p, bool complete_refill) { - return _diskstream->seek (p, complete_refill); + if (_disk_reader->seek (p, complete_refill)) { + return -1; + } + return _disk_writer->seek (p, complete_refill); } bool Track::hidden () const { - return _diskstream->hidden (); + return _disk_writer->hidden () || _disk_reader->hidden(); } int Track::can_internal_playback_seek (framecnt_t p) { - return _diskstream->can_internal_playback_seek (p); + return _disk_reader->can_internal_playback_seek (p); } int Track::internal_playback_seek (framecnt_t p) { - return _diskstream->internal_playback_seek (p); -} - -void -Track::non_realtime_input_change () -{ - _diskstream->non_realtime_input_change (); + return _disk_reader->internal_playback_seek (p); } void @@ -679,74 +648,82 @@ Track::non_realtime_locate (framepos_t p) /* don't waste i/o cycles and butler calls for hidden (secret) tracks */ - _diskstream->non_realtime_locate (p); + _disk_reader->non_realtime_locate (p); + _disk_writer->non_realtime_locate (p); } } void Track::non_realtime_set_speed () { - _diskstream->non_realtime_set_speed (); + _disk_reader->non_realtime_set_speed (); } int Track::overwrite_existing_buffers () { - return _diskstream->overwrite_existing_buffers (); + return _disk_reader->overwrite_existing_buffers (); } framecnt_t Track::get_captured_frames (uint32_t n) const { - return _diskstream->get_captured_frames (n); + return _disk_writer->get_captured_frames (n); } int Track::set_loop (Location* l) { - return _diskstream->set_loop (l); + if (_disk_reader->set_loop (l)) { + return -1; + } + return _disk_writer->set_loop (l); } void Track::transport_looped (framepos_t p) { - _diskstream->transport_looped (p); + return _disk_writer->transport_looped (p); } bool Track::realtime_set_speed (double s, bool g) { - return _diskstream->realtime_set_speed (s, g); + if (_disk_reader->realtime_set_speed (s, g)) { + return -1; + } + return _disk_writer->realtime_set_speed (s, g); } void Track::transport_stopped_wallclock (struct tm & n, time_t t, bool g) { - _diskstream->transport_stopped_wallclock (n, t, g); + _disk_writer->transport_stopped_wallclock (n, t, g); } bool Track::pending_overwrite () const { - return _diskstream->pending_overwrite (); + return _disk_reader->pending_overwrite (); } double Track::speed () const { - return _diskstream->speed (); + return _disk_reader->speed (); } void Track::prepare_to_stop (framepos_t t, framepos_t a) { - _diskstream->prepare_to_stop (t, a); + _disk_writer->prepare_to_stop (t, a); } void Track::set_slaved (bool s) { - _diskstream->set_slaved (s); + _disk_reader->set_slaved (s); + _disk_writer->set_slaved (s); } ChanCount @@ -758,71 +735,113 @@ Track::n_channels () framepos_t Track::get_capture_start_frame (uint32_t n) const { - return _diskstream->get_capture_start_frame (n); + return _disk_writer->get_capture_start_frame (n); } AlignStyle Track::alignment_style () const { - return _diskstream->alignment_style (); + return _disk_writer->alignment_style (); } AlignChoice Track::alignment_choice () const { - return _diskstream->alignment_choice (); + return _disk_writer->alignment_choice (); } framepos_t Track::current_capture_start () const { - return _diskstream->current_capture_start (); + return _disk_writer->current_capture_start (); } framepos_t Track::current_capture_end () const { - return _diskstream->current_capture_end (); + return _disk_writer->current_capture_end (); } void Track::playlist_modified () { - _diskstream->playlist_modified (); + _disk_reader->playlist_modified (); +} + +int +Track::find_and_use_playlist (DataType dt, const string& name) +{ + boost::shared_ptr 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 -Track::use_playlist (boost::shared_ptr p) +Track::use_playlist (DataType dt, boost::shared_ptr p) { - int ret = _diskstream->use_playlist (p); - if (ret == 0) { - p->set_orig_track_id (id()); + int ret; + + if ((ret = _disk_reader->use_playlist (dt, p)) == 0) { + if ((ret = _disk_writer->use_playlist (dt, p)) == 0) { + p->set_orig_track_id (id()); + } } + return ret; } int Track::use_copy_playlist () { - int ret = _diskstream->use_copy_playlist (); + assert (_playlists[data_type()]); - if (ret == 0) { - _diskstream->playlist()->set_orig_track_id (id()); + if (_playlists[data_type()] == 0) { + error << string_compose(_("DiskIOProcessor %1: there is no existing playlist to make a copy of!"), _name) << endmsg; + return -1; } - return ret; + string newname; + boost::shared_ptr playlist; + + newname = Playlist::bump_name (_playlists[data_type()]->name(), _session); + + if ((playlist = PlaylistFactory::create (_playlists[data_type()], newname)) == 0) { + return -1; + } + + playlist->reset_shares(); + + return use_playlist (data_type(), playlist); } int Track::use_new_playlist () { - int ret = _diskstream->use_new_playlist (); + string newname; + boost::shared_ptr playlist = _playlists[data_type()]; - if (ret == 0) { - _diskstream->playlist()->set_orig_track_id (id()); + if (playlist) { + newname = Playlist::bump_name (playlist->name(), _session); + } else { + newname = Playlist::bump_name (_name, _session); } - return ret; + playlist = PlaylistFactory::create (data_type(), _session, newname, hidden()); + + if (!playlist) { + return -1; + } + + return use_playlist (data_type(), playlist); } void @@ -955,7 +974,7 @@ Track::monitoring_state () const */ bool const roll = _session.transport_rolling (); - bool const track_rec = _diskstream->record_enabled (); + bool const track_rec = _disk_writer->record_enabled (); bool const auto_input = _session.config.get_auto_input (); bool const software_monitor = Config->get_monitoring_model() == SoftwareMonitoring; bool const tape_machine_mode = Config->get_tape_machine_mode (); @@ -1078,10 +1097,16 @@ Track::metering_state () const bool rv; if (_session.transport_rolling ()) { // audio_track.cc || midi_track.cc roll() runs meter IFF: - rv = _meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled()); + rv = _meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _disk_writer->record_enabled()); } else { // track no_roll() always metering if rv = _meter_point == MeterInput; } return rv ? MeteringInput : MeteringRoute; } + +void +Track::non_realtime_input_change () +{ + // XXX DISK do we need to do anything here anymore ? +} -- cgit v1.2.3