| @@ -1 +1 @@ | |||
| Subproject commit d0063527aa360a3f1fd34e76fc0dd9125efb9202 | |||
| Subproject commit 38275d616800d5af826fd4a9c761a98068e7311a | |||
| @@ -395,6 +395,32 @@ Chain::configure_ports ( void ) | |||
| parent()->redraw(); | |||
| } | |||
| /** invoked from the JACK latency callback... We need to update the latency values on this chains ports */ | |||
| void | |||
| Chain::set_latency ( JACK::Port::direction_e dir ) | |||
| { | |||
| nframes_t total_latency = 0; | |||
| if ( dir == JACK::Port::Input ) | |||
| { | |||
| for ( int i = 0; i < modules(); ++i ) | |||
| { | |||
| Module *m = module( i ); | |||
| total_latency += m->get_latency( dir ); | |||
| m->set_latency( dir, total_latency ); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for ( int i = modules(); i--; ) | |||
| { | |||
| Module *m = module( i ); | |||
| total_latency += m->get_latency( dir ); | |||
| m->set_latency( dir, total_latency ); | |||
| } | |||
| } | |||
| } | |||
| int | |||
| Chain::get_module_instance_number ( Module *m ) | |||
| { | |||
| @@ -125,6 +125,8 @@ public: | |||
| _configure_outputs_userdata = v; | |||
| } | |||
| void set_latency ( JACK::Port::direction_e ); | |||
| Fl_Callback * configure_outputs_callback ( void ) const { return _configure_outputs_callback; } | |||
| virtual void log_children ( void ) const; | |||
| @@ -98,6 +98,18 @@ Group::set ( Log_Entry &e ) | |||
| /* Callbacks */ | |||
| /*************/ | |||
| void | |||
| Group::latency ( jack_latency_callback_mode_t mode ) | |||
| { | |||
| for ( std::list<Mixer_Strip*>::iterator i = strips.begin(); | |||
| i != strips.end(); | |||
| i++ ) | |||
| { | |||
| if ( (*i)->chain() ) | |||
| (*i)->chain()->set_latency(mode == JackCaptureLatency ? JACK::Port::Input : JACK::Port::Output ); | |||
| } | |||
| } | |||
| /* THREAD: RT */ | |||
| /** This is the jack xrun callback */ | |||
| int | |||
| @@ -52,6 +52,7 @@ class Group : public Loggable, public JACK::Client, public Mutex | |||
| int buffer_size ( nframes_t nframes ); | |||
| void thread_init ( void ); | |||
| void port_connect ( jack_port_id_t a, jack_port_id_t b, int connect ); | |||
| virtual void latency ( jack_latency_callback_mode_t mode ); | |||
| /* not allowed */ | |||
| Group ( const Group &rhs ); | |||
| @@ -79,6 +79,7 @@ public: | |||
| LOG_CREATE_FUNC( JACK_Module ); | |||
| protected: | |||
| virtual void process ( nframes_t nframes ); | |||
| @@ -127,6 +127,7 @@ Module::~Module ( ) | |||
| void | |||
| Module::init ( void ) | |||
| { | |||
| // _latency = 0; | |||
| _is_default = false; | |||
| _editor = 0; | |||
| _chain = 0; | |||
| @@ -1089,6 +1090,76 @@ Module::thaw_ports ( void ) | |||
| } | |||
| } | |||
| nframes_t | |||
| Module::get_latency ( JACK::Port::direction_e dir ) const | |||
| { | |||
| nframes_t tmin = 0; | |||
| nframes_t tmax = 0; | |||
| if ( dir == JACK::Port::Input ) | |||
| { | |||
| if ( aux_audio_input.size() ) | |||
| { | |||
| for ( unsigned int i = 0; i < aux_audio_input.size(); i++ ) | |||
| { | |||
| nframes_t min,max; | |||
| aux_audio_input[i].jack_port()->get_latency( dir, &min, &max ); | |||
| tmin += min; | |||
| tmax += max; | |||
| } | |||
| tmin /= aux_audio_input.size(); | |||
| tmax /= aux_audio_input.size(); | |||
| } | |||
| return tmin; | |||
| /* for ( unsigned int i = 0; i < aux_audio_output.size(); i++ ) */ | |||
| /* aux_audio_output[i].set_latency( dir, tmin, tmax ); */ | |||
| } | |||
| else | |||
| { | |||
| if ( aux_audio_output.size() ) | |||
| { | |||
| for ( unsigned int i = 0; i < aux_audio_output.size(); i++ ) | |||
| { | |||
| nframes_t min,max; | |||
| aux_audio_output[i].jack_port()->get_latency( dir, &min, &max ); | |||
| tmin += min; | |||
| tmax += max; | |||
| } | |||
| tmin /= aux_audio_output.size(); | |||
| tmax /= aux_audio_output.size(); | |||
| } | |||
| return tmin; | |||
| /* for ( unsigned int i = 0; i < aux_audio_output.size(); i++ ) */ | |||
| /* aux_audio_output[i].set_latency( dir, tmin, tmax ); */ | |||
| } | |||
| } | |||
| void | |||
| Module::set_latency ( JACK::Port::direction_e dir, nframes_t latency ) | |||
| { | |||
| if ( dir == JACK::Port::Input ) | |||
| { | |||
| for ( unsigned int i = 0; i < aux_audio_output.size(); i++ ) | |||
| aux_audio_output[i].jack_port()->set_latency( dir, latency, latency ); | |||
| } | |||
| else | |||
| { | |||
| for ( unsigned int i = 0; i < aux_audio_input.size(); i++ ) | |||
| aux_audio_input[i].jack_port()->set_latency( dir, latency, latency ); | |||
| } | |||
| } | |||
| bool | |||
| Module::add_aux_port ( bool input, const char *prefix, int i ) | |||
| { | |||
| @@ -46,6 +46,7 @@ class Module : public Fl_Group, public Loggable { | |||
| nframes_t _nframes; | |||
| Chain *_chain; | |||
| bool _is_default; | |||
| // nframes_t _latency; | |||
| Module_Parameter_Editor *_editor; | |||
| @@ -71,6 +72,9 @@ protected: | |||
| public: | |||
| virtual nframes_t get_latency ( JACK::Port::direction_e dir ) const; | |||
| virtual void set_latency ( JACK::Port::direction_e dir, nframes_t latency ); | |||
| /* true if this module was added by default and not under normal user control */ | |||
| bool is_default ( void ) const { return _is_default; } | |||
| void is_default ( bool v ) { _is_default = v; } | |||
| @@ -276,7 +280,7 @@ public: | |||
| } | |||
| void jack_port ( JACK::Port *v ) { _jack_port = v; } | |||
| JACK::Port *jack_port ( void ) { return _jack_port; } | |||
| JACK::Port *jack_port ( void ) const { return _jack_port; } | |||
| private: | |||
| @@ -124,6 +124,8 @@ Plugin_Module::set ( Log_Entry &e ) | |||
| void | |||
| Plugin_Module::init ( void ) | |||
| { | |||
| _latency = 0; | |||
| _last_latency = 0; | |||
| _idata = new Plugin_Module::ImplementationData(); | |||
| _idata->handle.clear(); | |||
| /* module will be bypassed until plugin is loaded */ | |||
| @@ -138,6 +140,19 @@ Plugin_Module::init ( void ) | |||
| bbox( tx, ty, tw, th ); | |||
| } | |||
| void | |||
| Plugin_Module::update ( void ) | |||
| { | |||
| if ( _last_latency != _latency ) | |||
| { | |||
| DMESSAGE( "Plugin latency changed to %lu", (unsigned long)_latency ); | |||
| chain()->client()->recompute_latencies(); | |||
| } | |||
| _last_latency = _latency; | |||
| } | |||
| int | |||
| Plugin_Module::can_support_inputs ( int n ) | |||
| { | |||
| @@ -341,7 +356,7 @@ Plugin_Module::plugin_instances ( unsigned int n ) | |||
| { | |||
| LADSPA_Handle h; | |||
| DMESSAGE( "Instantiating plugin..." ); | |||
| DMESSAGE( "Instantiating plugin... with sample rate %lu", (unsigned long)sample_rate()); | |||
| if ( ! (h = _idata->descriptor->instantiate( _idata->descriptor, sample_rate() ) ) ) | |||
| { | |||
| @@ -391,6 +406,31 @@ Plugin_Module::bypass ( bool v ) | |||
| } | |||
| } | |||
| nframes_t | |||
| Plugin_Module::get_plugin_latency ( void ) const | |||
| { | |||
| for ( unsigned int i = ncontrol_outputs(); i--; ) | |||
| { | |||
| if ( !strcasecmp( "latency", control_output[i].name() ) ) | |||
| { | |||
| return control_output[i].control_value(); | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| nframes_t | |||
| Plugin_Module::get_latency ( JACK::Port::direction_e dir ) const | |||
| { | |||
| nframes_t latency = Module::get_latency( dir ); | |||
| latency += get_plugin_latency(); | |||
| return latency; | |||
| } | |||
| bool | |||
| Plugin_Module::load ( unsigned long id ) | |||
| { | |||
| @@ -467,6 +507,7 @@ Plugin_Module::load ( unsigned long id ) | |||
| Port p( this, d, Port::CONTROL, _idata->descriptor->PortNames[ i ] ); | |||
| p.hints.default_value = 0; | |||
| LADSPA_PortRangeHintDescriptor hd = _idata->descriptor->PortRangeHints[i].HintDescriptor; | |||
| @@ -768,6 +809,8 @@ Plugin_Module::process ( nframes_t nframes ) | |||
| buffer_copy( (sample_t*)audio_output[1].buffer(), (sample_t*)audio_input[0].buffer(), nframes ); | |||
| } | |||
| } | |||
| _latency = get_plugin_latency(); | |||
| } | |||
| @@ -65,6 +65,11 @@ public: | |||
| private: | |||
| volatile nframes_t _latency; | |||
| nframes_t _last_latency; | |||
| nframes_t get_plugin_latency ( void ) const; | |||
| void init ( void ); | |||
| void bbox ( int &X, int &Y, int &W, int &H ) | |||
| @@ -106,6 +111,9 @@ private: | |||
| public: | |||
| virtual void update ( void ); | |||
| virtual nframes_t get_latency ( JACK::Port::direction_e dir ) const; | |||
| static std::list<Plugin_Info> get_all_plugins ( void ); | |||
| static void spawn_discover_thread ( void ); | |||
| @@ -123,6 +123,12 @@ namespace JACK | |||
| ((Client*)arg)->thread_init(); | |||
| } | |||
| void | |||
| Client::latency ( jack_latency_callback_mode_t mode, void *arg ) | |||
| { | |||
| ((Client*)arg)->latency( mode ); | |||
| } | |||
| void | |||
| Client::shutdown ( void *arg ) | |||
| { | |||
| @@ -175,6 +181,10 @@ namespace JACK | |||
| set_callback( port_connect ); | |||
| jack_set_sample_rate_callback( _client, &Client::sample_rate_changed, this ); | |||
| #ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE | |||
| set_callback( latency ); | |||
| #endif | |||
| /* FIXME: should we wait to register this until after the project | |||
| has been loaded (and we have disk threads running)? */ | |||
| @@ -296,6 +306,12 @@ namespace JACK | |||
| return s; | |||
| } | |||
| void | |||
| Client::recompute_latencies ( void ) | |||
| { | |||
| jack_recompute_total_latencies( _client ); | |||
| } | |||
| void | |||
| Client::transport_stop ( ) | |||
| { | |||
| @@ -66,6 +66,9 @@ namespace JACK | |||
| static void thread_init ( void *arg ); | |||
| virtual void thread_init ( void ) = 0; | |||
| static void latency ( jack_latency_callback_mode_t mode, void *arg ); | |||
| virtual void latency ( jack_latency_callback_mode_t mode ) { } | |||
| Client ( const Client &rhs ); | |||
| Client & operator = ( const Client &rhs ); | |||
| @@ -89,7 +92,7 @@ namespace JACK | |||
| SLOW_SYNC = 1 << 0, | |||
| TIMEBASE_MASTER = 1 << 1 }; | |||
| jack_client_t * jack_client ( void ) { return _client; } | |||
| jack_client_t * jack_client ( void ) const { return _client; } | |||
| void port_added ( JACK::Port * p ); | |||
| void port_removed ( JACK::Port *p ); | |||
| @@ -116,7 +119,8 @@ namespace JACK | |||
| void transport_start ( void ); | |||
| void transport_locate ( nframes_t frame ); | |||
| jack_transport_state_t transport_query ( jack_position_t *pos ); | |||
| void recompute_latencies ( void ); | |||
| static int maximum_name_length ( void ) { return jack_client_name_size(); } | |||
| }; | |||
| @@ -38,8 +38,7 @@ namespace JACK | |||
| { | |||
| return jack_port_name_size() - jack_client_name_size() - 6; | |||
| } | |||
| Port::Port ( const Port &rhs ) | |||
| { | |||
| _connections = NULL; | |||
| @@ -131,16 +130,16 @@ namespace JACK | |||
| { | |||
| _client->port_removed( this ); | |||
| if ( _name ) | |||
| { | |||
| free( _name ); | |||
| _name = NULL; | |||
| } | |||
| if ( _trackname ) | |||
| { | |||
| free( _trackname ); | |||
| _trackname = NULL; | |||
| } | |||
| if ( _name ) | |||
| { | |||
| free( _name ); | |||
| _name = NULL; | |||
| } | |||
| if ( _trackname ) | |||
| { | |||
| free( _trackname ); | |||
| _trackname = NULL; | |||
| } | |||
| } | |||
| @@ -207,18 +206,13 @@ namespace JACK | |||
| /** 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 | |||
| { | |||
| #ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE | |||
| jack_latency_range_t range; | |||
| jack_port_get_latency_range( _port, _direction == Output ? JackPlaybackLatency : JackCaptureLatency, &range ); | |||
| jack_port_get_latency_range( _port, _direction == Input ? JackPlaybackLatency : JackCaptureLatency, &range ); | |||
| return range.max; | |||
| #else | |||
| @@ -227,33 +221,36 @@ namespace JACK | |||
| } | |||
| /** returns the number of frames of latency assigned to this port */ | |||
| nframes_t | |||
| Port::latency ( void ) const | |||
| void | |||
| Port::get_latency ( direction_e dir, nframes_t *min, nframes_t *max ) const | |||
| { | |||
| #ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE | |||
| jack_latency_range_t range; | |||
| jack_port_get_latency_range( _port, _direction == Output ? JackPlaybackLatency : JackCaptureLatency, &range ); | |||
| jack_port_get_latency_range( _port, dir == Output ? JackPlaybackLatency : JackCaptureLatency, &range ); | |||
| return range.max; | |||
| *min = range.min; | |||
| *max = range.max; | |||
| #else | |||
| return jack_port_get_latency( _port ); | |||
| *min = *max = jack_port_get_latency( _port ); | |||
| #endif | |||
| } | |||
| /** inform JACK that port has /frames/ frames of latency */ | |||
| void | |||
| Port::latency ( nframes_t frames ) | |||
| Port::set_latency ( direction_e dir, nframes_t min, nframes_t max ) | |||
| { | |||
| #ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE | |||
| jack_latency_range_t range; | |||
| // DMESSAGE( "Setting port latency!" ); | |||
| range.min = range.max = frames; | |||
| jack_port_set_latency_range( _port, _direction == Output ? JackPlaybackLatency : JackCaptureLatency, &range ); | |||
| range.max = max; | |||
| range.min = min; | |||
| jack_port_set_latency_range( _port, dir == Output ? JackPlaybackLatency : JackCaptureLatency, &range ); | |||
| #else | |||
| jack_port_set_latency( _port, frames ); | |||
| jack_port_set_latency( _port, max ); | |||
| #endif | |||
| } | |||
| @@ -72,8 +72,11 @@ namespace JACK | |||
| // 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 get_latency ( direction_e dir, nframes_t *min, nframes_t *max ) const; | |||
| /* it's only valid to call this in a latency callback! */ | |||
| void set_latency ( direction_e dir, nframes_t min, nframes_t max ); | |||
| void terminal ( bool b ) { _terminal = b; } | |||
| bool activate ( void ); | |||
| @@ -96,6 +99,8 @@ namespace JACK | |||
| private: | |||
| friend class Client; | |||
| direction_e _direction; | |||
| type_e _type; | |||
| bool _terminal; | |||
| @@ -643,7 +643,8 @@ Audio_Region::draw ( void ) | |||
| } | |||
| } | |||
| else | |||
| WARNING( "Pbuf == %p, peaks = %lu", pbuf, (unsigned long)peaks ); | |||
| ; | |||
| // WARNING( "Pbuf == %p, peaks = %lu", pbuf, (unsigned long)peaks ); | |||
| if ( peaks < loop_peaks_needed ) | |||
| { | |||
| @@ -80,6 +80,22 @@ Engine::buffer_size ( nframes_t nframes ) | |||
| return 0; | |||
| } | |||
| nframes_t | |||
| Engine::playback_latency ( void ) const | |||
| { | |||
| #ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE | |||
| jack_latency_range_t range; | |||
| jack_port_get_latency_range( jack_port_by_name( jack_client(), "system:playback_1" ), | |||
| JackPlaybackLatency, | |||
| &range ); | |||
| return range.min; | |||
| #else | |||
| return jack_port_get_latency( jack_port_by_name( jack_client(), "system:playback_1" ) ); | |||
| #endif | |||
| } | |||
| /* THREAD: RT */ | |||
| /** This is the jack slow-sync callback. */ | |||
| int | |||
| @@ -61,6 +61,7 @@ public: | |||
| int dropped ( void ) const { return _buffers_dropped; } | |||
| nframes_t system_latency ( void ) const { return nframes(); } | |||
| nframes_t playback_latency ( void ) const; | |||
| float frames_to_milliseconds ( nframes_t frames ) | |||
| { | |||
| @@ -323,15 +323,3 @@ Timeline::total_capture_xruns ( void ) | |||
| 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(); | |||
| } | |||
| @@ -301,6 +301,28 @@ Track::record ( Capture *c, nframes_t frame ) | |||
| // Fl::unlock(); | |||
| c->region->prepare(); | |||
| nframes_t min,max; | |||
| input[0].get_latency( JACK::Port::Input, &min, &max ); | |||
| if ( transport->freewheel_enabled() ) | |||
| { | |||
| /* in freewheeling mode, assume we're bouncing and only | |||
| * compensate for capture latency */ | |||
| _capture_offset = min; | |||
| } | |||
| else | |||
| { | |||
| /* not freewheeling, so assume we're overdubbing and need to | |||
| * compensate for both capture and playback latency */ | |||
| _capture_offset = min; | |||
| /* since the track output might not be connected to | |||
| * anything, just get the playback latency */ | |||
| _capture_offset += engine->playback_latency(); | |||
| } | |||
| } | |||
| /** write a block to the (already opened) capture file */ | |||
| @@ -331,22 +353,10 @@ Track::finalize ( Capture *c, nframes_t frame ) | |||
| c->region->finalize( frame ); | |||
| 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 ); | |||
| DMESSAGE( "Adjusting capture by %lu frames.", (unsigned long)_capture_offset ); | |||
| c->region->offset( capture_offset ); | |||
| c->region->offset( _capture_offset ); | |||
| _capture_offset = 0; | |||
| timeline->unlock(); | |||
| } | |||
| @@ -923,7 +923,7 @@ static char stats[100]; | |||
| if ( engine && ! engine->zombified() ) | |||
| { | |||
| snprintf( stats, sizeof( stats ), "latency: %.1fms, xruns: %d", | |||
| engine->frames_to_milliseconds( timeline->total_output_latency() ), | |||
| engine->frames_to_milliseconds( engine->system_latency() ), | |||
| engine->xruns() ); | |||
| } | |||
| else | |||
| @@ -263,7 +263,6 @@ public: | |||
| int total_playback_xruns ( void ); | |||
| int total_capture_xruns ( void ); | |||
| nframes_t total_output_latency ( void ) const; | |||
| bool record ( void ); | |||
| void stop ( void ); | |||
| @@ -128,6 +128,7 @@ Track::~Track ( ) | |||
| void | |||
| Track::init ( void ) | |||
| { | |||
| _capture_offset = 0; | |||
| _row = 0; | |||
| _sequence = NULL; | |||
| _name = NULL; | |||
| @@ -105,6 +105,8 @@ private: | |||
| int _row; | |||
| nframes_t _capture_offset; | |||
| enum { AUDIO } _type; | |||
| Audio_Sequence *_sequence; | |||
| @@ -190,6 +190,12 @@ Transport::toggle_record ( void ) | |||
| update_record_state(); | |||
| } | |||
| bool | |||
| Transport::freewheel_enabled ( void ) const | |||
| { | |||
| return _freewheel_button->value(); | |||
| } | |||
| bool | |||
| Transport::rec_enabled ( void ) const | |||
| { | |||
| @@ -58,6 +58,7 @@ public: | |||
| Transport ( int X, int Y, int W, int H, const char *L=0 ); | |||
| bool freewheel_enabled ( void ) const; | |||
| bool rec_enabled ( void ) const; | |||
| bool punch_enabled ( void ) const; | |||
| bool loop_enabled ( void ) const; | |||