Timeline: Fix numerous issues with diskstream.
This commit is contained in:
parent
9fba8a8577
commit
58fdf7d914
|
@ -61,13 +61,14 @@ Disk_Stream::Disk_Stream ( Track *track, float frame_rate, nframes_t nframes, in
|
|||
|
||||
_frame = 0;
|
||||
_terminate = false;
|
||||
_pending_seek = -1;
|
||||
_pending_seek = false;
|
||||
_seek_frame = 0;
|
||||
_xruns = 0;
|
||||
_frame_rate = frame_rate;
|
||||
|
||||
sem_init( &_blocks, 0, 0 );
|
||||
|
||||
_resize_buffers( nframes, channels );
|
||||
|
||||
sem_init( &_blocks, 0, _total_blocks );
|
||||
}
|
||||
|
||||
Disk_Stream::~Disk_Stream ( )
|
||||
|
@ -94,37 +95,18 @@ Disk_Stream::~Disk_Stream ( )
|
|||
void
|
||||
Disk_Stream::base_flush ( bool is_output )
|
||||
{
|
||||
THREAD_ASSERT( RT );
|
||||
// THREAD_ASSERT( RT );
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
int n;
|
||||
sem_getvalue( &_blocks, &n );
|
||||
|
||||
n = _total_blocks - n;
|
||||
|
||||
while ( n-- )
|
||||
sem_post( &_blocks );
|
||||
}
|
||||
sem_init( &_blocks, 0, _total_blocks );
|
||||
else
|
||||
{
|
||||
sem_destroy( &_blocks );
|
||||
|
||||
sem_init( &_blocks, 0, 0 );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/** signal thread to terminate, then detach it */
|
||||
|
@ -156,10 +138,6 @@ Disk_Stream::shutdown ( void )
|
|||
}
|
||||
|
||||
_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;
|
||||
|
||||
_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 );
|
||||
|
||||
|
@ -222,10 +200,10 @@ Disk_Stream::resize_buffers ( nframes_t nframes )
|
|||
if ( was_running )
|
||||
shutdown();
|
||||
|
||||
flush();
|
||||
|
||||
_resize_buffers( nframes, channels() );
|
||||
|
||||
flush();
|
||||
|
||||
if ( was_running )
|
||||
run();
|
||||
}
|
||||
|
|
|
@ -56,14 +56,15 @@ 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 */
|
||||
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 */
|
||||
|
||||
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 _xruns;
|
||||
|
@ -82,6 +83,9 @@ protected:
|
|||
void block_processed ( void ) { sem_post( &_blocks ); }
|
||||
bool wait_for_block ( void )
|
||||
{
|
||||
if ( _terminate )
|
||||
return false;
|
||||
|
||||
while ( ! sem_wait( &_blocks ) && errno == EINTR )
|
||||
{}
|
||||
|
||||
|
@ -117,6 +121,6 @@ public:
|
|||
|
||||
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() )
|
||||
{
|
||||
/* freewheeling mode/export. We're actually running
|
||||
non-RT. Assume that everything is quiescent. do I/O
|
||||
synchronously */
|
||||
|
||||
if ( timeline )
|
||||
{
|
||||
timeline->rdlock();
|
||||
|
@ -185,9 +181,6 @@ Engine::process ( nframes_t nframes )
|
|||
timeline->process( nframes );
|
||||
|
||||
timeline->unlock();
|
||||
|
||||
/* because we're going faster than realtime. */
|
||||
timeline->wait_for_buffers();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
bool
|
||||
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
|
||||
|
@ -56,9 +56,11 @@ Playback_DS::seek ( nframes_t frame )
|
|||
if ( seek_pending() )
|
||||
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
|
||||
|
@ -82,30 +84,28 @@ Playback_DS::read_block ( sample_t *buf, nframes_t nframes )
|
|||
if ( !timeline )
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
@ -117,61 +117,63 @@ Playback_DS::disk_thread ( void )
|
|||
|
||||
/* buffer to hold the interleaved data returned by the track reader */
|
||||
sample_t *buf = buffer_alloc( _nframes * channels() * _disk_io_blocks );
|
||||
sample_t *cbuf = buffer_alloc( _nframes * _disk_io_blocks );
|
||||
sample_t *cbuf = buffer_alloc( _nframes );
|
||||
|
||||
int blocks_ready = 0;
|
||||
const nframes_t nframes = _nframes;
|
||||
nframes_t blocks_written;
|
||||
|
||||
const nframes_t nframes = _nframes * _disk_io_blocks;
|
||||
|
||||
while ( wait_for_block() )
|
||||
while ( ! _terminate )
|
||||
{
|
||||
|
||||
seek:
|
||||
|
||||
blocks_written = 0;
|
||||
read_block( buf, nframes * _disk_io_blocks );
|
||||
|
||||
while ( blocks_written < _disk_io_blocks &&
|
||||
wait_for_block() )
|
||||
{
|
||||
// lock(); // for seeking
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if ( ++blocks_ready < _disk_io_blocks )
|
||||
{
|
||||
/* wait for more space */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* reset */
|
||||
blocks_ready = 0;
|
||||
|
||||
read_block( buf, nframes );
|
||||
|
||||
/* might have received terminate signal while waiting for block */
|
||||
if ( _terminate )
|
||||
goto done;
|
||||
|
||||
// unlock(); // for seeking
|
||||
|
||||
/* deinterleave the buffer and stuff it into the per-channel ringbuffers */
|
||||
|
||||
const size_t block_size = nframes * sizeof( sample_t );
|
||||
|
||||
for ( int i = channels(); i--; )
|
||||
{
|
||||
buffer_deinterleave_one_channel( cbuf, buf, i, channels(), nframes );
|
||||
|
||||
size_t wr = 0;
|
||||
|
||||
while ( wr < block_size )
|
||||
|
||||
if ( _pending_seek )
|
||||
{
|
||||
wr += jack_ringbuffer_write( _rb[ i ], ((char*)cbuf) + wr, block_size - wr );
|
||||
// usleep( 10 * 1000 );
|
||||
}
|
||||
}
|
||||
/* FIXME: non-RT-safe IO */
|
||||
DMESSAGE( "performing seek to frame %lu", (unsigned long)_seek_frame );
|
||||
|
||||
_frame = _seek_frame;
|
||||
_pending_seek = false;
|
||||
|
||||
flush();
|
||||
|
||||
goto seek;
|
||||
}
|
||||
|
||||
/* might have received terminate signal while waiting for block */
|
||||
if ( _terminate )
|
||||
goto done;
|
||||
|
||||
// unlock(); // for seeking
|
||||
|
||||
/* deinterleave the buffer and stuff it into the per-channel ringbuffers */
|
||||
|
||||
const size_t block_size = nframes * sizeof( sample_t );
|
||||
|
||||
for ( int i = 0; i < channels(); i++ )
|
||||
{
|
||||
buffer_deinterleave_one_channel( cbuf,
|
||||
buf + ( blocks_written * nframes * channels() ),
|
||||
i,
|
||||
channels(),
|
||||
nframes );
|
||||
|
||||
while ( jack_ringbuffer_write_space( _rb[ i ] ) < block_size )
|
||||
usleep( 100 * 1000 );
|
||||
|
||||
jack_ringbuffer_write( _rb[ i ], ((char*)cbuf), block_size );
|
||||
}
|
||||
|
||||
blocks_written++;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -203,22 +205,25 @@ Playback_DS::process ( nframes_t nframes )
|
|||
|
||||
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
|
||||
{
|
||||
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;
|
||||
memset( buf, 0, block_size );
|
||||
/* 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 */
|
||||
|
|
|
@ -74,39 +74,40 @@ Record_DS::disk_thread ( void )
|
|||
|
||||
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 */
|
||||
sample_t *buf = buffer_alloc( nframes * channels() );
|
||||
sample_t *buf = buffer_alloc( nframes * channels() * _disk_io_blocks );
|
||||
sample_t *cbuf = buffer_alloc( nframes );
|
||||
|
||||
const size_t block_size = nframes * sizeof( sample_t );
|
||||
|
||||
int blocks_ready = 0;
|
||||
nframes_t blocks_read = 0;
|
||||
|
||||
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 */
|
||||
for ( int i = channels(); i--; )
|
||||
{
|
||||
size_t rd = 0;
|
||||
while ( jack_ringbuffer_read_space( _rb[ i ] ) < block_size )
|
||||
usleep( 10 * 1000 );
|
||||
|
||||
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 );
|
||||
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" );
|
||||
|
@ -114,11 +115,7 @@ Record_DS::disk_thread ( void )
|
|||
/* 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--; )
|
||||
{
|
||||
|
@ -138,8 +135,6 @@ Record_DS::disk_thread ( void )
|
|||
else
|
||||
write_block( buf, nframes );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
@ -259,21 +254,22 @@ Record_DS::process ( nframes_t nframes )
|
|||
|
||||
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
|
||||
{
|
||||
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 );
|
||||
/* 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 );
|
||||
|
||||
if ( t->playback_ds )
|
||||
if ( t->playback_ds->buffer_percent() < 50 )
|
||||
if ( t->playback_ds->seek_pending() )
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -283,15 +283,6 @@ Timeline::total_output_buffer_percent ( void )
|
|||
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
|
||||
Timeline::total_playback_xruns ( void )
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue