X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Faudioengine.cc;h=0058acc7e2cf9080b2edee1894b39c07f4106ad2;hb=d488ce49bb71d99d1f23a30ff6594e2ad759f6fa;hp=398a4936cc283481fe4e8dea0f41fe3d5865f428;hpb=6ef5d85ae1f2c500c4163cd1df580b3f26991c1e;p=ardour.git diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 398a4936cc..0058acc7e2 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -25,11 +25,15 @@ #include #include +#include +#include + #include "pbd/pthread_utils.h" #include "pbd/stacktrace.h" #include "pbd/unknown_type.h" #include "midi++/jack.h" +#include "midi++/mmc.h" #include "ardour/amp.h" #include "ardour/audio_port.h" @@ -43,6 +47,7 @@ #include "ardour/io.h" #include "ardour/meter.h" #include "ardour/midi_port.h" +#include "ardour/process_thread.h" #include "ardour/port.h" #include "ardour/port_set.h" #include "ardour/session.h" @@ -61,7 +66,7 @@ AudioEngine* AudioEngine::_instance = 0; #define GET_PRIVATE_JACK_POINTER(j) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return; } #define GET_PRIVATE_JACK_POINTER_RET(j,r) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return r; } -AudioEngine::AudioEngine (string client_name) +AudioEngine::AudioEngine (string client_name, string session_uuid) : ports (new Ports) { _instance = this; /* singleton */ @@ -77,11 +82,12 @@ AudioEngine::AudioEngine (string client_name) _frame_rate = 0; _buffer_size = 0; _freewheeling = false; + _main_thread = 0; m_meter_thread = 0; g_atomic_int_set (&m_meter_exit, 0); - if (connect_to_jack (client_name)) { + if (connect_to_jack (client_name, session_uuid)) { throw NoBackendAvailable (); } @@ -141,6 +147,20 @@ _thread_init_callback (void * /*arg*/) SessionEvent::create_per_thread_pool (X_("Audioengine"), 512); MIDI::JACK_MidiPort::set_process_thread (pthread_self()); + MIDI::MachineControl::set_sending_thread (pthread_self ()); +} + +typedef void (*_JackInfoShutdownCallback)(jack_status_t code, const char* reason, void *arg); + +static void (*on_info_shutdown)(jack_client_t*, _JackInfoShutdownCallback, void *); +extern void jack_on_info_shutdown (jack_client_t*, _JackInfoShutdownCallback, void *) __attribute__((weak)); + +static void check_jack_symbols () __attribute__((constructor)); + +void check_jack_symbols () +{ + /* use weak linking to see if we really have various late-model JACK function */ + on_info_shutdown = jack_on_info_shutdown; } static void @@ -181,16 +201,26 @@ AudioEngine::start () _processed_frames = 0; last_monitor_check = 0; - jack_on_shutdown (_priv_jack, halted, this); + if (on_info_shutdown) { + jack_on_info_shutdown (_priv_jack, halted_info, this); + } else { + jack_on_shutdown (_priv_jack, halted, this); + } jack_set_graph_order_callback (_priv_jack, _graph_order_callback, this); jack_set_thread_init_callback (_priv_jack, _thread_init_callback, this); - jack_set_process_callback (_priv_jack, _process_callback, this); + // jack_set_process_callback (_priv_jack, _process_callback, this); + jack_set_process_thread (_priv_jack, _process_thread, this); jack_set_sample_rate_callback (_priv_jack, _sample_rate_callback, this); jack_set_buffer_size_callback (_priv_jack, _bufsize_callback, this); jack_set_xrun_callback (_priv_jack, _xrun_callback, this); +#ifdef HAVE_JACK_SESSION + if( jack_set_session_callback ) + jack_set_session_callback (_priv_jack, _session_callback, this); +#endif jack_set_sync_callback (_priv_jack, _jack_sync_callback, this); jack_set_freewheel_callback (_priv_jack, _freewheel_callback, this); jack_set_port_registration_callback (_priv_jack, _registration_callback, this); + jack_set_port_connect_callback (_priv_jack, _connect_callback, this); if (_session && _session->config.get_jack_time_master()) { jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this); @@ -207,6 +237,15 @@ AudioEngine::start () } _raw_buffer_sizes[DataType::AUDIO] = blocksize * sizeof(float); + + jack_port_t* midi_port = jack_port_register (_priv_jack, "m", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); + if (!midi_port) { + error << _("Cannot create temporary MIDI port to determine MIDI buffer size") << endmsg; + } else { + _raw_buffer_sizes[DataType::MIDI] = jack_midi_max_event_size (jack_port_get_buffer(midi_port, blocksize)); + cerr << "MIDI port buffers = " << _raw_buffer_sizes[DataType::MIDI] << endl; + jack_port_unregister (_priv_jack, midi_port); + } } return _running ? 0 : -1; @@ -299,6 +338,17 @@ AudioEngine::_xrun_callback (void *arg) return 0; } +#ifdef HAVE_JACK_SESSION +void +AudioEngine::_session_callback (jack_session_event_t *event, void *arg) +{ + printf( "helo.... " ); + AudioEngine* ae = static_cast (arg); + if (ae->connected()) { + ae->JackSessionEvent ( event ); /* EMIT SIGNAL */ + } +} +#endif int AudioEngine::_graph_order_callback (void *arg) { @@ -320,6 +370,12 @@ AudioEngine::_process_callback (nframes_t nframes, void *arg) return static_cast (arg)->process_callback (nframes); } +void* +AudioEngine::_process_thread (void *arg) +{ + return static_cast (arg)->process_thread (); +} + void AudioEngine::_freewheel_callback (int onoff, void *arg) { @@ -333,6 +389,13 @@ AudioEngine::_registration_callback (jack_port_id_t /*id*/, int /*reg*/, void* a ae->PortRegisteredOrUnregistered (); /* EMIT SIGNAL */ } +void +AudioEngine::_connect_callback (jack_port_id_t /*id_a*/, jack_port_id_t /*id_b*/, int /*conn*/, void* arg) +{ + AudioEngine* ae = static_cast (arg); + ae->PortConnectedOrDisconnected (); /* EMIT SIGNAL */ +} + void AudioEngine::split_cycle (nframes_t offset) { @@ -349,6 +412,38 @@ AudioEngine::split_cycle (nframes_t offset) } } +void +AudioEngine::finish_process_cycle (int status) +{ + GET_PRIVATE_JACK_POINTER(_jack); + jack_cycle_signal (_jack, 0); +} + +void* +AudioEngine::process_thread () +{ + /* JACK doesn't do this for us when we use the wait API + */ + + _thread_init_callback (0); + + _main_thread = new ProcessThread; + + while (1) { + GET_PRIVATE_JACK_POINTER_RET(_jack,0); + jack_nframes_t nframes = jack_cycle_wait (_jack); + + if (process_callback (nframes)) { + cerr << "--- process\n"; + return 0; + } + + finish_process_cycle (0); + } + + return 0; +} + /** Method called by JACK (via _process_callback) which says that there * is work to be done. * @param nframes Number of frames to process. @@ -356,7 +451,7 @@ AudioEngine::split_cycle (nframes_t offset) int AudioEngine::process_callback (nframes_t nframes) { - GET_PRIVATE_JACK_POINTER_RET(_jack,0) + GET_PRIVATE_JACK_POINTER_RET(_jack,0); // CycleTimer ct ("AudioEngine::process"); Glib::Mutex::Lock tm (_process_lock, Glib::TRY_LOCK); @@ -402,15 +497,16 @@ AudioEngine::process_callback (nframes_t nframes) if (_freewheeling) { /* emit the Freewheel signal and stop freewheeling in the event of trouble - * the indirection is to pick up the return value of the signal. */ - if (*Freewheel (nframes)) { + boost::optional r = Freewheel (nframes); + if (r.get_value_or (0)) { jack_set_freewheel (_priv_jack, false); } } else { if (_session) { _session->process (nframes); + } } @@ -502,16 +598,40 @@ AudioEngine::_bufsize_callback (nframes_t nframes, void *arg) int AudioEngine::jack_bufsize_callback (nframes_t nframes) { + bool need_midi_size = true; + bool need_audio_size = true; + _buffer_size = nframes; - _raw_buffer_sizes[DataType::AUDIO] = nframes * sizeof(float); - cout << "FIXME: Assuming maximum MIDI buffer size " << nframes * 4 << "bytes" << endl; - _raw_buffer_sizes[DataType::MIDI] = nframes * 4; _usecs_per_cycle = (int) floor ((((double) nframes / frame_rate())) * 1000000.0); last_monitor_check = 0; boost::shared_ptr p = ports.reader(); + /* crude guesses, see below where we try to get the right answers. + + Note that our guess for MIDI deliberatey tries to overestimate + by a little. It would be nicer if we could get the actual + size from a port, but we have to use this estimate in the + event that there are no MIDI ports currently. If there are + the value will be adjusted below. + */ + + _raw_buffer_sizes[DataType::AUDIO] = nframes * sizeof (Sample); + _raw_buffer_sizes[DataType::MIDI] = nframes * 4 - (nframes/2); + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { + + if (need_audio_size && (*i)->type() == DataType::AUDIO) { + _raw_buffer_sizes[DataType::AUDIO] = (*i)->raw_buffer_size (nframes); + need_audio_size = false; + } + + + if (need_midi_size && (*i)->type() == DataType::MIDI) { + _raw_buffer_sizes[DataType::MIDI] = (*i)->raw_buffer_size (nframes); + need_midi_size = false; + } + (*i)->reset(); } @@ -904,9 +1024,41 @@ AudioEngine::get_ports (const string& port_name_pattern, const string& type_name return jack_get_ports (_priv_jack, port_name_pattern.c_str(), type_name_pattern.c_str(), flags); } +void +AudioEngine::halted_info (jack_status_t code, const char* reason, void *arg) +{ + /* called from jack shutdown handler */ + + AudioEngine* ae = static_cast (arg); + bool was_running = ae->_running; + + ae->stop_metering_thread (); + + ae->_running = false; + ae->_buffer_size = 0; + ae->_frame_rate = 0; + ae->_jack = 0; + + if (was_running) { +#ifdef HAVE_JACK_ON_INFO_SHUTDOWN + switch (code) { + case JackBackendError: + ae->Halted(reason); /* EMIT SIGNAL */ + break; + default: + ae->Halted(""); /* EMIT SIGNAL */ + } +#else + ae->Halted(""); /* EMIT SIGNAL */ +#endif + } +} + void AudioEngine::halted (void *arg) { + cerr << "HALTED by JACK\n"; + /* called from jack shutdown handler */ AudioEngine* ae = static_cast (arg); @@ -917,9 +1069,10 @@ AudioEngine::halted (void *arg) ae->_running = false; ae->_buffer_size = 0; ae->_frame_rate = 0; + ae->_jack = 0; if (was_running) { - ae->Halted(); /* EMIT SIGNAL */ + ae->Halted(""); /* EMIT SIGNAL */ MIDI::JACK_MidiPort::JackHalted (); /* EMIT SIGNAL */ } } @@ -958,16 +1111,21 @@ AudioEngine::n_physical_outputs (DataType type) const { GET_PRIVATE_JACK_POINTER_RET (_jack,0); const char ** ports; - uint32_t i = 0; + uint32_t cnt = 0; if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical|JackPortIsInput)) == 0) { return 0; } - for (i = 0; ports[i]; ++i) {} + for (uint32_t i = 0; ports[i]; ++i) { + if (!strstr (ports[i], "Midi-Through")) { + cnt++; + } + } + free (ports); - return i; + return cnt; } uint32_t @@ -975,16 +1133,21 @@ AudioEngine::n_physical_inputs (DataType type) const { GET_PRIVATE_JACK_POINTER_RET (_jack,0); const char ** ports; - uint32_t i = 0; - + uint32_t cnt = 0; + if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical|JackPortIsOutput)) == 0) { return 0; } - for (i = 0; ports[i]; ++i) {} + for (uint32_t i = 0; ports[i]; ++i) { + if (!strstr (ports[i], "Midi-Through")) { + cnt++; + } + } + free (ports); - return i; + return cnt; } void @@ -999,6 +1162,9 @@ AudioEngine::get_physical_inputs (DataType type, vector& ins) if (ports) { for (uint32_t i = 0; ports[i]; ++i) { + if (strstr (ports[i], "Midi-Through")) { + continue; + } ins.push_back (ports[i]); } free (ports); @@ -1017,6 +1183,9 @@ AudioEngine::get_physical_outputs (DataType type, vector& outs) } for (i = 0; ports[i]; ++i) { + if (strstr (ports[i], "Midi-Through")) { + continue; + } outs.push_back (ports[i]); } free (ports); @@ -1028,6 +1197,7 @@ AudioEngine::get_nth_physical (DataType type, uint32_t n, int flag) GET_PRIVATE_JACK_POINTER_RET (_jack,""); const char ** ports; uint32_t i; + uint32_t idx; string ret; assert(type != DataType::NIL); @@ -1036,10 +1206,14 @@ AudioEngine::get_nth_physical (DataType type, uint32_t n, int flag) return ret; } - for (i = 0; i < n && ports[i]; ++i) {} + for (i = 0, idx = 0; idx < n && ports[i]; ++i) { + if (!strstr (ports[i], "Midi-Through")) { + ++idx; + } + } - if (ports[i]) { - ret = ports[i]; + if (ports[idx]) { + ret = ports[idx]; } free ((const char **) ports); @@ -1133,14 +1307,19 @@ AudioEngine::remove_all_ports () } int -AudioEngine::connect_to_jack (string client_name) +AudioEngine::connect_to_jack (string client_name, string session_uuid) { jack_options_t options = JackNullOption; jack_status_t status; 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); +#ifdef HAVE_JACK_SESSION + if (! session_uuid.empty()) + _jack = jack_client_open (jack_client_name.c_str(), JackSessionID, &status, session_uuid.c_str()); + else +#endif + _jack = jack_client_open (jack_client_name.c_str(), options, &status, server_name); if (_jack == NULL) { // error message is not useful here @@ -1193,7 +1372,7 @@ AudioEngine::reconnect_to_jack () Glib::usleep (250000); } - if (connect_to_jack (jack_client_name)) { + if (connect_to_jack (jack_client_name, "")) { error << _("failed to connect to JACK") << endmsg; return -1; } @@ -1218,13 +1397,8 @@ AudioEngine::reconnect_to_jack () if (_session) { _session->reset_jack_connection (_priv_jack); - nframes_t blocksize = jack_get_buffer_size (_priv_jack); - _session->set_block_size (blocksize); + jack_bufsize_callback (jack_get_buffer_size (_priv_jack)); _session->set_frame_rate (jack_get_sample_rate (_priv_jack)); - - _raw_buffer_sizes[DataType::AUDIO] = blocksize * sizeof(float); - cout << "FIXME: Assuming maximum MIDI buffer size " << blocksize * 4 << "bytes" << endl; - _raw_buffer_sizes[DataType::MIDI] = blocksize * 4; } last_monitor_check = 0; @@ -1232,10 +1406,15 @@ AudioEngine::reconnect_to_jack () jack_on_shutdown (_priv_jack, halted, this); jack_set_graph_order_callback (_priv_jack, _graph_order_callback, this); jack_set_thread_init_callback (_priv_jack, _thread_init_callback, this); - jack_set_process_callback (_priv_jack, _process_callback, this); + // jack_set_process_callback (_priv_jack, _process_callback, this); + jack_set_process_thread (_priv_jack, _process_thread, this); jack_set_sample_rate_callback (_priv_jack, _sample_rate_callback, this); jack_set_buffer_size_callback (_priv_jack, _bufsize_callback, this); jack_set_xrun_callback (_priv_jack, _xrun_callback, this); +#ifdef HAVE_JACK_SESSION + if( jack_set_session_callback ) + jack_set_session_callback (_priv_jack, _session_callback, this); +#endif jack_set_sync_callback (_priv_jack, _jack_sync_callback, this); jack_set_freewheel_callback (_priv_jack, _freewheel_callback, this); @@ -1327,3 +1506,30 @@ AudioEngine::is_realtime () const GET_PRIVATE_JACK_POINTER_RET (_jack,false); return jack_is_realtime (_priv_jack); } + +pthread_t +AudioEngine::create_process_thread (boost::function f, size_t stacksize) +{ + GET_PRIVATE_JACK_POINTER_RET (_jack, 0); + pthread_t thread; + ThreadData* td = new ThreadData (this, f, stacksize); + + if (jack_client_create_thread (_priv_jack, &thread, jack_client_real_time_priority (_priv_jack), + jack_is_realtime (_priv_jack), _start_process_thread, td)) { + return -1; + } + + return thread; +} + +void* +AudioEngine::_start_process_thread (void* arg) +{ + ThreadData* td = reinterpret_cast (arg); + boost::function f = td->f; + delete td; + + f (); + + return 0; +}