fix reverse play buffer refilling; turn on Blink signal again
[ardour.git] / libs / ardour / audio_diskstream.cc
index 5f682789ef1bbf9def5fb3afba8b1cd4ff68734c..c22ba75c7d7868186a82474a2ce4558eb592fb5a 100644 (file)
@@ -35,6 +35,7 @@
 #include <glibmm/thread.h>
 #include <pbd/xml++.h>
 #include <pbd/memento_command.h>
+#include <pbd/enumwriter.h>
 
 #include <ardour/ardour.h>
 #include <ardour/audioengine.h>
@@ -46,6 +47,7 @@
 #include <ardour/send.h>
 #include <ardour/region_factory.h>
 #include <ardour/audioplaylist.h>
+#include <ardour/playlist_factory.h>
 #include <ardour/cycle_timer.h>
 #include <ardour/audioregion.h>
 #include <ardour/source_factory.h>
@@ -94,34 +96,6 @@ AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
        }
 }
 
-void
-AudioDiskstream::init_channel (ChannelInfo &chan)
-{
-       chan.playback_wrap_buffer = 0;
-       chan.capture_wrap_buffer = 0;
-       chan.speed_buffer = 0;
-       chan.peak_power = 0.0f;
-       chan.source = 0;
-       chan.current_capture_buffer = 0;
-       chan.current_playback_buffer = 0;
-       chan.curr_capture_cnt = 0;
-       
-       chan.playback_buf = new RingBufferNPT<Sample> (_session.diskstream_buffer_size());
-       chan.capture_buf = new RingBufferNPT<Sample> (_session.diskstream_buffer_size());
-       chan.capture_transition_buf = new RingBufferNPT<CaptureTransition> (128);
-       
-       
-       /* touch the ringbuffer buffers, 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 (chan.playback_buf->buffer(), 0, sizeof (Sample) * chan.playback_buf->bufsize());
-       memset (chan.capture_buf->buffer(), 0, sizeof (Sample) * chan.capture_buf->bufsize());
-       memset (chan.capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * chan.capture_transition_buf->bufsize());
-}
-
-
 void
 AudioDiskstream::init (Diskstream::Flag f)
 {
@@ -139,40 +113,21 @@ AudioDiskstream::init (Diskstream::Flag f)
        assert(_n_channels == 1);
 }
 
-void
-AudioDiskstream::destroy_channel (ChannelInfo &chan)
-{
-       if (chan.write_source) {
-               chan.write_source.reset ();
-       }
-               
-       if (chan.speed_buffer) {
-               delete [] chan.speed_buffer;
-       }
-
-       if (chan.playback_wrap_buffer) {
-               delete [] chan.playback_wrap_buffer;
-       }
-       if (chan.capture_wrap_buffer) {
-               delete [] chan.capture_wrap_buffer;
-       }
-       
-       delete chan.playback_buf;
-       delete chan.capture_buf;
-       delete chan.capture_transition_buf;
-       
-       chan.playback_buf = 0;
-       chan.capture_buf = 0;
-}
-
 AudioDiskstream::~AudioDiskstream ()
 {
-       Glib::Mutex::Lock lm (state_lock);
+       notify_callbacks ();
 
-       for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan)
-               destroy_channel((*chan));
-       
-       channels.clear();
+       {
+               /* don't be holding this lock as we exit the destructor, glib will wince
+                  visibly since the mutex gets destroyed before we release it.
+               */
+
+               Glib::Mutex::Lock lm (state_lock);
+               for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
+                       (*chan).release ();
+               }
+               channels.clear();
+       }
 }
 
 void
@@ -286,15 +241,13 @@ AudioDiskstream::get_input_sources ()
 int
 AudioDiskstream::find_and_use_playlist (const string& name)
 {
-       Playlist* pl;
-       AudioPlaylist* playlist;
+       boost::shared_ptr<AudioPlaylist> playlist;
                
-       if ((pl = _session.playlist_by_name (name)) == 0) {
-               playlist = new AudioPlaylist(_session, name);
-               pl = playlist;
+       if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (_session.playlist_by_name (name))) == 0) {
+               playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (_session, name));
        }
 
