merge almost all audio & midi diskstream code, redistribute between DiskIOProcessor...
authorPaul Davis <paul@linuxaudiosystems.com>
Thu, 16 Mar 2017 16:26:53 +0000 (17:26 +0100)
committerPaul Davis <paul@linuxaudiosystems.com>
Mon, 18 Sep 2017 15:40:52 +0000 (11:40 -0400)
libs/ardour/ardour/disk_io.h
libs/ardour/ardour/disk_reader.h
libs/ardour/ardour/disk_writer.h
libs/ardour/disk_io.cc
libs/ardour/disk_reader.cc
libs/ardour/disk_writer.cc

index d1fa1e7d6628fca9b9f5a231ee1be8f9cddb22b4..dd775cbe8481c003b8a5ee4bd740b8d82d322c85 100644 (file)
 
 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
index 6a50594dc4a92f1530e56dfec088ee8957c34987..7b076ab23fd0f38f2eaf7f9fd102597d3cf47405 100644 (file)
@@ -44,12 +44,8 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
        static void set_chunk_frames (framecnt_t n) { _chunk_frames = n; }
 
        void run (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framepos_t /*end_frame*/, double speed, pframes_t /*nframes*/, bool /*result_required*/);
-       int set_block_size (pframes_t);
-       bool configure_io (ChanCount in, ChanCount out);
-       bool can_support_io_configuration (const ChanCount& in, ChanCount& out) = 0;
        void realtime_handle_transport_stopped ();
        void realtime_locate ();
-       void non_realtime_locate (framepos_t);
        int overwrite_existing_buffers ();
        void set_pending_overwrite (bool yn);
 
@@ -59,16 +55,6 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
        virtual XMLNode& state (bool full);
        int set_state (const XMLNode&, int version);
 
-       boost::shared_ptr<Playlist>      get_playlist (DataType dt) const { return _playlists[dt]; }
-       boost::shared_ptr<MidiPlaylist>  midi_playlist() const;
-       boost::shared_ptr<AudioPlaylist> audio_playlist() const;
-
-       virtual void playlist_modified ();
-       virtual int use_playlist (DataType, boost::shared_ptr<Playlist>);
-       virtual int use_new_playlist (DataType);
-       virtual int use_copy_playlist (DataType);
-
-       PBD::Signal1<void,DataType>   PlaylistChanged;
        PBD::Signal0<void>            AlignmentStyleChanged;
 
        float buffer_load() const;
@@ -93,8 +79,6 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
 
        bool pending_overwrite () const { return _pending_overwrite; }
 
-       virtual int find_and_use_playlist (DataType, std::string const &);
-
        // Working buffers for do_refill (butler thread)
        static void allocate_working_buffers();
        static void free_working_buffers();
@@ -104,19 +88,19 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
        int can_internal_playback_seek (framecnt_t distance);
        int seek (framepos_t frame, bool complete_refill = false);
 
-       PBD::Signal0<void> Underrun;
-
-  protected:
-       boost::shared_ptr<Playlist> _playlists[DataType::num_types];
+       static PBD::Signal0<void> Underrun;
 
-       virtual void playlist_changed (const PBD::PropertyChange&);
-       virtual void playlist_deleted (boost::weak_ptr<Playlist>);
-       virtual void playlist_ranges_moved (std::list< Evoral::RangeMove<framepos_t> > const &, bool);
+       void playlist_modified ();
 
+  protected:
        void reset_tracker ();
        void resolve_tracker (Evoral::EventSink<framepos_t>& buffer, framepos_t time);
        boost::shared_ptr<MidiBuffer> get_gui_feed_buffer () const;
 
+       void playlist_changed (const PBD::PropertyChange&);
+       int use_playlist (DataType, boost::shared_ptr<Playlist>);
+       void playlist_ranges_moved (std::list< Evoral::RangeMove<framepos_t> > const &, bool);
+
   private:
        /** The number of frames by which this diskstream's output should be delayed
            with respect to the transport frame.  This is used for latency compensation.
@@ -133,8 +117,6 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
        framepos_t     playback_sample;
        MonitorChoice   _monitoring_choice;
 
-       PBD::ScopedConnectionList playlist_connections;
-
        int _do_refill_with_alloc (bool partial_fill);
 
        static framecnt_t _chunk_frames;
@@ -142,17 +124,11 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
 
        /* The MIDI stuff */
 
