@@ -127,6 +127,6 @@ Thread::join ( void ) | |||||
void | void | ||||
Thread::exit ( void *retval ) | Thread::exit ( void *retval ) | ||||
{ | { | ||||
pthread_exit( retval ); | |||||
_thread = 0; | _thread = 0; | ||||
pthread_exit( retval ); | |||||
} | } |
@@ -651,7 +651,7 @@ Audio_Region::draw ( void ) | |||||
if ( peaks < loop_peaks_needed ) | 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; | fo += loop_frames_needed; | ||||
@@ -783,11 +783,11 @@ Control_Sequence::handle ( int m ) | |||||
test_press( FL_BUTTON1 ) ) | test_press( FL_BUTTON1 ) ) | ||||
{ | { | ||||
/* insert new control point */ | /* 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() ); | 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; | return 1; | ||||
} | } | ||||
@@ -340,8 +340,12 @@ Audio_Region::write ( nframes_t nframes ) | |||||
} | } | ||||
} | } | ||||
timeline->sequence_lock.wrlock(); | |||||
_range.length += nframes; | _range.length += nframes; | ||||
timeline->sequence_lock.unlock(); | |||||
return nframes; | return nframes; | ||||
} | } | ||||
@@ -354,8 +358,12 @@ Audio_Region::finalize ( nframes_t frame ) | |||||
DMESSAGE( "finalizing capture region" ); | DMESSAGE( "finalizing capture region" ); | ||||
timeline->sequence_lock.wrlock(); | |||||
_range.length = frame - _range.start; | _range.length = frame - _range.start; | ||||
timeline->sequence_lock.unlock(); | |||||
_clip->close(); | _clip->close(); | ||||
_clip->open(); | _clip->open(); | ||||
@@ -172,40 +172,55 @@ Engine::process ( nframes_t nframes ) | |||||
transport->poll(); | 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; | return 0; | ||||
} | } | ||||
@@ -84,7 +84,7 @@ Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) | |||||
if ( !timeline ) | if ( !timeline ) | ||||
return; | return; | ||||
while ( timeline->tryrdlock() ) | |||||
while ( timeline->sequence_lock.tryrdlock() ) | |||||
{ | { | ||||
if ( _terminate ) | if ( _terminate ) | ||||
return; | return; | ||||
@@ -100,7 +100,7 @@ Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) | |||||
_frame += nframes; | _frame += nframes; | ||||
} | } | ||||
timeline->unlock(); | |||||
timeline->sequence_lock.unlock(); | |||||
} | } | ||||
void | void | ||||
@@ -60,10 +60,14 @@ Record_DS::write_block ( sample_t *buf, nframes_t nframes ) | |||||
if ( ! ( timeline && sequence() ) ) | if ( ! ( timeline && sequence() ) ) | ||||
return; | return; | ||||
if ( ! _capture ) | |||||
{ | |||||
_capture = new Track::Capture; | |||||
if ( ! _capture->audio_file ) | |||||
/* if ( ! _capture->audio_file ) */ | |||||
/* create the file */ | /* create the file */ | ||||
track()->record( _capture, _frame ); | track()->record( _capture, _frame ); | ||||
} | |||||
track()->write( _capture, buf, nframes ); | track()->write( _capture, buf, nframes ); | ||||
@@ -79,83 +83,162 @@ Record_DS::disk_thread ( void ) | |||||
const nframes_t nframes = _nframes; | const nframes_t nframes = _nframes; | ||||
_disk_io_blocks = 1; | |||||
/* buffer to hold the interleaved data returned by the track reader */ | /* buffer to hold the interleaved data returned by the track reader */ | ||||
sample_t *buf = buffer_alloc( nframes * channels() * _disk_io_blocks ); | sample_t *buf = buffer_alloc( nframes * channels() * _disk_io_blocks ); | ||||
sample_t *cbuf = buffer_alloc( nframes ); | 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() ) | while ( wait_for_block() ) | ||||
{ | { | ||||
/* pull data from the per-channel ringbuffers and interlace it */ | /* pull data from the per-channel ringbuffers and interlace it */ | ||||
for ( int i = channels(); i--; ) | |||||
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 ); | 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, | cbuf, | ||||
i, | i, | ||||
channels(), | 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 */ | /* 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(buf); | ||||
free(cbuf); | 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(); | flush(); | ||||
_terminate = false; | _terminate = false; | ||||
@@ -168,7 +251,7 @@ Record_DS::disk_thread ( void ) | |||||
/** begin recording */ | /** begin recording */ | ||||
void | void | ||||
Record_DS::start ( nframes_t frame ) | |||||
Record_DS::start ( nframes_t frame, nframes_t start_frame, nframes_t stop_frame ) | |||||
{ | { | ||||
THREAD_ASSERT( UI ); | THREAD_ASSERT( UI ); | ||||
@@ -183,9 +266,10 @@ Record_DS::start ( nframes_t frame ) | |||||
DMESSAGE( "recording started at frame %lu", (unsigned long)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(); | run(); | ||||
@@ -227,40 +311,36 @@ Record_DS::process ( nframes_t nframes ) | |||||
if ( ! _recording ) | if ( ! _recording ) | ||||
return 0; | 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); */ | /* DMESSAGE( "recording actually happening at %lu (start frame %lu)", (unsigned long)transport->frame, (unsigned long)_frame); */ | ||||
nframes_t offset = 0; | 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 offset_size = offset * sizeof( sample_t ); | ||||
const size_t block_size = ( nframes * sizeof( sample_t ) ) - offset_size; | 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 */ | /* read the entire input buffer */ | ||||
void *buf = track()->input[ i ].buffer( nframes ); | 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() ) | 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 ); | usleep( 10 * 1000 ); | ||||
jack_ringbuffer_write( _rb[ i ], ((char*)buf) + offset_size, block_size ); | 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 ); | memset( buf, 0, block_size ); | ||||
/* FIXME: we need to resync somehow */ | /* FIXME: we need to resync somehow */ | ||||
WARNING( "xrun" ); | |||||
++_xruns; | ++_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; | nframes_t _frames_written; | ||||
volatile nframes_t _stop_frame; | volatile nframes_t _stop_frame; | ||||
volatile nframes_t _first_frame; | |||||
volatile bool _recording; | volatile bool _recording; | ||||
Audio_File_SF *_af; /* capture file */ | Audio_File_SF *_af; /* capture file */ | ||||
@@ -56,8 +57,9 @@ public: | |||||
_capture = NULL; | _capture = NULL; | ||||
_recording = false; | _recording = false; | ||||
_stop_frame = -1; | |||||
_stop_frame = 0; | |||||
_frames_written = 0; | _frames_written = 0; | ||||
_first_frame = 0; | |||||
} | } | ||||
virtual ~Record_DS ( ) { shutdown(); } | virtual ~Record_DS ( ) { shutdown(); } | ||||
@@ -67,7 +69,7 @@ public: | |||||
const Audio_Region * capture_region ( void ) const; | const Audio_Region * capture_region ( void ) const; | ||||
Track::Capture * capture ( void ); | 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 ); | void stop ( nframes_t frame ); | ||||
nframes_t process ( nframes_t nframes ); | nframes_t process ( nframes_t nframes ); | ||||
@@ -28,6 +28,8 @@ | |||||
#include "Thread.H" | #include "Thread.H" | ||||
#include "../Cursor_Sequence.H" | #include "../Cursor_Sequence.H" | ||||
#include "Engine.H" | |||||
#include <unistd.h> | #include <unistd.h> | ||||
/** Initiate recording for all armed tracks */ | /** Initiate recording for all armed tracks */ | ||||
@@ -48,14 +50,15 @@ Timeline::record ( void ) | |||||
_created_new_takes = true; | _created_new_takes = true; | ||||
} | } | ||||
transport->recording = true; | |||||
deactivate(); | deactivate(); | ||||
Loggable::block_start(); | Loggable::block_start(); | ||||
nframes_t frame = transport->frame; | nframes_t frame = transport->frame; | ||||
nframes_t _punch_out_frame = 0; | |||||
// nframes_t _punch_in_frame = 0; | |||||
if ( transport->punch_enabled() ) | if ( transport->punch_enabled() ) | ||||
{ | { | ||||
DMESSAGE( "Finding next punch region following frame %lu...", (unsigned long)frame); | DMESSAGE( "Finding next punch region following frame %lu...", (unsigned long)frame); | ||||
@@ -65,7 +68,7 @@ Timeline::record ( void ) | |||||
if (p || n ) | 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 | /* recording started in the middle of a punch | ||||
* cursor... Just start recording and punch out at the | * 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-- ; ) | for ( int i = tracks->children(); i-- ; ) | ||||
{ | { | ||||
Track *t = (Track*)tracks->child( i ); | Track *t = (Track*)tracks->child( i ); | ||||
if ( t->armed() && t->record_ds ) | 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 | void | ||||
@@ -142,10 +135,6 @@ Timeline::punch_out ( nframes_t frame ) | |||||
} | } | ||||
DMESSAGE( "All record threads stopped." ); | 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. */ | /** stop recording for all armed tracks. Does not affect transport. */ | ||||
@@ -156,13 +145,13 @@ Timeline::stop ( void ) | |||||
nframes_t frame = transport->frame; | 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 ); | punch_out( frame ); | ||||
@@ -178,30 +167,72 @@ Timeline::stop ( void ) | |||||
/* Engine */ | /* Engine */ | ||||
/**********/ | /**********/ | ||||
/** call process() on each track header */ | |||||
nframes_t | 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-- ; ) | for ( int i = tracks->children(); i-- ; ) | ||||
{ | { | ||||
Track *t = (Track*)tracks->child( 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-- ; ) | for ( int i = tracks->children(); i-- ; ) | ||||
{ | { | ||||
Track *t = (Track*)tracks->child( i ); | Track *t = (Track*)tracks->child( i ); | ||||
t->process_input( nframes ); | |||||
t->process_output( nframes ); | |||||
} | } | ||||
/* FIXME: BOGUS */ | |||||
if ( ! r ) | |||||
track_lock.unlock(); | |||||
return nframes; | return nframes; | ||||
} | } | ||||
void | void | ||||
@@ -261,13 +261,15 @@ Track::resize_buffers ( nframes_t nframes ) | |||||
#include <time.h> | #include <time.h> | ||||
static unsigned long uuid_counter = 0; | |||||
/** very cheap UUID generator... */ | /** very cheap UUID generator... */ | ||||
unsigned long long | unsigned long long | ||||
uuid ( void ) | uuid ( void ) | ||||
{ | { | ||||
time_t t = time( NULL ); | time_t t = time( NULL ); | ||||
return (unsigned long long) t; | |||||
return (unsigned long long) t + uuid_counter++; | |||||
} | } | ||||
/** create capture region and prepare to record */ | /** 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 | /* must acquire a write lock because the Audio_Region constructor | ||||
* will add the region to the specified sequence, which might affect playback */ | * 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 ); | c->region = new Audio_Region( c->audio_file, sequence(), frame ); | ||||
timeline->unlock(); | |||||
timeline->sequence_lock.unlock(); | |||||
// Fl::unlock(); | // Fl::unlock(); | ||||
@@ -313,6 +316,10 @@ Track::record ( Capture *c, nframes_t frame ) | |||||
/* in freewheeling mode, assume we're bouncing and only | /* in freewheeling mode, assume we're bouncing and only | ||||
* compensate for capture latency */ | * compensate for capture latency */ | ||||
_capture_offset = max; | _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 | else | ||||
{ | { | ||||
@@ -353,17 +360,18 @@ Track::finalize ( Capture *c, nframes_t frame ) | |||||
DMESSAGE( "finalizing audio file" ); | DMESSAGE( "finalizing audio file" ); | ||||
c->audio_file->finalize(); | c->audio_file->finalize(); | ||||
timeline->wrlock(); | |||||
DMESSAGE( "Adjusting capture by %lu frames.", (unsigned long)_capture_offset ); | DMESSAGE( "Adjusting capture by %lu frames.", (unsigned long)_capture_offset ); | ||||
timeline->sequence_lock.wrlock(); | |||||
c->region->offset( _capture_offset ); | c->region->offset( _capture_offset ); | ||||
timeline->sequence_lock.unlock(); | |||||
_capture_offset = 0; | _capture_offset = 0; | ||||
/* have to do this last, as it logs the create */ | /* have to do this last, as it logs the create */ | ||||
c->region->finalize( frame ); | c->region->finalize( frame ); | ||||
timeline->unlock(); | |||||
} | } | ||||
void | void | ||||
@@ -327,7 +327,7 @@ Sequence::handle ( int m ) | |||||
case FL_SHORTCUT: | case FL_SHORTCUT: | ||||
if ( Fl::test_shortcut( FL_CTRL + FL_Right ) ) | 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 ) | if ( w ) | ||||
transport->locate( w->start() ); | transport->locate( w->start() ); | ||||
@@ -399,9 +399,9 @@ Sequence::handle ( int m ) | |||||
{ | { | ||||
/* accept objects dragged from other sequences of this type */ | /* accept objects dragged from other sequences of this type */ | ||||
timeline->wrlock(); | |||||
timeline->sequence_lock.wrlock(); | |||||
add( Sequence_Widget::pushed() ); | add( Sequence_Widget::pushed() ); | ||||
timeline->unlock(); | |||||
timeline->sequence_lock.unlock(); | |||||
damage( FL_DAMAGE_USER1 ); | damage( FL_DAMAGE_USER1 ); | ||||
@@ -503,9 +503,9 @@ Sequence::handle ( int m ) | |||||
Sequence_Widget::belowmouse( NULL ); | Sequence_Widget::belowmouse( NULL ); | ||||
} | } | ||||
timeline->wrlock(); | |||||
timeline->sequence_lock.wrlock(); | |||||
delete t; | delete t; | ||||
timeline->unlock(); | |||||
timeline->sequence_lock.unlock(); | |||||
} | } | ||||
Loggable::block_end(); | Loggable::block_end(); | ||||
@@ -557,8 +557,8 @@ 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 ) | |||||
// if ( (*i)->start() > from ) | |||||
return *i; | return *i; | ||||
if ( _widgets.size() ) | if ( _widgets.size() ) | ||||
@@ -172,7 +172,7 @@ Sequence_Widget::begin_drag ( const Drag &d ) | |||||
{ | { | ||||
_drag = new Drag( d ); | _drag = new Drag( d ); | ||||
timeline->wrlock(); | |||||
timeline->sequence_lock.wrlock(); | |||||
/* copy current values */ | /* copy current values */ | ||||
@@ -181,7 +181,7 @@ Sequence_Widget::begin_drag ( const Drag &d ) | |||||
/* tell display to use temporary */ | /* tell display to use temporary */ | ||||
_r = &_dragging_range; | _r = &_dragging_range; | ||||
timeline->unlock(); | |||||
timeline->sequence_lock.unlock(); | |||||
} | } | ||||
void | void | ||||
@@ -190,7 +190,7 @@ Sequence_Widget::end_drag ( void ) | |||||
/* swap in the new value */ | /* swap in the new value */ | ||||
/* go back to playback and display using same values */ | /* go back to playback and display using same values */ | ||||
timeline->wrlock(); | |||||
timeline->sequence_lock.wrlock(); | |||||
_range = _dragging_range; | _range = _dragging_range; | ||||
_r = &_range; | _r = &_range; | ||||
@@ -201,7 +201,7 @@ Sequence_Widget::end_drag ( void ) | |||||
/* this will result in a sort */ | /* this will result in a sort */ | ||||
sequence()->handle_widget_change( _r->start, _r->length ); | sequence()->handle_widget_change( _r->start, _r->length ); | ||||
timeline->unlock(); | |||||
timeline->sequence_lock.unlock(); | |||||
} | } | ||||
/** set position of widget on the timeline. */ | /** 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 ) | if ( test_press( FL_BUTTON1 + FL_CTRL ) && ! _drag->state ) | ||||
{ | { | ||||
/* duplication */ | /* duplication */ | ||||
timeline->wrlock(); | |||||
timeline->sequence_lock.wrlock(); | |||||
sequence()->add( this->clone() ); | sequence()->add( this->clone() ); | ||||
timeline->unlock(); | |||||
timeline->sequence_lock.unlock(); | |||||
_drag->state = 1; | _drag->state = 1; | ||||
return 1; | return 1; | ||||
@@ -62,11 +62,11 @@ Tempo_Sequence::handle ( int m ) | |||||
if ( Tempo_Point::edit( &t ) ) | if ( Tempo_Point::edit( &t ) ) | ||||
{ | { | ||||
timeline->wrlock(); | |||||
timeline->sequence_lock.wrlock(); | |||||
new Tempo_Point( timeline->x_to_offset( Fl::event_x() ), t ); | new Tempo_Point( timeline->x_to_offset( Fl::event_x() ), t ); | ||||
timeline->unlock(); | |||||
timeline->sequence_lock.unlock(); | |||||
timeline->redraw(); | timeline->redraw(); | ||||
} | } | ||||
@@ -65,11 +65,11 @@ Time_Sequence::handle ( int m ) | |||||
if ( Time_Point::edit( &t ) ) | 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 ); | 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(); | timeline->redraw(); | ||||
} | } | ||||
@@ -360,7 +360,7 @@ Timeline::add_take_for_armed_tracks ( void ) | |||||
{ | { | ||||
THREAD_ASSERT( UI ); | THREAD_ASSERT( UI ); | ||||
wrlock(); | |||||
track_lock.wrlock(); | |||||
for ( int i = tracks->children(); i-- ; ) | for ( int i = tracks->children(); i-- ; ) | ||||
{ | { | ||||
@@ -370,7 +370,7 @@ Timeline::add_take_for_armed_tracks ( void ) | |||||
t->sequence( new Audio_Sequence( t ) ); | t->sequence( new Audio_Sequence( t ) ); | ||||
} | } | ||||
unlock(); | |||||
track_lock.unlock(); | |||||
} | } | ||||
void | 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; | play_cursor_track = NULL; | ||||
_created_new_takes = 0; | _created_new_takes = 0; | ||||
_punched_in = 0; | |||||
_punch_in_frame = 0; | |||||
_punch_out_frame = 0; | |||||
osc_thread = 0; | osc_thread = 0; | ||||
_sample_rate = 44100; | _sample_rate = 44100; | ||||
@@ -1271,6 +1268,8 @@ Timeline::draw ( void ) | |||||
* another thread must use Fl::lock()/unlock()! */ | * another thread must use Fl::lock()/unlock()! */ | ||||
THREAD_ASSERT( UI ); | THREAD_ASSERT( UI ); | ||||
// rdlock(); | |||||
int X, Y, W, H; | int X, Y, W, H; | ||||
int bdx = 0; | int bdx = 0; | ||||
@@ -1368,6 +1367,8 @@ Timeline::draw ( void ) | |||||
done: | done: | ||||
// unlock(); | |||||
/* panzoomer->redraw(); */ | /* panzoomer->redraw(); */ | ||||
// update_child( *panzoomer ); | // update_child( *panzoomer ); | ||||
@@ -1416,6 +1417,26 @@ Timeline::draw_cursor ( nframes_t frame, Fl_Color color, void (*symbol)(Fl_Color | |||||
fl_pop_clip(); | 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 | void | ||||
Timeline::draw_playhead ( void ) | Timeline::draw_playhead ( void ) | ||||
{ | { | ||||
@@ -1429,36 +1450,6 @@ Timeline::redraw_playhead ( void ) | |||||
// static nframes_t last_playhead = -1; | // static nframes_t last_playhead = -1; | ||||
static int last_playhead_x = -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 ( transport->rolling ) | ||||
{ | { | ||||
if ( play_cursor_track->active_cursor() ) | if ( play_cursor_track->active_cursor() ) | ||||
@@ -1987,36 +1978,36 @@ Timeline::remove_track ( Track *track ) | |||||
void | void | ||||
Timeline::command_move_track_up ( Track *track ) | Timeline::command_move_track_up ( Track *track ) | ||||
{ | { | ||||
wrlock(); | |||||
track_lock.wrlock(); | |||||
insert_track( track, find_track( track ) - 1 ); | insert_track( track, find_track( track ) - 1 ); | ||||
unlock(); | |||||
track_lock.unlock(); | |||||
} | } | ||||
void | void | ||||
Timeline::command_move_track_down ( Track *track ) | Timeline::command_move_track_down ( Track *track ) | ||||
{ | { | ||||
wrlock(); | |||||
track_lock.wrlock(); | |||||
insert_track( track, find_track( track ) + 2 ); | insert_track( track, find_track( track ) + 2 ); | ||||
unlock(); | |||||
track_lock.unlock(); | |||||
} | } | ||||
void | void | ||||
Timeline::command_remove_track ( Track *track ) | Timeline::command_remove_track ( Track *track ) | ||||
{ | { | ||||
wrlock(); | |||||
track_lock.wrlock(); | |||||
remove_track(track); | remove_track(track); | ||||
unlock(); | |||||
track_lock.unlock(); | |||||
} | } | ||||
void | void | ||||
Timeline::command_quit ( void ) | Timeline::command_quit ( void ) | ||||
{ | { | ||||
timeline->wrlock(); | |||||
track_lock.wrlock(); | |||||
Project::close(); | Project::close(); | ||||
timeline->unlock(); | |||||
track_lock.unlock(); | |||||
command_save(); | command_save(); | ||||
while ( Fl::first_window() ) Fl::first_window()->hide(); | while ( Fl::first_window() ) Fl::first_window()->hide(); | ||||
@@ -2025,9 +2016,10 @@ Timeline::command_quit ( void ) | |||||
void | void | ||||
Timeline::command_undo ( void ) | Timeline::command_undo ( void ) | ||||
{ | { | ||||
wrlock(); | |||||
/* FIXME: sequence lock too? */ | |||||
track_lock.wrlock(); | |||||
Project::undo(); | Project::undo(); | ||||
unlock(); | |||||
track_lock.unlock(); | |||||
} | } | ||||
bool | bool | ||||
@@ -2036,9 +2028,9 @@ Timeline::command_load ( const char *name, const char *display_name ) | |||||
if ( ! name ) | if ( ! name ) | ||||
return false; | return false; | ||||
wrlock(); | |||||
track_lock.wrlock(); | |||||
int r = Project::open( name ); | int r = Project::open( name ); | ||||
unlock(); | |||||
track_lock.unlock(); | |||||
if ( r < 0 ) | 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 ) {} | 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; | class Timeline_Panzoomer; | ||||
static void draw_clip_rulers ( void * v, int X, int Y, int W, int H ); | 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: | public: | ||||
RWLock track_lock; /* tracks/sequences */ | |||||
RWLock sequence_lock; /* sequence contents */ | |||||
void redraw_overlay ( void ); | void redraw_overlay ( void ); | ||||
void insert_track ( Track *track, Track *before ); | void insert_track ( Track *track, Track *before ); | ||||
@@ -166,9 +168,6 @@ public: | |||||
Fl_Menu_Button *menu; | Fl_Menu_Button *menu; | ||||
int _punched_in; | |||||
nframes_t _punch_out_frame; | |||||
nframes_t _punch_in_frame; | |||||
bool _created_new_takes; | bool _created_new_takes; | ||||
nframes_t xoffset; | nframes_t xoffset; | ||||
@@ -257,6 +256,7 @@ public: | |||||
void zoom_out ( void ); | void zoom_out ( void ); | ||||
void zoom_fit ( void ); | void zoom_fit ( void ); | ||||
bool next_punch ( nframes_t frame, nframes_t *in, nframes_t *out ) const; | |||||
/* Engine */ | /* Engine */ | ||||
int total_input_buffer_percent ( void ); | int total_input_buffer_percent ( void ); | ||||
@@ -267,7 +267,6 @@ public: | |||||
bool record ( void ); | bool record ( void ); | ||||
void stop ( void ); | void stop ( void ); | ||||
void punch_in ( nframes_t frame ); | |||||
void punch_out ( nframes_t frame ); | void punch_out ( nframes_t frame ); | ||||
void wait_for_buffers ( void ); | void wait_for_buffers ( void ); | ||||
@@ -315,6 +314,7 @@ private: | |||||
/* Engine */ | /* Engine */ | ||||
void resize_buffers ( nframes_t nframes ); | 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 ); | void seek ( nframes_t frame ); | ||||
}; | }; |
@@ -737,10 +737,10 @@ void | |||||
Track::command_configure_channels ( int n ) | Track::command_configure_channels ( int n ) | ||||
{ | { | ||||
/* due to locking this should only be invoked by direct user action */ | /* due to locking this should only be invoked by direct user action */ | ||||
timeline->wrlock(); | |||||
timeline->track_lock.wrlock(); | |||||
configure_inputs( n ); | configure_inputs( n ); | ||||
configure_outputs( n ); | configure_outputs( n ); | ||||
timeline->unlock(); | |||||
timeline->track_lock.unlock(); | |||||
} | } | ||||
void | void | ||||
@@ -787,9 +787,9 @@ Track::menu_cb ( const Fl_Menu_ *m ) | |||||
/* add audio track */ | /* add audio track */ | ||||
char *name = get_unique_control_name( "Control" ); | char *name = get_unique_control_name( "Control" ); | ||||
timeline->wrlock(); | |||||
timeline->track_lock.wrlock(); | |||||
new Control_Sequence( this, name ); | new Control_Sequence( this, name ); | ||||
timeline->unlock(); | |||||
timeline->track_lock.unlock(); | |||||
} | } | ||||
else if ( ! strcmp( picked, "/Overlay controls" ) ) | else if ( ! strcmp( picked, "/Overlay controls" ) ) | ||||
{ | { | ||||
@@ -868,9 +868,9 @@ Track::menu_cb ( const Fl_Menu_ *m ) | |||||
} | } | ||||
else if ( !strcmp( picked, "Takes/New" ) ) | else if ( !strcmp( picked, "Takes/New" ) ) | ||||
{ | { | ||||
timeline->wrlock(); | |||||
timeline->track_lock.wrlock(); | |||||
sequence( (Audio_Sequence*)sequence()->clone_empty() ); | sequence( (Audio_Sequence*)sequence()->clone_empty() ); | ||||
timeline->unlock(); | |||||
timeline->track_lock.unlock(); | |||||
} | } | ||||
else if ( !strcmp( picked, "Takes/Remove" ) ) | else if ( !strcmp( picked, "Takes/Remove" ) ) | ||||
{ | { | ||||
@@ -878,7 +878,7 @@ Track::menu_cb ( const Fl_Menu_ *m ) | |||||
{ | { | ||||
Loggable::block_start(); | Loggable::block_start(); | ||||
timeline->wrlock(); | |||||
timeline->track_lock.wrlock(); | |||||
Audio_Sequence *s = sequence(); | Audio_Sequence *s = sequence(); | ||||
@@ -886,7 +886,7 @@ Track::menu_cb ( const Fl_Menu_ *m ) | |||||
delete s; | delete s; | ||||
timeline->unlock(); | |||||
timeline->track_lock.unlock(); | |||||
Loggable::block_end(); | Loggable::block_end(); | ||||
} | } | ||||
@@ -906,9 +906,9 @@ Track::menu_cb ( const Fl_Menu_ *m ) | |||||
{ | { | ||||
Audio_Sequence* s = (Audio_Sequence*)m->mvalue()->user_data(); | Audio_Sequence* s = (Audio_Sequence*)m->mvalue()->user_data(); | ||||
timeline->wrlock(); | |||||
timeline->track_lock.wrlock(); | |||||
sequence( s ); | sequence( s ); | ||||
timeline->unlock(); | |||||
timeline->track_lock.unlock(); | |||||
} | } | ||||
} | } | ||||
@@ -27,6 +27,7 @@ | |||||
#include <algorithm> | #include <algorithm> | ||||
using std::min; | using std::min; | ||||
using std::max; | using std::max; | ||||
#include <dsp.h> | |||||