add initial support for vari-fill

When refilling playback buffer, try to fill it completely, or at least
using the next-lowest power-of-2 as the amount to read. When locating,
where we use do_refill_with_alloc(), only partially fill the buffer.

Work not yet finished, but possibly promising.

Conflicts:
	libs/ardour/ardour/midi_diskstream.h
	libs/ardour/diskstream.cc
This commit is contained in:
Paul Davis 2015-01-28 13:43:17 -05:00
parent 0ffc6cef5d
commit a846c8fc0e
7 changed files with 89 additions and 47 deletions

View file

@ -209,9 +209,8 @@ class LIBARDOUR_API AudioDiskstream : public Diskstream
/* The two central butler operations */
int do_flush (RunContext context, bool force = false);
int do_refill () { return _do_refill(_mixdown_buffer, _gain_buffer); }
int do_refill () { return _do_refill(_mixdown_buffer, _gain_buffer, 0); }
int do_refill_with_alloc ();
int read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
framepos_t& start, framecnt_t cnt,
@ -258,9 +257,12 @@ class LIBARDOUR_API AudioDiskstream : public Diskstream
SerializedRCUManager<ChannelList> channels;
protected:
int _do_refill_with_alloc (bool one_chunk_only);
/* really */
private:
int _do_refill (Sample *mixdown_buffer, float *gain_buffer);
int _do_refill (Sample *mixdown_buffer, float *gain_buffer, framecnt_t fill_level);
int add_channel_to (boost::shared_ptr<ChannelList>, uint32_t how_many);
int remove_channel_from (boost::shared_ptr<ChannelList>, uint32_t how_many);

View file

@ -173,8 +173,15 @@ class LIBARDOUR_API Diskstream : public SessionObject, public PublicDiskstream
void move_processor_automation (boost::weak_ptr<Processor>,
std::list<Evoral::RangeMove<framepos_t> > const &);
/** For non-butler contexts (allocates temporary working buffers) */
virtual int do_refill_with_alloc() = 0;
/** For non-butler contexts (allocates temporary working buffers)
*
* This accessible method has a default argument; derived classes
* must inherit the virtual method that we call which does NOT
* have a default argument, to avoid complications with inheritance
*/
int do_refill_with_alloc(bool partial_fill = true) {
return _do_refill_with_alloc (partial_fill);
}
virtual void set_block_size (pframes_t) = 0;
bool pending_overwrite () const {
@ -206,6 +213,11 @@ class LIBARDOUR_API Diskstream : public SessionObject, public PublicDiskstream
virtual int can_internal_playback_seek (framecnt_t distance) = 0;
virtual void reset_write_sources (bool, bool force = false) = 0;
virtual void non_realtime_input_change () = 0;
/* accessible method has default argument, so use standard C++ "trick"
to avoid complications with inheritance, by adding this virtual
method which does NOT have a default argument.
*/
virtual int _do_refill_with_alloc (bool partial_fill) = 0;
protected:
friend class Auditioner;

View file

@ -122,11 +122,13 @@ class LIBARDOUR_API MidiDiskstream : public Diskstream
protected:
friend class MidiTrack;
friend class Auditioner;
int seek (framepos_t which_sample, bool complete_refill = false);
int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_diskstream);
frameoffset_t calculate_playback_distance (pframes_t nframes);
bool commit (framecnt_t nframes);
int seek (framepos_t which_sample, bool complete_refill = false);
int _do_refill_with_alloc (bool one_chunk_only);
int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_diskstream);
frameoffset_t calculate_playback_distance (pframes_t nframes);
bool commit (framecnt_t nframes);
static framecnt_t midi_readahead;
private:
@ -135,8 +137,6 @@ class LIBARDOUR_API MidiDiskstream : public Diskstream
int do_flush (RunContext context, bool force = false);
int do_refill ();
int do_refill_with_alloc();
int read (framepos_t& start, framecnt_t cnt, bool reversed);
void finish_capture ();

View file

