| @@ -205,7 +205,6 @@ Engine::timebase ( jack_transport_state_t, jack_nframes_t, jack_position_t *pos, | |||||
| /* FIXME: fill this in */ | /* FIXME: fill this in */ | ||||
| pos->bar_start_tick = 0; | pos->bar_start_tick = 0; | ||||
| } | } | ||||
| /* THREAD: RT */ | /* THREAD: RT */ | ||||
| @@ -86,7 +86,14 @@ public: | |||||
| void freewheeling ( bool yes ); | void freewheeling ( bool yes ); | ||||
| bool zombified ( void ) const { return _zombified; } | bool zombified ( void ) const { return _zombified; } | ||||
| nframes_t system_latency ( void ) const { return nframes(); } | |||||
| float cpu_load ( void ) const { return jack_cpu_load( _client ); } | float cpu_load ( void ) const { return jack_cpu_load( _client ); } | ||||
| float frames_to_milliseconds ( nframes_t frames ) | |||||
| { | |||||
| return ( frames * 1000 ) / (float)frame_rate(); | |||||
| } | |||||
| }; | }; | ||||
| @@ -60,6 +60,14 @@ Playback_DS::seek ( nframes_t frame ) | |||||
| flush(); | flush(); | ||||
| } | } | ||||
| /** set the playback delay to /frames/ frames. This be called prior to | |||||
| a seek. */ | |||||
| void | |||||
| Playback_DS::delay ( nframes_t frames ) | |||||
| { | |||||
| _delay = frames; | |||||
| } | |||||
| /** read /nframes/ from the attached track into /buf/ */ | /** read /nframes/ from the attached track into /buf/ */ | ||||
| void | void | ||||
| Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) | Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) | ||||
| @@ -76,16 +84,22 @@ Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) | |||||
| if ( ! sequence() ) | if ( ! sequence() ) | ||||
| { | { | ||||
| /* FIXME: what to do here? */ | |||||
| // _frame += _nframes; | // _frame += _nframes; | ||||
| return; | return; | ||||
| } | } | ||||
| timeline->rdlock(); | timeline->rdlock(); | ||||
| if ( sequence()->play( buf, _frame, nframes, channels() ) ) | |||||
| _frame += nframes; | |||||
| else | |||||
| WARNING( "Programming error?" ); | |||||
| /* 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?" ); | |||||
| } | |||||
| _frame += nframes; | |||||
| timeline->unlock(); | timeline->unlock(); | ||||
| } | } | ||||
| @@ -27,11 +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 */ | |||||
| 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; | |||||
| run(); | run(); | ||||
| } | } | ||||
| @@ -39,4 +43,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 ); | |||||
| }; | }; | ||||
| @@ -95,6 +95,33 @@ Port::activate ( const char *name, type_e dir ) | |||||
| 0 ); | 0 ); | ||||
| } | } | ||||
| /** returns the sum of latency of all ports between this one and a | |||||
| terminal port. */ | |||||
| /* FIMXE: how does JACK know that input A of client Foo connects to | |||||
| output Z of the same client in order to draw the line through Z to a | |||||
| terminal port? And, if this determination cannot be made, what use is | |||||
| this function? */ | |||||
| nframes_t | |||||
| Port::total_latency ( void ) const | |||||
| { | |||||
| return jack_port_get_total_latency( engine->client(), _port ); | |||||
| } | |||||
| /** returns the number of frames of latency assigned to this port */ | |||||
| nframes_t | |||||
| Port::latency ( void ) const | |||||
| { | |||||
| return jack_port_get_latency( _port ); | |||||
| } | |||||
| /** inform JACK that port has /frames/ frames of latency */ | |||||
| void | |||||
| Port::latency ( nframes_t frames ) | |||||
| { | |||||
| jack_port_set_latency( _port, frames ); | |||||
| } | |||||
| void | void | ||||
| Port::shutdown ( void ) | Port::shutdown ( void ) | ||||
| { | { | ||||
| @@ -63,7 +63,9 @@ public: | |||||
| const char * name ( void ) const { return _name; } | const char * name ( void ) const { return _name; } | ||||
| bool name ( const char *name ); | bool name ( const char *name ); | ||||
| bool name ( const char *base, int n, const char *type=0 ); | bool name ( const char *base, int n, const char *type=0 ); | ||||
| nframes_t total_latency ( void ) const; | |||||
| nframes_t latency ( void ) const; | |||||
| void latency ( nframes_t frames ); | |||||
| void activate ( const char *name, type_e dir ); | void activate ( const char *name, type_e dir ); | ||||
| void shutdown ( void ); | void shutdown ( void ); | ||||
| @@ -235,3 +235,15 @@ Timeline::total_capture_xruns ( void ) | |||||
| return r; | return r; | ||||
| } | } | ||||
| #include "Engine.H" | |||||
| extern Engine *engine; | |||||
| nframes_t | |||||
| Timeline::total_output_latency ( void ) const | |||||
| { | |||||
| /* Due to flaws in the JACK latency reporting API, we cannot | |||||
| * reliably account for software latency. Using the system latency | |||||
| * is the best we can do here. */ | |||||
| return engine->system_latency(); | |||||
| } | |||||
| @@ -204,6 +204,15 @@ Track::seek ( nframes_t frame ) | |||||
| return playback_ds->seek( frame ); | return playback_ds->seek( frame ); | ||||
| } | } | ||||
| void | |||||
| Track::delay ( nframes_t frames ) | |||||
| { | |||||
| // THREAD_ASSERT( RT ); | |||||
| if ( playback_ds ) | |||||
| playback_ds->delay( frames ); | |||||
| } | |||||
| /* THREAD: RT (non-RT) */ | /* THREAD: RT (non-RT) */ | ||||
| void | void | ||||
| Track::resize_buffers ( nframes_t nframes ) | Track::resize_buffers ( nframes_t nframes ) | ||||
| @@ -266,9 +275,29 @@ Track::finalize ( Capture *c, nframes_t frame ) | |||||
| { | { | ||||
| THREAD_ASSERT( Capture ); | THREAD_ASSERT( Capture ); | ||||
| /* adjust region start for latency */ | |||||
| /* FIXME: is just looking at the first channel good enough? */ | |||||
| c->region->finalize( frame ); | c->region->finalize( frame ); | ||||
| DMESSAGE( "finalizing audio file" ); | DMESSAGE( "finalizing audio file" ); | ||||
| c->audio_file->finalize(); | c->audio_file->finalize(); | ||||
| nframes_t capture_offset = 0; | |||||
| /* Add the system latency twice. Once for the input (usually | |||||
| * required) and again for the output latency of whatever we're | |||||
| * playing along to (should only apply when overdubbing) */ | |||||
| /* Limitations in the JACK latency reporting API prevent us from | |||||
| * compensating from any software latency introduced by other | |||||
| * clients in our graph... Oh well */ | |||||
| capture_offset += engine->system_latency(); | |||||
| capture_offset += engine->system_latency(); | |||||
| DMESSAGE( "Adjusting capture by %lu frames.", (unsigned long)capture_offset ); | |||||
| c->region->offset( capture_offset ); | |||||
| delete c->audio_file; | delete c->audio_file; | ||||
| } | } | ||||
| @@ -205,7 +205,7 @@ Loggable::progress_callback( &TLE::progress_cb, this );} {} | |||||
| label Timeline | label Timeline | ||||
| callback {if ( Fl::event_key() != FL_Escape ) | callback {if ( Fl::event_key() != FL_Escape ) | ||||
| o->hide();} open | o->hide();} open | ||||
| private xywh {133 113 1025 770} type Double resizable xclass Non_DAW visible | |||||
| private xywh {102 111 1025 770} type Double resizable xclass Non_DAW visible | |||||
| } { | } { | ||||
| Fl_Menu_Bar menubar {open | Fl_Menu_Bar menubar {open | ||||
| private xywh {0 0 1024 25} | private xywh {0 0 1024 25} | ||||
| @@ -577,7 +577,7 @@ ab.run();} | |||||
| } | } | ||||
| } | } | ||||
| Fl_Group {} {open | Fl_Group {} {open | ||||
| xywh {0 23 1025 51} | |||||
| xywh {0 1 1025 73} | |||||
| } { | } { | ||||
| Fl_Pack {} {open | Fl_Pack {} {open | ||||
| xywh {0 23 483 46} type HORIZONTAL | xywh {0 23 483 46} type HORIZONTAL | ||||
| @@ -669,6 +669,10 @@ ab.run();} | |||||
| code0 {\#include "FL/Fl_Blinker.H"} | code0 {\#include "FL/Fl_Blinker.H"} | ||||
| class Fl_Blinker | class Fl_Blinker | ||||
| } | } | ||||
| Fl_Box stats_box { | |||||
| label {<stats>} selected | |||||
| xywh {810 1 215 21} labelsize 13 labelcolor 53 align 88 | |||||
| } | |||||
| } | } | ||||
| Fl_Progress progress { | Fl_Progress progress { | ||||
| label {0%} | label {0%} | ||||
| @@ -682,13 +686,9 @@ ab.run();} | |||||
| } | } | ||||
| Fl_Box project_name { | Fl_Box project_name { | ||||
| label {<project name>} | label {<project name>} | ||||
| private xywh {450 0 475 22} labeltype SHADOW_LABEL labelfont 2 | |||||
| private xywh {450 0 365 22} labeltype SHADOW_LABEL labelfont 2 | |||||
| code0 {o->label( Project::name() );} | code0 {o->label( Project::name() );} | ||||
| } | } | ||||
| Fl_Value_Output xruns_output { | |||||
| label {xruns:} | |||||
| private xywh {980 2 44 20} maximum 40000 step 1 | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| Function {menu_picked_value( const Fl_Menu_ *m )} {private return_type {static int} | Function {menu_picked_value( const Fl_Menu_ *m )} {private return_type {static int} | ||||
| @@ -757,9 +757,13 @@ if ( timeline->total_capture_xruns() ) | |||||
| if ( timeline->total_playback_xruns() ) | if ( timeline->total_playback_xruns() ) | ||||
| playback_buffer_progress->selection_color( FL_RED ); | playback_buffer_progress->selection_color( FL_RED ); | ||||
| static char stats[100]; | |||||
| snprintf( stats, sizeof( stats ), "latency: %.1fms, xruns: %d", | |||||
| engine->frames_to_milliseconds( timeline->total_output_latency() ), | |||||
| engine->xruns() ); | |||||
| xruns_output->value( engine->xruns() ); | |||||
| stats_box->label( stats ); | |||||
| static bool zombie = false; | static bool zombie = false; | ||||
| @@ -205,6 +205,7 @@ public: | |||||
| int total_playback_xruns ( void ); | int total_playback_xruns ( void ); | ||||
| int total_capture_xruns ( void ); | int total_capture_xruns ( void ); | ||||
| nframes_t total_output_latency ( void ) const; | |||||
| bool record ( void ); | bool record ( void ); | ||||
| void stop ( void ); | void stop ( void ); | ||||
| @@ -215,6 +215,7 @@ 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 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 ); | ||||