-       MidiRingBuffer<framepos_t>*  _midi_buf;
-
        /** A buffer that we use to put newly-arrived MIDI data in for
            the GUI to read (so that it can update itself).
        */
        MidiBuffer                   _gui_feed_buffer;
        mutable Glib::Threads::Mutex _gui_feed_buffer_mutex;
-       CubicMidiInterpolation midi_interpolation;
-       gint                         _frames_written_to_ringbuffer;
-       gint                         _frames_read_from_ringbuffer;
-
 
        int audio_read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
                        framepos_t& start, framecnt_t cnt,
@@ -169,7 +145,6 @@ class LIBARDOUR_API DiskReader : public DiskIOProcessor
        int internal_playback_seek (framecnt_t distance);
        frameoffset_t calculate_playback_distance (pframes_t);
 
-       void allocate_temporary_buffers();
        void get_playback (MidiBuffer& dst, framecnt_t nframes);
        void flush_playback (framepos_t start, framepos_t end);
 };
index 984a84a938ef48b7e2b010fda1045489077af6ec..3eed3c6d994fe7822b99fd25097b2efcbfeca44a 100644 (file)
 #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
index 48b58ca3f10b02a4ab2467c931c1d7e1e2f55d97..1516933e321c9c4e3e20381e5088b0a5310b7d2a 100644 (file)
 #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;
+}
index 73d905b4c89c703f39e7f1824a155f28268bc3a7..aa26f2644f31d3bb978a1e69f6bc25a956344b00 100644 (file)
@@ -28,6 +28,7 @@
 #include "ardour/disk_reader.h"
 #include "ardour/midi_ring_buffer.h"
 #include "ardour/midi_playlist.h"
+#include "ardour/pannable.h"
 #include "ardour/playlist.h"
 #include "ardour/playlist_factory.h"
 #include "ardour/session.h"
@@ -38,6 +39,10 @@ using namespace PBD;
 using namespace std;
 
 ARDOUR::framecnt_t DiskReader::_chunk_frames = default_chunk_frames ();
+PBD::Signal0<void> DiskReader::Underrun;
+Sample* DiskReader::_mixdown_buffer = 0;
+gain_t* DiskReader::_gain_buffer = 0;
+framecnt_t DiskReader::midi_readahead = 4096;
 
 DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f)
        : DiskIOProcessor (s, str, f)
@@ -50,8 +55,6 @@ DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f)
         , playback_sample (0)
        , _monitoring_choice (MonitorDisk)
        , _gui_feed_buffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
-       , _frames_written_to_ringbuffer (0)
-       , _frames_read_from_ringbuffer (0)
 {
 }
 
@@ -130,6 +133,13 @@ DiskReader::set_roll_delay (ARDOUR::framecnt_t nframes)
        _roll_delay = nframes;
 }
 
+XMLNode&
+DiskReader::state (bool full)
+{
+       XMLNode& node (DiskIOProcessor::state (full));
+       return node;
+}
+
 int
 DiskReader::set_state (const XMLNode& node, int version)
 {
@@ -158,51 +168,6 @@ DiskReader::set_state (const XMLNode& node, int version)
        return 0;
 }
 
