| @@ -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> | |||