diff --git a/Timeline/Disk_Stream.C b/Timeline/Disk_Stream.C index df42447..005e92f 100644 --- a/Timeline/Disk_Stream.C +++ b/Timeline/Disk_Stream.C @@ -37,25 +37,24 @@ that is, at startup time. The default is 5 seconds, which may or may not be excessive depending on various external factors. */ -/* FIXME: handle termination of IO thread in destructor */ /* FIXME: deal with (jack) buffer size changes */ /* FIXME: needs error handling everywhere! */ -/* TODO: handle capture too. For this to work with some kind of - * punch-in/out system, I believe we'll have to always keep at least - * one buffer's worth of input. We would need this anyway in order to - * pass input through to output (software monitoring). What about - * looped recording? */ -/* TODO: latency compensation? Does this really apply to us? (we're - * not hosting plugins here) */ /* TODO: read/write data from/to disk in larger chunks to avoid * excessive seeking. 256k is supposedly the sweetspot. */ -// float Disk_Stream::seconds_to_buffer = 5.0f; -float Disk_Stream::seconds_to_buffer = 2.0f; -// size_t Disk_Stream::disk_block_frames = 2048; +float Disk_Stream::seconds_to_buffer = 5.0f; +//float Disk_Stream::seconds_to_buffer = 2.0f; +/* this is really only a rough estimate. The actual amount of data + read depends on many factors. Overlapping regions, for example, will + require more data to be read from disk, as will varying channel + counts.*/ +size_t Disk_Stream::disk_io_kbytes = 256; Disk_Stream::Disk_Stream ( Track *th, float frame_rate, nframes_t nframes, int channels ) : _th( th ) { + + assert( channels ); + _frame = 0; _thread = 0; _terminate = false; @@ -69,6 +68,10 @@ Disk_Stream::Disk_Stream ( Track *th, float frame_rate, nframes_t nframes, int c size_t bufsize = _total_blocks * nframes * sizeof( sample_t ); + _disk_io_blocks = ( bufsize * channels ) / ( disk_io_kbytes * 1024 ); + + assert( _disk_io_blocks ); + for ( int i = channels; i--; ) _rb.push_back( jack_ringbuffer_create( bufsize ) ); diff --git a/Timeline/Disk_Stream.H b/Timeline/Disk_Stream.H index e1cc307..e310bf2 100644 --- a/Timeline/Disk_Stream.H +++ b/Timeline/Disk_Stream.H @@ -53,6 +53,7 @@ protected: sem_t _blocks; /* semaphore to wake the IO thread with */ int _total_blocks; /* total number of blocks that we can buffer */ + int _disk_io_blocks; /* the number of blocks to read/write to/from disk at once */ volatile nframes_t _pending_seek; /* absolute transport position to seek to */ volatile int _terminate; @@ -83,6 +84,7 @@ public: /* must be set before any Disk_Streams are created */ static float seconds_to_buffer; + static size_t disk_io_kbytes; Disk_Stream ( Track *th, float frame_rate, nframes_t nframes, int channels ); diff --git a/Timeline/Playback_DS.C b/Timeline/Playback_DS.C index e53ddbd..877dc4f 100644 --- a/Timeline/Playback_DS.C +++ b/Timeline/Playback_DS.C @@ -109,9 +109,10 @@ Playback_DS::disk_thread ( void ) printf( "IO thread running...\n" ); /* buffer to hold the interleaved data returned by the track reader */ - sample_t *buf = new sample_t[ _nframes * channels() ]; + sample_t *buf = new sample_t[ _nframes * channels() * _disk_io_blocks ]; - const size_t block_size = _nframes * sizeof( sample_t ); +// const size_t block_size = _nframes * sizeof( sample_t ); + int blocks_ready = 1; while ( wait_for_block() ) { @@ -126,6 +127,7 @@ Playback_DS::disk_thread ( void ) printf( "performing seek\n" ); _frame = _pending_seek; _pending_seek = -1; + blocks_ready = 1; /* finish flushing the buffer */ /* for ( int i = channels(); i-- ) */ @@ -133,8 +135,20 @@ Playback_DS::disk_thread ( void ) } + if ( blocks_ready < _disk_io_blocks ) + { + ++blocks_ready; + /* wait for more space */ + continue; + } + + /* reset */ + blocks_ready = 1; + + const nframes_t nframes = _nframes * _disk_io_blocks; + /* FIXME: should we not read from disk in larger-than-JACK-buffer blocks? */ - read_block( buf, _nframes ); + read_block( buf, nframes ); // unlock(); // for seeking @@ -157,22 +171,22 @@ Playback_DS::disk_thread ( void ) jack_ringbuffer_get_write_vector( _rb[ i ], rbd ); - if ( rbd[ 0 ].len >= _nframes ) + if ( rbd[ 0 ].len >= nframes ) /* it'll all fit in one go */ - buffer_deinterleave_one_channel( (sample_t*)rbd[ 0 ].buf, buf, i, channels(), _nframes ); + buffer_deinterleave_one_channel( (sample_t*)rbd[ 0 ].buf, buf, i, channels(), nframes ); else if ( rbd[ 1 ].len ) { /* there's enough space in the ringbuffer, but it's not contiguous */ /* do the first half */ - const nframes_t f = rbd[ 1 ].len / sizeof( sample_t ); + const nframes_t f = rbd[ 0 ].len / sizeof( sample_t ); buffer_deinterleave_one_channel( (sample_t*)rbd[ 0 ].buf, buf, i, channels(), f ); - assert( rbd[ 1 ].len >= (_nframes - f) * sizeof( sample_t ) ); + assert( rbd[ 1 ].len >= (nframes - f) * sizeof( sample_t ) ); /* do the second half */ - buffer_deinterleave_one_channel( (sample_t*)rbd[ 1 ].buf, buf + f, i, channels(), _nframes - f ); + buffer_deinterleave_one_channel( (sample_t*)rbd[ 1 ].buf, buf + f, i, channels(), nframes - f ); } else printf( "programming error: expected more space in ringbuffer\n" ); @@ -180,7 +194,7 @@ Playback_DS::disk_thread ( void ) /* buffer_deinterleave_one_channel( (sample_t*)rbd.buf, buf, i, channels(), _nframes ); */ /* jack_ringbuffer_write( _rb[ i ], (char*)cbuf, block_size ); */ - jack_ringbuffer_write_advance( _rb[ i ], _nframes * sizeof( sample_t ) ); + jack_ringbuffer_write_advance( _rb[ i ], nframes * sizeof( sample_t ) ); diff --git a/Timeline/Record_DS.C b/Timeline/Record_DS.C index f755315..4257903 100644 --- a/Timeline/Record_DS.C +++ b/Timeline/Record_DS.C @@ -55,16 +55,29 @@ Record_DS::disk_thread ( void ) printf( "IO thread running...\n" ); + const nframes_t nframes = _nframes * _disk_io_blocks; + /* buffer to hold the interleaved data returned by the track reader */ - sample_t *buf = new sample_t[ _nframes * channels() ]; - sample_t *cbuf = new sample_t[ _nframes ]; + sample_t *buf = new sample_t[ nframes * channels() ]; + sample_t *cbuf = new sample_t[ nframes ]; + + const size_t block_size = nframes * sizeof( sample_t ); - const size_t block_size = _nframes * sizeof( sample_t ); + int blocks_ready = 1; while ( wait_for_block() ) { - /* pull data from the per-channel ringbuffers and interlace it */ + if ( blocks_ready < _disk_io_blocks ) + { + ++blocks_ready; + continue; + } + + blocks_ready = 1; + + + /* pull data from the per-channel ringbuffers and interlace it */ for ( int i = channels(); i--; ) { @@ -79,7 +92,7 @@ Record_DS::disk_thread ( void ) jack_ringbuffer_read( _rb[ i ], (char*)cbuf, block_size ); - buffer_interleave_one_channel( buf, cbuf, i, channels(), _nframes ); + buffer_interleave_one_channel( buf, cbuf, i, channels(), nframes ); /* /\* deinterleave direcectly into the ringbuffer to avoid */ @@ -118,7 +131,7 @@ Record_DS::disk_thread ( void ) } - write_block( buf, _nframes ); + write_block( buf, nframes ); } diff --git a/Timeline/Track.C b/Timeline/Track.C index 95bc66e..7507551 100644 --- a/Timeline/Track.C +++ b/Timeline/Track.C @@ -119,6 +119,9 @@ Track::init ( void ) _show_all_takes = false; _size = 1; + record_ds = NULL; + playback_ds = NULL; + labeltype( FL_NO_LABEL ); Fl_Group::size( timeline->w(), height() ); @@ -215,8 +218,6 @@ Track::init ( void ) } end(); - playback_ds = new Playback_DS( this, engine->frame_rate(), engine->nframes(), output.size() ); - record_ds = new Record_DS( this, engine->frame_rate(), engine->nframes(), input.size() ); } @@ -561,11 +562,14 @@ Track::configure_outputs ( int n ) // engine->lock(); - Playback_DS *ds = playback_ds; - playback_ds = NULL; + if ( playback_ds ) + { + Playback_DS *ds = playback_ds; + playback_ds = NULL; - ds->shutdown(); - delete ds; + ds->shutdown(); + delete ds; + } if ( n > on ) { @@ -606,11 +610,14 @@ Track::configure_inputs ( int n ) // engine->lock(); - Record_DS *ds = record_ds; - record_ds = NULL; + if ( record_ds ) + { + Record_DS *ds = record_ds; + record_ds = NULL; - ds->shutdown(); - delete ds; + ds->shutdown(); + delete ds; + } if ( n > on ) {