-/* Processor interface */
-
-bool
-DiskReader::configure_io (ChanCount in, ChanCount out)
-{
-       Glib::Threads::Mutex::Lock lm (state_lock);
-
-       RCUWriter<ChannelList> writer (channels);
-       boost::shared_ptr<ChannelList> c = writer.get_copy();
-
-       uint32_t n_audio = in.n_audio();
-
-       if (n_audio > c->size()) {
-               add_channel_to (c, n_audio - c->size());
-       } else if (n_audio < c->size()) {
-               remove_channel_from (c, c->size() - n_audio);
-       }
-
-       if (in.n_midi() > 0 && !_midi_buf) {
-               const size_t size = _session.butler()->midi_diskstream_buffer_size();
-               _midi_buf = new MidiRingBuffer<framepos_t>(size);
-               midi_interpolation.add_channel_to (0,0);
-       }
-
-       Processor::configure_io (in, out);
-
-       return true;
-}
-
-bool
-DiskReader::can_support_io_configuration (const ChanCount& in, ChanCount& out)
-{
-       if (in.n_midi() != 0 && in.n_midi() != 1) {
-               /* we only support zero or 1 MIDI stream */
-               return false;
-       }
-
-       if (in != out) {
-               /* currently no way to deliver different channels that we receive */
-               return false;
-       }
-
-       return true;
-}
-
 void
 DiskReader::realtime_handle_transport_stopped ()
 {
@@ -247,87 +212,6 @@ DiskReader::adjust_buffering ()
        }
 }
 
-DiskReader::ChannelInfo::ChannelInfo (framecnt_t bufsize, framecnt_t speed_size, framecnt_t wrap_size)
-{
-       current_buffer = 0;
-
-       speed_buffer = new Sample[speed_size];
-       wrap_buffer = new Sample[wrap_size];
-
-       buf = new RingBufferNPT<Sample> (bufsize);
-
-       /* touch the ringbuffer buffer, which will cause
-          them to be mapped into locked physical RAM if
-          we're running with mlockall(). this doesn't do
-          much if we're not.
-       */
-
-       memset (buf->buffer(), 0, sizeof (Sample) * buf->bufsize());
-}
-
-void
-DiskReader::ChannelInfo::resize (framecnt_t bufsize)
-{
-       delete buf;
-       buf = new RingBufferNPT<Sample> (bufsize);
-       memset (buf->buffer(), 0, sizeof (Sample) * buf->bufsize());
-}
-
-DiskReader::ChannelInfo::~ChannelInfo ()
-{
-       delete [] speed_buffer;
-       speed_buffer = 0;
-
-       delete [] wrap_buffer;
-       wrap_buffer = 0;
-
-       delete buf;
-       buf = 0;
-}
-
-int
-DiskReader::set_block_size (pframes_t /*nframes*/)
-{
-       if (_session.get_block_size() > speed_buffer_size) {
-               speed_buffer_size = _session.get_block_size();
-               boost::shared_ptr<ChannelList> c = channels.reader();
-
-               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
-                       delete [] (*chan)->speed_buffer;
-                       (*chan)->speed_buffer = new Sample[speed_buffer_size];
-               }
-       }
-       allocate_temporary_buffers ();
-       return 0;
-}
-
-void
-DiskReader::allocate_temporary_buffers ()
-{
-       /* make sure the wrap buffer is at least large enough to deal
-          with the speeds up to 1.2, to allow for micro-variation
-          when slaving to MTC, Timecode etc.
-       */
-
-       double const sp = max (fabs (_actual_speed), 1.2);
-       framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() * sp) + 2;
-
-       if (required_wrap_size > wrap_buffer_size) {
-
-               boost::shared_ptr<ChannelList> c = channels.reader();
-
-               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
-                       if ((*chan)->wrap_buffer) {
-                               delete [] (*chan)->wrap_buffer;
-                       }
-                       (*chan)->wrap_buffer = new Sample[required_wrap_size];
-               }
-
-               wrap_buffer_size = required_wrap_size;
-       }
-}
-
-
 void
 DiskReader::playlist_changed (const PropertyChange&)
 {
@@ -343,70 +227,17 @@ DiskReader::playlist_modified ()
        }
 }
 
-void
-DiskReader::playlist_deleted (boost::weak_ptr<Playlist> wpl)
-{
-       boost::shared_ptr<Playlist> pl (wpl.lock());
-
-       if (!pl) {
-               return;
-       }
-
-       for (uint32_t n = 0; n < DataType::num_types; ++n) {
-               if (pl == _playlists[n]) {
-
-                       /* this catches an ordering issue with session destruction. playlists
-                          are destroyed before disk readers. we have to invalidate any handles
-                          we have to the playlist.
-                       */
-                       _playlists[n].reset ();
-                       break;
-               }
-       }
-}
-
-boost::shared_ptr<AudioPlaylist>
-DiskReader::audio_playlist () const
-{
-       return boost::dynamic_pointer_cast<AudioPlaylist> (_playlists[DataType::AUDIO]);
-}
-
-boost::shared_ptr<MidiPlaylist>
-DiskReader::midi_playlist () const
-{
-       return boost::dynamic_pointer_cast<MidiPlaylist> (_playlists[DataType::MIDI]);
-}
-
 int
 DiskReader::use_playlist (DataType dt, boost::shared_ptr<Playlist> playlist)
 {
-        if (!playlist) {
-                return 0;
-        }
-
         bool prior_playlist = false;
 
-       {
-               Glib::Threads::Mutex::Lock lm (state_lock);
-
-               if (playlist == _playlists[dt]) {
-                       return 0;
-               }
-
-               playlist_connections.drop_connections ();
-
-               if (_playlists[dt]) {
-                       _playlists[dt]->release();
-                        prior_playlist = true;
-               }
-
-               _playlists[dt] = playlist;
-               playlist->use();
+        if (_playlists[dt]) {
+               prior_playlist = true;
+        }
 
-               playlist->ContentsChanged.connect_same_thread (playlist_connections, boost::bind (&DiskReader::playlist_modified, this));
-               playlist->LayeringChanged.connect_same_thread (playlist_connections, boost::bind (&DiskReader::playlist_modified, this));
-               playlist->DropReferences.connect_same_thread (playlist_connections, boost::bind (&DiskReader::playlist_deleted, this, boost::weak_ptr<Playlist>(playlist)));
-               playlist->RangesMoved.connect_same_thread (playlist_connections, boost::bind (&DiskReader::playlist_ranges_moved, this, _1, _2));
+        if (DiskIOProcessor::use_playlist (dt, playlist)) {
+               return -1;
        }
 
        /* don't do this if we've already asked for it *or* if we are setting up
@@ -419,83 +250,9 @@ DiskReader::use_playlist (DataType dt, boost::shared_ptr<Playlist> playlist)
                overwrite_queued = true;
        }
 
-       PlaylistChanged (dt); /* EMIT SIGNAL */
-       _session.set_dirty ();
-
        return 0;
 }
 
-int
-DiskReader::find_and_use_playlist (DataType dt, const string& name)
-{
-       boost::shared_ptr<Playlist> playlist;
-
-       if ((playlist = _session.playlists->by_name (name)) == 0) {
-               playlist = PlaylistFactory::create (dt, _session, name);
-       }
-
-       if (!playlist) {
-               error << string_compose(_("DiskReader: \"%1\" isn't an playlist"), name) << endmsg;
-               return -1;
-       }
-
-       return use_playlist (dt, playlist);
-}
-
-int
-DiskReader::use_new_playlist (DataType dt)
-{
-       string newname;
-       boost::shared_ptr<Playlist> playlist = _playlists[dt];
-
-       if (playlist) {
-               newname = Playlist::bump_name (playlist->name(), _session);
-       } else {
-               newname = Playlist::bump_name (_name, _session);
-       }
-
-       playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (dt, _session, newname, hidden()));
-
-       if (!playlist) {
-               return -1;
-       }
-
-       return use_playlist (dt, playlist);
-}
-
-int
-DiskReader::use_copy_playlist (DataType dt)
-{
-       assert (_playlists[dt]);
-
-       if (_playlists[dt] == 0) {
-               error << string_compose(_("DiskReader %1: there is no existing playlist to make a copy of!"), _name) << endmsg;
-               return -1;
-       }
-
-       string newname;
-       boost::shared_ptr<Playlist> playlist;
-
-       newname = Playlist::bump_name (_playlists[dt]->name(), _session);
-
-       if ((playlist = PlaylistFactory::create (_playlists[dt], newname)) == 0) {
-               return -1;
-       }
-
-       playlist->reset_shares();
-
-       return use_playlist (dt, playlist);
-}
-
-
-/** Do some record stuff [not described in this comment!]
- *
- *  Also:
- *    - Setup playback_distance with the nframes, or nframes adjusted
- *      for current varispeed, if appropriate.
- *    - Setup current_buffer in each ChannelInfo to point to data
- *      that someone can read playback_distance worth of data from.
- */
 void
 DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                  double speed, pframes_t nframes, bool result_required)
