Work on making diskstreams follow the transport.
parent
3d2580dd64
commit
904daf8fe4
|
@ -41,10 +41,10 @@ Audio_File::from_file ( const char * filename )
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
||||||
a->_peaks = new Peaks;
|
/* a->_peaks = new Peaks; */
|
||||||
|
|
||||||
a->_peaks->clip( a );
|
/* a->_peaks->clip( a ); */
|
||||||
a->_peaks->open();
|
/* a->_peaks->open(); */
|
||||||
|
|
||||||
_open_files[ string( filename ) ] = a;
|
_open_files[ string( filename ) ] = a;
|
||||||
|
|
||||||
|
|
|
@ -21,33 +21,42 @@
|
||||||
#include "Track_Header.H"
|
#include "Track_Header.H"
|
||||||
#include "Audio_Track.H"
|
#include "Audio_Track.H"
|
||||||
#include "Port.H"
|
#include "Port.H"
|
||||||
|
#include "Engine.H" // for locking.
|
||||||
|
|
||||||
// float Disk_Stream::seconds_to_buffer = 5.0f;
|
/**********/
|
||||||
float Disk_Stream::seconds_to_buffer = 5.0f;
|
/* Engine */
|
||||||
// size_t Disk_Stream::disk_block_frames = 2048;
|
/**********/
|
||||||
|
|
||||||
/* A Disk_Stream uses a separate I/O thread to stream a track's
|
/* A Disk_Stream uses a separate I/O thread to stream a track's
|
||||||
regions from disk into a ringbuffer, to be processed by the RT
|
regions from disk into a ringbuffer, to be processed by the RT
|
||||||
thread (or vice-versa). */
|
thread (or vice-versa). The I/O thread syncronizes access with the
|
||||||
|
user thread via the Timeline mutex. The size of the buffer (in
|
||||||
|
seconds) must be set before any Disk_Stream objects are created;
|
||||||
|
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: handle termination of IO thread in destructor */
|
||||||
/* FIXME: could all of this not simply be included in the Track_Header
|
|
||||||
class? */
|
|
||||||
/* FIXME: deal with (jack) buffer size changes */
|
/* FIXME: deal with (jack) buffer size changes */
|
||||||
/* FIXME: can this be made to actually handle capture? */
|
|
||||||
/* FIXME: needs error handling everywhere! */
|
/* FIXME: needs error handling everywhere! */
|
||||||
|
/* TODO: handle capture too */
|
||||||
|
|
||||||
|
float Disk_Stream::seconds_to_buffer = 1.0f;
|
||||||
|
// size_t Disk_Stream::disk_block_frames = 2048;
|
||||||
|
|
||||||
Disk_Stream::Disk_Stream ( Track_Header *th, float frame_rate, nframes_t nframes, int channels ) : _th( th )
|
Disk_Stream::Disk_Stream ( Track_Header *th, float frame_rate, nframes_t nframes, int channels ) : _th( th )
|
||||||
{
|
{
|
||||||
_frame = 0;
|
_frame = 0;
|
||||||
_thread = 0;
|
_thread = 0;
|
||||||
|
_terminate = false;
|
||||||
|
_pending_seek = -1;
|
||||||
|
|
||||||
printf( "nframes %lu\n", nframes );
|
printf( "nframes %lu\n", nframes );
|
||||||
|
|
||||||
const int blocks = frame_rate * seconds_to_buffer / nframes;
|
_total_blocks = frame_rate * seconds_to_buffer / nframes;
|
||||||
|
|
||||||
_nframes = nframes;
|
_nframes = nframes;
|
||||||
|
|
||||||
size_t bufsize = blocks * nframes * sizeof( sample_t );
|
size_t bufsize = _total_blocks * nframes * sizeof( sample_t );
|
||||||
|
|
||||||
/* const int blocks = 64; */
|
/* const int blocks = 64; */
|
||||||
/* const size_t bufsize = (blocks * (nframes * sizeof( sample_t ))) + sizeof( sample_t ); */
|
/* const size_t bufsize = (blocks * (nframes * sizeof( sample_t ))) + sizeof( sample_t ); */
|
||||||
|
@ -55,19 +64,29 @@ Disk_Stream::Disk_Stream ( Track_Header *th, float frame_rate, nframes_t nframes
|
||||||
for ( int i = channels; i--; )
|
for ( int i = channels; i--; )
|
||||||
_rb.push_back( jack_ringbuffer_create( bufsize ) );
|
_rb.push_back( jack_ringbuffer_create( bufsize ) );
|
||||||
|
|
||||||
sem_init( &_blocks, 0, blocks );
|
sem_init( &_blocks, 0, _total_blocks );
|
||||||
|
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
|
|
||||||
Disk_Stream::~Disk_Stream ( )
|
Disk_Stream::~Disk_Stream ( )
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/* stop the IO thread */
|
||||||
|
_terminate = true;
|
||||||
|
pthread_join( _thread, NULL );
|
||||||
|
|
||||||
|
/* it isn't safe to do all this with the RT thread running */
|
||||||
|
engine->lock();
|
||||||
|
|
||||||
_th = NULL;
|
_th = NULL;
|
||||||
|
|
||||||
sem_destroy( &_blocks );
|
sem_destroy( &_blocks );
|
||||||
|
|
||||||
for ( int i = channels(); i--; )
|
for ( int i = channels(); i--; )
|
||||||
jack_ringbuffer_free( _rb[ i ] );
|
jack_ringbuffer_free( _rb[ i ] );
|
||||||
|
|
||||||
|
engine->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
Audio_Track *
|
Audio_Track *
|
||||||
|
@ -84,6 +103,56 @@ Disk_Stream::run ( void )
|
||||||
/* error */;
|
/* error */;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* to be called when the JACK buffer size changes. */
|
||||||
|
void
|
||||||
|
Disk_Stream::resize ( nframes_t nframes )
|
||||||
|
{
|
||||||
|
if ( nframes != _nframes )
|
||||||
|
/* FIXME: to something here! */;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Disk_Stream::seek_pending ( void )
|
||||||
|
{
|
||||||
|
return _pending_seek != (nframes_t)-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* THREAD: RT */
|
||||||
|
/** request that the IO thread perform a seek and rebuffer. This is
|
||||||
|
called for each Disk_Stream whenever the RT thread determines that
|
||||||
|
the transport has jumped to a new position. This is called *before*
|
||||||
|
process. */
|
||||||
|
void
|
||||||
|
Disk_Stream::seek ( nframes_t frame )
|
||||||
|
{
|
||||||
|
printf( "requesting seek\n" );
|
||||||
|
|
||||||
|
if ( seek_pending() )
|
||||||
|
printf( "seek error, attempt to seek while seek is pending\n" );
|
||||||
|
|
||||||
|
_pending_seek = frame;
|
||||||
|
|
||||||
|
/* flush buffers */
|
||||||
|
for ( int i = channels(); i--; )
|
||||||
|
jack_ringbuffer_read_advance( _rb[ i ], jack_ringbuffer_read_space( _rb[ i ] ) );
|
||||||
|
|
||||||
|
/* dirty hack... reset the semaphore. Should we just call sem_init
|
||||||
|
* again instead? */
|
||||||
|
|
||||||
|
/* sem_init( &_blocks, 0, _total_blocks ); */
|
||||||
|
|
||||||
|
int n;
|
||||||
|
sem_getvalue( &_blocks, &n );
|
||||||
|
|
||||||
|
n = _total_blocks - n;
|
||||||
|
|
||||||
|
while ( n-- )
|
||||||
|
sem_post( &_blocks );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* void */
|
/* void */
|
||||||
/* DIsk_Stream::shutdown ( void ) */
|
/* DIsk_Stream::shutdown ( void ) */
|
||||||
/* { */
|
/* { */
|
||||||
|
@ -100,9 +169,9 @@ Disk_Stream::io_thread ( void *arg )
|
||||||
}
|
}
|
||||||
|
|
||||||
/* THREAD: IO */
|
/* THREAD: IO */
|
||||||
/** read a block of data from the track into /buf/ */
|
/** read /nframes/ from the attached track into /buf/ */
|
||||||
void
|
void
|
||||||
Disk_Stream::read_block ( sample_t *buf )
|
Disk_Stream::read_block ( sample_t *buf, nframes_t nframes )
|
||||||
{
|
{
|
||||||
|
|
||||||
/* stupid chicken/egg */
|
/* stupid chicken/egg */
|
||||||
|
@ -119,14 +188,24 @@ Disk_Stream::read_block ( sample_t *buf )
|
||||||
|
|
||||||
timeline->rdlock();
|
timeline->rdlock();
|
||||||
|
|
||||||
if ( track()->play( buf, _frame, _nframes, channels() ) )
|
if ( track()->play( buf, _frame, nframes, channels() ) )
|
||||||
_frame += _nframes;
|
_frame += nframes;
|
||||||
else
|
else
|
||||||
/* error */;
|
/* error */;
|
||||||
|
|
||||||
timeline->unlock();
|
timeline->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Disk_Stream::output_buffer_percent ( void )
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
sem_getvalue( &_blocks, &n );
|
||||||
|
|
||||||
|
return 100 - (n * 100 / _total_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
/* THREAD: IO */
|
/* THREAD: IO */
|
||||||
void
|
void
|
||||||
Disk_Stream::io_thread ( void )
|
Disk_Stream::io_thread ( void )
|
||||||
|
@ -145,7 +224,26 @@ Disk_Stream::io_thread ( void )
|
||||||
{
|
{
|
||||||
// printf( "IO: RT thread is ready for more data...\n" );
|
// printf( "IO: RT thread is ready for more data...\n" );
|
||||||
|
|
||||||
read_block( buf );
|
printf( "IO: disk buffer is %3d%% full\r", output_buffer_percent() );
|
||||||
|
|
||||||
|
// lock(); // for seeking
|
||||||
|
|
||||||
|
if ( seek_pending() )
|
||||||
|
{
|
||||||
|
printf( "performing seek\n" );
|
||||||
|
_frame = _pending_seek;
|
||||||
|
_pending_seek = -1;
|
||||||
|
/* finish flushing the buffer */
|
||||||
|
|
||||||
|
/* for ( int i = channels(); i-- ) */
|
||||||
|
/* jack_ringbuffer_write_advance( _rb[ i ], jack_ringbuffer_write_space( _rb[ i ] ) ); */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: should we not read from disk in larger-than-JACK-buffer blocks? */
|
||||||
|
read_block( buf, _nframes );
|
||||||
|
|
||||||
|
// 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 */
|
||||||
|
|
||||||
|
@ -158,6 +256,7 @@ Disk_Stream::io_thread ( void )
|
||||||
while ( jack_ringbuffer_write_space( _rb[ i ] ) < block_size )
|
while ( jack_ringbuffer_write_space( _rb[ i ] ) < block_size )
|
||||||
{
|
{
|
||||||
printf( "IO: disk buffer overrun!\n" );
|
printf( "IO: disk buffer overrun!\n" );
|
||||||
|
/* FIXME: is this *really* the right thing to do? */
|
||||||
usleep( 2000 );
|
usleep( 2000 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,13 +264,15 @@ Disk_Stream::io_thread ( void )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf( "IO thread terminating.\n" );
|
||||||
|
|
||||||
delete[] buf;
|
delete[] buf;
|
||||||
delete[] cbuf;
|
delete[] cbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* THREAD: RT */
|
/* THREAD: RT */
|
||||||
/** take a block from the ringbuffers and send it out the track's
|
/** take a single block from the ringbuffers and send it out the
|
||||||
* ports */
|
* attached track's ports */
|
||||||
nframes_t
|
nframes_t
|
||||||
Disk_Stream::process ( nframes_t nframes )
|
Disk_Stream::process ( nframes_t nframes )
|
||||||
{
|
{
|
||||||
|
@ -184,19 +285,11 @@ Disk_Stream::process ( nframes_t nframes )
|
||||||
|
|
||||||
void *buf = _th->output[ i ].buffer( nframes );
|
void *buf = _th->output[ i ].buffer( nframes );
|
||||||
|
|
||||||
/* FIXME: handle underrun */
|
|
||||||
|
|
||||||
/* if ( jack_ringbuffer_read_space( _rb[ i ] ) < block_size ) */
|
|
||||||
/* { */
|
|
||||||
/* printf( "disktream (rt): buffer underrun!\n" ); */
|
|
||||||
/* memset( buf, 0, block_size ); */
|
|
||||||
/* } */
|
|
||||||
/* else */
|
|
||||||
|
|
||||||
if ( jack_ringbuffer_read( _rb[ i ], (char*)buf, block_size ) < block_size )
|
if ( jack_ringbuffer_read( _rb[ i ], (char*)buf, block_size ) < block_size )
|
||||||
{
|
{
|
||||||
printf( "RT: buffer underrun (disk can't keep up).\n" );
|
printf( "RT: buffer underrun (disk can't keep up).\n" );
|
||||||
memset( buf, 0, block_size );
|
memset( buf, 0, block_size );
|
||||||
|
/* FIXME: we need to resync somehow */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* /\* testing. *\/ */
|
/* /\* testing. *\/ */
|
||||||
|
|
|
@ -27,13 +27,15 @@
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "Mutex.H"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
class Track_Header;
|
class Track_Header;
|
||||||
class Audio_Track;
|
class Audio_Track;
|
||||||
|
|
||||||
class Disk_Stream
|
class Disk_Stream : public Mutex
|
||||||
{
|
{
|
||||||
|
|
||||||
pthread_t _thread;
|
pthread_t _thread;
|
||||||
|
@ -49,7 +51,11 @@ class Disk_Stream
|
||||||
|
|
||||||
sem_t _blocks; /* semaphore to wake the IO thread with */
|
sem_t _blocks; /* semaphore to wake the IO thread with */
|
||||||
|
|
||||||
// volatile nframes_t _seek_request;
|
int _total_blocks;
|
||||||
|
|
||||||
|
volatile nframes_t _pending_seek; /* absolute transport position to seek to */
|
||||||
|
|
||||||
|
volatile int _terminate;
|
||||||
|
|
||||||
int channels ( void ) const { return _rb.size(); }
|
int channels ( void ) const { return _rb.size(); }
|
||||||
|
|
||||||
|
@ -60,7 +66,19 @@ class Disk_Stream
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void block_processed ( void ) { sem_post( &_blocks ); }
|
void block_processed ( void ) { sem_post( &_blocks ); }
|
||||||
bool wait_for_block ( void ) { while ( sem_wait( &_blocks ) == EINTR ); return true; }
|
bool wait_for_block ( void )
|
||||||
|
{
|
||||||
|
if ( _terminate )
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while ( sem_wait( &_blocks ) == EINTR );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_block ( sample_t *buf, nframes_t nframes );
|
||||||
|
void io_thread ( void );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -71,23 +89,12 @@ public:
|
||||||
|
|
||||||
virtual ~Disk_Stream ( );
|
virtual ~Disk_Stream ( );
|
||||||
|
|
||||||
void
|
void resize ( nframes_t nframes );
|
||||||
resize ( nframes_t nframes )
|
void seek ( nframes_t frame );
|
||||||
{
|
bool seek_pending ( void );
|
||||||
if ( nframes != _nframes )
|
|
||||||
/* FIXME: to something here! */;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
seek ( nframes_t frame )
|
|
||||||
{
|
|
||||||
_frame = frame;
|
|
||||||
/* FIXME: need to signal the IO thread somehow? */
|
|
||||||
}
|
|
||||||
|
|
||||||
void run ( void );
|
void run ( void );
|
||||||
void read_block ( sample_t *buf );
|
|
||||||
void io_thread ( void );
|
|
||||||
nframes_t process ( nframes_t nframes );
|
nframes_t process ( nframes_t nframes );
|
||||||
|
|
||||||
|
int output_buffer_percent ( void );
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,6 +40,63 @@ Engine::process ( nframes_t nframes, void *arg )
|
||||||
return ((Engine*)arg)->process( nframes );
|
return ((Engine*)arg)->process( nframes );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static wrapper */
|
||||||
|
int
|
||||||
|
Engine::sync ( jack_transport_state_t state, jack_position_t *pos, void *arg )
|
||||||
|
{
|
||||||
|
return ((Engine*)arg)->sync( state, pos );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Engine::request_locate ( nframes_t frame )
|
||||||
|
{
|
||||||
|
if ( timeline )
|
||||||
|
timeline->seek( frame );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* THREAD: RT */
|
||||||
|
/** This is the jack slow-sync callback. */
|
||||||
|
int
|
||||||
|
Engine::sync ( jack_transport_state_t state, jack_position_t *pos )
|
||||||
|
{
|
||||||
|
static bool seeking = false;
|
||||||
|
|
||||||
|
switch ( state )
|
||||||
|
{
|
||||||
|
case JackTransportStopped: /* new position requested */
|
||||||
|
/* JACK docs lie. This is only called when the transport
|
||||||
|
is *really* stopped, not when starting a slow-sync
|
||||||
|
cycle */
|
||||||
|
request_locate( pos->frame );
|
||||||
|
return 1;
|
||||||
|
case JackTransportStarting: /* this means JACK is polling slow-sync clients */
|
||||||
|
{
|
||||||
|
if ( ! seeking )
|
||||||
|
{
|
||||||
|
request_locate( pos->frame );
|
||||||
|
seeking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int r = timeline->seek_pending();
|
||||||
|
|
||||||
|
if ( ! r )
|
||||||
|
seeking = false;
|
||||||
|
|
||||||
|
return ! seeking;
|
||||||
|
}
|
||||||
|
case JackTransportRolling: /* JACK's timeout has expired */
|
||||||
|
/* FIXME: what's the right thing to do here? */
|
||||||
|
// request_locate( pos->frame );
|
||||||
|
return 1;
|
||||||
|
// return transport.frame == pos->frame;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf( "unknown transport state.\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* THREAD: RT */
|
/* THREAD: RT */
|
||||||
int
|
int
|
||||||
Engine::process ( nframes_t nframes )
|
Engine::process ( nframes_t nframes )
|
||||||
|
@ -83,6 +140,10 @@ Engine::init ( void )
|
||||||
|
|
||||||
jack_set_process_callback( _client, &Engine::process, this );
|
jack_set_process_callback( _client, &Engine::process, this );
|
||||||
|
|
||||||
|
/* FIXME: should we wait to register this until after the session
|
||||||
|
has been loaded (and we have disk threads running)? */
|
||||||
|
jack_set_sync_callback( _client, &Engine::sync, this );
|
||||||
|
|
||||||
jack_activate( _client );
|
jack_activate( _client );
|
||||||
|
|
||||||
/* we don't need to create any ports until tracks are created */
|
/* we don't need to create any ports until tracks are created */
|
||||||
|
|
|
@ -44,6 +44,8 @@ class Engine : public Mutex
|
||||||
|
|
||||||
static int process ( nframes_t nframes, void *arg );
|
static int process ( nframes_t nframes, void *arg );
|
||||||
int process ( nframes_t nframes );
|
int process ( nframes_t nframes );
|
||||||
|
static int sync ( jack_transport_state_t state, jack_position_t *pos, void *arg );
|
||||||
|
int sync ( jack_transport_state_t state, jack_position_t *pos );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -52,12 +54,12 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Engine ( );
|
Engine ( );
|
||||||
|
|
||||||
int init ( void );
|
int init ( void );
|
||||||
|
|
||||||
|
void request_locate ( nframes_t frame );
|
||||||
|
|
||||||
nframes_t nframes ( void ) const { return jack_get_buffer_size( _client ); }
|
nframes_t nframes ( void ) const { return jack_get_buffer_size( _client ); }
|
||||||
float frame_rate ( void ) const { return jack_get_sample_rate( _client ); }
|
float frame_rate ( void ) const { return jack_get_sample_rate( _client ); }
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <FL/Fl.H> // for Fl::check();
|
||||||
|
|
||||||
Peaks::peakbuffer Peaks::_peakbuf;
|
Peaks::peakbuffer Peaks::_peakbuf;
|
||||||
|
|
||||||
|
|
||||||
|
@ -305,6 +307,8 @@ Peaks::current ( void ) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* FIXME: we need to work out a way to run this in another thread and
|
||||||
|
possibly stream back the data to the GUI */
|
||||||
/** build peaks file for /filename/ if necessary */
|
/** build peaks file for /filename/ if necessary */
|
||||||
bool
|
bool
|
||||||
Peaks::make_peaks ( int chunksize )
|
Peaks::make_peaks ( int chunksize )
|
||||||
|
@ -334,6 +338,8 @@ Peaks::make_peaks ( int chunksize )
|
||||||
do {
|
do {
|
||||||
len = read_source_peaks( peaks, 1, chunksize );
|
len = read_source_peaks( peaks, 1, chunksize );
|
||||||
fwrite( peaks, sizeof( peaks ), 1, fp );
|
fwrite( peaks, sizeof( peaks ), 1, fp );
|
||||||
|
/* FIXME: GUI code shouldn't be here! */
|
||||||
|
Fl::check();
|
||||||
}
|
}
|
||||||
while ( len );
|
while ( len );
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
|
|
||||||
#include "Track_Header.H"
|
#include "Track_Header.H"
|
||||||
|
|
||||||
|
|
||||||
|
#include "Disk_Stream.H"
|
||||||
|
|
||||||
void
|
void
|
||||||
Timeline::cb_scroll ( Fl_Widget *w, void *v )
|
Timeline::cb_scroll ( Fl_Widget *w, void *v )
|
||||||
{
|
{
|
||||||
|
@ -153,7 +156,7 @@ Timeline::Timeline ( int X, int Y, int W, int H, const char* L ) : Fl_Overlay_Wi
|
||||||
o->type( Fl_Pack::VERTICAL );
|
o->type( Fl_Pack::VERTICAL );
|
||||||
o->spacing( 0 );
|
o->spacing( 0 );
|
||||||
|
|
||||||
for ( int i = 1; i--; )
|
for ( int i = 2; i--; )
|
||||||
{
|
{
|
||||||
// Track_Header *t = new Track_Header( 0, 0, W, 75 );
|
// Track_Header *t = new Track_Header( 0, 0, W, 75 );
|
||||||
Track_Header *t = new Track_Header( 0, 0, W, 30 );
|
Track_Header *t = new Track_Header( 0, 0, W, 30 );
|
||||||
|
@ -616,3 +619,30 @@ Timeline::process ( nframes_t nframes )
|
||||||
/* FIXME: BOGUS */
|
/* FIXME: BOGUS */
|
||||||
return nframes;
|
return nframes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* THREAD: RT */
|
||||||
|
void
|
||||||
|
Timeline::seek ( nframes_t frame )
|
||||||
|
{
|
||||||
|
for ( int i = tracks->children(); i-- ; )
|
||||||
|
{
|
||||||
|
Track_Header *t = (Track_Header*)tracks->child( i );
|
||||||
|
|
||||||
|
t->seek( frame );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* THREAD: RT */
|
||||||
|
int
|
||||||
|
Timeline::seek_pending ( void )
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
for ( int i = tracks->children(); i-- ; )
|
||||||
|
{
|
||||||
|
Track_Header *t = (Track_Header*)tracks->child( i );
|
||||||
|
|
||||||
|
if ( t->diskstream )
|
||||||
|
r += t->diskstream->output_buffer_percent() < 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -148,5 +148,8 @@ private:
|
||||||
|
|
||||||
friend class Engine; // FIXME: only Engine::process() needs to be friended.x
|
friend class Engine; // FIXME: only Engine::process() needs to be friended.x
|
||||||
|
|
||||||
|
/* Engine */
|
||||||
nframes_t process ( nframes_t nframes );
|
nframes_t process ( nframes_t nframes );
|
||||||
|
void seek ( nframes_t frame );
|
||||||
|
int seek_pending ( void );
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,7 +100,13 @@ Track_Header::Track_Header ( int X, int Y, int W, int H, const char *L ) :
|
||||||
_show_all_takes = false;
|
_show_all_takes = false;
|
||||||
_size = 1;
|
_size = 1;
|
||||||
|
|
||||||
output.push_back( Port( "foo" ) );
|
{
|
||||||
|
char pname[40];
|
||||||
|
static int n = 0;
|
||||||
|
snprintf( pname, sizeof( pname ), "out-%d", n++ );
|
||||||
|
|
||||||
|
output.push_back( Port( strdup( pname ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
diskstream = new Disk_Stream( this, engine->frame_rate(), engine->nframes(), 1 );
|
diskstream = new Disk_Stream( this, engine->frame_rate(), engine->nframes(), 1 );
|
||||||
|
|
||||||
|
@ -299,3 +305,11 @@ Track_Header::process ( nframes_t nframes )
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* THREAD: RT */
|
||||||
|
void
|
||||||
|
Track_Header::seek ( nframes_t frame )
|
||||||
|
{
|
||||||
|
if ( diskstream )
|
||||||
|
return diskstream->seek( frame );
|
||||||
|
}
|
||||||
|
|
|
@ -264,6 +264,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Engine */
|
||||||
nframes_t process ( nframes_t nframes );
|
nframes_t process ( nframes_t nframes );
|
||||||
|
void seek ( nframes_t frame );
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue