Implement capture-offset latency compensation.

This commit is contained in:
Jonathan Moore Liles 2009-01-18 21:25:28 -06:00
parent 1917e9295c
commit 86fa1ce238
11 changed files with 116 additions and 14 deletions

View File

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

View File

@ -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();
}
}; };

View File

@ -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() ) ) /* FIXME: how does this work if _delay is not a multiple of bufsize? */
_frame += nframes;
else if ( _frame >= _delay )
WARNING( "Programming error?" ); {
if ( ! sequence()->play( buf, _frame - _delay, nframes, channels() ) )
WARNING( "Programming error?" );
}
_frame += nframes;
timeline->unlock(); timeline->unlock();
} }

View File

@ -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 );
}; };

View File

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

View File

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

View File

@ -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();
}

View File

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

View 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;

View File

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

View File

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