-       if ((playlist = dynamic_cast<AudioPlaylist*> (pl)) == 0) {
+       if (!playlist) {
                error << string_compose(_("AudioDiskstream: Playlist \"%1\" isn't an audio playlist"), name) << endmsg;
                return -1;
        }
@@ -303,9 +256,9 @@ AudioDiskstream::find_and_use_playlist (const string& name)
 }
 
 int
-AudioDiskstream::use_playlist (Playlist* playlist)
+AudioDiskstream::use_playlist (boost::shared_ptr<Playlist> playlist)
 {
-       assert(dynamic_cast<AudioPlaylist*>(playlist));
+       assert(boost::dynamic_pointer_cast<AudioPlaylist>(playlist));
 
        Diskstream::use_playlist(playlist);
 
@@ -316,7 +269,7 @@ int
 AudioDiskstream::use_new_playlist ()
 {
        string newname;
-       AudioPlaylist* playlist;
+       boost::shared_ptr<AudioPlaylist> playlist;
 
        if (!in_set_state && destructive()) {
                return 0;
@@ -328,9 +281,11 @@ AudioDiskstream::use_new_playlist ()
                newname = Playlist::bump_name (_name, _session);
        }
 
-       if ((playlist = new AudioPlaylist (_session, newname, hidden())) != 0) {
+       if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (_session, newname, hidden()))) != 0) {
+               
                playlist->set_orig_diskstream_id (id());
                return use_playlist (playlist);
+
        } else { 
                return -1;
        }
@@ -351,11 +306,11 @@ AudioDiskstream::use_copy_playlist ()
        }
 
        string newname;
-       AudioPlaylist* playlist;
+       boost::shared_ptr<AudioPlaylist> playlist;
 
        newname = Playlist::bump_name (_playlist->name(), _session);
        
