| @@ -37,25 +37,24 @@ | |||||
| that is, at startup time. The default is 5 seconds, which may or | that is, at startup time. The default is 5 seconds, which may or | ||||
| may not be excessive depending on various external factors. */ | 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: deal with (jack) buffer size changes */ | ||||
| /* FIXME: needs error handling everywhere! */ | /* 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 | /* TODO: read/write data from/to disk in larger chunks to avoid | ||||
| * excessive seeking. 256k is supposedly the sweetspot. */ | * 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 ) | Disk_Stream::Disk_Stream ( Track *th, float frame_rate, nframes_t nframes, int channels ) : _th( th ) | ||||
| { | { | ||||
| assert( channels ); | |||||
| _frame = 0; | _frame = 0; | ||||
| _thread = 0; | _thread = 0; | ||||
| _terminate = false; | _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 ); | 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--; ) | for ( int i = channels; i--; ) | ||||
| _rb.push_back( jack_ringbuffer_create( bufsize ) ); | _rb.push_back( jack_ringbuffer_create( bufsize ) ); | ||||
| @@ -53,6 +53,7 @@ protected: | |||||
| sem_t _blocks; /* semaphore to wake the IO thread with */ | sem_t _blocks; /* semaphore to wake the IO thread with */ | ||||
| int _total_blocks; /* total number of blocks that we can buffer */ | 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 nframes_t _pending_seek; /* absolute transport position to seek to */ | ||||
| volatile int _terminate; | volatile int _terminate; | ||||
| @@ -83,6 +84,7 @@ public: | |||||
| /* must be set before any Disk_Streams are created */ | /* must be set before any Disk_Streams are created */ | ||||
| static float seconds_to_buffer; | static float seconds_to_buffer; | ||||
| static size_t disk_io_kbytes; | |||||
| Disk_Stream ( Track *th, float frame_rate, nframes_t nframes, int channels ); | Disk_Stream ( Track *th, float frame_rate, nframes_t nframes, int channels ); | ||||
| @@ -109,9 +109,10 @@ Playback_DS::disk_thread ( void ) | |||||
| printf( "IO thread running...\n" ); | printf( "IO thread running...\n" ); | ||||
| /* buffer to hold the interleaved data returned by the track reader */ | /* 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() ) | while ( wait_for_block() ) | ||||
| { | { | ||||
| @@ -126,6 +127,7 @@ Playback_DS::disk_thread ( void ) | |||||
| printf( "performing seek\n" ); | printf( "performing seek\n" ); | ||||
| _frame = _pending_seek; | _frame = _pending_seek; | ||||
| _pending_seek = -1; | _pending_seek = -1; | ||||
| blocks_ready = 1; | |||||
| /* finish flushing the buffer */ | /* finish flushing the buffer */ | ||||
| /* for ( int i = channels(); i-- ) */ | /* 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? */ | /* FIXME: should we not read from disk in larger-than-JACK-buffer blocks? */ | ||||
| read_block( buf, _nframes ); | |||||
| read_block( buf, nframes ); | |||||
| // unlock(); // for seeking | // unlock(); // for seeking | ||||
| @@ -157,22 +171,22 @@ Playback_DS::disk_thread ( void ) | |||||
| jack_ringbuffer_get_write_vector( _rb[ i ], rbd ); | 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 */ | /* 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 ) | else if ( rbd[ 1 ].len ) | ||||
| { | { | ||||
| /* there's enough space in the ringbuffer, but it's not contiguous */ | /* there's enough space in the ringbuffer, but it's not contiguous */ | ||||
| /* do the first half */ | /* 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 ); | 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 */ | /* 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 | else | ||||
| printf( "programming error: expected more space in ringbuffer\n" ); | 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 ); */ | /* buffer_deinterleave_one_channel( (sample_t*)rbd.buf, buf, i, channels(), _nframes ); */ | ||||
| /* jack_ringbuffer_write( _rb[ i ], (char*)cbuf, block_size ); */ | /* 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 ) ); | |||||
| @@ -55,16 +55,29 @@ Record_DS::disk_thread ( void ) | |||||
| printf( "IO thread running...\n" ); | printf( "IO thread running...\n" ); | ||||
| const nframes_t nframes = _nframes * _disk_io_blocks; | |||||
| /* buffer to hold the interleaved data returned by the track reader */ | /* 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() ) | 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--; ) | for ( int i = channels(); i--; ) | ||||
| { | { | ||||
| @@ -79,7 +92,7 @@ Record_DS::disk_thread ( void ) | |||||
| jack_ringbuffer_read( _rb[ i ], (char*)cbuf, block_size ); | 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 */ | /* /\* deinterleave direcectly into the ringbuffer to avoid */ | ||||
| @@ -118,7 +131,7 @@ Record_DS::disk_thread ( void ) | |||||
| } | } | ||||
| write_block( buf, _nframes ); | |||||
| write_block( buf, nframes ); | |||||
| } | } | ||||
| @@ -119,6 +119,9 @@ Track::init ( void ) | |||||
| _show_all_takes = false; | _show_all_takes = false; | ||||
| _size = 1; | _size = 1; | ||||
| record_ds = NULL; | |||||
| playback_ds = NULL; | |||||
| labeltype( FL_NO_LABEL ); | labeltype( FL_NO_LABEL ); | ||||
| Fl_Group::size( timeline->w(), height() ); | Fl_Group::size( timeline->w(), height() ); | ||||
| @@ -215,8 +218,6 @@ Track::init ( void ) | |||||
| } | } | ||||
| end(); | 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(); | // 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 ) | if ( n > on ) | ||||
| { | { | ||||
| @@ -606,11 +610,14 @@ Track::configure_inputs ( int n ) | |||||
| // engine->lock(); | // 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 ) | if ( n > on ) | ||||
| { | { | ||||