@@ -511,133 +268,122 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                return;
        }
 
-       for (chan = c->begin(); chan != c->end(); ++chan) {
-               (*chan)->current_buffer = 0;
-       }
-
        const bool need_disk_signal = result_required || _monitoring_choice == MonitorDisk || _monitoring_choice == MonitorCue;
 
-       if (need_disk_signal) {
-
-               /* we're doing playback */
+       if (fabsf (_actual_speed) != 1.0f) {
+               midi_interpolation.set_speed (_target_speed);
+               interpolation.set_speed (_target_speed);
+               playback_distance = midi_interpolation.distance (nframes);
+       } else {
+               playback_distance = nframes;
+       }
 
-               framecnt_t necessary_samples;
+       if (!need_disk_signal) {
 
-               if (_actual_speed != 1.0) {
-                       necessary_samples = (framecnt_t) ceil ((nframes * fabs (_actual_speed))) + 2;
-               } else {
-                       necessary_samples = nframes;
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+                       (*chan)->buf->increment_read_ptr (playback_distance);
                }
 
-               for (chan = c->begin(); chan != c->end(); ++chan) {
-                       (*chan)->buf->get_read_vector (&(*chan)->read_vector);
-               }
+               return;
+       }
 
-               n = 0;
+       /* we're doing playback */
 
-               /* Setup current_buffer in each ChannelInfo to point to data that someone
-                  can read necessary_samples (== nframes at a transport speed of 1) worth of data
-                  from right now.
-               */
+       size_t n_buffers = bufs.count().n_audio();
+       size_t n_chans = c->size();
+       gain_t scaling;
 
-               for (chan = c->begin(); chan != c->end(); ++chan, ++n) {
+       if (n_chans > n_buffers) {
+               scaling = ((float) n_buffers)/n_chans;
+       } else {
+               scaling = 1.0;
+       }
 
-                       ChannelInfo* chaninfo (*chan);
+       for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
 
-                       if (necessary_samples <= (framecnt_t) chaninfo->read_vector.len[0]) {
-                               /* There are enough samples in the first part of the ringbuffer */
-                               chaninfo->current_buffer = chaninfo->read_vector.buf[0];
+               AudioBuffer& buf (bufs.get_audio (n%n_buffers));
+               Sample* outgoing = buf.data ();
 
-                       } else {
-                               framecnt_t total = chaninfo->read_vector.len[0] + chaninfo->read_vector.len[1];
+               ChannelInfo* chaninfo (*chan);
 
-                               if (necessary_samples > total) {
-                                       cerr << _name << " Need " << necessary_samples << " total = " << total << endl;
-                                       cerr << "underrun for " << _name << endl;
-                                        DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, total space = %3\n",
-                                                                                    DEBUG_THREAD_SELF, name(), total));
-                                       Underrun ();
-                                       return;
+               chaninfo->buf->get_read_vector (&(*chan)->rw_vector);
 
-                               } else {
-
-                                       /* We have enough samples, but not in one lump.  Coalesce the two parts
-                                          into one in wrap_buffer in our ChannelInfo, and specify that
-                                          as our current_buffer.
-                                       */
+               if (playback_distance <= (framecnt_t) chaninfo->rw_vector.len[0]) {
 
-                                       assert(wrap_buffer_size >= necessary_samples);
+                       if (fabsf (_actual_speed) != 1.0f) {
+                               (void) interpolation.interpolate (
+                                       n, nframes,
+                                       chaninfo->rw_vector.buf[0],
+                                       outgoing);
+                       } else {
+                               memcpy (outgoing, chaninfo->rw_vector.buf[0], sizeof (Sample) * playback_distance);
+                       }
 
-                                       /* Copy buf[0] from buf */
-                                       memcpy ((char *) chaninfo->wrap_buffer,
-                                                       chaninfo->read_vector.buf[0],
-                                                       chaninfo->read_vector.len[0] * sizeof (Sample));
+               } else {
 
-                                       /* Copy buf[1] from buf */
-                                       memcpy (chaninfo->wrap_buffer + chaninfo->read_vector.len[0],
-                                                       chaninfo->read_vector.buf[1],
-                                                       (necessary_samples - chaninfo->read_vector.len[0])
-                                                                       * sizeof (Sample));
+                       const framecnt_t total = chaninfo->rw_vector.len[0] + chaninfo->rw_vector.len[1];
 
-                                       chaninfo->current_buffer = chaninfo->wrap_buffer;
-                               }
-                       }
-               }
+                       if (playback_distance <= total) {
 
-               if (_actual_speed != 1.0f && _actual_speed != -1.0f) {
+                               /* We have enough samples, but not in one lump.
+                               */
 
-                       interpolation.set_speed (_target_speed);
+                               if (fabsf (_actual_speed) != 1.0f) {
+                                       interpolation.interpolate (n, chaninfo->rw_vector.len[0],
+                                                                  chaninfo->rw_vector.buf[0],
+                                                                  outgoing);
+                                       outgoing += chaninfo->rw_vector.len[0];
+                                       interpolation.interpolate (n, playback_distance - chaninfo->rw_vector.len[0],
+                                                                  chaninfo->rw_vector.buf[1],
+                                                                  outgoing);
+                               } else {
+                                       memcpy (outgoing,
+                                               chaninfo->rw_vector.buf[0],
+                                               chaninfo->rw_vector.len[0] * sizeof (Sample));
+                                       outgoing += chaninfo->rw_vector.len[0];
+                                       memcpy (outgoing,
+                                               chaninfo->rw_vector.buf[1],
+                                               (playback_distance - chaninfo->rw_vector.len[0]) * sizeof (Sample));
+                               }
 
-                       int channel = 0;
-                       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) {
-                               ChannelInfo* chaninfo (*chan);
+                       } else {
 
-                               playback_distance = interpolation.interpolate (
-                                       channel, nframes, chaninfo->current_buffer, chaninfo->speed_buffer);
+                               cerr << _name << " Need " << playback_distance << " total = " << total << endl;
+                               cerr << "underrun for " << _name << endl;
+                               DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, total space = %3\n",
+                                                                           DEBUG_THREAD_SELF, name(), total));
+                               Underrun ();
+                               return;
 
-                               chaninfo->current_buffer = chaninfo->speed_buffer;
                        }
 
-               } else {
-                       playback_distance = nframes;
                }
 
+               if (scaling != 1.0f) {
+                       apply_gain_to_buffer (outgoing, nframes, scaling);
+               }
+
+               chaninfo->buf->increment_read_ptr (playback_distance);
                _speed = _target_speed;
        }
 
-       if (need_disk_signal) {
-
-               /* copy data over to buffer set */
-
-               size_t n_buffers = bufs.count().n_audio();
-               size_t n_chans = c->size();
-               gain_t scaling = 1.0f;
+       /* MIDI data handling */
 
-               if (n_chans > n_buffers) {
-                       scaling = ((float) n_buffers)/n_chans;
-               }
+       if (!_session.declick_out_pending()) {
 
-               for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
+               /* copy the diskstream data to all output buffers */
 
-                       AudioBuffer& buf (bufs.get_audio (n%n_buffers));
-                       ChannelInfo* chaninfo (*chan);
+               MidiBuffer& mbuf (bufs.get_midi (0));
+               get_playback (mbuf, playback_distance);
 
-                       if (n < n_chans) {
-                               if (scaling != 1.0f) {
-                                       buf.read_from_with_gain (chaninfo->current_buffer, nframes, scaling);
-                               } else {
-                                       buf.read_from (chaninfo->current_buffer, nframes);
-                               }
-                       } else {
-                               if (scaling != 1.0f) {
-                                       buf.accumulate_with_gain_from (chaninfo->current_buffer, nframes, scaling);
-                               } else {
-                                       buf.accumulate_from (chaninfo->current_buffer, nframes);
-                               }
+               /* vari-speed */
+               if (_target_speed > 0 && _actual_speed != 1.0f) {
+                       MidiBuffer& mbuf (bufs.get_midi (0));
+                       for (MidiBuffer::iterator i = mbuf.begin(); i != mbuf.end(); ++i) {
+                               MidiBuffer::TimeType *tme = i.timeptr();
+                               *tme = (*tme) * nframes / playback_distance;
                        }
                }
-
-               /* extra buffers will already be silent, so leave them alone */
        }
 
        _need_butler = false;
@@ -648,10 +394,6 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                playback_sample += playback_distance;
        }
 
-       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
-               (*chan)->buf->increment_read_ptr (playback_distance);
-       }
-
        if (!c->empty()) {
                if (_slaved) {
                        if (c->front()->buf->write_space() >= c->front()->buf->bufsize() / 2) {
@@ -664,40 +406,6 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                }
        }
 
-       /* MIDI data handling */
-
-       if (_actual_speed != 1.0f && _target_speed > 0) {
-
-               interpolation.set_speed (_target_speed);
-
-               playback_distance = midi_interpolation.distance  (nframes);
-
-       } else {
-               playback_distance = nframes;
-       }
-
-       if (need_disk_signal && !_session.declick_out_pending()) {
-
-               /* copy the diskstream data to all output buffers */
-
-               MidiBuffer& mbuf (bufs.get_midi (0));
-               get_playback (mbuf, playback_distance);
-
-               /* leave the audio count alone */
-               ChanCount cnt (DataType::MIDI, 1);
-               cnt.set (DataType::AUDIO, bufs.count().n_audio());
-               bufs.set_count (cnt);
-
-               /* vari-speed */
-               if (_target_speed > 0 && _actual_speed != 1.0f) {
-                       MidiBuffer& mbuf (bufs.get_midi (0));
-                       for (MidiBuffer::iterator i = mbuf.begin(); i != mbuf.end(); ++i) {
-                               MidiBuffer::TimeType *tme = i.timeptr();
-                               *tme = (*tme) * nframes / playback_distance;
-                       }
-               }
-       }
-
        /* MIDI butler needed part */
 
        uint32_t frames_read = g_atomic_int_get(const_cast<gint*>(&_frames_read_from_ringbuffer));
@@ -890,18 +598,6 @@ DiskReader::overwrite_existing_buffers ()
        return ret;
 }
 
-void
-DiskReader::non_realtime_locate (framepos_t location)
-{
-       /* now refill channel buffers */
-
-       if (speed() != 1.0f || speed() != -1.0f) {
-               seek ((framepos_t) (location * (double) speed()), true);
-       } else {
-               seek (location, true);
-       }
-}
-
 int
 DiskReader::seek (framepos_t frame, bool complete_refill)
 {
@@ -1411,8 +1107,7 @@ DiskReader::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const &
                return;
        }
 
-#if 0
-       if (!_track || Config->get_automation_follows_regions () == false) {
+       if (!_route || Config->get_automation_follows_regions () == false) {
                return;
        }
 
@@ -1426,7 +1121,7 @@ DiskReader::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const &
        }
 
        /* move panner automation */
-       boost::shared_ptr<Pannable> pannable = _track->pannable();
+       boost::shared_ptr<Pannable> pannable = _route->pannable();
         Evoral::ControlSet::Controls& c (pannable->controls());
 
         for (Evoral::ControlSet::Controls::iterator ci = c.begin(); ci != c.end(); ++ci) {
@@ -1446,8 +1141,7 @@ DiskReader::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const &
                 }
         }
        /* move processor automation */
-       _track->foreach_processor (boost::bind (&Diskstream::move_processor_automation, this, _1, movements_frames));
-#endif
+        _route->foreach_processor (boost::bind (&DiskReader::move_processor_automation, this, _1, movements_frames));
 }
 
 void
index a436324320801262765d3f36be5317adecd54cab..c6f5c92c76c3fa59b12f0ac7bf7baaebb01866cc 100644 (file)
 
 #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());
+}