-       if ((playlist  = new AudioPlaylist (*audio_playlist(), newname)) != 0) {
+       if ((playlist  = boost::dynamic_pointer_cast<AudioPlaylist>(PlaylistFactory::create (audio_playlist(), newname))) != 0) {
                playlist->set_orig_diskstream_id (id());
                return use_playlist (playlist);
        } else { 
@@ -1027,7 +982,10 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
        nframes_t offset = 0;
        Location *loc = 0;
 
+       /* XXX we don't currently play loops in reverse. not sure why */
+
        if (!reversed) {
+
                /* Make the use of a Location atomic for this read operation.
                   
                   Note: Locations don't get deleted, so all we care about
@@ -1051,11 +1009,16 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
                        start = loop_start + ((start - loop_start) % loop_length);
                        //cerr << "to " << start << endl;
                }
+
                //cerr << "start is " << start << "  loopstart: " << loop_start << "  loopend: " << loop_end << endl;
        }
 
        while (cnt) {
 
+               if (reversed) {
+                       start -= cnt;
+               }
+                       
                /* take any loop into account. we can't read past the end of the loop. */
 
                if (loc && (loop_end - start < cnt)) {
@@ -1083,9 +1046,6 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
                
                if (reversed) {
 
-                       /* don't adjust start, since caller has already done that
-                        */
-
                        swap_by_ptr (buf, buf + this_read - 1);
                        
                } else {
@@ -1141,7 +1101,7 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
        if ((total_space = vector.len[0] + vector.len[1]) == 0) {
                return 0;
        }
-       
+
        /* 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.
@@ -1171,6 +1131,8 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                return 0;
        }
 
+       /* never do more than disk_io_chunk_frames worth of disk input per call (limit doesn't apply for memset) */
+
        total_space = min (disk_io_chunk_frames, total_space);
 
        if (reversed) {
@@ -1204,11 +1166,6 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
                } else {
                        
-                       /* move read position backwards because we are going
-                          to reverse the data.
-                       */
-                       
-                       file_frame -= total_space;
                        zero_fill = 0;
                }
 
@@ -1254,21 +1211,36 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
                chan.playback_buf->get_write_vector (&vector);
 
+               if (vector.len[0] > disk_io_chunk_frames) {
+                       
+                       /* we're not going to fill the first chunk, so certainly do not bother with the
+                          other part. it won't be connected with the part we do fill, as in:
+                          
+                          .... => writable space
+                          ++++ => readable space
+                          ^^^^ => 1 x disk_io_chunk_frames that would be filled
+                          
+                          |......|+++++++++++++|...............................|
+                          buf1                buf0
+                                               ^^^^^^^^^^^^^^^
+                          
+                          
+                          So, just pretend that the buf1 part isn't there.                                     
+                          
+                       */
+               
+                       vector.buf[1] = 0;
+                       vector.len[1] = 0;
+               
+               } 
+
                ts = total_space;
                file_frame_tmp = file_frame;
 
-               if (reversed) {
-                       buf1 = vector.buf[1];
-                       len1 = vector.len[1];
-                       buf2 = vector.buf[0];
-                       len2 = vector.len[0];
-               } else {
-                       buf1 = vector.buf[0];
-                       len1 = vector.len[0];
-                       buf2 = vector.buf[1];
-                       len2 = vector.len[1];
-               }
-
+               buf1 = vector.buf[0];
+               len1 = vector.len[0];
+               buf2 = vector.buf[1];
+               len2 = vector.len[1];
 
                to_read = min (ts, len1);
                to_read = min (to_read, disk_io_chunk_frames);
@@ -1279,7 +1251,7 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                                ret = -1;
                                goto out;
                        }
-                       
+
                        chan.playback_buf->increment_write_ptr (to_read);
                        ts -= to_read;
                }
@@ -1288,7 +1260,6 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
                if (to_read) {
 
-                       
                        /* we read all of vector.len[0], but it wasn't an entire disk_io_chunk_frames of data,
                           so read some or all of vector.len[1] as well.
                        */
@@ -1525,7 +1496,7 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca
                        srcs.push_back (s);
                        s->update_header (capture_info.front()->start, when, twhen);
                        s->set_captured_for (_name);
-                       
+                       s->mark_immutable ();
                }
        }
 
@@ -1544,7 +1515,7 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca
        } else {
 
                string whole_file_region_name;
-               whole_file_region_name = region_name_from_path (channels[0].write_source->name());
+               whole_file_region_name = region_name_from_path (channels[0].write_source->name(), true);
 
                /* Register a new region with the Session that
                   describes the entire source. Do this first
@@ -1675,7 +1646,7 @@ AudioDiskstream::finish_capture (bool rec_monitors_input)
 void
 AudioDiskstream::set_record_enabled (bool yn)
 {
-       if (!recordable() || !_session.record_enabling_legal()) {
+       if (!recordable() || !_session.record_enabling_legal() || _io->n_inputs() == 0) {
                return;
        }
 
@@ -1753,8 +1724,7 @@ AudioDiskstream::get_state ()
        char buf[64] = "";
        LocaleGuard lg (X_("POSIX"));
 
-       snprintf (buf, sizeof(buf), "0x%x", _flags);
-       node->add_property ("flags", buf);
+       node->add_property ("flags", enum_2_string (_flags));
 
        snprintf (buf, sizeof(buf), "%zd", channels.size());
        node->add_property ("channels", buf);
@@ -1841,7 +1811,7 @@ AudioDiskstream::set_state (const XMLNode& node)
        }
 
        if ((prop = node.property ("flags")) != 0) {
-               _flags = strtol (prop->value().c_str(), 0, 0);
+               _flags = Flag (string_2_enum (prop->value(), _flags));
        }
 
        if ((prop = node.property ("channels")) != 0) {
@@ -1973,7 +1943,7 @@ AudioDiskstream::reset_write_sources (bool mark_write_complete, bool force)
        }
        
        capturing_sources.clear ();
-       
+
        for (chan = channels.begin(), n = 0; chan != channels.end(); ++chan, ++n) {
                if (!destructive()) {
 
@@ -2100,15 +2070,19 @@ AudioDiskstream::add_channel ()
 {
        /* XXX need to take lock??? */
 
-       ChannelInfo chan;
+       /* this copies the ChannelInfo, which currently has no buffers. kind
+          of pointless really, but we want the channels list to contain
+          actual objects, not pointers to objects. mostly for convenience,
+          which isn't much in evidence.
+       */
 
-       init_channel (chan);
+       channels.push_back (ChannelInfo());
 
-       chan.speed_buffer = new Sample[speed_buffer_size];
-       chan.playback_wrap_buffer = new Sample[wrap_buffer_size];
-       chan.capture_wrap_buffer = new Sample[wrap_buffer_size];
+       /* now allocate the buffers */
 
-       channels.push_back (chan);
+       channels.back().init (_session.diskstream_buffer_size(), 
+                             speed_buffer_size,
+                             wrap_buffer_size);
 
        _n_channels = channels.size();
 
@@ -2120,10 +2094,8 @@ AudioDiskstream::remove_channel ()
 {
        if (channels.size() > 1) {
                /* XXX need to take lock??? */
-               ChannelInfo & chan = channels.back();
-               destroy_channel (chan);
+               channels.back().release ();
                channels.pop_back();
-
                _n_channels = channels.size();
                return 0;
        }
@@ -2207,7 +2179,7 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
        
        try {
                region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(),
-                                                                                         region_name_from_path (first_fs->name()), 
+                                                                                         region_name_from_path (first_fs->name(), true), 
                                                                                          0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile)));
                region->special_set_position (0);
        }