@ -918,9 +918,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;
@ -1061,12 +1067,13 @@ 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];
Sample* mix_buf = new Sample[1048576];
float* gain_buf = new float[1048576];
int ret = _do_refill(mix_buf, gain_buf);
int ret = _do_refill (mix_buf, gain_buf, (partial_fill ?
(_session.butler()->audio_diskstream_playback_buffer_size() / 4.0) : 0));
delete [] mix_buf;
delete [] gain_buf;
@ -1076,9 +1083,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;
@ -1110,13 +1123,12 @@ 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 && (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;
}
/* if we're running close to normal speed and there isn't enough
@ -1143,10 +1155,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) {
@ -1213,6 +1221,34 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
framepos_t file_frame_tmp = 0;
/* this needs to be 32 bit for the power-of-two hack below to work.
Clever, but poses ugly casting problems since the value is
semantically a framecnt_t.
*/
uint32_t chunk_size_for_read = (uint32_t) max ((framecnt_t) 65536, min ((framecnt_t) 1048576, total_space));
/* if not already a power of 2, go to next lower power of 2 */
if ((chunk_size_for_read & (chunk_size_for_read - 1)) != 0) {
/* move to the next largest power of two. Hack from stanford bithacks page. */
chunk_size_for_read--;
chunk_size_for_read |= chunk_size_for_read >> 1;
chunk_size_for_read |= chunk_size_for_read >> 2;
chunk_size_for_read |= chunk_size_for_read >> 4;
chunk_size_for_read |= chunk_size_for_read >> 8;
chunk_size_for_read |= chunk_size_for_read >> 16;
chunk_size_for_read++;
/* go back to previous power */
chunk_size_for_read >>= 1;
}
cerr << name() << " will read " << chunk_size_for_read << " out of total space " << total_space << " in buffer of " << c->front()->playback_buf->bufsize() << endl;
for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
ChannelInfo* chan (*i);
@ -1222,7 +1258,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] > chunk_size_for_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:
@ -1254,7 +1290,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) chunk_size_for_read);
assert (to_read >= 0);
@ -1273,8 +1309,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 chunk_size_for_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)) {
@ -1294,8 +1331,9 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
file_frame = file_frame_tmp;
assert (file_frame >= 0);
ret = ((total_space - chunk_size_for_read) > disk_read_chunk_frames);
out:
return ret;
}

View file

@ -803,22 +803,12 @@ Diskstream::set_own_replication_path (const std::string& path)
framecnt_t
Diskstream::default_disk_read_chunk_frames()
{
/*
#ifdef PLATFORM_WINDOWS
return (2 * 1048576) / sizeof (Sample);
#elif defined __APPLE__
return (4 * 1048576) / sizeof (Sample);
#else
return 65536;
#endif*/
//GZ to Paul Davis FIX-ME restored old value which used to work with current ring buffer capacity
return 65536;
}
framecnt_t
Diskstream::default_disk_write_chunk_frames ()
{
//GZ to Paul Davis FIX-ME restored old value which used to work with current ring buffer capacity
return 65536;
}

View file

@ -749,7 +749,7 @@ MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed)
}
int
MidiDiskstream::do_refill_with_alloc ()
MidiDiskstream::_do_refill_with_alloc (bool /* partial_fill */)
{
return do_refill();
}

View file

@ -319,7 +319,7 @@ Session::butler_transport_work ()
finished = true;
ptw = post_transport_work();
DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1\n", enum_2_string (ptw)));
DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1 at %2\n", enum_2_string (ptw), g_get_monotonic_time()));
if (ptw & PostTransportAdjustPlaybackBuffering) {
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
@ -409,7 +409,7 @@ Session::butler_transport_work ()
g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
DEBUG_TRACE (DEBUG::Transport, X_("Butler transport work all done\n"));
DEBUG_TRACE (DEBUG::Transport, string_compose (X_("Butler transport work all done at %1\n"), g_get_monotonic_time()));
DEBUG_TRACE (DEBUG::Transport, X_(string_compose ("Frame %1\n", _transport_frame)));
}