@@ -127,6 +127,6 @@ Thread::join ( void ) | |||
void | |||
Thread::exit ( void *retval ) | |||
{ | |||
pthread_exit( retval ); | |||
_thread = 0; | |||
pthread_exit( retval ); | |||
} |
@@ -651,7 +651,7 @@ Audio_Region::draw ( void ) | |||
if ( peaks < loop_peaks_needed ) | |||
{ | |||
DMESSAGE( "Peak read came up %lu peaks short", (unsigned long)loop_peaks_needed - peaks ); | |||
// DMESSAGE( "Peak read came up %lu peaks short", (unsigned long)loop_peaks_needed - peaks ); | |||
} | |||
fo += loop_frames_needed; | |||
@@ -783,11 +783,11 @@ Control_Sequence::handle ( int m ) | |||
test_press( FL_BUTTON1 ) ) | |||
{ | |||
/* insert new control point */ | |||
timeline->wrlock(); | |||
timeline->sequence_lock.wrlock(); | |||
new Control_Point( this, timeline->xoffset + timeline->x_to_ts( Fl::event_x() - drawable_x() ), (float)(Fl::event_y() - y()) / h() ); | |||
timeline->unlock(); | |||
timeline->sequence_lock.unlock(); | |||
return 1; | |||
} | |||
@@ -340,8 +340,12 @@ Audio_Region::write ( nframes_t nframes ) | |||
} | |||
} | |||
timeline->sequence_lock.wrlock(); | |||
_range.length += nframes; | |||
timeline->sequence_lock.unlock(); | |||
return nframes; | |||
} | |||
@@ -354,8 +358,12 @@ Audio_Region::finalize ( nframes_t frame ) | |||
DMESSAGE( "finalizing capture region" ); | |||
timeline->sequence_lock.wrlock(); | |||
_range.length = frame - _range.start; | |||
timeline->sequence_lock.unlock(); | |||
_clip->close(); | |||
_clip->open(); | |||
@@ -172,40 +172,55 @@ Engine::process ( nframes_t nframes ) | |||
transport->poll(); | |||
if ( freewheeling() ) | |||
if ( !timeline) | |||
/* handle chicken/egg problem */ | |||
return 0; | |||
nframes_t n = 0; | |||
n += timeline->process_input(nframes); | |||
n += timeline->process_output(nframes); | |||
if ( n != nframes * 2 ) | |||
{ | |||
if ( timeline ) | |||
{ | |||
timeline->rdlock(); | |||
timeline->process( nframes ); | |||
timeline->unlock(); | |||
} | |||
} | |||
else | |||
{ | |||
if ( !timeline) | |||
/* handle chicken/egg problem */ | |||
return 0; | |||
if ( timeline->tryrdlock() ) | |||
{ | |||
/* the data structures we need to access here (tracks and | |||
* their ports, but not track contents) may be in an | |||
* inconsistent state at the moment. Just punt and drop this | |||
* buffer. */ | |||
++_buffers_dropped; | |||
return 0; | |||
} | |||
/* this will initiate the process() call graph for the various | |||
* number and types of tracks, which will in turn send data out | |||
* the appropriate ports. */ | |||
timeline->process( nframes ); | |||
timeline->unlock(); | |||
_buffers_dropped++; | |||
WARNING("xrun"); | |||
} | |||
/* if ( freewheeling() ) */ | |||
/* { */ | |||
/* if ( timeline ) */ | |||
/* { */ | |||
/* timeline->rdlock(); */ | |||
/* timeline->process( nframes ); */ | |||
/* timeline->unlock(); */ | |||
/* } */ | |||
/* } */ | |||
/* else */ | |||
/* { */ | |||
/* if ( !timeline) */ | |||
/* /\* handle chicken/egg problem *\/ */ | |||
/* return 0; */ | |||
/* if ( timeline->tryrdlock() ) */ | |||
/* { */ | |||
/* /\* the data structures we need to access here (tracks and */ | |||
/* * their ports, but not track contents) may be in an */ | |||
/* * inconsistent state at the moment. Just punt and drop this */ | |||
/* * buffer. *\/ */ | |||
/* ++_buffers_dropped; */ | |||
/* return 0; */ | |||
/* } */ | |||
/* /\* this will initiate the process() call graph for the various */ | |||
/* * number and types of tracks, which will in turn send data out */ | |||
/* * the appropriate ports. *\/ */ | |||
/* timeline->process( nframes ); */ | |||
/* timeline->unlock(); */ | |||
/* } */ | |||
return 0; | |||
} | |||
@@ -84,7 +84,7 @@ Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) | |||
if ( !timeline ) | |||
return; | |||
while ( timeline->tryrdlock() ) | |||
while ( timeline->sequence_lock.tryrdlock() ) | |||
{ | |||
if ( _terminate ) | |||
return; | |||
@@ -100,7 +100,7 @@ Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) | |||
_frame += nframes; | |||
} | |||
timeline->unlock(); | |||
timeline->sequence_lock.unlock(); | |||
} | |||
void | |||
@@ -60,10 +60,14 @@ Record_DS::write_block ( sample_t *buf, nframes_t nframes ) | |||
if ( ! ( timeline && sequence() ) ) | |||
return; | |||
if ( ! _capture ) | |||
{ | |||
_capture = new Track::Capture; | |||
if ( ! _capture->audio_file ) | |||
/* if ( ! _capture->audio_file ) */ | |||
/* create the file */ | |||
track()->record( _capture, _frame ); | |||
} | |||
track()->write( _capture, buf, nframes ); | |||
@@ -79,83 +83,162 @@ Record_DS::disk_thread ( void ) | |||
const nframes_t nframes = _nframes; | |||
_disk_io_blocks = 1; | |||
/* 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 ); | |||
const size_t block_size = nframes * sizeof( sample_t ); | |||
// const size_t block_size = nframes * sizeof( sample_t ); | |||
nframes_t frames_read = 0; | |||
nframes_t blocks_read = 0; | |||
bool punching_in = false; | |||
bool punching_out = false; | |||
bool punched_in = false; | |||
nframes_t bS = 0; | |||
nframes_t bE = 0; | |||
again: | |||
_capture = NULL; | |||
punched_in = false; | |||
punching_out = false; | |||
nframes_t pS = _frame; | |||
nframes_t pE = _stop_frame; | |||
if ( punching_in ) | |||
{ | |||
/* write remainder of buffer */ | |||
write_block( buf + ((pS - bS) * channels()), | |||
bE - pS ); | |||
punching_in = false; | |||
punched_in = true; | |||
} | |||
while ( wait_for_block() ) | |||
{ | |||
/* pull data from the per-channel ringbuffers and interlace it */ | |||
for ( int i = channels(); i--; ) | |||
size_t frames_to_read = nframes; | |||
/* we read the entire block if a partial... */ | |||
for ( int i = 0; i < channels(); i++ ) | |||
{ | |||
while ( jack_ringbuffer_read_space( _rb[ i ] ) < block_size ) | |||
while ( jack_ringbuffer_read_space( _rb[ i ] ) < frames_to_read * sizeof( sample_t ) ) | |||
usleep( 10 * 1000 ); | |||
jack_ringbuffer_read( _rb[ i ], ((char*)cbuf), block_size ); | |||
buffer_interleave_one_channel( buf + ( blocks_read * nframes * channels() ), | |||
jack_ringbuffer_read( _rb[ i ], ((char*)cbuf), frames_to_read * sizeof( sample_t ) ); | |||
buffer_interleave_one_channel( buf, | |||
cbuf, | |||
i, | |||
channels(), | |||
nframes ); | |||
frames_to_read); | |||
} | |||
bS = _first_frame + frames_read; | |||
blocks_read++; | |||
frames_read += frames_to_read; | |||
if ( blocks_read == _disk_io_blocks ) | |||
bE = _first_frame + frames_read; | |||
punching_in = ! punched_in && bE > pS; | |||
punching_out = punched_in && pE < bE; | |||
if ( punching_out ) | |||
{ | |||
write_block( buf, nframes * _disk_io_blocks ); | |||
blocks_read = 0; | |||
write_block( buf, | |||
pE - bS ); | |||
break; | |||
} | |||
else | |||
if ( punching_in ) | |||
{ | |||
write_block( buf + ((pS - bS) * channels()), | |||
bE - pS ); | |||
punching_in = false; | |||
punched_in = true; | |||
} | |||
else if ( punched_in ) | |||
{ | |||
write_block( buf, bE - bS ); | |||
} | |||
} | |||
DMESSAGE( "capture thread terminating" ); | |||
// DMESSAGE( "capture thread terminating" ); | |||
/* flush what remains in the buffer out to disk */ | |||
/* { */ | |||
/* while ( blocks_read-- > 0 || ( ! sem_trywait( &_blocks ) && errno != EAGAIN ) ) */ | |||
/* { */ | |||
/* for ( int i = channels(); i--; ) */ | |||
/* { */ | |||
/* jack_ringbuffer_read( _rb[ i ], (char*)cbuf, block_size ); */ | |||
/* buffer_interleave_one_channel( buf, cbuf, i, channels(), nframes ); */ | |||
/* } */ | |||
/* const nframes_t frames_remaining = (_stop_frame - _frame ) - _frames_written; */ | |||
/* if ( frames_remaining < nframes ) */ | |||
/* { */ | |||
/* /\* this is the last block, might be partial *\/ */ | |||
/* write_block( buf, frames_remaining ); */ | |||
/* break; */ | |||
/* } */ | |||
/* else */ | |||
/* write_block( buf, nframes ); */ | |||
/* } */ | |||
/* } */ | |||
if ( _capture ) | |||
{ | |||
while ( blocks_read-- > 0 || ( ! sem_trywait( &_blocks ) && errno != EAGAIN ) ) | |||
DMESSAGE( "finalzing capture" ); | |||
Track::Capture *c = _capture; | |||
_capture = NULL; | |||
/* now finalize the recording */ | |||
// if ( c->audio_file ) | |||
track()->finalize( c, _stop_frame ); | |||
delete c; | |||
} | |||
if ( ! _terminate ) | |||
{ | |||
nframes_t in, out; | |||
if ( timeline->next_punch( _stop_frame, &in, &out ) ) | |||
{ | |||
for ( int i = channels(); i--; ) | |||
{ | |||
jack_ringbuffer_read( _rb[ i ], (char*)cbuf, block_size ); | |||
_frame = in; | |||
_stop_frame = out; | |||
_frames_written = 0; | |||
buffer_interleave_one_channel( buf, cbuf, i, channels(), nframes ); | |||
} | |||
punched_in = false; | |||
punching_out = false; | |||
punching_in = bE > in; | |||
const nframes_t frames_remaining = (_stop_frame - _frame ) - _frames_written; | |||
DMESSAGE( "Next punch: %lu:%lu", (unsigned long)in,(unsigned long)out ); | |||
if ( frames_remaining < nframes ) | |||
{ | |||
/* this is the last block, might be partial */ | |||
write_block( buf, frames_remaining ); | |||
break; | |||
} | |||
else | |||
write_block( buf, nframes ); | |||
goto again; | |||
} | |||
} | |||
free(buf); | |||
free(cbuf); | |||
DMESSAGE( "finalzing capture" ); | |||
Track::Capture *c = _capture; | |||
_capture = NULL; | |||
/* now finalize the recording */ | |||
if ( c->audio_file ) | |||
track()->finalize( c, _stop_frame ); | |||
delete c; | |||
flush(); | |||
_terminate = false; | |||
@@ -168,7 +251,7 @@ Record_DS::disk_thread ( void ) | |||
/** begin recording */ | |||
void | |||
Record_DS::start ( nframes_t frame ) | |||
Record_DS::start ( nframes_t frame, nframes_t start_frame, nframes_t stop_frame ) | |||
{ | |||
THREAD_ASSERT( UI ); | |||
@@ -183,9 +266,10 @@ Record_DS::start ( nframes_t frame ) | |||
DMESSAGE( "recording started at frame %lu", (unsigned long)frame); | |||
_frame = frame; | |||
_frame = start_frame; | |||
_stop_frame = stop_frame; | |||
_capture = new Track::Capture; | |||
_first_frame = frame; | |||
run(); | |||
@@ -227,40 +311,36 @@ Record_DS::process ( nframes_t nframes ) | |||
if ( ! _recording ) | |||
return 0; | |||
if ( transport->frame < _frame ) | |||
return 0; | |||
/* if ( transport->frame < _frame ) */ | |||
/* return 0; */ | |||
/* DMESSAGE( "recording actually happening at %lu (start frame %lu)", (unsigned long)transport->frame, (unsigned long)_frame); */ | |||
nframes_t offset = 0; | |||
if ( _frame > transport->frame && | |||
_frame < transport->frame + nframes ) | |||
{ | |||
/* The record start frame falls somewhere within the current | |||
buffer. We must discard the unneeded portion and only | |||
stuff the part requested into the ringbuffer. */ | |||
/* if ( _frame > transport->frame && */ | |||
/* _frame < transport->frame + nframes ) */ | |||
/* { */ | |||
/* /\* The record start frame falls somewhere within the current */ | |||
/* buffer. We must discard the unneeded portion and only */ | |||
/* stuff the part requested into the ringbuffer. *\/ */ | |||
offset = _frame - transport->frame; | |||
/* offset = _frame - transport->frame; */ | |||
/* DMESSAGE( "offset = %lu", (unsigned long)offset ); */ | |||
} | |||
/* /\* DMESSAGE( "offset = %lu", (unsigned long)offset ); *\/ */ | |||
/* } */ | |||
const size_t offset_size = offset * sizeof( sample_t ); | |||
const size_t block_size = ( nframes * sizeof( sample_t ) ) - offset_size; | |||
for ( int i = channels(); i--; ) | |||
for ( int i = 0; i < channels(); i++ ) | |||
{ | |||
/* read the entire input buffer */ | |||
void *buf = track()->input[ i ].buffer( nframes ); | |||
/* FIXME: this results in a ringbuffer size that is no longer | |||
necessarily a multiple of nframes... how will the other side | |||
handle that? */ | |||
if ( engine->freewheeling() ) | |||
{ | |||
while ( jack_ringbuffer_write_space( _rb[i] ) < block_size ) | |||
while ( transport->recording && jack_ringbuffer_write_space( _rb[i] ) < block_size ) | |||
usleep( 10 * 1000 ); | |||
jack_ringbuffer_write( _rb[ i ], ((char*)buf) + offset_size, block_size ); | |||
@@ -271,12 +351,14 @@ Record_DS::process ( nframes_t nframes ) | |||
{ | |||
memset( buf, 0, block_size ); | |||
/* FIXME: we need to resync somehow */ | |||
WARNING( "xrun" ); | |||
++_xruns; | |||
} | |||
else | |||
{ | |||
jack_ringbuffer_write( _rb[ i ], ((char*)buf) + offset_size, block_size ); | |||
} | |||
jack_ringbuffer_write( _rb[ i ], ((char*)buf) + offset_size, block_size ); | |||
// DMESSAGE( "wrote %lu", (unsigned long) nframes ); | |||
} | |||
} | |||
@@ -36,7 +36,8 @@ class Record_DS : public Disk_Stream | |||
nframes_t _frames_written; | |||
volatile nframes_t _stop_frame; | |||
volatile nframes_t _first_frame; | |||
volatile bool _recording; | |||
Audio_File_SF *_af; /* capture file */ | |||
@@ -56,8 +57,9 @@ public: | |||
_capture = NULL; | |||
_recording = false; | |||
_stop_frame = -1; | |||
_stop_frame = 0; | |||
_frames_written = 0; | |||
_first_frame = 0; | |||
} | |||
virtual ~Record_DS ( ) { shutdown(); } | |||
@@ -67,7 +69,7 @@ public: | |||
const Audio_Region * capture_region ( void ) const; | |||
Track::Capture * capture ( void ); | |||
void start ( nframes_t frame ); | |||
void start ( nframes_t frame, nframes_t start_frame, nframes_t stop_frame = 0 ); | |||
void stop ( nframes_t frame ); | |||
nframes_t process ( nframes_t nframes ); | |||
@@ -28,6 +28,8 @@ | |||
#include "Thread.H" | |||
#include "../Cursor_Sequence.H" | |||
#include "Engine.H" | |||
#include <unistd.h> | |||
/** Initiate recording for all armed tracks */ | |||
@@ -48,14 +50,15 @@ Timeline::record ( void ) | |||
_created_new_takes = true; | |||
} | |||
transport->recording = true; | |||
deactivate(); | |||
Loggable::block_start(); | |||
nframes_t frame = transport->frame; | |||
nframes_t _punch_out_frame = 0; | |||
// nframes_t _punch_in_frame = 0; | |||
if ( transport->punch_enabled() ) | |||
{ | |||
DMESSAGE( "Finding next punch region following frame %lu...", (unsigned long)frame); | |||
@@ -65,7 +68,7 @@ Timeline::record ( void ) | |||
if (p || n ) | |||
{ | |||
if ( p && frame > p->start() && frame < p->start() + p->length() ) | |||
if ( p && frame >= p->start() && frame < p->start() + p->length() ) | |||
{ | |||
/* recording started in the middle of a punch | |||
* cursor... Just start recording and punch out at the | |||
@@ -86,33 +89,23 @@ Timeline::record ( void ) | |||
} | |||
} | |||
_punch_in_frame = frame; | |||
// _punch_in_frame = frame; | |||
punch_in( frame ); | |||
return true; | |||
} | |||
void | |||
Timeline::punch_in ( nframes_t frame ) | |||
{ | |||
if ( _punched_in ) | |||
{ | |||
WARNING( "Programming error. Attempt to punch in twice" ); | |||
return; | |||
} | |||
DMESSAGE( "Going to record starting at frame %lu", (unsigned long)frame ); | |||
for ( int i = tracks->children(); i-- ; ) | |||
{ | |||
Track *t = (Track*)tracks->child( i ); | |||
if ( t->armed() && t->record_ds ) | |||
t->record_ds->start( frame ); | |||
{ | |||
t->record_ds->start( transport->frame, frame, _punch_out_frame ); | |||
} | |||
} | |||
_punched_in = true; | |||
transport->recording = true; | |||
return true; | |||
} | |||
void | |||
@@ -142,10 +135,6 @@ Timeline::punch_out ( nframes_t frame ) | |||
} | |||
DMESSAGE( "All record threads stopped." ); | |||
_punched_in = false; | |||
_punch_in_frame = 0; | |||
_punch_out_frame = 0; | |||
} | |||
/** stop recording for all armed tracks. Does not affect transport. */ | |||
@@ -156,13 +145,13 @@ Timeline::stop ( void ) | |||
nframes_t frame = transport->frame; | |||
if ( transport->punch_enabled() ) | |||
{ | |||
const Sequence_Widget *w = punch_cursor_track->prev( frame ); | |||
/* if ( transport->punch_enabled() ) */ | |||
/* { */ | |||
/* const Sequence_Widget *w = punch_cursor_track->prev( frame ); */ | |||
if ( w && w->start() + w->length() < frame ) | |||
frame = w->start() + w->length(); | |||
} | |||
/* if ( w && w->start() + w->length() < frame ) */ | |||
/* frame = w->start() + w->length(); */ | |||
/* } */ | |||
punch_out( frame ); | |||
@@ -178,30 +167,72 @@ Timeline::stop ( void ) | |||
/* Engine */ | |||
/**********/ | |||
/** call process() on each track header */ | |||
nframes_t | |||
Timeline::process ( nframes_t nframes ) | |||
Timeline::process_input ( nframes_t nframes ) | |||
{ | |||
/* there is no need to acquire a readlock here because track * | |||
addition/removal locks process() and track process() calls deal with | |||
ringbuffers instead of reading the sequence data directly. */ | |||
/* if ( engine->freewheeling() ) */ | |||
/* { */ | |||
/* rdlock(); */ | |||
/* } */ | |||
/* else */ | |||
/* { */ | |||
/* if ( tryrdlock() ) */ | |||
/* return 0; */ | |||
/* } */ | |||
if ( ! transport->recording ) | |||
return nframes; | |||
/* we don't lock here in order to avoid deadlocks. we already know | |||
* that only the record diskthread will be altering timeline | |||
* structures during recording since the GUI is disabled. */ | |||
for ( int i = tracks->children(); i-- ; ) | |||
{ | |||
Track *t = (Track*)tracks->child( i ); | |||
t->process_output( nframes ); | |||
t->process_input( nframes ); | |||
} | |||
/* unlock(); */ | |||
return nframes; | |||
} | |||
nframes_t | |||
Timeline::process_output ( nframes_t nframes ) | |||
{ | |||
/* if ( engine->freewheeling() ) */ | |||
/* { */ | |||
/* rdlock(); */ | |||
/* } */ | |||
/* else */ | |||
/* { */ | |||
/* if ( tryrdlock() ) */ | |||
/* return 0; */ | |||
/* } */ | |||
bool r = transport->recording || engine->freewheeling(); | |||
if ( ! r ) | |||
if ( track_lock.tryrdlock() ) | |||
return 0; | |||
// rdlock(); | |||
for ( int i = tracks->children(); i-- ; ) | |||
{ | |||
Track *t = (Track*)tracks->child( i ); | |||
t->process_input( nframes ); | |||
t->process_output( nframes ); | |||
} | |||
/* FIXME: BOGUS */ | |||
if ( ! r ) | |||
track_lock.unlock(); | |||
return nframes; | |||
} | |||
void | |||
@@ -261,13 +261,15 @@ Track::resize_buffers ( nframes_t nframes ) | |||
#include <time.h> | |||
static unsigned long uuid_counter = 0; | |||
/** very cheap UUID generator... */ | |||
unsigned long long | |||
uuid ( void ) | |||
{ | |||
time_t t = time( NULL ); | |||
return (unsigned long long) t; | |||
return (unsigned long long) t + uuid_counter++; | |||
} | |||
/** create capture region and prepare to record */ | |||
@@ -294,11 +296,12 @@ Track::record ( Capture *c, nframes_t frame ) | |||
/* must acquire a write lock because the Audio_Region constructor | |||
* will add the region to the specified sequence, which might affect playback */ | |||
timeline->wrlock(); | |||
timeline->sequence_lock.wrlock(); | |||
c->region = new Audio_Region( c->audio_file, sequence(), frame ); | |||
timeline->unlock(); | |||
timeline->sequence_lock.unlock(); | |||
// Fl::unlock(); | |||
@@ -313,6 +316,10 @@ Track::record ( Capture *c, nframes_t frame ) | |||
/* in freewheeling mode, assume we're bouncing and only | |||
* compensate for capture latency */ | |||
_capture_offset = max; | |||
/* FIXME: hack to compensate for jack 1 period delay when looped back */ | |||
// _capture_offset += engine->playback_latency() / 2; | |||
_capture_offset += engine->nframes(); | |||
} | |||
else | |||
{ | |||
@@ -353,17 +360,18 @@ Track::finalize ( Capture *c, nframes_t frame ) | |||
DMESSAGE( "finalizing audio file" ); | |||
c->audio_file->finalize(); | |||
timeline->wrlock(); | |||
DMESSAGE( "Adjusting capture by %lu frames.", (unsigned long)_capture_offset ); | |||
timeline->sequence_lock.wrlock(); | |||
c->region->offset( _capture_offset ); | |||
timeline->sequence_lock.unlock(); | |||
_capture_offset = 0; | |||
/* have to do this last, as it logs the create */ | |||
c->region->finalize( frame ); | |||
timeline->unlock(); | |||
} | |||
void | |||
@@ -327,7 +327,7 @@ Sequence::handle ( int m ) | |||
case FL_SHORTCUT: | |||
if ( Fl::test_shortcut( FL_CTRL + FL_Right ) ) | |||
{ | |||
const Sequence_Widget *w = next( transport->frame ); | |||
const Sequence_Widget *w = next( transport->frame + 1 ); | |||
if ( w ) | |||
transport->locate( w->start() ); | |||
@@ -399,9 +399,9 @@ Sequence::handle ( int m ) | |||
{ | |||
/* accept objects dragged from other sequences of this type */ | |||
timeline->wrlock(); | |||
timeline->sequence_lock.wrlock(); | |||
add( Sequence_Widget::pushed() ); | |||
timeline->unlock(); | |||
timeline->sequence_lock.unlock(); | |||
damage( FL_DAMAGE_USER1 ); | |||
@@ -503,9 +503,9 @@ Sequence::handle ( int m ) | |||
Sequence_Widget::belowmouse( NULL ); | |||
} | |||
timeline->wrlock(); | |||
timeline->sequence_lock.wrlock(); | |||
delete t; | |||
timeline->unlock(); | |||
timeline->sequence_lock.unlock(); | |||
} | |||
Loggable::block_end(); | |||
@@ -557,8 +557,8 @@ const Sequence_Widget * | |||
Sequence::next ( nframes_t from ) const | |||
{ | |||
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 ) | |||
// if ( (*i)->start() > from ) | |||
return *i; | |||
if ( _widgets.size() ) | |||
@@ -172,7 +172,7 @@ Sequence_Widget::begin_drag ( const Drag &d ) | |||
{ | |||
_drag = new Drag( d ); | |||
timeline->wrlock(); | |||
timeline->sequence_lock.wrlock(); | |||
/* copy current values */ | |||
@@ -181,7 +181,7 @@ Sequence_Widget::begin_drag ( const Drag &d ) | |||
/* tell display to use temporary */ | |||
_r = &_dragging_range; | |||
timeline->unlock(); | |||
timeline->sequence_lock.unlock(); | |||
} | |||
void | |||
@@ -190,7 +190,7 @@ Sequence_Widget::end_drag ( void ) | |||
/* swap in the new value */ | |||
/* go back to playback and display using same values */ | |||
timeline->wrlock(); | |||
timeline->sequence_lock.wrlock(); | |||
_range = _dragging_range; | |||
_r = &_range; | |||
@@ -201,7 +201,7 @@ Sequence_Widget::end_drag ( void ) | |||
/* this will result in a sort */ | |||
sequence()->handle_widget_change( _r->start, _r->length ); | |||
timeline->unlock(); | |||
timeline->sequence_lock.unlock(); | |||
} | |||
/** set position of widget on the timeline. */ | |||
@@ -449,9 +449,9 @@ Sequence_Widget::handle ( int m ) | |||
if ( test_press( FL_BUTTON1 + FL_CTRL ) && ! _drag->state ) | |||
{ | |||
/* duplication */ | |||
timeline->wrlock(); | |||
timeline->sequence_lock.wrlock(); | |||
sequence()->add( this->clone() ); | |||
timeline->unlock(); | |||
timeline->sequence_lock.unlock(); | |||
_drag->state = 1; | |||
return 1; | |||
@@ -62,11 +62,11 @@ Tempo_Sequence::handle ( int m ) | |||
if ( Tempo_Point::edit( &t ) ) | |||
{ | |||
timeline->wrlock(); | |||
timeline->sequence_lock.wrlock(); | |||
new Tempo_Point( timeline->x_to_offset( Fl::event_x() ), t ); | |||
timeline->unlock(); | |||
timeline->sequence_lock.unlock(); | |||
timeline->redraw(); | |||
} | |||
@@ -65,11 +65,11 @@ Time_Sequence::handle ( int m ) | |||
if ( Time_Point::edit( &t ) ) | |||
{ | |||
timeline->wrlock(); | |||
timeline->sequence_lock.wrlock(); | |||
new Time_Point( timeline->x_to_offset( Fl::event_x() ), t.beats_per_bar, t.beat_type ); | |||
timeline->unlock(); | |||
timeline->sequence_lock.unlock(); | |||
timeline->redraw(); | |||
} | |||
@@ -360,7 +360,7 @@ Timeline::add_take_for_armed_tracks ( void ) | |||
{ | |||
THREAD_ASSERT( UI ); | |||
wrlock(); | |||
track_lock.wrlock(); | |||
for ( int i = tracks->children(); i-- ; ) | |||
{ | |||
@@ -370,7 +370,7 @@ Timeline::add_take_for_armed_tracks ( void ) | |||
t->sequence( new Audio_Sequence( t ) ); | |||
} | |||
unlock(); | |||
track_lock.unlock(); | |||
} | |||
void | |||
@@ -598,9 +598,6 @@ Timeline::Timeline ( int X, int Y, int W, int H, const char* L ) : BASE( X, Y, W | |||
play_cursor_track = NULL; | |||
_created_new_takes = 0; | |||
_punched_in = 0; | |||
_punch_in_frame = 0; | |||
_punch_out_frame = 0; | |||
osc_thread = 0; | |||
_sample_rate = 44100; | |||
@@ -1271,6 +1268,8 @@ Timeline::draw ( void ) | |||
* another thread must use Fl::lock()/unlock()! */ | |||
THREAD_ASSERT( UI ); | |||
// rdlock(); | |||
int X, Y, W, H; | |||
int bdx = 0; | |||
@@ -1368,6 +1367,8 @@ Timeline::draw ( void ) | |||
done: | |||
// unlock(); | |||
/* panzoomer->redraw(); */ | |||
// update_child( *panzoomer ); | |||
@@ -1416,6 +1417,26 @@ Timeline::draw_cursor ( nframes_t frame, Fl_Color color, void (*symbol)(Fl_Color | |||
fl_pop_clip(); | |||
} | |||
/** set /in/ and /out/ to start and end of next punch region from /frame/ */ | |||
bool | |||
Timeline::next_punch ( nframes_t frame, nframes_t *in, nframes_t *out ) const | |||
{ | |||
if ( !transport->punch_enabled() ) | |||
return false; | |||
const Sequence_Widget *w = punch_cursor_track->next( frame ); | |||
if ( w && w->start() >= frame ) | |||
{ | |||
*in = w->start(); | |||
*out = w->start() + w->length(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
void | |||
Timeline::draw_playhead ( void ) | |||
{ | |||
@@ -1429,36 +1450,6 @@ Timeline::redraw_playhead ( void ) | |||
// static nframes_t last_playhead = -1; | |||
static int last_playhead_x = -1; | |||
/* FIXME: kind of a hackish way to invoke punch / looping stuff from the UI thread... */ | |||
if ( transport->rolling && | |||
transport->rec_enabled() && | |||
transport->punch_enabled() ) | |||
{ | |||
if ( _punched_in && | |||
transport->frame > _punch_in_frame && | |||
transport->frame > _punch_out_frame ) | |||
{ | |||
punch_out( _punch_out_frame ); | |||
} | |||
else if ( ! _punched_in ) | |||
{ | |||
/* 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 ); | |||
DMESSAGE( "Delayed punch in" ); | |||
if ( w && | |||
w->start() > transport->frame ) | |||
{ | |||
_punch_in_frame = w->start(); | |||
_punch_out_frame = w->start() + w->length(); | |||
punch_in( w->start() ); | |||
} | |||
} | |||
} | |||
if ( transport->rolling ) | |||
{ | |||
if ( play_cursor_track->active_cursor() ) | |||
@@ -1987,36 +1978,36 @@ Timeline::remove_track ( Track *track ) | |||
void | |||
Timeline::command_move_track_up ( Track *track ) | |||
{ | |||
wrlock(); | |||
track_lock.wrlock(); | |||
insert_track( track, find_track( track ) - 1 ); | |||
unlock(); | |||
track_lock.unlock(); | |||
} | |||
void | |||
Timeline::command_move_track_down ( Track *track ) | |||
{ | |||
wrlock(); | |||
track_lock.wrlock(); | |||
insert_track( track, find_track( track ) + 2 ); | |||
unlock(); | |||
track_lock.unlock(); | |||
} | |||
void | |||
Timeline::command_remove_track ( Track *track ) | |||
{ | |||
wrlock(); | |||
track_lock.wrlock(); | |||
remove_track(track); | |||
unlock(); | |||
track_lock.unlock(); | |||
} | |||
void | |||
Timeline::command_quit ( void ) | |||
{ | |||
timeline->wrlock(); | |||
track_lock.wrlock(); | |||
Project::close(); | |||
timeline->unlock(); | |||
track_lock.unlock(); | |||
command_save(); | |||
while ( Fl::first_window() ) Fl::first_window()->hide(); | |||
@@ -2025,9 +2016,10 @@ Timeline::command_quit ( void ) | |||
void | |||
Timeline::command_undo ( void ) | |||
{ | |||
wrlock(); | |||
/* FIXME: sequence lock too? */ | |||
track_lock.wrlock(); | |||
Project::undo(); | |||
unlock(); | |||
track_lock.unlock(); | |||
} | |||
bool | |||
@@ -2036,9 +2028,9 @@ Timeline::command_load ( const char *name, const char *display_name ) | |||
if ( ! name ) | |||
return false; | |||
wrlock(); | |||
track_lock.wrlock(); | |||
int r = Project::open( name ); | |||
unlock(); | |||
track_lock.unlock(); | |||
if ( r < 0 ) | |||
{ | |||
@@ -77,9 +77,8 @@ struct Rectangle | |||
Rectangle ( int X, int Y, int W, int H ) : x( X ), y( Y ), w( W ), h( H ) {} | |||
}; | |||
class Timeline : public Fl_Group, public RWLock | |||
class Timeline : public Fl_Group | |||
{ | |||
class Timeline_Panzoomer; | |||
static void draw_clip_rulers ( void * v, int X, int Y, int W, int H ); | |||
@@ -125,6 +124,9 @@ class Timeline : public Fl_Group, public RWLock | |||
public: | |||
RWLock track_lock; /* tracks/sequences */ | |||
RWLock sequence_lock; /* sequence contents */ | |||
void redraw_overlay ( void ); | |||
void insert_track ( Track *track, Track *before ); | |||
@@ -166,9 +168,6 @@ public: | |||
Fl_Menu_Button *menu; | |||
int _punched_in; | |||
nframes_t _punch_out_frame; | |||
nframes_t _punch_in_frame; | |||
bool _created_new_takes; | |||
nframes_t xoffset; | |||
@@ -257,6 +256,7 @@ public: | |||
void zoom_out ( void ); | |||
void zoom_fit ( void ); | |||
bool next_punch ( nframes_t frame, nframes_t *in, nframes_t *out ) const; | |||
/* Engine */ | |||
int total_input_buffer_percent ( void ); | |||
@@ -267,7 +267,6 @@ public: | |||
bool record ( void ); | |||
void stop ( void ); | |||
void punch_in ( nframes_t frame ); | |||
void punch_out ( nframes_t frame ); | |||
void wait_for_buffers ( void ); | |||
@@ -315,6 +314,7 @@ private: | |||
/* Engine */ | |||
void resize_buffers ( nframes_t nframes ); | |||
nframes_t process ( nframes_t nframes ); | |||
nframes_t process_input ( nframes_t nframes ); | |||
nframes_t process_output ( nframes_t nframes ); | |||
void seek ( nframes_t frame ); | |||
}; |
@@ -737,10 +737,10 @@ void | |||
Track::command_configure_channels ( int n ) | |||
{ | |||
/* due to locking this should only be invoked by direct user action */ | |||
timeline->wrlock(); | |||
timeline->track_lock.wrlock(); | |||
configure_inputs( n ); | |||
configure_outputs( n ); | |||
timeline->unlock(); | |||
timeline->track_lock.unlock(); | |||
} | |||
void | |||
@@ -787,9 +787,9 @@ Track::menu_cb ( const Fl_Menu_ *m ) | |||
/* add audio track */ | |||
char *name = get_unique_control_name( "Control" ); | |||
timeline->wrlock(); | |||
timeline->track_lock.wrlock(); | |||
new Control_Sequence( this, name ); | |||
timeline->unlock(); | |||
timeline->track_lock.unlock(); | |||
} | |||
else if ( ! strcmp( picked, "/Overlay controls" ) ) | |||
{ | |||
@@ -868,9 +868,9 @@ Track::menu_cb ( const Fl_Menu_ *m ) | |||
} | |||
else if ( !strcmp( picked, "Takes/New" ) ) | |||
{ | |||
timeline->wrlock(); | |||
timeline->track_lock.wrlock(); | |||
sequence( (Audio_Sequence*)sequence()->clone_empty() ); | |||
timeline->unlock(); | |||
timeline->track_lock.unlock(); | |||
} | |||
else if ( !strcmp( picked, "Takes/Remove" ) ) | |||
{ | |||
@@ -878,7 +878,7 @@ Track::menu_cb ( const Fl_Menu_ *m ) | |||
{ | |||
Loggable::block_start(); | |||
timeline->wrlock(); | |||
timeline->track_lock.wrlock(); | |||
Audio_Sequence *s = sequence(); | |||
@@ -886,7 +886,7 @@ Track::menu_cb ( const Fl_Menu_ *m ) | |||
delete s; | |||
timeline->unlock(); | |||
timeline->track_lock.unlock(); | |||
Loggable::block_end(); | |||
} | |||
@@ -906,9 +906,9 @@ Track::menu_cb ( const Fl_Menu_ *m ) | |||
{ | |||
Audio_Sequence* s = (Audio_Sequence*)m->mvalue()->user_data(); | |||
timeline->wrlock(); | |||
timeline->track_lock.wrlock(); | |||
sequence( s ); | |||
timeline->unlock(); | |||
timeline->track_lock.unlock(); | |||
} | |||
} | |||
@@ -27,6 +27,7 @@ | |||
#include <algorithm> | |||
using std::min; | |||
using std::max; | |||
#include <dsp.h> | |||