Implement capture-offset latency compensation.
This commit is contained in:
parent
1917e9295c
commit
86fa1ce238
|
@ -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() ) )
|
/* 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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
Loading…
Reference in New Issue