@@ -2221,7 +2193,7 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
        }
 
        try {
-               region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(), region_name_from_path (first_fs->name())));
+               region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(), region_name_from_path (first_fs->name(), true)));
        }
 
        catch (failed_constructor& err) {
@@ -2251,10 +2223,10 @@ AudioDiskstream::set_destructive (bool yn)
                        if (!can_become_destructive (bounce_ignored)) {
                                return -1;
                        }
-                       _flags |= Destructive;
+                       _flags = Flag (_flags | Destructive);
                        use_destructive_playlist ();
                } else {
-                       _flags &= ~Destructive;
+                       _flags = Flag (_flags & ~Destructive);
                        reset_write_sources (true, true);
                }
        }
@@ -2303,3 +2275,82 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const
        requires_bounce = false;
        return true;
 }
+
+AudioDiskstream::ChannelInfo::ChannelInfo ()
+{
+       playback_wrap_buffer = 0;
+       capture_wrap_buffer = 0;
+       speed_buffer = 0;
+       peak_power = 0.0f;
+       source = 0;
+       current_capture_buffer = 0;
+       current_playback_buffer = 0;
+       curr_capture_cnt = 0;
+       playback_buf = 0;
+       capture_buf = 0;
+       capture_transition_buf = 0;
+}
+
+void
+AudioDiskstream::ChannelInfo::init (nframes_t bufsize, nframes_t speed_size, nframes_t wrap_size)
+{
+       speed_buffer = new Sample[speed_size];
+       playback_wrap_buffer = new Sample[wrap_size];
+       capture_wrap_buffer = new Sample[wrap_size];
+
+       playback_buf = new RingBufferNPT<Sample> (bufsize);
+       capture_buf = new RingBufferNPT<Sample> (bufsize);
+       capture_transition_buf = new RingBufferNPT<CaptureTransition> (128);
+       
+       /* touch the ringbuffer buffers, 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 (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize());
+       memset (capture_buf->buffer(), 0, sizeof (Sample) * capture_buf->bufsize());
+       memset (capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * capture_transition_buf->bufsize());
+}
+
+AudioDiskstream::ChannelInfo::~ChannelInfo ()
+{
+}
+
+void
+AudioDiskstream::ChannelInfo::release ()
+{
+       if (write_source) {
+               write_source.reset ();
+       }
+               
+       if (speed_buffer) {
+               delete [] speed_buffer;
+               speed_buffer = 0;
+       }
+
+       if (playback_wrap_buffer) {
+               delete [] playback_wrap_buffer;
+               playback_wrap_buffer = 0;
+       }
+
+       if (capture_wrap_buffer) {
+               delete [] capture_wrap_buffer;
+               capture_wrap_buffer = 0;
+       }
+       
+       if (playback_buf) {
+               delete playback_buf;
+               playback_buf = 0;
+       }
+
+       if (capture_buf) {
+               delete capture_buf;
+               capture_buf = 0;
+       }
+
+       if (capture_transition_buf) {
+               delete capture_transition_buf;
+               capture_transition_buf = 0;
+       }
+}