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