X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Faudioengine.cc;h=cbdd1fda1ce8fc088c66161295cd3775a356dd66;hb=c7e404a1c0ee9af941a335e4bdd2f667b0c6317a;hp=982a7c5971cb0fa0c718444fd6e32a97d608aeb1;hpb=79986643c0c904f6574bb5323e2233a43a9e622e;p=ardour.git diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 982a7c5971..cbdd1fda1c 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -15,19 +15,24 @@ 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 @@ -43,10 +48,16 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -jack_nframes_t Port::_short_over_length = 2; -jack_nframes_t Port::_long_over_length = 10; +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) { session = 0; session_remove_pending = false; @@ -62,27 +73,30 @@ AudioEngine::AudioEngine (string client_name) _buffer_size = 0; _freewheeling = false; _freewheel_thread_registered = false; - - m_meter_thread = 0; - m_meter_exit = false; - start_metering_thread(); - + m_meter_thread = 0; + g_atomic_int_set (&m_meter_exit, 0); + if (connect_to_jack (client_name)) { throw NoBackendAvailable (); } + start_metering_thread(); + } AudioEngine::~AudioEngine () { - if (_running) { - jack_client_close (_jack); + { + Glib::Mutex::Lock tm (_process_lock); + session_removed.signal (); + + if (_running) { + jack_client_close (_jack); + } + + stop_metering_thread (); } - - if(m_meter_thread) { - g_atomic_int_inc(&m_meter_exit); - } } void @@ -101,7 +115,7 @@ AudioEngine::start () if (!_running) { if (session) { - jack_nframes_t blocksize = jack_get_buffer_size (_jack); + nframes_t blocksize = jack_get_buffer_size (_jack); session->set_block_size (blocksize); session->set_frame_rate (jack_get_sample_rate (_jack)); @@ -150,11 +164,18 @@ AudioEngine::start () } int -AudioEngine::stop () +AudioEngine::stop (bool forever) { if (_running) { _running = false; - jack_deactivate (_jack); + if (forever) { + jack_client_t* foo = _jack; + _jack = 0; + jack_client_close (foo); + stop_metering_thread (); + } else { + jack_deactivate (_jack); + } Stopped(); /* EMIT SIGNAL */ } @@ -162,20 +183,38 @@ AudioEngine::stop () } -void -AudioEngine::_jack_timebase_callback (jack_transport_state_t state, jack_nframes_t nframes, +bool +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; + } - jack_position_t* pos, int new_position, void *arg) +#endif + + return false; +} + +void +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, - - jack_position_t* pos, int new_position) +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); } } @@ -189,29 +228,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->jack()) { + 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->jack()) { + 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); } @@ -222,12 +272,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 */ @@ -236,13 +292,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(); @@ -251,26 +309,41 @@ AudioEngine::process_callback (jack_nframes_t nframes) } if (_freewheeling) { + /* emit the Freewheel signal and stop freewheeling in the event of trouble */ if (Freewheel (nframes)) { + cerr << "Freewheeling returned non-zero!\n"; _freewheeling = false; jack_set_freewheel (_jack, false); } return 0; } - session->process (nframes); + boost::shared_ptr p = ports.reader(); + + // Prepare ports (ie read data if necessary) + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { + (*i)->cycle_start (nframes); + } + + if (session) { + session->process (nframes); + } 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) { - for (Ports::iterator i = ports.begin(); i != ports.end(); ++i) { + + boost::shared_ptr p = ports.reader(); + + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { Port *port = (*i); bool x; @@ -291,13 +364,13 @@ AudioEngine::process_callback (jack_nframes_t nframes) } 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); @@ -317,19 +390,21 @@ 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); last_monitor_check = 0; - for (Ports::iterator i = ports.begin(); i != ports.end(); ++i) { + boost::shared_ptr p = ports.reader(); + + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { (*i)->reset(); } @@ -340,29 +415,69 @@ 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) { + 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) + (*i)->cycle_start (blocksize); + + 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 (); + } } @@ -376,16 +491,13 @@ AudioEngine::remove_session () if (session) { session_remove_pending = true; session_removed.wait(_process_lock); - } + } } else { - session = 0; - } remove_all_ports (); - } Port * @@ -400,20 +512,29 @@ AudioEngine::register_input_port (DataType type, const string& portname) } } - jack_port_t *p = jack_port_register (_jack, portname.c_str(), - type.to_jack_type(), JackPortIsInput, 0); + jack_port_t *p = jack_port_register (_jack, portname.c_str(), type.to_jack_type(), JackPortIsInput, 0); if (p) { - Port *newport; - if ((newport = new Port (p)) != 0) { - ports.insert (ports.begin(), newport); + 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 */ } + return newport; } else { - - _process_lock.unlock(); throw PortRegistrationFailure(); } @@ -432,17 +553,30 @@ AudioEngine::register_output_port (DataType type, const string& portname) } } - jack_port_t *p; - + jack_port_t* p = 0; + if ((p = jack_port_register (_jack, portname.c_str(), - type.to_jack_type(), JackPortIsOutput, 0)) != 0) { - Port *newport = new Port (p); - ports.insert (ports.begin(), newport); + 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 */ + } + return newport; - + } else { - - _process_lock.unlock(); throw PortRegistrationFailure (); } @@ -451,36 +585,38 @@ AudioEngine::register_output_port (DataType type, const string& portname) int -AudioEngine::unregister_port (Port *port) +AudioEngine::unregister_port (Port& port) { if (!_running) { /* probably happening when the engine has been halted by JACK, in which case, there is nothing we can do here. - */ + */ return 0; } - if (port) { + int ret = jack_port_unregister (_jack, port._port); - int ret = jack_port_unregister (_jack, port->_port); - - if (ret == 0) { + if (ret == 0) { + + { + + RCUWriter writer (ports); + boost::shared_ptr ps = writer.get_copy (); - for (Ports::iterator i = ports.begin(); i != ports.end(); ++i) { - if ((*i) == port) { - ports.erase (i); + for (Ports::iterator i = ps->begin(); i != ps->end(); ++i) { + if ((*i) == &port) { + ps->erase (i); break; } } - remove_connections_for (port); + /* writer goes out of scope, forces update */ } - return ret; - - } else { - return -1; + remove_connections_for (port); } + + return ret; } int @@ -503,6 +639,10 @@ AudioEngine::connect (const string& source, const string& destination) if (ret == 0) { pair c (s, d); port_connections.push_back (c); + } else if (ret == EEXIST) { + error << string_compose(_("AudioEngine: connection already exists: %1 (%2) to %3 (%4)"), + source, s, destination, d) + << endmsg; } else { error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"), source, s, destination, d) @@ -542,7 +682,7 @@ AudioEngine::disconnect (const string& source, const string& destination) } int -AudioEngine::disconnect (Port *port) +AudioEngine::disconnect (Port& port) { if (!_running) { if (!_has_run) { @@ -553,7 +693,7 @@ AudioEngine::disconnect (Port *port) } } - int ret = jack_port_disconnect (_jack, port->_port); + int ret = jack_port_disconnect (_jack, port._port); if (ret == 0) { remove_connections_for (port); @@ -563,7 +703,7 @@ AudioEngine::disconnect (Port *port) } -jack_nframes_t +ARDOUR::nframes_t AudioEngine::frame_rate () { if (_jack) { @@ -580,7 +720,7 @@ AudioEngine::frame_rate () } } -jack_nframes_t +ARDOUR::nframes_t AudioEngine::frames_per_cycle () { if (_jack) { @@ -597,6 +737,9 @@ AudioEngine::frames_per_cycle () } } +/** Get a port by name. + * Note this can return NULL, it will NOT create a port if it is not found (any more). + */ Port * AudioEngine::get_port_by_name (const string& portname, bool keep) { @@ -611,25 +754,15 @@ AudioEngine::get_port_by_name (const string& portname, bool keep) } } - /* check to see if we have a Port for this name already */ - - for (Ports::iterator i = ports.begin(); i != ports.end(); ++i) { + boost::shared_ptr pr = ports.reader(); + + for (Ports::iterator i = pr->begin(); i != pr->end(); ++i) { if (portname == (*i)->name()) { return (*i); } } - jack_port_t *p; - - if ((p = jack_port_by_name (_jack, portname.c_str())) != 0) { - Port *newport = new Port (p); - if (keep && newport->is_mine (_jack)) { - ports.insert (newport); - } - return newport; - } else { - return 0; - } + return 0; } const char ** @@ -649,13 +782,12 @@ 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); ae->_running = false; - ae->_jack = 0; - ae->_buffer_size = 0; ae->_frame_rate = 0; + ae->_jack = 0; ae->Halted(); /* EMIT SIGNAL */ } @@ -702,13 +834,59 @@ AudioEngine::n_physical_inputs () const return i; } +void +AudioEngine::get_physical_inputs (vector& ins) +{ + const char ** ports; + uint32_t i = 0; + + if (!_jack) { + return; + } + + if ((ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == 0) { + return; + } + + if (ports) { + for (i = 0; ports[i]; ++i) { + ins.push_back (ports[i]); + } + free (ports); + } +} + +void +AudioEngine::get_physical_outputs (vector& outs) +{ + const char ** ports; + uint32_t i = 0; + + if (!_jack) { + return; + } + + if ((ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == 0) { + return; + } + + if (ports) { + for (i = 0; ports[i]; ++i) { + outs.push_back (ports[i]); + } + free (ports); + } +} + string -AudioEngine::get_nth_physical (uint32_t n, int flag) +AudioEngine::get_nth_physical (DataType type, uint32_t n, int flag) { const char ** ports; uint32_t i; string ret; + assert(type != DataType::NIL); + if (!_running || !_jack) { if (!_has_run) { fatal << _("get_nth_physical called before engine was started") << endmsg; @@ -718,7 +896,7 @@ AudioEngine::get_nth_physical (uint32_t n, int flag) } } - ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|flag); + ports = jack_get_ports (_jack, NULL, type.to_jack_type(), JackPortIsPhysical|flag); if (ports == 0) { return ""; @@ -735,7 +913,7 @@ AudioEngine::get_nth_physical (uint32_t n, int flag) return ret; } -jack_nframes_t +ARDOUR::nframes_t AudioEngine::get_port_total_latency (const Port& port) { if (!_jack) { @@ -772,7 +950,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) { @@ -827,17 +1005,24 @@ AudioEngine::remove_all_ports () /* process lock MUST be held */ if (_jack) { - for (Ports::iterator i = ports.begin(); i != ports.end(); ++i) { + boost::shared_ptr p = ports.reader(); + + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { jack_port_unregister (_jack, (*i)->_port); } } - ports.clear (); + { + RCUWriter writer (ports); + boost::shared_ptr ps = writer.get_copy (); + ps->clear (); + } + port_connections.clear (); } void -AudioEngine::remove_connections_for (Port* port) +AudioEngine::remove_connections_for (Port& port) { for (PortConnections::iterator i = port_connections.begin(); i != port_connections.end(); ) { PortConnections::iterator tmp; @@ -845,7 +1030,7 @@ AudioEngine::remove_connections_for (Port* port) tmp = i; ++tmp; - if ((*i).first == port->name()) { + if ((*i).first == port.name()) { port_connections.erase (i); } @@ -883,6 +1068,8 @@ AudioEngine::connect_to_jack (string client_name) if (status & JackNameNotUnique) { jack_client_name = jack_get_client_name (_jack); } + + jack_set_error_function (ardour_jack_error); return 0; } @@ -910,9 +1097,7 @@ AudioEngine::disconnect_from_jack () return 0; } - if (jack_client_close (_jack)) { - error << _("cannot shutdown connection to JACK") << endmsg; - } + jack_client_close (_jack); _buffer_size = 0; _frame_rate = 0; @@ -932,7 +1117,7 @@ AudioEngine::reconnect_to_jack () if (_jack) { disconnect_from_jack (); /* XXX give jackd a chance */ - Glib::usleep (250000); + Glib::usleep (250000); } if (connect_to_jack (jack_client_name)) { @@ -942,7 +1127,9 @@ AudioEngine::reconnect_to_jack () Ports::iterator i; - for (i = ports.begin(); i != ports.end(); ++i) { + boost::shared_ptr p = ports.reader (); + + for (i = p->begin(); i != p->end(); ++i) { /* XXX hack hack hack */ @@ -951,7 +1138,7 @@ AudioEngine::reconnect_to_jack () short_name = long_name.substr (long_name.find_last_of (':') + 1); - if (((*i)->_port = jack_port_register (_jack, short_name.c_str(), (*i)->type(), (*i)->flags(), 0)) == 0) { + 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; break; } else { @@ -960,12 +1147,13 @@ AudioEngine::reconnect_to_jack () (*i)->reset (); if ((*i)->flags() & JackPortIsOutput) { - (*i)->silence (jack_get_buffer_size (_jack), 0); + (*i)->get_buffer().silence (jack_get_buffer_size (_jack), 0); } } - if (i != ports.end()) { - for (Ports::iterator i = ports.begin(); i != ports.end(); ++i) { + if (i != p->end()) { + /* failed */ + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { jack_port_unregister (_jack, (*i)->_port); } return -1; @@ -973,7 +1161,8 @@ AudioEngine::reconnect_to_jack () 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)); } @@ -1022,11 +1211,16 @@ AudioEngine::reconnect_to_jack () } 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; } @@ -1077,3 +1271,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; + } +}