X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Faudioengine.cc;h=3122ff760c71eb22d8378dd213310b039cebe881;hb=1d210a54f9b1c0da7a196413bd760ff53f198270;hp=027a6233017b215ac376d57cc878de91b281c35b;hpb=017e16c530bb1a9f186aa81893089dc79b4ddc24;p=ardour.git diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 027a623301..3122ff760c 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -15,22 +15,28 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include #include #include +#include +#include +#include #include #include +#include #include +#include + #include #include #include +#include +#include #include -#include #include #include #include @@ -46,6 +52,14 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +gint AudioEngine::m_meter_exit; + +static void +ardour_jack_error (const char* msg) +{ + error << "JACK: " << msg << endmsg; +} + AudioEngine::AudioEngine (string client_name) : ports (new Ports) { @@ -56,34 +70,59 @@ AudioEngine::AudioEngine (string client_name) last_monitor_check = 0; monitor_check_interval = max_frames; _processed_frames = 0; - _freewheeling = false; _usecs_per_cycle = 0; _jack = 0; _frame_rate = 0; _buffer_size = 0; - _freewheeling = false; _freewheel_thread_registered = false; + _freewheeling = false; m_meter_thread = 0; - m_meter_exit = false; + g_atomic_int_set (&m_meter_exit, 0); if (connect_to_jack (client_name)) { throw NoBackendAvailable (); } - start_metering_thread(); - + Port::set_engine (this); + + // Initialize parameter metadata (e.g. ranges) + Evoral::Parameter p(NullAutomation); + p = EventTypeMap::instance().new_parameter(NullAutomation); + p = EventTypeMap::instance().new_parameter(GainAutomation); + p = EventTypeMap::instance().new_parameter(PanAutomation); + p = EventTypeMap::instance().new_parameter(PluginAutomation); + p = EventTypeMap::instance().new_parameter(SoloAutomation); + p = EventTypeMap::instance().new_parameter(MuteAutomation); + p = EventTypeMap::instance().new_parameter(MidiCCAutomation); + p = EventTypeMap::instance().new_parameter(MidiPgmChangeAutomation); + p = EventTypeMap::instance().new_parameter(MidiPitchBenderAutomation); + p = EventTypeMap::instance().new_parameter(MidiChannelPressureAutomation); + p = EventTypeMap::instance().new_parameter(FadeInAutomation); + p = EventTypeMap::instance().new_parameter(FadeOutAutomation); + p = EventTypeMap::instance().new_parameter(EnvelopeAutomation); + p = EventTypeMap::instance().new_parameter(MidiCCAutomation); } AudioEngine::~AudioEngine () { - if (_running) { - jack_client_close (_jack); + { + Glib::Mutex::Lock tm (_process_lock); + session_removed.signal (); + + if (_running) { + jack_client_close (_jack); + _jack = 0; + } + + stop_metering_thread (); } +} - if(m_meter_thread) { - g_atomic_int_inc(&m_meter_exit); - } +jack_client_t* +AudioEngine::jack() const +{ + return _jack; } void @@ -94,15 +133,23 @@ _thread_init_callback (void *arg) */ PBD::ThreadCreatedWithRequestSize (pthread_self(), X_("Audioengine"), 4096); + MIDI::JACK_MidiPort::set_process_thread (pthread_self()); } int AudioEngine::start () { + if (!_jack) { + error << _("AudioEngine::start() called while disconnected from JACK") << endmsg; + return -1; + } + if (!_running) { if (session) { - jack_nframes_t blocksize = jack_get_buffer_size (_jack); + nframes_t blocksize = jack_get_buffer_size (_jack); + + BootMessage (_("Connect session to engine")); session->set_block_size (blocksize); session->set_frame_rate (jack_get_sample_rate (_jack)); @@ -143,54 +190,65 @@ AudioEngine::start () _has_run = true; Running(); /* EMIT SIGNAL */ } else { - error << _("cannot activate JACK client") << endmsg; + // error << _("cannot activate JACK client") << endmsg; } + + start_metering_thread(); } return _running ? 0 : -1; } int -AudioEngine::stop () +AudioEngine::stop (bool forever) { - if (_running) { - _running = false; - jack_deactivate (_jack); - Stopped(); /* EMIT SIGNAL */ + if (_jack) { + if (forever) { + disconnect_from_jack (); + } else { + jack_deactivate (_jack); + Stopped(); /* EMIT SIGNAL */ + } } return _running ? -1 : 0; } - bool -AudioEngine::get_sync_offset (jack_nframes_t& offset) const +AudioEngine::get_sync_offset (nframes_t& offset) const { + +#ifdef HAVE_JACK_VIDEO_SUPPORT + jack_position_t pos; - (void) jack_transport_query (_jack, &pos); - - if (pos.valid & JackVideoFrameOffset) { - offset = pos.video_offset; - return true; + if (_jack) { + (void) jack_transport_query (_jack, &pos); + + if (pos.valid & JackVideoFrameOffset) { + offset = pos.video_offset; + return true; + } } +#endif + return false; } void -AudioEngine::_jack_timebase_callback (jack_transport_state_t state, jack_nframes_t nframes, +AudioEngine::_jack_timebase_callback (jack_transport_state_t state, nframes_t nframes, jack_position_t* pos, int new_position, void *arg) { static_cast (arg)->jack_timebase_callback (state, nframes, pos, new_position); } void -AudioEngine::jack_timebase_callback (jack_transport_state_t state, jack_nframes_t nframes, +AudioEngine::jack_timebase_callback (jack_transport_state_t state, nframes_t nframes, jack_position_t* pos, int new_position) { - if (session && session->synced_to_jack()) { + if (_jack && session && session->synced_to_jack()) { session->jack_timebase_callback (state, nframes, pos, new_position); } } @@ -204,29 +262,40 @@ AudioEngine::_jack_sync_callback (jack_transport_state_t state, jack_position_t* int AudioEngine::jack_sync_callback (jack_transport_state_t state, jack_position_t* pos) { - if (session) { + if (_jack && session) { return session->jack_sync_callback (state, pos); - } else { - return true; } + + return true; } int AudioEngine::_xrun_callback (void *arg) { - static_cast(arg)->Xrun (); /* EMIT SIGNAL */ + AudioEngine* ae = static_cast (arg); + if (ae->connected()) { + ae->Xrun (); /* EMIT SIGNAL */ + } return 0; } int AudioEngine::_graph_order_callback (void *arg) { - static_cast(arg)->GraphReordered (); /* EMIT SIGNAL */ + AudioEngine* ae = static_cast (arg); + if (ae->connected()) { + ae->GraphReordered (); /* EMIT SIGNAL */ + } return 0; } +/** Wrapped which is called by JACK as its process callback. It is just + * here to get us back into C++ land by calling AudioEngine::process_callback() + * @param nframes Number of frames passed by JACK. + * @param arg User argument passed by JACK, which will be the AudioEngine*. + */ int -AudioEngine::_process_callback (jack_nframes_t nframes, void *arg) +AudioEngine::_process_callback (nframes_t nframes, void *arg) { return static_cast (arg)->process_callback (nframes); } @@ -237,12 +306,18 @@ AudioEngine::_freewheel_callback (int onoff, void *arg) static_cast(arg)->_freewheeling = onoff; } +/** Method called by JACK (via _process_callback) which says that there + * is work to be done. + * @param nframes Number of frames to process. + */ int -AudioEngine::process_callback (jack_nframes_t nframes) +AudioEngine::process_callback (nframes_t nframes) { // CycleTimer ct ("AudioEngine::process"); Glib::Mutex::Lock tm (_process_lock, Glib::TRY_LOCK); - jack_nframes_t next_processed_frames; + + /// The number of frames that will have been processed when we've finished + nframes_t next_processed_frames; /* handle wrap around of total frames counter */ @@ -251,13 +326,15 @@ AudioEngine::process_callback (jack_nframes_t nframes) } else { next_processed_frames = _processed_frames + nframes; } - + if (!tm.locked() || session == 0) { + /* return having done nothing */ _processed_frames = next_processed_frames; return 0; } if (session_remove_pending) { + /* perform the actual session removal */ session = 0; session_remove_pending = false; session_removed.signal(); @@ -265,34 +342,46 @@ AudioEngine::process_callback (jack_nframes_t nframes) return 0; } + boost::shared_ptr p = ports.reader(); + + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { + + /* Only run cycle_start() on output ports, because + inputs must be done in the correct processing order, + which requires interleaving with route processing. + */ + + if ((*i)->sends_output()) { + (*i)->cycle_start (nframes, 0); + } + } + if (_freewheeling) { + /* emit the Freewheel signal and stop freewheeling in the event of trouble */ if (Freewheel (nframes)) { - _freewheeling = false; jack_set_freewheel (_jack, false); } - return 0; + + } else { + if (session) { + session->process (nframes); + } } + + // Finalize ports (ie write data if necessary) - boost::shared_ptr p = ports.reader(); + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { + (*i)->cycle_end (nframes, 0); + } - // Prepare ports (ie read data if necessary) - for (Ports::iterator i = p->begin(); i != p->end(); ++i) - (*i)->cycle_start(nframes); - - session->process (nframes); + if (_freewheeling) { + return 0; + } if (!_running) { - /* we were zombified, maybe because a ladspa plugin took - too long, or jackd exited, or something like that. - */ - _processed_frames = next_processed_frames; return 0; } - - // Finalize ports (ie write data if necessary) - for (Ports::iterator i = p->begin(); i != p->end(); ++i) - (*i)->cycle_end(); if (last_monitor_check + monitor_check_interval < next_processed_frames) { @@ -314,18 +403,32 @@ AudioEngine::process_callback (jack_nframes_t nframes) last_monitor_check = next_processed_frames; } + if (session->silent()) { + + boost::shared_ptr p = ports.reader(); + + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { + + Port *port = (*i); + + if (port->sends_output()) { + port->get_buffer().silence(nframes); + } + } + } + _processed_frames = next_processed_frames; return 0; } int -AudioEngine::_sample_rate_callback (jack_nframes_t nframes, void *arg) +AudioEngine::_sample_rate_callback (nframes_t nframes, void *arg) { return static_cast (arg)->jack_sample_rate_callback (nframes); } int -AudioEngine::jack_sample_rate_callback (jack_nframes_t nframes) +AudioEngine::jack_sample_rate_callback (nframes_t nframes) { _frame_rate = nframes; _usecs_per_cycle = (int) floor ((((double) frames_per_cycle() / nframes)) * 1000000.0); @@ -345,13 +448,13 @@ AudioEngine::jack_sample_rate_callback (jack_nframes_t nframes) } int -AudioEngine::_bufsize_callback (jack_nframes_t nframes, void *arg) +AudioEngine::_bufsize_callback (nframes_t nframes, void *arg) { return static_cast (arg)->jack_bufsize_callback (nframes); } int -AudioEngine::jack_bufsize_callback (jack_nframes_t nframes) +AudioEngine::jack_bufsize_callback (nframes_t nframes) { _buffer_size = nframes; _usecs_per_cycle = (int) floor ((((double) nframes / frame_rate())) * 1000000.0); @@ -370,29 +473,73 @@ AudioEngine::jack_bufsize_callback (jack_nframes_t nframes) return 0; } +void +AudioEngine::stop_metering_thread () +{ + if (m_meter_thread) { + g_atomic_int_set (&m_meter_exit, 1); + m_meter_thread->join (); + m_meter_thread = 0; + } +} + void AudioEngine::start_metering_thread () { - if(m_meter_thread == 0) { - m_meter_thread = Glib::Thread::create (sigc::mem_fun(this, &AudioEngine::meter_thread), false); + if (m_meter_thread == 0) { + g_atomic_int_set (&m_meter_exit, 0); + m_meter_thread = Glib::Thread::create (sigc::mem_fun(this, &AudioEngine::meter_thread), + 500000, true, true, Glib::THREAD_PRIORITY_NORMAL); } } void AudioEngine::meter_thread () { - while (g_atomic_int_get(&m_meter_exit) != true) { - Glib::usleep (10000); /* 1/100th sec interval */ - IO::update_meters (); + while (true) { + Glib::usleep (10000); /* 1/100th sec interval */ + if (g_atomic_int_get(&m_meter_exit)) { + break; + } + IO::update_meters (); } - return; } void AudioEngine::set_session (Session *s) { + Glib::Mutex::Lock pl (_process_lock); + if (!session) { + session = s; + + nframes_t blocksize = jack_get_buffer_size (_jack); + + /* page in as much of the session process code as we + can before we really start running. + */ + + boost::shared_ptr p = ports.reader(); + + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { + if ((*i)->sends_output()) { + (*i)->cycle_start (blocksize, 0); + } + } + + s->process (blocksize); + s->process (blocksize); + s->process (blocksize); + s->process (blocksize); + s->process (blocksize); + s->process (blocksize); + s->process (blocksize); + s->process (blocksize); + + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { + (*i)->cycle_end (blocksize, 0); + } } } @@ -406,144 +553,145 @@ AudioEngine::remove_session () if (session) { session_remove_pending = true; session_removed.wait(_process_lock); - } + } } else { - session = 0; - } - remove_all_ports (); - + //FIXME: Preliminary bugfix for http://tracker.ardour.org/view.php?id=1985 + //remove_all_ports (); } +void +AudioEngine::port_registration_failure (const std::string& portname) +{ + string full_portname = jack_client_name; + full_portname += ':'; + full_portname += portname; + + + jack_port_t* p = jack_port_by_name (_jack, full_portname.c_str()); + string reason; + + if (p) { + reason = _("a port with this name already exists: check for duplicated track/bus names"); + } else { + reason = _("unknown error"); + } + + throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str()); +} + Port * -AudioEngine::register_input_port (DataType type, const string& portname) +AudioEngine::register_port (DataType dtype, const string& portname, bool input, bool publish) { - if (!_running) { - if (!_has_run) { - fatal << _("register input port called before engine was started") << endmsg; - /*NOTREACHED*/ + Port* newport = 0; + + /*cerr << "trying to register port with name " << portname << endl;*/ + try { + if (dtype == DataType::AUDIO) { + newport = new AudioPort (portname, (input ? Port::IsInput : Port::IsOutput), publish, frames_per_cycle()); + } else if (dtype == DataType::MIDI) { + newport = new MidiPort (portname, (input ? Port::IsInput : Port::IsOutput), publish, frames_per_cycle()); } else { - return 0; + throw unknown_type(); } - } - jack_port_t *p = jack_port_register (_jack, portname.c_str(), type.to_jack_type(), JackPortIsInput, 0); + /*cerr << "successfully got port " << portname << " with address " << newport << endl;*/ - if (p) { - - Port* newport = 0; - - if (type == DataType::AUDIO) - newport = new AudioPort (p); - else if (type == DataType::MIDI) - newport = new MidiPort (p); - else - throw unknown_type(); + RCUWriter writer (ports); + boost::shared_ptr ps = writer.get_copy (); + /*cerr << "Address of ports list: " << ps << endl + << "Ports set size before insert: " << ps->size() << endl;*/ + ps->insert (ps->begin(), newport); + /*cerr << "Ports set size after insert: " << ps->size() << endl;*/ - if (newport != 0) { - RCUWriter writer (ports); - boost::shared_ptr ps = writer.get_copy (); - ps->insert (ps->begin(), newport); - /* writer goes out of scope, forces update */ - } + /* writer goes out of scope, forces update */ return newport; - - } else { - - _process_lock.unlock(); - throw PortRegistrationFailure(); } - return 0; + catch (...) { + throw PortRegistrationFailure("unable to create port (unknown type?)"); + } } -Port * -AudioEngine::register_output_port (DataType type, const string& portname) +Port* +AudioEngine::get_port (const std::string& full_name) { - if (!_running) { - if (!_has_run) { - fatal << _("register output port called before engine was started") << endmsg; - /*NOTREACHED*/ - } else { - return 0; - } - } - - jack_port_t* p = 0; + boost::shared_ptr p = ports.reader(); - if ((p = jack_port_register (_jack, portname.c_str(), - type.to_jack_type(), JackPortIsOutput, 0)) != 0) { - - Port* newport = 0; - - if (type == DataType::AUDIO) - newport = new AudioPort (p); - else if (type == DataType::MIDI) - newport = new MidiPort (p); - else - throw unknown_type (); - - if (newport != 0) { - RCUWriter writer (ports); - boost::shared_ptr ps = writer.get_copy (); - ps->insert (ps->begin(), newport); - /* writer goes out of scope, forces update */ + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { + //cerr << "comparing port name '" << (*i)->name() << "' with '" << full_name << "'" << endl; + if ((*i)->name() == full_name) { + return *i; } - - return newport; - - } else { - - _process_lock.unlock(); - throw PortRegistrationFailure (); } - return 0; } -int +Port * +AudioEngine::register_input_port (DataType type, const string& portname, bool publish) +{ + return register_port (type, portname, true, publish); +} + +Port * +AudioEngine::register_output_port (DataType type, const string& portname, bool publish) +{ + return register_port (type, portname, false, publish); +} + +int AudioEngine::unregister_port (Port& port) { + /* caller must hold process lock */ + + cerr << "about to unregister Port xx x" << &port << "\n"; + if (!_running) { /* probably happening when the engine has been halted by JACK, in which case, there is nothing we can do here. */ + cerr << "not running\n"; return 0; } - int ret = jack_port_unregister (_jack, port._port); - - if (ret == 0) { - - { - - RCUWriter writer (ports); - boost::shared_ptr ps = writer.get_copy (); - - for (Ports::iterator i = ps->begin(); i != ps->end(); ++i) { - if ((*i) == &port) { - ps->erase (i); - break; - } + { + cerr << "before getcopy\n"; + + RCUWriter writer (ports); + boost::shared_ptr ps = writer.get_copy (); + + cerr << "Ports set size: " << ps.get()->size() << endl; + + for (Ports::iterator i = ps->begin(); i != ps->end(); ++i) { + cerr << "before delete" << endl; + if ((*i) == &port) { + cerr << "About to delete " << &port << endl; + delete *i; + ps->erase (i); + cerr << "After erasing ports size: " << ps->size(); + break; } - - /* writer goes out of scope, forces update */ } - - remove_connections_for (port); + + /* writer goes out of scope, forces update */ } + + cerr << "before remove_connections\n"; + remove_connections_for (port); - return ret; + return 0; } int AudioEngine::connect (const string& source, const string& destination) { + int ret; + if (!_running) { if (!_has_run) { fatal << _("connect called before engine was started") << endmsg; @@ -552,18 +700,59 @@ AudioEngine::connect (const string& source, const string& destination) return -1; } } - + string s = make_port_name_non_relative (source); string d = make_port_name_non_relative (destination); + + //cerr << "Trying to connect source: " << s << " with destination " << d << endl; + + Port* src = get_port (s); + Port* dst = get_port (d); + + if (src && dst) { + + /* both ports are known to us, so do the internal connect stuff */ - int ret = jack_connect (_jack, s.c_str(), d.c_str()); + if ((ret = src->connect (*dst)) == 0) { + ret = dst->connect (*src); + } + + } else if (src || dst) { + + /* one port is known to us, try to connect it to something external */ + + PortConnectableByName* pcn; + string other; + + if (src) { + pcn = dynamic_cast(src); + other = d; + } else { + pcn = dynamic_cast(dst); + other = s; + } + + if (pcn) { + ret = pcn->connect (other); + } else { + ret = -1; + } - if (ret == 0) { - pair c (s, d); - port_connections.push_back (c); } else { + + /* neither port is known to us, and this API isn't intended for use as a general patch bay */ + + ret = -1; + + } + + if (ret > 0) { + error << string_compose(_("AudioEngine: connection already exists: %1 (%2) to %3 (%4)"), + source, s, destination, d) + << endmsg; + } else if (ret < 0) { error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"), - source, s, destination, d) + source, s, destination, d) << endmsg; } @@ -573,6 +762,8 @@ AudioEngine::connect (const string& source, const string& destination) int AudioEngine::disconnect (const string& source, const string& destination) { + int ret; + if (!_running) { if (!_has_run) { fatal << _("disconnect called before engine was started") << endmsg; @@ -585,17 +776,49 @@ AudioEngine::disconnect (const string& source, const string& destination) string s = make_port_name_non_relative (source); string d = make_port_name_non_relative (destination); - int ret = jack_disconnect (_jack, s.c_str(), d.c_str()); + //cerr << "trying to disconnect port '" << s << "' from port '" << d << endl; + + Port* src = get_port (s); + Port* dst = get_port (d); + + if (src && dst) { - if (ret == 0) { - pair c (s, d); - PortConnections::iterator i; + /* both ports are known to us, so do the internal connect stuff */ - if ((i = find (port_connections.begin(), port_connections.end(), c)) != port_connections.end()) { - port_connections.erase (i); + if ((ret = src->disconnect (*dst)) == 0) { + ret = dst->disconnect (*src); } + + } else if (src || dst) { + + /* one port is known to us, try to connect it to something external */ + + + PortConnectableByName* pcn; + string other; + + if (src) { + pcn = dynamic_cast(src); + other = d; + } else { + pcn = dynamic_cast(dst); + other = s; + } + + if (pcn) { + ret = pcn->disconnect (other); + } else { + ret = -1; + } + + } else { + + /* neither port is known to us, and this API isn't intended for use as a general patch bay */ + + ret = -1; + } - + return ret; } @@ -611,17 +834,10 @@ AudioEngine::disconnect (Port& port) } } - int ret = jack_port_disconnect (_jack, port._port); - - if (ret == 0) { - remove_connections_for (port); - } - - return ret; - + return port.disconnect_all (); } -jack_nframes_t +ARDOUR::nframes_t AudioEngine::frame_rate () { if (_jack) { @@ -638,7 +854,7 @@ AudioEngine::frame_rate () } } -jack_nframes_t +ARDOUR::nframes_t AudioEngine::frames_per_cycle () { if (_jack) { @@ -700,19 +916,43 @@ AudioEngine::get_ports (const string& port_name_pattern, const string& type_name void AudioEngine::halted (void *arg) { - AudioEngine *ae = reinterpret_cast (arg); + AudioEngine* ae = static_cast (arg); + bool was_running = ae->_running; - ae->_running = false; - ae->_jack = 0; + ae->stop_metering_thread (); + ae->_running = false; ae->_buffer_size = 0; ae->_frame_rate = 0; - ae->Halted(); /* EMIT SIGNAL */ + cerr << "!!! HALTED !!!\n"; + + if (was_running) { + ae->Halted(); /* EMIT SIGNAL */ + } +} + +bool +AudioEngine::can_request_hardware_monitoring () +{ + const char ** ports; + + if (!_jack) { + return 0; + } + + if ((ports = jack_get_ports (_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) { + return false; + } + + free (ports); + + return true; } + uint32_t -AudioEngine::n_physical_outputs () const +AudioEngine::n_physical_outputs (DataType type) const { const char ** ports; uint32_t i = 0; @@ -721,19 +961,18 @@ AudioEngine::n_physical_outputs () const return 0; } - if ((ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == 0) { + if ((ports = jack_get_ports (_jack, NULL, type.to_jack_type(), JackPortIsPhysical|JackPortIsInput)) == 0) { return 0; } - if (ports) { - for (i = 0; ports[i]; ++i); - free (ports); - } + for (i = 0; ports[i]; ++i); + free (ports); + return i; } uint32_t -AudioEngine::n_physical_inputs () const +AudioEngine::n_physical_inputs (DataType type) const { const char ** ports; uint32_t i = 0; @@ -742,7 +981,7 @@ AudioEngine::n_physical_inputs () const return 0; } - if ((ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == 0) { + if ((ports = jack_get_ports (_jack, NULL, type.to_jack_type(), JackPortIsPhysical|JackPortIsOutput)) == 0) { return 0; } @@ -754,7 +993,7 @@ AudioEngine::n_physical_inputs () const } void -AudioEngine::get_physical_inputs (vector& ins) +AudioEngine::get_physical_inputs (DataType type, vector& ins) { const char ** ports; uint32_t i = 0; @@ -763,7 +1002,7 @@ AudioEngine::get_physical_inputs (vector& ins) return; } - if ((ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == 0) { + if ((ports = jack_get_ports (_jack, NULL, type.to_jack_type(), JackPortIsPhysical|JackPortIsOutput)) == 0) { return; } @@ -776,7 +1015,7 @@ AudioEngine::get_physical_inputs (vector& ins) } void -AudioEngine::get_physical_outputs (vector& outs) +AudioEngine::get_physical_outputs (DataType type, vector& outs) { const char ** ports; uint32_t i = 0; @@ -785,7 +1024,7 @@ AudioEngine::get_physical_outputs (vector& outs) return; } - if ((ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == 0) { + if ((ports = jack_get_ports (_jack, NULL, type.to_jack_type(), JackPortIsPhysical|JackPortIsInput)) == 0) { return; } @@ -832,28 +1071,33 @@ AudioEngine::get_nth_physical (DataType type, uint32_t n, int flag) return ret; } -jack_nframes_t +ARDOUR::nframes_t AudioEngine::get_port_total_latency (const Port& port) +{ + return port.total_latency (); +} + +void +AudioEngine::update_total_latency (const Port& port) { if (!_jack) { - fatal << _("get_port_total_latency() called with no JACK client connection") << endmsg; + fatal << _("update_total_latency() called with no JACK client connection") << endmsg; /*NOTREACHED*/ } if (!_running) { if (!_has_run) { - fatal << _("get_port_total_latency() called before engine was started") << endmsg; + fatal << _("update_total_latency() called before engine was started") << endmsg; /*NOTREACHED*/ } } - return jack_port_get_total_latency (_jack, port._port); + port.recompute_total_latency (); } void AudioEngine::transport_stop () { - // cerr << "tell JACK to stop\n"; if (_jack) { jack_transport_stop (_jack); } @@ -869,7 +1113,7 @@ AudioEngine::transport_start () } void -AudioEngine::transport_locate (jack_nframes_t where) +AudioEngine::transport_locate (nframes_t where) { // cerr << "tell JACK to locate to " << where << endl; if (_jack) { @@ -907,11 +1151,18 @@ AudioEngine::freewheel (bool onoff) { if (_jack) { - if (onoff) { - _freewheel_thread_registered = false; - } + if (onoff != _freewheeling) { - return jack_set_freewheel (_jack, onoff); + if (onoff) { + _freewheel_thread_registered = false; + } + + return jack_set_freewheel (_jack, onoff); + + } else { + /* already doing what has been asked for */ + return 0; + } } else { return -1; @@ -923,21 +1174,11 @@ AudioEngine::remove_all_ports () { /* process lock MUST be held */ - if (_jack) { - boost::shared_ptr p = ports.reader(); - - for (Ports::iterator i = p->begin(); i != p->end(); ++i) { - jack_port_unregister (_jack, (*i)->_port); - } - } - { RCUWriter writer (ports); boost::shared_ptr ps = writer.get_copy (); ps->clear (); } - - port_connections.clear (); } void @@ -957,6 +1198,7 @@ AudioEngine::remove_connections_for (Port& port) } } + #ifdef HAVE_JACK_CLIENT_OPEN int @@ -967,26 +1209,18 @@ AudioEngine::connect_to_jack (string client_name) const char *server_name = NULL; jack_client_name = client_name; /* might be reset below */ - _jack = jack_client_open (jack_client_name.c_str(), options, &status, server_name); - - if (_jack == NULL) { - if (status & JackServerFailed) { - error << _("Unable to connect to JACK server") << endmsg; - } - - error << string_compose (_("Could not connect to JACK server as \"%1\""), jack_client_name) << endmsg; + if (_jack == NULL) { + // error message is not useful here return -1; } - if (status & JackServerStarted) { - info << _("JACK server started") << endmsg; - } - if (status & JackNameNotUnique) { jack_client_name = jack_get_client_name (_jack); } + + jack_set_error_function (ardour_jack_error); return 0; } @@ -998,7 +1232,7 @@ AudioEngine::connect_to_jack (string client_name) { jack_client_name = client_name; - if ((_jack = jack_client_new (client_name.c_str())) == NULL) { + if ((_jack = jack_client_new (client_name.c_str())) == 0) { return -1; } @@ -1010,12 +1244,19 @@ AudioEngine::connect_to_jack (string client_name) int AudioEngine::disconnect_from_jack () { - if (_jack == 0) { + if (!_jack) { return 0; } - if (jack_client_close (_jack)) { - error << _("cannot shutdown connection to JACK") << endmsg; + + if (_running) { + stop_metering_thread (); + } + + { + Glib::Mutex::Lock lm (_process_lock); + jack_client_close (_jack); + _jack = 0; } _buffer_size = 0; @@ -1026,14 +1267,13 @@ AudioEngine::disconnect_from_jack () Stopped(); /* EMIT SIGNAL */ } - _jack = 0; return 0; } int AudioEngine::reconnect_to_jack () { - if (_jack) { + if (_running) { disconnect_from_jack (); /* XXX give jackd a chance */ Glib::usleep (250000); @@ -1049,38 +1289,21 @@ AudioEngine::reconnect_to_jack () boost::shared_ptr p = ports.reader (); for (i = p->begin(); i != p->end(); ++i) { - - /* XXX hack hack hack */ - - string long_name = (*i)->name(); - string short_name; - - short_name = long_name.substr (long_name.find_last_of (':') + 1); - - if (((*i)->_port = jack_port_register (_jack, short_name.c_str(), (*i)->type().to_jack_type(), (*i)->flags(), 0)) == 0) { - error << string_compose (_("could not reregister %1"), (*i)->name()) << endmsg; + if ((*i)->reestablish ()) { break; - } else { - } - - (*i)->reset (); - - if ((*i)->flags() & JackPortIsOutput) { - (*i)->silence (jack_get_buffer_size (_jack), 0); - } + } } if (i != p->end()) { /* failed */ - for (Ports::iterator i = p->begin(); i != p->end(); ++i) { - jack_port_unregister (_jack, (*i)->_port); - } + remove_all_ports (); return -1; } if (session) { - jack_nframes_t blocksize = jack_get_buffer_size (_jack); + session->reset_jack_connection (_jack); + nframes_t blocksize = jack_get_buffer_size (_jack); session->set_block_size (blocksize); session->set_frame_rate (jack_get_sample_rate (_jack)); } @@ -1110,30 +1333,28 @@ AudioEngine::reconnect_to_jack () /* re-establish connections */ - for (PortConnections::iterator i = port_connections.begin(); i != port_connections.end(); ++i) { - - int err; - - if ((err = jack_connect (_jack, (*i).first.c_str(), (*i).second.c_str())) != 0) { - if (err != EEXIST) { - error << string_compose (_("could not reconnect %1 and %2 (err = %3)"), - (*i).first, (*i).second, err) - << endmsg; - } - } + for (i = p->begin(); i != p->end(); ++i) { + (*i)->reconnect (); } Running (); /* EMIT SIGNAL*/ + start_metering_thread (); + return 0; } int -AudioEngine::request_buffer_size (jack_nframes_t nframes) +AudioEngine::request_buffer_size (nframes_t nframes) { if (_jack) { - int ret = jack_set_buffer_size (_jack, nframes); - return ret; + + if (nframes == jack_get_buffer_size (_jack)) { + return 0; + } + + return jack_set_buffer_size (_jack, nframes); + } else { return -1; } @@ -1143,7 +1364,9 @@ void AudioEngine::update_total_latencies () { #ifdef HAVE_JACK_RECOMPUTE_LATENCIES - jack_recompute_total_latencies (_jack); + if (_jack) { + jack_recompute_total_latencies (_jack); + } #endif } @@ -1184,3 +1407,12 @@ AudioEngine::make_port_name_non_relative (string portname) return str; } +bool +AudioEngine::is_realtime () const +{ + if (_jack) { + return jack_is_realtime (_jack); + } else { + return false; + } +}