Timeline: More locking fixes. Also, build peaks cache levels in threads launched by UI thread, instead of in the record diskthreads.
This commit is contained in:
parent
88b552ea00
commit
8d282617e6
|
@ -618,13 +618,12 @@ Audio_Region::draw ( void )
|
||||||
oend = end;
|
oend = end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if ( _clip->peaks()->needs_more_peaks() && ! transport->rolling )
|
||||||
{
|
{
|
||||||
if ( ! transport->rolling )
|
/* maybe create a thread to make the peaks */
|
||||||
{
|
/* this function will just return if there's nothing to do. */
|
||||||
/* create a thread to make the peaks */
|
|
||||||
_clip->peaks()->make_peaks_asynchronously( Audio_Region::peaks_ready_callback, this );
|
_clip->peaks()->make_peaks_asynchronously( Audio_Region::peaks_ready_callback, this );
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -74,7 +74,7 @@ Disk_Stream::~Disk_Stream ( )
|
||||||
{
|
{
|
||||||
/* it isn't safe to do all this with the RT thread running */
|
/* it isn't safe to do all this with the RT thread running */
|
||||||
|
|
||||||
timeline->wrlock();
|
// timeline->wrlock();
|
||||||
|
|
||||||
shutdown();
|
shutdown();
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ Disk_Stream::~Disk_Stream ( )
|
||||||
for ( int i = channels(); i--; )
|
for ( int i = channels(); i--; )
|
||||||
jack_ringbuffer_free( _rb[ i ] );
|
jack_ringbuffer_free( _rb[ i ] );
|
||||||
|
|
||||||
timeline->unlock();
|
// timeline->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,13 +144,15 @@ Disk_Stream::shutdown ( void )
|
||||||
{
|
{
|
||||||
if ( _thread.running() )
|
if ( _thread.running() )
|
||||||
{
|
{
|
||||||
|
DMESSAGE( "Sending terminate signal to diskthread." );
|
||||||
|
|
||||||
_terminate = true;
|
_terminate = true;
|
||||||
|
|
||||||
/* try to wake the thread so it'll see that it's time to die */
|
/* try to wake the thread so it'll see that it's time to die */
|
||||||
while ( _terminate )
|
while ( _terminate )
|
||||||
{
|
{
|
||||||
usleep( 100 );
|
|
||||||
block_processed();
|
block_processed();
|
||||||
|
usleep( 1000 );
|
||||||
}
|
}
|
||||||
|
|
||||||
_thread.join();
|
_thread.join();
|
||||||
|
|
|
@ -104,9 +104,7 @@ class Peakfile
|
||||||
FILE *_fp;
|
FILE *_fp;
|
||||||
nframes_t _chunksize;
|
nframes_t _chunksize;
|
||||||
int _channels; /* number of channels this peakfile represents */
|
int _channels; /* number of channels this peakfile represents */
|
||||||
// nframes_t _length; /* length, in frames, of the clip this peakfile represents */
|
|
||||||
off_t _offset;
|
off_t _offset;
|
||||||
// int _blocks;
|
|
||||||
|
|
||||||
struct block_descriptor
|
struct block_descriptor
|
||||||
{
|
{
|
||||||
|
@ -127,14 +125,17 @@ class Peakfile
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
int nblocks ( void ) const
|
||||||
|
{
|
||||||
|
return blocks.size();
|
||||||
|
}
|
||||||
|
|
||||||
Peakfile ( )
|
Peakfile ( )
|
||||||
{
|
{
|
||||||
// _blocks = 0;
|
|
||||||
_fp = NULL;
|
_fp = NULL;
|
||||||
_offset = 0;
|
_offset = 0;
|
||||||
_chunksize = 0;
|
_chunksize = 0;
|
||||||
_channels = 0;
|
_channels = 0;
|
||||||
// _length = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~Peakfile ( )
|
~Peakfile ( )
|
||||||
|
@ -143,6 +144,11 @@ public:
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rescan ( void )
|
||||||
|
{
|
||||||
|
blocks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/* int blocks ( void ) const { return blocks.size(); } */
|
/* int blocks ( void ) const { return blocks.size(); } */
|
||||||
/** find the best block for /chunksize/ */
|
/** find the best block for /chunksize/ */
|
||||||
void
|
void
|
||||||
|
@ -184,9 +190,6 @@ public:
|
||||||
if ( ! blocks.size() )
|
if ( ! blocks.size() )
|
||||||
FATAL( "Peak file contains no blocks!" );
|
FATAL( "Peak file contains no blocks!" );
|
||||||
|
|
||||||
|
|
||||||
// DMESSAGE( "peakfile has %d blocks.", blocks.size() );
|
|
||||||
|
|
||||||
blocks.sort();
|
blocks.sort();
|
||||||
|
|
||||||
/* fall back on the smallest chunksize */
|
/* fall back on the smallest chunksize */
|
||||||
|
@ -204,7 +207,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// DMESSAGE( "using peakfile block for chunksize %lu", _chunksize );
|
// DMESSAGE( "using peakfile block for chunksize %lu", _chunksize );
|
||||||
// _blocks = blocks.size();
|
|
||||||
_offset = ftello( _fp );
|
_offset = ftello( _fp );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +241,7 @@ public:
|
||||||
bool
|
bool
|
||||||
open ( const char *name, int channels, nframes_t chunksize )
|
open ( const char *name, int channels, nframes_t chunksize )
|
||||||
{
|
{
|
||||||
|
assert( ! _fp );
|
||||||
// _chunksize = 0;
|
// _chunksize = 0;
|
||||||
_channels = channels;
|
_channels = channels;
|
||||||
|
|
||||||
|
@ -263,6 +266,8 @@ public:
|
||||||
bool
|
bool
|
||||||
open ( FILE *fp, int channels, nframes_t chunksize )
|
open ( FILE *fp, int channels, nframes_t chunksize )
|
||||||
{
|
{
|
||||||
|
assert( ! _fp );
|
||||||
|
|
||||||
_fp = fp;
|
_fp = fp;
|
||||||
_chunksize = 0;
|
_chunksize = 0;
|
||||||
_channels = channels;
|
_channels = channels;
|
||||||
|
@ -364,7 +369,9 @@ public:
|
||||||
|
|
||||||
Peaks::Peaks ( Audio_File *c )
|
Peaks::Peaks ( Audio_File *c )
|
||||||
{
|
{
|
||||||
_pending = false;
|
_rescan_needed = false;
|
||||||
|
_first_block_pending = false;
|
||||||
|
_mipmaps_pending = false;
|
||||||
_clip = c;
|
_clip = c;
|
||||||
_peak_writer = NULL;
|
_peak_writer = NULL;
|
||||||
_peakfile = new Peakfile();
|
_peakfile = new Peakfile();
|
||||||
|
@ -412,39 +419,50 @@ Peaks::ready ( nframes_t s, nframes_t npeaks, nframes_t chunksize ) const
|
||||||
bool
|
bool
|
||||||
Peaks::peakfile_ready ( void ) const
|
Peaks::peakfile_ready ( void ) const
|
||||||
{
|
{
|
||||||
return current() && ! _pending;
|
if ( _rescan_needed )
|
||||||
|
{
|
||||||
|
DMESSAGE( "Rescanning peakfile" );
|
||||||
|
_peakfile->rescan();
|
||||||
|
if ( _peakfile->open( _clip->filename(), _clip->channels(), 256 ) )
|
||||||
|
_peakfile->close();
|
||||||
|
|
||||||
|
_rescan_needed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current() && ! _first_block_pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** start building peaks and/or peak mipmap in another thread. It is
|
||||||
|
* safe to call this again before the thread finishes. /callback/ will
|
||||||
|
* be called with /userdata/ FROM THE PEAK BUILDING THREAD when the
|
||||||
|
* peaks are finished. */
|
||||||
void
|
void
|
||||||
Peaks::make_peaks_asynchronously ( void(*callback)(void*), void *userdata ) const
|
Peaks::make_peaks_asynchronously ( void(*callback)(void*), void *userdata ) const
|
||||||
{
|
{
|
||||||
/* already working on it... */
|
/* already working on it... */
|
||||||
if( _pending )
|
if( _first_block_pending || _mipmaps_pending )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// make_peaks();
|
/* maybe still building mipmaps... */
|
||||||
|
_first_block_pending = _peakfile->nblocks() < 1;
|
||||||
_pending = true;
|
_mipmaps_pending = _peakfile->nblocks() <= 1;
|
||||||
|
|
||||||
peak_thread_data *pd = new peak_thread_data();
|
peak_thread_data *pd = new peak_thread_data();
|
||||||
|
|
||||||
pd->callback = callback;
|
pd->callback = callback;
|
||||||
pd->userdata = userdata;
|
pd->userdata = userdata;
|
||||||
pd->peaks = const_cast<Peaks*>(this);
|
pd->peaks = const_cast<Peaks*>(this);
|
||||||
|
|
||||||
_make_peaks_thread.clone( &Peaks::make_peaks, pd );
|
_make_peaks_thread.clone( &Peaks::make_peaks, pd );
|
||||||
_make_peaks_thread.detach();
|
_make_peaks_thread.detach();
|
||||||
|
|
||||||
|
DMESSAGE( "Starting new peak building thread" );
|
||||||
}
|
}
|
||||||
|
|
||||||
nframes_t
|
nframes_t
|
||||||
Peaks::read_peakfile_peaks ( Peak *peaks, nframes_t s, nframes_t npeaks, nframes_t chunksize ) const
|
Peaks::read_peakfile_peaks ( Peak *peaks, nframes_t s, nframes_t npeaks, nframes_t chunksize ) const
|
||||||
{
|
{
|
||||||
/* if ( _pending ) */
|
if ( ! _peakfile->open( _clip->filename(), _clip->channels(), chunksize ) )
|
||||||
/* return 0; */
|
|
||||||
|
|
||||||
// Peakfile _peakfile;
|
|
||||||
|
|
||||||
if ( ! _peakfile->open( _clip->filename(), _clip->channels(), chunksize ) )
|
|
||||||
{
|
{
|
||||||
DMESSAGE( "Failed to open peakfile!" );
|
DMESSAGE( "Failed to open peakfile!" );
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -545,7 +563,7 @@ Peaks::current ( void ) const
|
||||||
{
|
{
|
||||||
char *pn = peakname( _clip->filename() );
|
char *pn = peakname( _clip->filename() );
|
||||||
|
|
||||||
bool b = ! newer( _clip->filename(), pn );
|
bool b = newer( pn, _clip->filename() );
|
||||||
|
|
||||||
free( pn );
|
free( pn );
|
||||||
|
|
||||||
|
@ -557,47 +575,40 @@ void *
|
||||||
Peaks::make_peaks ( void *v )
|
Peaks::make_peaks ( void *v )
|
||||||
{
|
{
|
||||||
peak_thread_data *pd = (peak_thread_data*)v;
|
peak_thread_data *pd = (peak_thread_data*)v;
|
||||||
|
|
||||||
pd->peaks->make_peaks();
|
|
||||||
|
|
||||||
if ( pd->callback )
|
if ( pd->peaks->make_peaks() )
|
||||||
pd->callback( pd->userdata );
|
{
|
||||||
|
if ( pd->callback )
|
||||||
|
pd->callback( pd->userdata );
|
||||||
|
|
||||||
|
pd->peaks->_rescan_needed = true;
|
||||||
|
}
|
||||||
|
|
||||||
delete pd;
|
delete pd;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Peaks::needs_more_peaks ( void ) const
|
||||||
|
{
|
||||||
|
return _peakfile->nblocks() <= 1 && ! ( _first_block_pending || _mipmaps_pending );
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Peaks::make_peaks ( void ) const
|
Peaks::make_peaks ( void ) const
|
||||||
{
|
{
|
||||||
Peaks::Builder pb( this );
|
Peaks::Builder pb( this );
|
||||||
|
|
||||||
|
/* make the first block */
|
||||||
int b = pb.make_peaks();
|
int b = pb.make_peaks();
|
||||||
|
|
||||||
_pending = false;
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* thread entry point */
|
|
||||||
void *
|
|
||||||
Peaks::make_peaks_mipmap ( void *v )
|
|
||||||
{
|
|
||||||
((Peaks*)v)->make_peaks_mipmap();
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
Peaks::make_peaks_mipmap ( void ) const
|
|
||||||
{
|
|
||||||
Peaks::Builder pb( this );
|
|
||||||
|
|
||||||
bool b = pb.make_peaks_mipmap();
|
|
||||||
|
|
||||||
_pending = false;
|
|
||||||
|
|
||||||
|
_first_block_pending = false;
|
||||||
|
|
||||||
|
b = pb.make_peaks_mipmap();
|
||||||
|
|
||||||
|
_mipmaps_pending = false;
|
||||||
|
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,8 +649,6 @@ Peaks::finish_writing ( void )
|
||||||
|
|
||||||
delete _peak_writer;
|
delete _peak_writer;
|
||||||
_peak_writer = NULL;
|
_peak_writer = NULL;
|
||||||
|
|
||||||
make_peaks_mipmap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -792,15 +801,12 @@ Peaks::Builder::write_block_header ( nframes_t chunksize )
|
||||||
fflush( fp );
|
fflush( fp );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** generate additional cache levels for a peakfile with only 1 block (ie. that of a new capture) */
|
/** generate additional cache levels for a peakfile with only 1 block (ie. that of a new capture) */
|
||||||
bool
|
bool
|
||||||
Peaks::Builder::make_peaks_mipmap ( void )
|
Peaks::Builder::make_peaks_mipmap ( void )
|
||||||
{
|
{
|
||||||
if ( ! Peaks::mipmapped_peakfiles )
|
if ( ! Peaks::mipmapped_peakfiles )
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
Audio_File *_clip = _peaks->_clip;
|
Audio_File *_clip = _peaks->_clip;
|
||||||
|
|
||||||
|
@ -895,10 +901,7 @@ Peaks::Builder::make_peaks_mipmap ( void )
|
||||||
}
|
}
|
||||||
while ( len > 0 && s < _clip->length() );
|
while ( len > 0 && s < _clip->length() );
|
||||||
|
|
||||||
DMESSAGE( "Last sample was %lu", s );
|
DMESSAGE( "Last sample was %lu", (unsigned long)s );
|
||||||
|
|
||||||
/* fflush( fp ); */
|
|
||||||
/* fsync( fileno( fp ) ); */
|
|
||||||
|
|
||||||
pf.leave_open();
|
pf.leave_open();
|
||||||
}
|
}
|
||||||
|
@ -918,43 +921,46 @@ Peaks::Builder::make_peaks ( void )
|
||||||
|
|
||||||
const char *filename = _clip->filename();
|
const char *filename = _clip->filename();
|
||||||
|
|
||||||
DMESSAGE( "building peaks for \"%s\"", filename );
|
if ( _peaks->_peakfile && _peaks->_peakfile->nblocks() > 1 )
|
||||||
|
|
||||||
char *pn = peakname( filename );
|
|
||||||
|
|
||||||
if ( ! ( fp = fopen( pn, "w+" ) ) )
|
|
||||||
{
|
{
|
||||||
free( pn );
|
/* this peakfile already has enough blocks */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DMESSAGE( "building peaks for \"%s\"", filename );
|
||||||
|
|
||||||
|
char *pn = peakname( filename );
|
||||||
|
|
||||||
|
if ( ! ( fp = fopen( pn, "w+" ) ) )
|
||||||
|
{
|
||||||
|
free( pn );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free( pn );
|
||||||
|
|
||||||
|
_clip->seek( 0 );
|
||||||
|
|
||||||
|
Peak buf[ _clip->channels() ];
|
||||||
|
|
||||||
|
DMESSAGE( "building level 1 peak cache" );
|
||||||
|
|
||||||
|
write_block_header( Peaks::cache_minimum );
|
||||||
|
|
||||||
|
/* build first level from source */
|
||||||
|
off_t len;
|
||||||
|
do {
|
||||||
|
len = _peaks->read_source_peaks( buf, 1, Peaks::cache_minimum );
|
||||||
|
|
||||||
|
fwrite( buf, sizeof( buf ), len, fp );
|
||||||
|
}
|
||||||
|
while ( len );
|
||||||
|
|
||||||
|
fclose( fp );
|
||||||
|
|
||||||
free( pn );
|
DMESSAGE( "done building peaks" );
|
||||||
|
|
||||||
_clip->seek( 0 );
|
|
||||||
|
|
||||||
Peak buf[ _clip->channels() ];
|
|
||||||
|
|
||||||
DMESSAGE( "building level 1 peak cache" );
|
|
||||||
|
|
||||||
write_block_header( Peaks::cache_minimum );
|
|
||||||
|
|
||||||
/* build first level from source */
|
|
||||||
off_t len;
|
|
||||||
do {
|
|
||||||
len = _peaks->read_source_peaks( buf, 1, Peaks::cache_minimum );
|
|
||||||
|
|
||||||
fwrite( buf, sizeof( buf ), len, fp );
|
|
||||||
}
|
}
|
||||||
while ( len );
|
|
||||||
|
|
||||||
/* reopen for reading */
|
|
||||||
/* fflush( fp ); */
|
|
||||||
/* fsync( fileno( fp ) ); */
|
|
||||||
fclose( fp );
|
|
||||||
|
|
||||||
make_peaks_mipmap();
|
|
||||||
|
|
||||||
DMESSAGE( "done building peaks" );
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,9 @@ class Peakfile;
|
||||||
|
|
||||||
class Peaks
|
class Peaks
|
||||||
{
|
{
|
||||||
mutable volatile bool _pending;
|
/* true if first block is still being built */
|
||||||
|
mutable volatile bool _first_block_pending;
|
||||||
|
mutable volatile bool _mipmaps_pending;
|
||||||
|
|
||||||
mutable Thread _make_peaks_thread;
|
mutable Thread _make_peaks_thread;
|
||||||
mutable Thread _make_peaks_mipmap_thread;
|
mutable Thread _make_peaks_mipmap_thread;
|
||||||
|
@ -110,6 +112,8 @@ class Peaks
|
||||||
|
|
||||||
mutable float _fpp;
|
mutable float _fpp;
|
||||||
|
|
||||||
|
volatile mutable bool _rescan_needed;
|
||||||
|
|
||||||
nframes_t read_peaks ( nframes_t s, nframes_t npeaks, nframes_t chunksize ) const;
|
nframes_t read_peaks ( nframes_t s, nframes_t npeaks, nframes_t chunksize ) const;
|
||||||
nframes_t read_source_peaks ( Peak *peaks, nframes_t s, nframes_t npeaks, nframes_t chunksize ) const;
|
nframes_t read_source_peaks ( Peak *peaks, nframes_t s, nframes_t npeaks, nframes_t chunksize ) const;
|
||||||
nframes_t read_source_peaks ( Peak *peaks, nframes_t npeaks, nframes_t chunksize ) const;
|
nframes_t read_source_peaks ( Peak *peaks, nframes_t npeaks, nframes_t chunksize ) const;
|
||||||
|
@ -145,11 +149,11 @@ public:
|
||||||
bool ready ( nframes_t s, nframes_t npeaks, nframes_t chunksize ) const;
|
bool ready ( nframes_t s, nframes_t npeaks, nframes_t chunksize ) const;
|
||||||
|
|
||||||
bool make_peaks ( void ) const;
|
bool make_peaks ( void ) const;
|
||||||
bool make_peaks_mipmap ( void ) const;
|
|
||||||
void make_peaks_asynchronously ( void(*callback)(void*), void *userdata ) const;
|
void make_peaks_asynchronously ( void(*callback)(void*), void *userdata ) const;
|
||||||
|
|
||||||
void prepare_for_writing ( void );
|
void prepare_for_writing ( void );
|
||||||
void finish_writing ( void );
|
void finish_writing ( void );
|
||||||
void write ( sample_t *buf, nframes_t nframes );
|
void write ( sample_t *buf, nframes_t nframes );
|
||||||
|
|
||||||
|
bool needs_more_peaks ( void ) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,11 +34,16 @@
|
||||||
bool
|
bool
|
||||||
Timeline::record ( void )
|
Timeline::record ( void )
|
||||||
{
|
{
|
||||||
|
THREAD_ASSERT( UI );
|
||||||
|
|
||||||
|
DMESSAGE( "Initiating recording." );
|
||||||
|
|
||||||
/* FIXME: right place for this? */
|
/* FIXME: right place for this? */
|
||||||
|
|
||||||
if ( transport->automatically_create_takes() &&
|
if ( transport->automatically_create_takes() &&
|
||||||
! _created_new_takes )
|
! _created_new_takes )
|
||||||
{
|
{
|
||||||
|
DMESSAGE( "Creating new takes." );
|
||||||
add_take_for_armed_tracks();
|
add_take_for_armed_tracks();
|
||||||
_created_new_takes = true;
|
_created_new_takes = true;
|
||||||
}
|
}
|
||||||
|
@ -53,12 +58,16 @@ Timeline::record ( void )
|
||||||
|
|
||||||
if ( transport->punch_enabled() )
|
if ( transport->punch_enabled() )
|
||||||
{
|
{
|
||||||
|
DMESSAGE( "Finding next punch region following frame %lu...", (unsigned long)frame);
|
||||||
|
|
||||||
const Sequence_Widget *w = punch_cursor_track->next( frame );
|
const Sequence_Widget *w = punch_cursor_track->next( frame );
|
||||||
|
|
||||||
if ( w && w->start() >= frame )
|
if ( w && w->start() >= frame )
|
||||||
{
|
{
|
||||||
frame = w->start();
|
frame = w->start();
|
||||||
_punch_out_frame = w->start() + w->length();
|
_punch_out_frame = w->start() + w->length();
|
||||||
|
|
||||||
|
DMESSAGE( "Punch enabled... Will punch in at frame %lu.", (unsigned long)frame );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +103,8 @@ Timeline::punch_in ( nframes_t frame )
|
||||||
void
|
void
|
||||||
Timeline::punch_out ( nframes_t frame )
|
Timeline::punch_out ( nframes_t frame )
|
||||||
{
|
{
|
||||||
|
THREAD_ASSERT( UI );
|
||||||
|
|
||||||
for ( int i = tracks->children(); i-- ; )
|
for ( int i = tracks->children(); i-- ; )
|
||||||
{
|
{
|
||||||
Track *t = (Track*)tracks->child( i );
|
Track *t = (Track*)tracks->child( i );
|
||||||
|
@ -102,9 +113,11 @@ Timeline::punch_out ( nframes_t frame )
|
||||||
t->record_ds->stop( frame );
|
t->record_ds->stop( frame );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait until finalization is complete before continuing */
|
DMESSAGE( "Waiting for record threads to shutdown." );
|
||||||
|
|
||||||
|
/* none of the record threads need to call Fl::lock, because we're
|
||||||
|
* holding up the UI thread waiting for them to join.*/
|
||||||
|
|
||||||
DMESSAGE( "Waiting for record threads to shutdown" );
|
|
||||||
for ( int i = tracks->children(); i-- ; )
|
for ( int i = tracks->children(); i-- ; )
|
||||||
{
|
{
|
||||||
Track *t = (Track*)tracks->child( i );
|
Track *t = (Track*)tracks->child( i );
|
||||||
|
@ -112,6 +125,8 @@ Timeline::punch_out ( nframes_t frame )
|
||||||
if ( t->armed() && t->record_ds )
|
if ( t->armed() && t->record_ds )
|
||||||
t->record_ds->shutdown();
|
t->record_ds->shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DMESSAGE( "All record threads stopped." );
|
||||||
|
|
||||||
_punched_in = false;
|
_punched_in = false;
|
||||||
_punch_in_frame = 0;
|
_punch_in_frame = 0;
|
||||||
|
@ -122,6 +137,8 @@ Timeline::punch_out ( nframes_t frame )
|
||||||
void
|
void
|
||||||
Timeline::stop ( void )
|
Timeline::stop ( void )
|
||||||
{
|
{
|
||||||
|
THREAD_ASSERT( UI );
|
||||||
|
|
||||||
nframes_t frame = transport->frame;
|
nframes_t frame = transport->frame;
|
||||||
|
|
||||||
if ( transport->punch_enabled() )
|
if ( transport->punch_enabled() )
|
||||||
|
|
|
@ -272,15 +272,19 @@ Track::record ( Capture *c, nframes_t frame )
|
||||||
|
|
||||||
/* open it again for reading in the GUI thread */
|
/* open it again for reading in the GUI thread */
|
||||||
// Audio_File *af = Audio_File::from_file( c->audio_file->name() );
|
// Audio_File *af = Audio_File::from_file( c->audio_file->name() );
|
||||||
|
/* must acquire the FLTK lock because adding a widget might interfere with drawing */
|
||||||
|
Fl::lock();
|
||||||
|
|
||||||
/* must acquire a write lock because the Audio_Region constructor
|
/* must acquire a write lock because the Audio_Region constructor
|
||||||
* will add the region to the specified sequence */
|
* will add the region to the specified sequence, which might affect playback */
|
||||||
timeline->wrlock();
|
timeline->wrlock();
|
||||||
|
|
||||||
c->region = new Audio_Region( c->audio_file, sequence(), frame );
|
c->region = new Audio_Region( c->audio_file, sequence(), frame );
|
||||||
|
|
||||||
timeline->unlock();
|
timeline->unlock();
|
||||||
|
|
||||||
|
Fl::unlock();
|
||||||
|
|
||||||
c->region->prepare();
|
c->region->prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,14 +309,11 @@ Track::finalize ( Capture *c, nframes_t frame )
|
||||||
/* adjust region start for latency */
|
/* adjust region start for latency */
|
||||||
/* FIXME: is just looking at the first channel good enough? */
|
/* FIXME: is just looking at the first channel good enough? */
|
||||||
|
|
||||||
timeline->wrlock();
|
|
||||||
|
|
||||||
DMESSAGE( "finalizing audio file" );
|
DMESSAGE( "finalizing audio file" );
|
||||||
/* must finalize audio before peaks file, otherwise another thread
|
|
||||||
* might think the peaks are out of date and attempt to regenerate
|
|
||||||
* them */
|
|
||||||
c->audio_file->finalize();
|
c->audio_file->finalize();
|
||||||
|
|
||||||
|
timeline->wrlock();
|
||||||
|
|
||||||
c->region->finalize( frame );
|
c->region->finalize( frame );
|
||||||
|
|
||||||
nframes_t capture_offset = 0;
|
nframes_t capture_offset = 0;
|
||||||
|
@ -333,6 +334,4 @@ Track::finalize ( Capture *c, nframes_t frame )
|
||||||
c->region->offset( capture_offset );
|
c->region->offset( capture_offset );
|
||||||
|
|
||||||
timeline->unlock();
|
timeline->unlock();
|
||||||
|
|
||||||
// delete c->audio_file;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,9 +131,7 @@ Project::write_info ( void )
|
||||||
void
|
void
|
||||||
Project::undo ( void )
|
Project::undo ( void )
|
||||||
{
|
{
|
||||||
timeline->wrlock();
|
|
||||||
Loggable::undo();
|
Loggable::undo();
|
||||||
timeline->unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -203,12 +201,8 @@ Project::close ( void )
|
||||||
if ( ! save() )
|
if ( ! save() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
timeline->wrlock();
|
|
||||||
|
|
||||||
Loggable::close();
|
Loggable::close();
|
||||||
|
|
||||||
timeline->unlock();
|
|
||||||
|
|
||||||
// write_info();
|
// write_info();
|
||||||
|
|
||||||
_is_open = false;
|
_is_open = false;
|
||||||
|
|
|
@ -188,14 +188,11 @@ Sequence::add ( Sequence_Widget *r )
|
||||||
{
|
{
|
||||||
// Logger _log( this );
|
// Logger _log( this );
|
||||||
|
|
||||||
if ( r->sequence() )
|
if ( r->sequence() && r->sequence() != this )
|
||||||
{
|
{
|
||||||
/* This method can be called from the Capture thread as well as the GUI thread, so we must lock FLTK before redraw */
|
/* This method can be called from the Capture thread as well as the GUI thread, so we must lock FLTK before redraw */
|
||||||
Fl::lock();
|
|
||||||
r->redraw();
|
r->redraw();
|
||||||
r->sequence()->remove( r );
|
r->sequence()->remove( r );
|
||||||
Fl::unlock();
|
|
||||||
// r->track()->redraw();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r->sequence( this );
|
r->sequence( this );
|
||||||
|
@ -481,28 +478,30 @@ Sequence::handle ( int m )
|
||||||
Sequence_Widget::pushed( NULL );
|
Sequence_Widget::pushed( NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
Loggable::block_start();
|
if ( _delete_queue.size() )
|
||||||
|
|
||||||
timeline->wrlock();
|
|
||||||
while ( _delete_queue.size() )
|
|
||||||
{
|
{
|
||||||
|
Loggable::block_start();
|
||||||
Sequence_Widget *t = _delete_queue.front();
|
|
||||||
_delete_queue.pop();
|
while ( _delete_queue.size() )
|
||||||
|
|
||||||
if ( Sequence_Widget::pushed() == t )
|
|
||||||
Sequence_Widget::pushed( NULL );
|
|
||||||
if ( Sequence_Widget::belowmouse() == t )
|
|
||||||
{
|
{
|
||||||
Sequence_Widget::belowmouse()->handle( FL_LEAVE );
|
Sequence_Widget *t = _delete_queue.front();
|
||||||
Sequence_Widget::belowmouse( NULL );
|
_delete_queue.pop();
|
||||||
}
|
|
||||||
|
if ( Sequence_Widget::pushed() == t )
|
||||||
|
Sequence_Widget::pushed( NULL );
|
||||||
|
if ( Sequence_Widget::belowmouse() == t )
|
||||||
|
{
|
||||||
|
Sequence_Widget::belowmouse()->handle( FL_LEAVE );
|
||||||
|
Sequence_Widget::belowmouse( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
delete t;
|
timeline->wrlock();
|
||||||
|
delete t;
|
||||||
|
timeline->unlock();
|
||||||
}
|
}
|
||||||
timeline->unlock();
|
|
||||||
|
|
||||||
Loggable::block_end();
|
Loggable::block_end();
|
||||||
|
}
|
||||||
|
|
||||||
if ( m == FL_PUSH )
|
if ( m == FL_PUSH )
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -550,6 +549,7 @@ const Sequence_Widget *
|
||||||
Sequence::next ( nframes_t from ) const
|
Sequence::next ( nframes_t from ) const
|
||||||
{
|
{
|
||||||
for ( list <Sequence_Widget*>::const_iterator i = _widgets.begin(); i != _widgets.end(); i++ )
|
for ( list <Sequence_Widget*>::const_iterator i = _widgets.begin(); i != _widgets.end(); i++ )
|
||||||
|
// if ( (*i)->start() >= from )
|
||||||
if ( (*i)->start() > from )
|
if ( (*i)->start() > from )
|
||||||
return *i;
|
return *i;
|
||||||
|
|
||||||
|
|
|
@ -396,7 +396,7 @@ Project::compact();}
|
||||||
} {
|
} {
|
||||||
MenuItem {} {
|
MenuItem {} {
|
||||||
label Undo
|
label Undo
|
||||||
callback {Project::undo();}
|
callback {timeline->command_undo();}
|
||||||
xywh {5 5 40 25} shortcut 0x4007a divider
|
xywh {5 5 40 25} shortcut 0x4007a divider
|
||||||
}
|
}
|
||||||
MenuItem {} {
|
MenuItem {} {
|
||||||
|
|
|
@ -1253,6 +1253,8 @@ Timeline::draw_cursors ( void ) const
|
||||||
void
|
void
|
||||||
Timeline::draw ( void )
|
Timeline::draw ( void )
|
||||||
{
|
{
|
||||||
|
/* Any code that might affect the structures used for drawing from
|
||||||
|
* another thread must use Fl::lock()/unlock()! */
|
||||||
THREAD_ASSERT( UI );
|
THREAD_ASSERT( UI );
|
||||||
|
|
||||||
int X, Y, W, H;
|
int X, Y, W, H;
|
||||||
|
@ -1260,8 +1262,6 @@ Timeline::draw ( void )
|
||||||
int bdx = 0;
|
int bdx = 0;
|
||||||
int bdw = 0;
|
int bdw = 0;
|
||||||
|
|
||||||
rdlock();
|
|
||||||
|
|
||||||
X = tracks->x() + bdx + 1;
|
X = tracks->x() + bdx + 1;
|
||||||
Y = tracks->y();
|
Y = tracks->y();
|
||||||
W = tracks->w() - bdw - 1;
|
W = tracks->w() - bdw - 1;
|
||||||
|
@ -1363,8 +1363,6 @@ done:
|
||||||
|
|
||||||
_old_xposition = xoffset;
|
_old_xposition = xoffset;
|
||||||
_old_yposition = panzoomer->y_value();
|
_old_yposition = panzoomer->y_value();
|
||||||
|
|
||||||
unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1438,6 +1436,7 @@ Timeline::redraw_playhead ( void )
|
||||||
/* we've passed one or more punch regions... punch in for the next, if available. */
|
/* we've passed one or more punch regions... punch in for the next, if available. */
|
||||||
const Sequence_Widget *w = punch_cursor_track->next( transport->frame );
|
const Sequence_Widget *w = punch_cursor_track->next( transport->frame );
|
||||||
|
|
||||||
|
DMESSAGE( "Delayed punch in" );
|
||||||
if ( w &&
|
if ( w &&
|
||||||
w->start() > transport->frame )
|
w->start() > transport->frame )
|
||||||
{
|
{
|
||||||
|
@ -1993,15 +1992,27 @@ Timeline::command_remove_track ( Track *track )
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Timeline::command_quit ( )
|
Timeline::command_quit ( void )
|
||||||
{
|
{
|
||||||
|
timeline->wrlock();
|
||||||
|
|
||||||
Project::close();
|
Project::close();
|
||||||
|
|
||||||
|
timeline->unlock();
|
||||||
|
|
||||||
command_save();
|
command_save();
|
||||||
|
|
||||||
while ( Fl::first_window() ) Fl::first_window()->hide();
|
while ( Fl::first_window() ) Fl::first_window()->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Timeline::command_undo ( void )
|
||||||
|
{
|
||||||
|
wrlock();
|
||||||
|
Project::undo();
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Timeline::command_load ( const char *name, const char *display_name )
|
Timeline::command_load ( const char *name, const char *display_name )
|
||||||
{
|
{
|
||||||
|
|
|
@ -257,6 +257,7 @@ public:
|
||||||
|
|
||||||
void command_move_track_up ( Track *track );
|
void command_move_track_up ( Track *track );
|
||||||
void command_move_track_down ( Track *track );
|
void command_move_track_down ( Track *track );
|
||||||
|
void command_undo ( void );
|
||||||
|
|
||||||
int find_track ( const Track * track ) const;
|
int find_track ( const Track * track ) const;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue