@@ -61,13 +61,14 @@ Disk_Stream::Disk_Stream ( Track *track, float frame_rate, nframes_t nframes, in | |||||
_frame = 0; | _frame = 0; | ||||
_terminate = false; | _terminate = false; | ||||
_pending_seek = -1; | |||||
_pending_seek = false; | |||||
_seek_frame = 0; | |||||
_xruns = 0; | _xruns = 0; | ||||
_frame_rate = frame_rate; | _frame_rate = frame_rate; | ||||
sem_init( &_blocks, 0, 0 ); | |||||
_resize_buffers( nframes, channels ); | _resize_buffers( nframes, channels ); | ||||
sem_init( &_blocks, 0, _total_blocks ); | |||||
} | } | ||||
Disk_Stream::~Disk_Stream ( ) | Disk_Stream::~Disk_Stream ( ) | ||||
@@ -94,37 +95,18 @@ Disk_Stream::~Disk_Stream ( ) | |||||
void | void | ||||
Disk_Stream::base_flush ( bool is_output ) | Disk_Stream::base_flush ( bool is_output ) | ||||
{ | { | ||||
THREAD_ASSERT( RT ); | |||||
// THREAD_ASSERT( RT ); | |||||
/* flush buffers */ | /* flush buffers */ | ||||
for ( int i = _rb.size(); i--; ) | |||||
jack_ringbuffer_read_advance( _rb[ i ], jack_ringbuffer_read_space( _rb[ i ] ) ); | |||||
/* sem_destroy( &_blocks ); */ | |||||
/* if ( is_output ) */ | |||||
/* sem_init( &_blocks, 0, _total_blocks ); */ | |||||
/* else */ | |||||
/* sem_init( &_blocks, 0, 0 ); */ | |||||
for ( unsigned int i = _rb.size(); i--; ) | |||||
jack_ringbuffer_reset( _rb[ i ] ); | |||||
sem_destroy( &_blocks ); | |||||
if ( is_output ) | if ( is_output ) | ||||
{ | |||||
int n; | |||||
sem_getvalue( &_blocks, &n ); | |||||
n = _total_blocks - n; | |||||
while ( n-- ) | |||||
sem_post( &_blocks ); | |||||
} | |||||
sem_init( &_blocks, 0, _total_blocks ); | |||||
else | else | ||||
{ | |||||
sem_destroy( &_blocks ); | |||||
sem_init( &_blocks, 0, 0 ); | sem_init( &_blocks, 0, 0 ); | ||||
} | |||||
} | } | ||||
/** signal thread to terminate, then detach it */ | /** signal thread to terminate, then detach it */ | ||||
@@ -156,10 +138,6 @@ Disk_Stream::shutdown ( void ) | |||||
} | } | ||||
_thread.join(); | _thread.join(); | ||||
sem_destroy( &_blocks ); | |||||
sem_init( &_blocks, 0, 0 ); | |||||
} | } | ||||
} | } | ||||
@@ -195,7 +173,7 @@ Disk_Stream::_resize_buffers ( nframes_t nframes, int channels ) | |||||
_nframes = nframes; | _nframes = nframes; | ||||
_total_blocks = _frame_rate * seconds_to_buffer / nframes; | |||||
_total_blocks = ( _frame_rate * seconds_to_buffer ) / nframes; | |||||
size_t bufsize = _total_blocks * nframes * sizeof( sample_t ); | size_t bufsize = _total_blocks * nframes * sizeof( sample_t ); | ||||
@@ -222,10 +200,10 @@ Disk_Stream::resize_buffers ( nframes_t nframes ) | |||||
if ( was_running ) | if ( was_running ) | ||||
shutdown(); | shutdown(); | ||||
flush(); | |||||
_resize_buffers( nframes, channels() ); | _resize_buffers( nframes, channels() ); | ||||
flush(); | |||||
if ( was_running ) | if ( was_running ) | ||||
run(); | run(); | ||||
} | } | ||||
@@ -56,14 +56,15 @@ 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 _disk_io_blocks; /* the number of blocks to read/write to/from disk at once */ | |||||
nframes_t _total_blocks; /* total number of blocks that we can buffer */ | |||||
nframes_t _disk_io_blocks; /* the number of blocks to read/write to/from disk at once */ | |||||
nframes_t _frame_rate; /* used for buffer size calculations */ | nframes_t _frame_rate; /* used for buffer size calculations */ | ||||
volatile nframes_t _frame; /* location of disk read */ | volatile nframes_t _frame; /* location of disk read */ | ||||
volatile nframes_t _pending_seek; /* absolute transport position to seek to */ | |||||
volatile nframes_t _seek_frame; /* absolute transport position to seek to */ | |||||
volatile bool _pending_seek; /* absolute transport position to seek to */ | |||||
volatile int _terminate; | volatile int _terminate; | ||||
volatile int _xruns; | volatile int _xruns; | ||||
@@ -82,6 +83,9 @@ protected: | |||||
void block_processed ( void ) { sem_post( &_blocks ); } | void block_processed ( void ) { sem_post( &_blocks ); } | ||||
bool wait_for_block ( void ) | bool wait_for_block ( void ) | ||||
{ | { | ||||
if ( _terminate ) | |||||
return false; | |||||
while ( ! sem_wait( &_blocks ) && errno == EINTR ) | while ( ! sem_wait( &_blocks ) && errno == EINTR ) | ||||
{} | {} | ||||
@@ -117,6 +121,6 @@ public: | |||||
virtual nframes_t process ( nframes_t nframes ) = 0; | virtual nframes_t process ( nframes_t nframes ) = 0; | ||||
int buffer_percent ( void ); | |||||
virtual int buffer_percent ( void ); | |||||
}; | }; |
@@ -174,10 +174,6 @@ Engine::process ( nframes_t nframes ) | |||||
if ( freewheeling() ) | if ( freewheeling() ) | ||||
{ | { | ||||
/* freewheeling mode/export. We're actually running | |||||
non-RT. Assume that everything is quiescent. do I/O | |||||
synchronously */ | |||||
if ( timeline ) | if ( timeline ) | ||||
{ | { | ||||
timeline->rdlock(); | timeline->rdlock(); | ||||
@@ -185,9 +181,6 @@ Engine::process ( nframes_t nframes ) | |||||
timeline->process( nframes ); | timeline->process( nframes ); | ||||
timeline->unlock(); | timeline->unlock(); | ||||
/* because we're going faster than realtime. */ | |||||
timeline->wait_for_buffers(); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
@@ -38,7 +38,7 @@ | |||||
bool | bool | ||||
Playback_DS::seek_pending ( void ) | Playback_DS::seek_pending ( void ) | ||||
{ | { | ||||
return _pending_seek != (nframes_t)-1; | |||||
return _pending_seek || buffer_percent() < 50; | |||||
} | } | ||||
/** request that the IO thread perform a seek and rebuffer. This is | /** request that the IO thread perform a seek and rebuffer. This is | ||||
@@ -56,9 +56,11 @@ Playback_DS::seek ( nframes_t frame ) | |||||
if ( seek_pending() ) | if ( seek_pending() ) | ||||
printf( "seek error, attempt to seek while seek is pending\n" ); | printf( "seek error, attempt to seek while seek is pending\n" ); | ||||
_pending_seek = frame; | |||||
_seek_frame = frame; | |||||
_pending_seek = true; | |||||
flush(); | |||||
/* wake the IO thread */ | |||||
block_processed(); | |||||
} | } | ||||
/** set the playback delay to /frames/ frames. This be called prior to | /** set the playback delay to /frames/ frames. This be called prior to | ||||
@@ -82,30 +84,28 @@ Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) | |||||
if ( !timeline ) | if ( !timeline ) | ||||
return; | return; | ||||
while ( ! _terminate ) | |||||
while ( timeline->tryrdlock() ) | |||||
{ | { | ||||
if ( ! timeline->tryrdlock() ) | |||||
{ | |||||
if ( sequence() ) | |||||
{ | |||||
/* FIXME: how does this work if _delay is not a multiple of bufsize? */ | |||||
if ( _frame >= _delay ) | |||||
{ | |||||
if ( ! sequence()->play( buf, _frame - _delay, nframes, channels() ) ) | |||||
WARNING( "Programming error?" ); | |||||
} | |||||
_frame += nframes; | |||||
} | |||||
timeline->unlock(); | |||||
if ( _terminate ) | |||||
return; | return; | ||||
} | |||||
usleep( 1000 * 10 ); | usleep( 1000 * 10 ); | ||||
} | } | ||||
if ( sequence() ) | |||||
{ | |||||
/* FIXME: how does this work if _delay is not a multiple of bufsize? */ | |||||
if ( _frame >= _delay ) | |||||
{ | |||||
if ( ! sequence()->play( buf, _frame - _delay, nframes, channels() ) ) | |||||
WARNING( "Programming error?" ); | |||||
} | |||||
_frame += nframes; | |||||
} | |||||
timeline->unlock(); | |||||
} | } | ||||
void | void | ||||
@@ -117,61 +117,63 @@ Playback_DS::disk_thread ( void ) | |||||
/* 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 = buffer_alloc( _nframes * channels() * _disk_io_blocks ); | sample_t *buf = buffer_alloc( _nframes * channels() * _disk_io_blocks ); | ||||
sample_t *cbuf = buffer_alloc( _nframes * _disk_io_blocks ); | |||||
int blocks_ready = 0; | |||||
sample_t *cbuf = buffer_alloc( _nframes ); | |||||
const nframes_t nframes = _nframes * _disk_io_blocks; | |||||
const nframes_t nframes = _nframes; | |||||
nframes_t blocks_written; | |||||
while ( wait_for_block() ) | |||||
while ( ! _terminate ) | |||||
{ | { | ||||
// lock(); // for seeking | |||||
seek: | |||||
if ( seek_pending() ) | |||||
{ | |||||
/* FIXME: non-RT-safe IO */ | |||||
DMESSAGE( "performing seek to frame %lu", (unsigned long)_pending_seek ); | |||||
_frame = _pending_seek; | |||||
_pending_seek = -1; | |||||
blocks_ready = 0; | |||||
} | |||||
blocks_written = 0; | |||||
read_block( buf, nframes * _disk_io_blocks ); | |||||
if ( ++blocks_ready < _disk_io_blocks ) | |||||
while ( blocks_written < _disk_io_blocks && | |||||
wait_for_block() ) | |||||
{ | { | ||||
/* wait for more space */ | |||||
continue; | |||||
} | |||||
/* reset */ | |||||
blocks_ready = 0; | |||||
read_block( buf, nframes ); | |||||
// lock(); // for seeking | |||||
if ( _pending_seek ) | |||||
{ | |||||
/* FIXME: non-RT-safe IO */ | |||||
DMESSAGE( "performing seek to frame %lu", (unsigned long)_seek_frame ); | |||||
_frame = _seek_frame; | |||||
_pending_seek = false; | |||||
/* might have received terminate signal while waiting for block */ | |||||
if ( _terminate ) | |||||
goto done; | |||||
flush(); | |||||
goto seek; | |||||
} | |||||
/* might have received terminate signal while waiting for block */ | |||||
if ( _terminate ) | |||||
goto done; | |||||
// unlock(); // for seeking | // unlock(); // for seeking | ||||
/* deinterleave the buffer and stuff it into the per-channel ringbuffers */ | |||||
/* deinterleave the buffer and stuff it into the per-channel ringbuffers */ | |||||
const size_t block_size = nframes * sizeof( sample_t ); | |||||
const size_t block_size = nframes * sizeof( sample_t ); | |||||
for ( int i = channels(); i--; ) | |||||
{ | |||||
buffer_deinterleave_one_channel( cbuf, buf, i, channels(), nframes ); | |||||
for ( int i = 0; i < channels(); i++ ) | |||||
{ | |||||
buffer_deinterleave_one_channel( cbuf, | |||||
buf + ( blocks_written * nframes * channels() ), | |||||
i, | |||||
channels(), | |||||
nframes ); | |||||
size_t wr = 0; | |||||
while ( jack_ringbuffer_write_space( _rb[ i ] ) < block_size ) | |||||
usleep( 100 * 1000 ); | |||||
while ( wr < block_size ) | |||||
{ | |||||
wr += jack_ringbuffer_write( _rb[ i ], ((char*)cbuf) + wr, block_size - wr ); | |||||
// usleep( 10 * 1000 ); | |||||
jack_ringbuffer_write( _rb[ i ], ((char*)cbuf), block_size ); | |||||
} | } | ||||
} | |||||
blocks_written++; | |||||
} | |||||
} | } | ||||
done: | done: | ||||
@@ -203,22 +205,25 @@ Playback_DS::process ( nframes_t nframes ) | |||||
if ( engine->freewheeling() ) | if ( engine->freewheeling() ) | ||||
{ | { | ||||
size_t rd = 0; | |||||
while ( rd < block_size ) | |||||
{ | |||||
rd += jack_ringbuffer_read( _rb[ i ], ((char*)buf) + rd, block_size - rd ); | |||||
// usleep( 10 * 1000 ); | |||||
} | |||||
/* only ever read nframes at a time */ | |||||
while ( jack_ringbuffer_read_space( _rb[i] ) < block_size ) | |||||
usleep( 10 * 1000 ); | |||||
jack_ringbuffer_read( _rb[ i ], ((char*)buf), block_size ); | |||||
} | } | ||||
else | else | ||||
{ | |||||
if ( jack_ringbuffer_read( _rb[ i ], (char*)buf, block_size ) < block_size ) | |||||
{ | |||||
/* only ever read nframes at a time */ | |||||
if ( jack_ringbuffer_read_space( _rb[i] ) < block_size ) | |||||
{ | { | ||||
++_xruns; | ++_xruns; | ||||
memset( buf, 0, block_size ); | memset( buf, 0, block_size ); | ||||
/* FIXME: we need to resync somehow */ | /* FIXME: we need to resync somehow */ | ||||
} | } | ||||
else | |||||
{ | |||||
jack_ringbuffer_read( _rb[ i ], (char*)buf, block_size ); | |||||
} | |||||
} | } | ||||
/* TODO: figure out a way to stop IO while muted without losing sync */ | /* TODO: figure out a way to stop IO while muted without losing sync */ | ||||
@@ -74,39 +74,40 @@ Record_DS::disk_thread ( void ) | |||||
track()->record( _capture, _frame ); | track()->record( _capture, _frame ); | ||||
const nframes_t nframes = _nframes * _disk_io_blocks; | |||||
const nframes_t nframes = _nframes; | |||||
/* 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 = buffer_alloc( nframes * channels() ); | |||||
sample_t *buf = buffer_alloc( nframes * channels() * _disk_io_blocks ); | |||||
sample_t *cbuf = buffer_alloc( nframes ); | sample_t *cbuf = buffer_alloc( nframes ); | ||||
const size_t block_size = nframes * sizeof( sample_t ); | const size_t block_size = nframes * sizeof( sample_t ); | ||||
int blocks_ready = 0; | |||||
nframes_t blocks_read = 0; | |||||
while ( wait_for_block() ) | while ( wait_for_block() ) | ||||
{ | { | ||||
if ( ++blocks_ready < _disk_io_blocks ) | |||||
continue; | |||||
else | |||||
blocks_ready = 0; | |||||
/* pull data from the per-channel ringbuffers and interlace it */ | /* pull data from the per-channel ringbuffers and interlace it */ | ||||
for ( int i = channels(); i--; ) | for ( int i = channels(); i--; ) | ||||
{ | { | ||||
size_t rd = 0; | |||||
while ( rd < block_size ) | |||||
{ | |||||
rd += jack_ringbuffer_read( _rb[ i ], ((char*)cbuf) + rd, block_size - rd ); | |||||
// usleep( 10 * 1000 ); | |||||
} | |||||
buffer_interleave_one_channel( buf, cbuf, i, channels(), nframes ); | |||||
while ( jack_ringbuffer_read_space( _rb[ i ] ) < block_size ) | |||||
usleep( 10 * 1000 ); | |||||
jack_ringbuffer_read( _rb[ i ], ((char*)cbuf), block_size ); | |||||
buffer_interleave_one_channel( buf + ( blocks_read * nframes * channels() ), | |||||
cbuf, | |||||
i, | |||||
channels(), | |||||
nframes ); | |||||
} | } | ||||
write_block( buf, nframes ); | |||||
blocks_read++; | |||||
if ( blocks_read == _disk_io_blocks ) | |||||
{ | |||||
write_block( buf, nframes * _disk_io_blocks ); | |||||
blocks_read = 0; | |||||
} | |||||
} | } | ||||
DMESSAGE( "capture thread terminating" ); | DMESSAGE( "capture thread terminating" ); | ||||
@@ -114,11 +115,7 @@ Record_DS::disk_thread ( void ) | |||||
/* flush what remains in the buffer out to disk */ | /* flush what remains in the buffer out to disk */ | ||||
{ | { | ||||
/* use JACk sized blocks for this last bit */ | |||||
const nframes_t nframes = _nframes; | |||||
const size_t block_size = _nframes * sizeof( sample_t ); | |||||
while ( blocks_ready-- > 0 || ( ! sem_trywait( &_blocks ) && errno != EAGAIN ) ) | |||||
while ( blocks_read-- > 0 || ( ! sem_trywait( &_blocks ) && errno != EAGAIN ) ) | |||||
{ | { | ||||
for ( int i = channels(); i--; ) | for ( int i = channels(); i--; ) | ||||
{ | { | ||||
@@ -138,8 +135,6 @@ Record_DS::disk_thread ( void ) | |||||
else | else | ||||
write_block( buf, nframes ); | write_block( buf, nframes ); | ||||
} | } | ||||
} | } | ||||
free(buf); | free(buf); | ||||
@@ -259,21 +254,22 @@ Record_DS::process ( nframes_t nframes ) | |||||
if ( engine->freewheeling() ) | if ( engine->freewheeling() ) | ||||
{ | { | ||||
size_t wr = 0; | |||||
while ( jack_ringbuffer_write_space( _rb[i] ) < block_size ) | |||||
usleep( 10 * 1000 ); | |||||
while ( wr < block_size ) | |||||
{ | |||||
wr += jack_ringbuffer_write( _rb[ i ], ((char*)buf) + offset_size + wr, block_size - wr ); | |||||
// usleep( 10 * 1000 ); | |||||
} | |||||
jack_ringbuffer_write( _rb[ i ], ((char*)buf) + offset_size, block_size ); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
if ( jack_ringbuffer_write( _rb[ i ], ((char*)buf) + offset_size, block_size ) < block_size ) | |||||
if ( jack_ringbuffer_write_space( _rb[i] ) < block_size ) | |||||
{ | { | ||||
++_xruns; | |||||
memset( buf, 0, block_size ); | memset( buf, 0, block_size ); | ||||
/* FIXME: we need to resync somehow */ | /* FIXME: we need to resync somehow */ | ||||
++_xruns; | |||||
} | |||||
else | |||||
{ | |||||
jack_ringbuffer_write( _rb[ i ], ((char*)buf) + offset_size, block_size ); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -225,7 +225,7 @@ Timeline::seek_pending ( void ) | |||||
Track *t = (Track*)tracks->child( i ); | Track *t = (Track*)tracks->child( i ); | ||||
if ( t->playback_ds ) | if ( t->playback_ds ) | ||||
if ( t->playback_ds->buffer_percent() < 50 ) | |||||
if ( t->playback_ds->seek_pending() ) | |||||
return true; | return true; | ||||
} | } | ||||
@@ -283,15 +283,6 @@ Timeline::total_output_buffer_percent ( void ) | |||||
return r / cnt; | return r / cnt; | ||||
} | } | ||||
/** wait for I/O threads to fill their buffers */ | |||||
void | |||||
Timeline::wait_for_buffers ( void ) | |||||
{ | |||||
while ( total_output_buffer_percent() + total_input_buffer_percent() < 200 ) | |||||
usleep( 5000 ); | |||||
} | |||||
int | int | ||||
Timeline::total_playback_xruns ( void ) | Timeline::total_playback_xruns ( void ) | ||||
{ | { | ||||