| @@ -66,9 +66,9 @@ Playback_DS::seek ( nframes_t frame ) | |||||
| /** set the playback delay to /frames/ frames. This be called prior to | /** set the playback delay to /frames/ frames. This be called prior to | ||||
| a seek. */ | a seek. */ | ||||
| void | void | ||||
| Playback_DS::delay ( nframes_t frames ) | |||||
| Playback_DS::undelay ( nframes_t delay ) | |||||
| { | { | ||||
| _delay = frames; | |||||
| _undelay = delay; | |||||
| } | } | ||||
| /** read /nframes/ from the attached track into /buf/ */ | /** read /nframes/ from the attached track into /buf/ */ | ||||
| @@ -94,13 +94,8 @@ Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) | |||||
| if ( sequence() ) | if ( sequence() ) | ||||
| { | { | ||||
| /* FIXME: how does this work if _delay is not a multiple of bufsize? */ | |||||
| if ( _frame >= _delay ) | |||||
| { | |||||
| if ( ! sequence()->play( buf, _frame - _delay, nframes, channels() ) ) | |||||
| WARNING( "Programming error?" ); | |||||
| } | |||||
| if ( ! sequence()->play( buf, _frame + _undelay, nframes, channels() ) ) | |||||
| WARNING( "Programming error?" ); | |||||
| _frame += nframes; | _frame += nframes; | ||||
| } | } | ||||
| @@ -27,14 +27,15 @@ class Playback_DS : public Disk_Stream | |||||
| void flush ( void ) { base_flush( true ); } | void flush ( void ) { base_flush( true ); } | ||||
| volatile nframes_t _delay; /* number of frames this diskstream should be delayed by */ | |||||
| volatile nframes_t _undelay; /* number of frames this diskstream | |||||
| * should be undelayed by */ | |||||
| public: | public: | ||||
| Playback_DS ( Track *th, float frame_rate, nframes_t nframes, int channels ) : | Playback_DS ( Track *th, float frame_rate, nframes_t nframes, int channels ) : | ||||
| Disk_Stream( th, frame_rate, nframes, channels ) | Disk_Stream( th, frame_rate, nframes, channels ) | ||||
| { | { | ||||
| _delay = 0; | |||||
| _undelay = 0; | |||||
| run(); | run(); | ||||
| } | } | ||||
| @@ -45,6 +46,6 @@ public: | |||||
| void seek ( nframes_t frame ); | void seek ( nframes_t frame ); | ||||
| nframes_t process ( nframes_t nframes ); | nframes_t process ( nframes_t nframes ); | ||||
| void delay ( nframes_t v ); | |||||
| void undelay ( nframes_t v ); | |||||
| }; | }; | ||||
| @@ -234,17 +234,19 @@ Track::seek ( nframes_t frame ) | |||||
| { | { | ||||
| THREAD_ASSERT( RT ); | THREAD_ASSERT( RT ); | ||||
| compute_latency_compensation(); | |||||
| if ( playback_ds ) | if ( playback_ds ) | ||||
| return playback_ds->seek( frame ); | return playback_ds->seek( frame ); | ||||
| } | } | ||||
| void | void | ||||
| Track::delay ( nframes_t frames ) | |||||
| Track::undelay ( nframes_t nframes ) | |||||
| { | { | ||||
| // THREAD_ASSERT( RT ); | // THREAD_ASSERT( RT ); | ||||
| if ( playback_ds ) | if ( playback_ds ) | ||||
| playback_ds->delay( frames ); | |||||
| playback_ds->undelay( nframes ); | |||||
| } | } | ||||
| /* THREAD: RT (non-RT) */ | /* THREAD: RT (non-RT) */ | ||||
| @@ -360,3 +362,21 @@ Track::finalize ( Capture *c, nframes_t frame ) | |||||
| timeline->unlock(); | timeline->unlock(); | ||||
| } | } | ||||
| void | |||||
| Track::compute_latency_compensation ( void ) | |||||
| { | |||||
| if ( Timeline::playback_latency_compensation && output.size() ) | |||||
| { | |||||
| nframes_t min,max; | |||||
| output[0].get_latency( JACK::Port::Output, &min, &max ); | |||||
| DMESSAGE( "Track %s, setting undelay to %lu", name(), (unsigned long)min); | |||||
| undelay( min ); | |||||
| } | |||||
| else | |||||
| { | |||||
| undelay( 0 ); | |||||
| } | |||||
| } | |||||
| @@ -325,6 +325,11 @@ pi.run();} | |||||
| label {Capture Format} open | label {Capture Format} open | ||||
| xywh {25 25 74 25} | xywh {25 25 74 25} | ||||
| } {} | } {} | ||||
| MenuItem {} { | |||||
| label {Playback Latency Compensation} | |||||
| callback {Timeline::playback_latency_compensation = menu_picked_value( o );} selected | |||||
| xywh {55 55 40 25} type Toggle | |||||
| } | |||||
| } | } | ||||
| MenuItem {} { | MenuItem {} { | ||||
| label {&New} | label {&New} | ||||
| @@ -364,9 +369,7 @@ main_window->redraw();} | |||||
| } | } | ||||
| MenuItem {} { | MenuItem {} { | ||||
| label {&Open} | label {&Open} | ||||
| callback { | |||||
| char *path = read_line( user_config_dir, "default_path" ); | |||||
| callback {char *path = read_line( user_config_dir, "default_path" ); | |||||
| const char *name = fl_dir_chooser( "Open Project", path ); | const char *name = fl_dir_chooser( "Open Project", path ); | ||||
| @@ -1107,7 +1110,7 @@ if ( logo_box->image() ) | |||||
| code1 {o->label( NULL );} | code1 {o->label( NULL );} | ||||
| } | } | ||||
| Fl_Text_Editor notes_field { | Fl_Text_Editor notes_field { | ||||
| label {Notes:} selected | |||||
| label {Notes:} | |||||
| private xywh {20 420 480 245} color 47 selection_color 31 textsize 18 textcolor 92 | private xywh {20 420 480 245} color 47 selection_color 31 textsize 18 textcolor 92 | ||||
| code0 {o->buffer( new Fl_Text_Buffer() );} | code0 {o->buffer( new Fl_Text_Buffer() );} | ||||
| code1 {o->buffer()->loadfile( "notes" );} | code1 {o->buffer()->loadfile( "notes" );} | ||||
| @@ -73,6 +73,7 @@ bool Timeline::snapping_on_hold = false; | |||||
| bool Timeline::snap_magnetic = true; | bool Timeline::snap_magnetic = true; | ||||
| bool Timeline::follow_playhead = true; | bool Timeline::follow_playhead = true; | ||||
| bool Timeline::center_playhead = true; | bool Timeline::center_playhead = true; | ||||
| bool Timeline::playback_latency_compensation = false; | |||||
| const float UPDATE_FREQ = 1.0f / 18.0f; | const float UPDATE_FREQ = 1.0f / 18.0f; | ||||
| @@ -155,6 +155,7 @@ public: | |||||
| static bool snap_magnetic; | static bool snap_magnetic; | ||||
| static bool follow_playhead; | static bool follow_playhead; | ||||
| static bool center_playhead; | static bool center_playhead; | ||||
| static bool playback_latency_compensation; | |||||
| Tempo_Sequence *tempo_track; | Tempo_Sequence *tempo_track; | ||||
| Time_Sequence *time_track; | Time_Sequence *time_track; | ||||
| @@ -253,8 +253,8 @@ public: | |||||
| nframes_t process_input ( nframes_t nframes ); | nframes_t process_input ( nframes_t nframes ); | ||||
| nframes_t process_output ( nframes_t nframes ); | nframes_t process_output ( nframes_t nframes ); | ||||
| void seek ( nframes_t frame ); | void seek ( nframes_t frame ); | ||||
| void delay ( nframes_t frames ); | |||||
| void undelay ( nframes_t frames ); | |||||
| void compute_latency_compensation ( void ); | |||||
| void record ( Capture *c, nframes_t frame ); | void record ( Capture *c, nframes_t frame ); | ||||
| void write ( Capture *c, sample_t *buf, nframes_t nframes ); | void write ( Capture *c, sample_t *buf, nframes_t nframes ); | ||||
| void finalize ( Capture *c, nframes_t frame ); | void finalize ( Capture *c, nframes_t frame ); | ||||