fix unused but usable variable warning
[ardour.git] / libs / ardour / audio_diskstream.cc
index d835c4a20fa89d166758fe28ee5855022cf6444d..e97af72b8c6396ec65415679bf884ba8cdd909bf 100644 (file)
 #include "ardour/debug.h"
 #include "ardour/io.h"
 #include "ardour/playlist_factory.h"
+#include "ardour/profile.h"
 #include "ardour/region_factory.h"
 #include "ardour/session.h"
 #include "ardour/session_playlists.h"
+#include "ardour/sndfile_helpers.h"
 #include "ardour/source_factory.h"
 #include "ardour/track.h"
 #include "ardour/types.h"
@@ -61,7 +63,6 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-size_t  AudioDiskstream::_working_buffers_size = 0;
 Sample* AudioDiskstream::_mixdown_buffer       = 0;
 gain_t* AudioDiskstream::_gain_buffer          = 0;
 
@@ -74,6 +75,10 @@ AudioDiskstream::AudioDiskstream (Session &sess, const string &name, Diskstream:
        in_set_state = true;
        use_new_playlist ();
        in_set_state = false;
+
+       if (flag & Destructive) {
+               use_destructive_playlist ();
+       }
 }
 
 AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
@@ -128,9 +133,13 @@ AudioDiskstream::~AudioDiskstream ()
 void
 AudioDiskstream::allocate_working_buffers()
 {
-       _working_buffers_size = max (disk_write_chunk_frames, disk_read_chunk_frames);
-       _mixdown_buffer       = new Sample[_working_buffers_size];
-       _gain_buffer          = new gain_t[_working_buffers_size];
+       /* with varifill buffer refilling, we compute the read size in bytes (to optimize
+          for disk i/o bandwidth) and then convert back into samples. These buffers
+          need to reflect the maximum size we could use, which is 4MB reads, or 2M samples
+          using 16 bit samples.
+       */
+       _mixdown_buffer       = new Sample[2*1048576];
+       _gain_buffer          = new gain_t[2*1048576];
 }
 
 void
@@ -138,7 +147,6 @@ AudioDiskstream::free_working_buffers()
 {
        delete [] _mixdown_buffer;
        delete [] _gain_buffer;
-       _working_buffers_size = 0;
        _mixdown_buffer       = 0;
        _gain_buffer          = 0;
 }
@@ -225,7 +233,7 @@ AudioDiskstream::get_input_sources ()
 
                connections.clear ();
 
