#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"
using namespace ARDOUR;
using namespace PBD;
-size_t AudioDiskstream::_working_buffers_size = 0;
Sample* AudioDiskstream::_mixdown_buffer = 0;
gain_t* AudioDiskstream::_gain_buffer = 0;
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
{
delete [] _mixdown_buffer;
delete [] _gain_buffer;
- _working_buffers_size = 0;
_mixdown_buffer = 0;
_gain_buffer = 0;
}
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;
}
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;
/** 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;
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;
}
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
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) {
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);
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:
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);
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)) {
}
+ // 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;
}