-               if (_io->nth (n)->get_connections (connections) == 0) {
+               if ((_io->nth (n).get()) && (_io->nth (n)->get_connections (connections) == 0)) {
                        if (!(*chan)->source.name.empty()) {
                                // _source->disable_metering ();
                        }
@@ -331,10 +339,13 @@ AudioDiskstream::setup_destructive_playlist ()
        PropertyList plist;
        plist.add (Properties::name, _name.val());
        plist.add (Properties::start, 0);
-       plist.add (Properties::length, max_framepos - (max_framepos - srcs.front()->natural_position()));
+       plist.add (Properties::length, max_framepos - srcs.front()->natural_position());
 
        boost::shared_ptr<Region> region (RegionFactory::create (srcs, plist));
        _playlist->add_region (region, srcs.front()->natural_position());
+
+       /* apply region properties and update write sources */
+       use_destructive_playlist();
 }
 
 void
@@ -346,7 +357,14 @@ AudioDiskstream::use_destructive_playlist ()
           with the (presumed single, full-extent) region.
        */
 
-       boost::shared_ptr<Region> rp = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
+       boost::shared_ptr<Region> rp;
+       {
+               const RegionList& rl (_playlist->region_list().rlist());
+               if (rl.size() > 0) {
+                       assert((rl.size() == 1));
+                       rp = rl.front();
+               }
+       }
 
        if (!rp) {
                reset_write_sources (false, true);
@@ -909,9 +927,15 @@ AudioDiskstream::seek (framepos_t frame, bool complete_refill)
        file_frame = frame;
 
        if (complete_refill) {
-               while ((ret = do_refill_with_alloc ()) > 0) ;
+               /* call _do_refill() to refill the entire buffer, using
+                  the largest reads possible.
+               */
+               while ((ret = do_refill_with_alloc (false)) > 0) ;
        } else {
-               ret = do_refill_with_alloc ();
+               /* call _do_refill() to refill just one chunk, and then
+                  return.
+               */
+               ret = do_refill_with_alloc (true);
        }
 
        return ret;
@@ -995,6 +1019,7 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
                if (loc && start >= loop_end) {
                        start = loop_start + ((start - loop_start) % loop_length);
                }
+
        }
 
        if (reversed) {
@@ -1052,12 +1077,18 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
 }
 
 int
-AudioDiskstream::do_refill_with_alloc ()
+AudioDiskstream::_do_refill_with_alloc (bool partial_fill)
 {
-       Sample* mix_buf  = new Sample[disk_read_chunk_frames];
-       float*  gain_buf = new float[disk_read_chunk_frames];
+       /* We limit disk reads to at most 4MB chunks, which with floating point
+          samples would be 1M samples. But we might use 16 or 14 bit samples,
+          in which case 4MB is more samples than that. Therefore size this for
+          the smallest sample value .. 4MB = 2M samples (16 bit).
+       */
+
+       Sample* mix_buf  = new Sample[2*1048576];
+       float*  gain_buf = new float[2*1048576];
 
-       int ret = _do_refill(mix_buf, gain_buf);
+       int ret = _do_refill (mix_buf, gain_buf, (partial_fill ? disk_read_chunk_frames : 0));
 
        delete [] mix_buf;
        delete [] gain_buf;
@@ -1067,9 +1098,15 @@ AudioDiskstream::do_refill_with_alloc ()
 
 /** Get some more data from disk and put it in our channels' playback_bufs,
  *  if there is suitable space in them.
+ *
+ * If fill_level is non-zero, then we will refill the buffer so that there is
+ * still at least fill_level samples of space left to be filled. This is used
+ * after locates so that we do not need to wait to fill the entire buffer.
+ *
  */
+
 int
-AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
+AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer, framecnt_t fill_level)
 {
        int32_t ret = 0;
        framecnt_t to_read;
@@ -1082,6 +1119,14 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
        boost::shared_ptr<ChannelList> c = channels.reader();
        framecnt_t ts;
 
+       /* do not read from disk while session is marked as Loading, to avoid
+          useless redundant I/O.
+       */
+       
+       if (_session.state_of_the_state() & Session::Loading) {
+               return 0;
+       }
+       
        if (c->empty()) {
                return 0;
        }
@@ -1101,13 +1146,17 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                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.
-       */
-
-       if (total_space >= (_slaved ? 3 : 2) * disk_read_chunk_frames) {
-               ret = 1;
+       if (fill_level) {
+               if (fill_level < total_space) {
+                       cerr << name() << " adjust total space of " << total_space << " to leave " << fill_level << " to still refill\n";
+                       if (fill_level < 0) {
+                               PBD::stacktrace (cerr, 20);
+                       }
+                       total_space -= fill_level;
+               } else {
+                       /* we can't do anything with it */
+                       fill_level = 0;
+               }
        }
 
        /* if we're running close to normal speed and there isn't enough
@@ -1134,10 +1183,6 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                return 0;
        }
 
-       /* never do more than disk_read_chunk_frames worth of disk input per call (limit doesn't apply for memset) */
-
-       total_space = min (disk_read_chunk_frames, total_space);
-
        if (reversed) {
 
                if (file_frame == 0) {
@@ -1204,6 +1249,30 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
        framepos_t file_frame_tmp = 0;
 
+       /* total_space is in samples. We want to optimize read sizes in various sizes using bytes */
+
+       const size_t bits_per_sample = format_data_width (_session.config.get_native_file_data_format());
+       size_t total_bytes = total_space * bits_per_sample / 8;
+
+       /* chunk size range is 256kB to 4MB. Bigger is faster in terms of MB/sec, but bigger chunk size always takes longer
+        */
+       size_t byte_size_for_read = max ((size_t) (256 * 1024), min ((size_t) (4 * 1048576), total_bytes));
+
+       /* find nearest (lower) multiple of 16384 */
+
+       byte_size_for_read = (byte_size_for_read / 16384) * 16384;
+
+       /* now back to samples */
+
+       framecnt_t samples_to_read = byte_size_for_read / (bits_per_sample / 8);
+       
+       //cerr << name() << " will read " << byte_size_for_read << " out of total bytes " << total_bytes << " in buffer of "
+       // << c->front()->playback_buf->bufsize() * bits_per_sample / 8 << " bps = " << bits_per_sample << endl;
+       // cerr << name () << " read samples = " << samples_to_read << " out of total space " << total_space << " in buffer of " << c->front()->playback_buf->bufsize() << " samples\n";
+
+       // uint64_t before = g_get_monotonic_time ();
+       // uint64_t elapsed;
+       
        for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
 
                ChannelInfo* chan (*i);
@@ -1213,7 +1282,7 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
                chan->playback_buf->get_write_vector (&vector);
 
-               if ((framecnt_t) vector.len[0] > disk_read_chunk_frames) {
+               if ((framecnt_t) vector.len[0] > samples_to_read) {
 
                        /* 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:
@@ -1245,7 +1314,7 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                len2 = vector.len[1];
 
                to_read = min (ts, len1);
-               to_read = min (to_read, disk_read_chunk_frames);
+               to_read = min (to_read, (framecnt_t) samples_to_read);
 
                assert (to_read >= 0);
 
@@ -1264,8 +1333,9 @@ 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_read_chunk_frames of data,
-                          so read some or all of vector.len[1] as well.
+                       /* we read all of vector.len[0], but it wasn't the
+                          entire samples_to_read of data, so read some or
+                          all of vector.len[1] as well.
                        */
 
                        if (read (buf2, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) {
@@ -1282,11 +1352,17 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
        }
 
+       // elapsed = g_get_monotonic_time () - before;
+       // cerr << "\tbandwidth = " << (byte_size_for_read / 1048576.0) / (elapsed/1000000.0) << "MB/sec\n";
+               
        file_frame = file_frame_tmp;
        assert (file_frame >= 0);
 
+       ret = ((total_space - samples_to_read) > disk_read_chunk_frames);
+       
+       c->front()->playback_buf->get_write_vector (&vector);
+       
   out:
-
        return ret;
 }
 
@@ -1721,7 +1797,7 @@ AudioDiskstream::finish_capture (boost::shared_ptr<ChannelList> c)
 void
 AudioDiskstream::set_record_enabled (bool yn)
 {
-       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) {
+       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0 || record_safe ()) {
                return;
        }
 
@@ -1746,10 +1822,39 @@ AudioDiskstream::set_record_enabled (bool yn)
        }
 }
 
+void
+AudioDiskstream::set_record_safe (bool yn)
+{
+       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) {
+               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
 AudioDiskstream::prep_record_enable ()
 {
-       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) {
+       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0 || record_safe ()) { // REQUIRES REVIEW "|| record_safe ()"
                return false;
        }
 
@@ -2318,11 +2423,22 @@ AudioDiskstream::set_destructive (bool yn)
 bool
 AudioDiskstream::can_become_destructive (bool& requires_bounce) const
 {
+       if (Profile->get_trx()) {
+               return false;
+       }
+       
        if (!_playlist) {
                requires_bounce = false;
                return false;
        }
 
+       /* if no regions are present: easy */
+
+       if (_playlist->n_regions() == 0) {
+               requires_bounce = false;
+               return true;
+       }
+
        /* is there only one region ? */
 
        if (_playlist->n_regions() != 1) {
@@ -2330,7 +2446,14 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const
                return false;
        }
 
-       boost::shared_ptr<Region> first = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
+       boost::shared_ptr<Region> first;
+       {
+               const RegionList& rl (_playlist->region_list().rlist());
+               assert((rl.size() == 1));
+               first = rl.front();
+
+       }
+
        if (!first) {
                requires_bounce = false;
                return true;
@@ -2339,12 +2462,24 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const
        /* do the source(s) for the region cover the session start position ? */
 
        if (first->position() != _session.current_start_frame()) {
+               // what is the idea here?  why start() ??
                if (first->start() > _session.current_start_frame()) {
                        requires_bounce = true;
                        return false;
                }
        }
 
+       /* currently RouteTimeAxisView::set_track_mode does not
+        * implement bounce. Existing regions cannot be converted.
+        *
+        * so let's make sure this region is already set up
+        * as tape-track (spanning the complete range)
+        */
+       if (first->length() != max_framepos - first->position()) {
+               requires_bounce = true;
+               return false;
+       }
+
        /* is the source used by only 1 playlist ? */
 
        boost::shared_ptr<AudioRegion> afirst = boost::dynamic_pointer_cast<AudioRegion> (first);
@@ -2444,6 +2579,24 @@ AudioDiskstream::ChannelInfo::resize_capture (framecnt_t capture_bufsize)
 
 AudioDiskstream::ChannelInfo::~ChannelInfo ()
 {
+       if (write_source) {
+               if (write_source->removable()) {
+                       /* this is a "stub" write source which exists in the
+                          Session source list, but is removable. We must emit
+                          a drop references call because it should not
+                          continue to exist. If we do not do this, then the
+                          Session retains a reference to it, it is not
+                          deleted, and later attempts to create a new source
+                          file will use wierd naming because it already 
+                          exists.
+
+                          XXX longer term TO-DO: do not add to session source
+                          list until we write to the source.
+                       */
+                       write_source->drop_references ();
+               }
+       }
+
        write_source.reset ();
 
        delete [] speed_buffer;