X-Git-Url: https://main.carlh.net/gitweb/?p=ardour.git;a=blobdiff_plain;f=libs%2Fardour%2Faudioengine.cc;h=30c2113c06ad39fe7e23d3f695e985c3586e7db1;hp=cb167c959579dfddc96348fca598fae5e3b6b707;hb=c8c6bca6587450ff64303dbc994a4cd28d6ce7aa;hpb=59631acc5f41153a294c97ab820a4b41a886e24c diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index cb167c9595..30c2113c06 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -41,27 +42,33 @@ #include "ardour/audio_port.h" #include "ardour/audio_backend.h" #include "ardour/audioengine.h" -#include "ardour/backend_search_path.h" +#include "ardour/search_paths.h" #include "ardour/buffer.h" #include "ardour/cycle_timer.h" #include "ardour/internal_send.h" #include "ardour/meter.h" #include "ardour/midi_port.h" #include "ardour/midiport_manager.h" +#include "ardour/mididm.h" #include "ardour/mtdm.h" #include "ardour/port.h" #include "ardour/process_thread.h" #include "ardour/session.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace std; using namespace ARDOUR; using namespace PBD; -gint AudioEngine::m_meter_exit; AudioEngine* AudioEngine::_instance = 0; +static gint audioengine_thread_cnt = 1; + +#ifdef SILENCE_AFTER +#define SILENCE_AFTER_SECONDS 600 +#endif + AudioEngine::AudioEngine () : session_remove_pending (false) , session_removal_countdown (-1) @@ -73,23 +80,41 @@ AudioEngine::AudioEngine () , m_meter_thread (0) , _main_thread (0) , _mtdm (0) - , _measuring_latency (false) + , _mididm (0) + , _measuring_latency (MeasureNone) , _latency_input_port (0) , _latency_output_port (0) , _latency_flush_frames (0) , _latency_signal_latency (0) , _stopped_for_latency (false) + , _started_for_latency (false) , _in_destructor (false) + , _last_backend_error_string(AudioBackend::get_error_string((AudioBackend::ErrorCode)-1)) + , _hw_reset_event_thread(0) + , _hw_reset_request_count(0) + , _stop_hw_reset_processing(0) + , _hw_devicelist_update_thread(0) + , _hw_devicelist_update_count(0) + , _stop_hw_devicelist_processing(0) +#ifdef SILENCE_AFTER_SECONDS + , _silence_countdown (0) + , _silence_hit_cnt (0) +#endif { - g_atomic_int_set (&m_meter_exit, 0); + reset_silence_countdown (); + start_hw_event_processing(); discover_backends (); } AudioEngine::~AudioEngine () { _in_destructor = true; - stop_metering_thread (); + stop_hw_event_processing(); drop_backend (); + for (BackendMap::const_iterator i = _backends.begin(); i != _backends.end(); ++i) { + i->second->deinstantiate(); + } + delete _main_thread; } AudioEngine* @@ -100,25 +125,8 @@ AudioEngine::create () } _instance = new AudioEngine (); - - return _instance; -} - -void -_thread_init_callback (void * /*arg*/) -{ - /* make sure that anybody who needs to know about this thread - knows about it. - */ - - pthread_set_name (X_("audioengine")); - - PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("Audioengine"), 4096); - PBD::notify_gui_about_thread_creation ("midiui", pthread_self(), X_("Audioengine"), 128); - SessionEvent::create_per_thread_pool (X_("Audioengine"), 512); - - AsyncMIDIPort::set_process_thread (pthread_self()); + return _instance; } void @@ -151,10 +159,14 @@ AudioEngine::sample_rate_change (pframes_t nframes) SampleRateChanged (nframes); /* EMIT SIGNAL */ +#ifdef SILENCE_AFTER_SECONDS + _silence_countdown = nframes * SILENCE_AFTER_SECONDS; +#endif + return 0; } -int +int AudioEngine::buffer_size_change (pframes_t bufsiz) { if (_session) { @@ -162,12 +174,17 @@ AudioEngine::buffer_size_change (pframes_t bufsiz) last_monitor_check = 0; } + BufferSizeChanged (bufsiz); /* EMIT SIGNAL */ + return 0; } /** Method called by our ::process_thread when there is work to be done. * @param nframes Number of frames to process. */ +#ifdef __clang__ +__attribute__((annotate("realtime"))) +#endif int AudioEngine::process_callback (pframes_t nframes) { @@ -189,20 +206,48 @@ AudioEngine::process_callback (pframes_t nframes) if (!tm.locked()) { /* return having done nothing */ - _processed_frames = next_processed_frames; + if (_session) { + Xrun(); + } + /* really only JACK requires this + * (other backends clear the output buffers + * before the process_callback. it may even be + * jack/alsa only). but better safe than sorry. + */ + PortManager::silence_outputs (nframes); return 0; } + /* The coreaudio-backend calls thread_init_callback() if + * the hardware changes or pthread_self() changes. + * + * However there are cases when neither holds true, yet + * the thread-pool changes: e.g. connect a headphone to + * a shared mic/headphone jack. + * It's probably related to, or caused by clocksource changes. + * + * For reasons yet unknown Glib::Threads::Private() can + * use a different thread-private in the same pthread + * (coreaudio render callback). + * + * Coreaudio must set something which influences + * pthread_key_t uniqness or reset the key using + * pthread_getspecific(). + */ + if (! SessionEvent::has_per_thread_pool ()) { + thread_init_callback (NULL); + } + bool return_after_remove_check = false; - if (_measuring_latency && _mtdm) { + if (_measuring_latency == MeasureAudio && _mtdm) { /* run a normal cycle from the perspective of the PortManager so that we get silence on all registered ports. - + we overwrite the silence on the two ports used for latency measurement. */ - + PortManager::cycle_start (nframes); PortManager::silence (nframes); @@ -218,8 +263,30 @@ AudioEngine::process_callback (pframes_t nframes) PortManager::cycle_end (nframes); return_after_remove_check = true; + } else if (_measuring_latency == MeasureMIDI && _mididm) { + /* run a normal cycle from the perspective of the PortManager + so that we get silence on all registered ports. + + we overwrite the silence on the two ports used for latency + measurement. + */ + + PortManager::cycle_start (nframes); + PortManager::silence (nframes); + + if (_latency_input_port && _latency_output_port) { + PortEngine& pe (port_engine()); + + _mididm->process (nframes, pe, + pe.get_buffer (_latency_input_port, nframes), + pe.get_buffer (_latency_output_port, nframes)); + } + + PortManager::cycle_end (nframes); + return_after_remove_check = true; + } else if (_latency_flush_frames) { - + /* wait for the appropriate duration for the MTDM signal to * drain from the ports before we revert to normal behaviour. */ @@ -227,7 +294,7 @@ AudioEngine::process_callback (pframes_t nframes) PortManager::cycle_start (nframes); PortManager::silence (nframes); PortManager::cycle_end (nframes); - + if (_latency_flush_frames > nframes) { _latency_flush_frames -= nframes; } else { @@ -245,18 +312,18 @@ AudioEngine::process_callback (pframes_t nframes) /* fade out over 1 second */ session_removal_countdown = sample_rate()/2; - session_removal_gain = 1.0; + session_removal_gain = GAIN_COEFF_UNITY; session_removal_gain_step = 1.0/session_removal_countdown; } else if (session_removal_countdown > 0) { /* we'll be fading audio out. - - if this is the last time we do this as part + + if this is the last time we do this as part of session removal, do a MIDI panic now to get MIDI stopped. This relies on the fact that "immediate data" (aka "out of band data") from - MIDI tracks is *appended* after any other data, + MIDI tracks is *appended* after any other data, so that it emerges after any outbound note ons, etc. */ @@ -303,14 +370,13 @@ AudioEngine::process_callback (pframes_t nframes) */ if (_freewheeling && !Freewheel.empty()) { - Freewheel (nframes); + Freewheel (nframes); } else { - if (_session) { - _session->process (nframes); - } + _session->process (nframes); } if (_freewheeling) { + PortManager::cycle_end (nframes); return 0; } @@ -320,19 +386,40 @@ AudioEngine::process_callback (pframes_t nframes) } if (last_monitor_check + monitor_check_interval < next_processed_frames) { - + PortManager::check_monitoring (); last_monitor_check = next_processed_frames; } - if (_session->silent()) { +#ifdef SILENCE_AFTER_SECONDS + + bool was_silent = (_silence_countdown == 0); + + if (_silence_countdown >= nframes) { + _silence_countdown -= nframes; + } else { + _silence_countdown = 0; + } + + if (!was_silent && _silence_countdown == 0) { + _silence_hit_cnt++; + BecameSilent (); /* EMIT SIGNAL */ + } + + if (_silence_countdown == 0 || _session->silent()) { PortManager::silence (nframes); } +#else + if (_session->silent()) { + PortManager::silence (nframes, _session); + } +#endif + if (session_remove_pending && session_removal_countdown) { PortManager::fade_out (session_removal_gain, session_removal_gain_step, nframes); - + if (session_removal_countdown > nframes) { session_removal_countdown -= nframes; } else { @@ -347,43 +434,173 @@ AudioEngine::process_callback (pframes_t nframes) _processed_frames = next_processed_frames; PT_TIMING_CHECK (2); - + return 0; } +void +AudioEngine::reset_silence_countdown () +{ +#ifdef SILENCE_AFTER_SECONDS + double sr = 48000; /* default in case there is no backend */ + + sr = sample_rate(); + + _silence_countdown = max (60 * sr, /* 60 seconds */ + sr * (SILENCE_AFTER_SECONDS / ::pow (2.0, (double) _silence_hit_cnt))); + +#endif +} void -AudioEngine::stop_metering_thread () +AudioEngine::launch_device_control_app() { - if (m_meter_thread) { - g_atomic_int_set (&m_meter_exit, 1); - m_meter_thread->join (); - m_meter_thread = 0; + if (_state_lock.trylock () ) { + _backend->launch_control_app (); + _state_lock.unlock (); } } + void -AudioEngine::start_metering_thread () +AudioEngine::request_backend_reset() { - if (m_meter_thread == 0) { - g_atomic_int_set (&m_meter_exit, 0); - m_meter_thread = Glib::Threads::Thread::create (boost::bind (&AudioEngine::meter_thread, this)); - } + Glib::Threads::Mutex::Lock guard (_reset_request_lock); + g_atomic_int_inc (&_hw_reset_request_count); + _hw_reset_condition.signal (); +} + +int +AudioEngine::backend_reset_requested() +{ + return g_atomic_int_get (&_hw_reset_request_count); } void -AudioEngine::meter_thread () +AudioEngine::do_reset_backend() { - pthread_set_name (X_("meter")); + SessionEvent::create_per_thread_pool (X_("Backend reset processing thread"), 1024); + + Glib::Threads::Mutex::Lock guard (_reset_request_lock); + + while (!_stop_hw_reset_processing) { + + if (g_atomic_int_get (&_hw_reset_request_count) != 0 && _backend) { + + _reset_request_lock.unlock(); + + Glib::Threads::RecMutex::Lock pl (_state_lock); + g_atomic_int_dec_and_test (&_hw_reset_request_count); + + std::cout << "AudioEngine::RESET::Reset request processing. Requests left: " << _hw_reset_request_count << std::endl; + DeviceResetStarted(); // notify about device reset to be started + + // backup the device name + std::string name = _backend->device_name (); + + std::cout << "AudioEngine::RESET::Reseting device..." << std::endl; + if ( ( 0 == stop () ) && + ( 0 == _backend->reset_device () ) && + ( 0 == start () ) ) { + + std::cout << "AudioEngine::RESET::Engine started..." << std::endl; + + // inform about possible changes + BufferSizeChanged (_backend->buffer_size() ); + DeviceResetFinished(); // notify about device reset finish + + } else { + + DeviceResetFinished(); // notify about device reset finish + // we've got an error + DeviceError(); + } + + std::cout << "AudioEngine::RESET::Done." << std::endl; + + _reset_request_lock.lock(); + + } else { + + _hw_reset_condition.wait (_reset_request_lock); - while (true) { - Glib::usleep (10000); /* 1/100th sec interval */ - if (g_atomic_int_get(&m_meter_exit)) { - break; } - Metering::Meter (); } } +void +AudioEngine::request_device_list_update() +{ + Glib::Threads::Mutex::Lock guard (_devicelist_update_lock); + g_atomic_int_inc (&_hw_devicelist_update_count); + _hw_devicelist_update_condition.signal (); +} + + +void +AudioEngine::do_devicelist_update() +{ + SessionEvent::create_per_thread_pool (X_("Device list update processing thread"), 512); + + Glib::Threads::Mutex::Lock guard (_devicelist_update_lock); + + while (!_stop_hw_devicelist_processing) { + + if (_hw_devicelist_update_count) { + + _devicelist_update_lock.unlock(); + + Glib::Threads::RecMutex::Lock pl (_state_lock); + + g_atomic_int_dec_and_test (&_hw_devicelist_update_count); + DeviceListChanged (); /* EMIT SIGNAL */ + + _devicelist_update_lock.lock(); + + } else { + _hw_devicelist_update_condition.wait (_devicelist_update_lock); + } + } +} + + +void +AudioEngine::start_hw_event_processing() +{ + if (_hw_reset_event_thread == 0) { + g_atomic_int_set(&_hw_reset_request_count, 0); + g_atomic_int_set(&_stop_hw_reset_processing, 0); + _hw_reset_event_thread = Glib::Threads::Thread::create (boost::bind (&AudioEngine::do_reset_backend, this)); + } + + if (_hw_devicelist_update_thread == 0) { + g_atomic_int_set(&_hw_devicelist_update_count, 0); + g_atomic_int_set(&_stop_hw_devicelist_processing, 0); + _hw_devicelist_update_thread = Glib::Threads::Thread::create (boost::bind (&AudioEngine::do_devicelist_update, this)); + } +} + + +void +AudioEngine::stop_hw_event_processing() +{ + if (_hw_reset_event_thread) { + g_atomic_int_set(&_stop_hw_reset_processing, 1); + g_atomic_int_set(&_hw_reset_request_count, 0); + _hw_reset_condition.signal (); + _hw_reset_event_thread->join (); + _hw_reset_event_thread = 0; + } + + if (_hw_devicelist_update_thread) { + g_atomic_int_set(&_stop_hw_devicelist_processing, 1); + g_atomic_int_set(&_hw_devicelist_update_count, 0); + _hw_devicelist_update_condition.signal (); + _hw_devicelist_update_thread->join (); + _hw_devicelist_update_thread = 0; + } + +} + void AudioEngine::set_session (Session *s) @@ -420,7 +637,8 @@ AudioEngine::remove_session () if (_session) { session_remove_pending = true; - session_removal_countdown = 0; + /* signal the start of the fade out countdown */ + session_removal_countdown = -1; session_removed.wait(_process_lock); } @@ -433,13 +651,21 @@ AudioEngine::remove_session () void -AudioEngine::died () +AudioEngine::reconnect_session_routes (bool reconnect_inputs, bool reconnect_outputs) { - /* called from a signal handler for SIGPIPE */ +#ifdef USE_TRACKS_CODE_FEATURES + if (_session) { + _session->reconnect_existing_routes(true, true, reconnect_inputs, reconnect_outputs); + } +#endif +} - stop_metering_thread (); - _running = false; +void +AudioEngine::died () +{ + /* called from a signal handler for SIGPIPE */ + _running = false; } int @@ -482,34 +708,46 @@ AudioEngine::discover_backends () #else Glib::PatternSpec dll_extension_pattern("*backend.dll"); #endif - - find_matching_files_in_search_path (backend_search_path (), - so_extension_pattern, backend_modules); - find_matching_files_in_search_path (backend_search_path (), - dylib_extension_pattern, backend_modules); + find_files_matching_pattern (backend_modules, backend_search_path (), + so_extension_pattern); + + find_files_matching_pattern (backend_modules, backend_search_path (), + dylib_extension_pattern); - find_matching_files_in_search_path (backend_search_path (), - dll_extension_pattern, backend_modules); + find_files_matching_pattern (backend_modules, backend_search_path (), + dll_extension_pattern); - DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for backends in %1\n"), backend_search_path().to_string())); + DEBUG_TRACE (DEBUG::AudioEngine, string_compose ("looking for backends in %1\n", backend_search_path().to_string())); for (vector::iterator i = backend_modules.begin(); i != backend_modules.end(); ++i) { AudioBackendInfo* info; + DEBUG_TRACE (DEBUG::AudioEngine, string_compose ("Checking possible backend in %1\n", *i)); + if ((info = backend_discover (*i)) != 0) { _backends.insert (make_pair (info->name, info)); } } + DEBUG_TRACE (DEBUG::AudioEngine, string_compose ("Found %1 backends\n", _backends.size())); + return _backends.size(); } AudioBackendInfo* AudioEngine::backend_discover (const string& path) { +#ifdef PLATFORM_WINDOWS + // do not show popup dialog (e.g. missing libjack.dll) + // win7+ should use SetThreadErrorMode() + SetErrorMode(SEM_FAILCRITICALERRORS); +#endif Glib::Module module (path); +#ifdef PLATFORM_WINDOWS + SetErrorMode(0); // reset to system default +#endif AudioBackendInfo* info; AudioBackendInfo* (*dfunc)(void); void* func = 0; @@ -519,18 +757,21 @@ AudioEngine::backend_discover (const string& path) Glib::Module::get_last_error()) << endmsg; return 0; } - + if (!module.get_symbol ("descriptor", func)) { error << string_compose(_("AudioEngine: backend at \"%1\" has no descriptor function."), path) << endmsg; error << Glib::Module::get_last_error() << endmsg; return 0; } - module.make_resident (); - dfunc = (AudioBackendInfo* (*)(void))func; info = dfunc(); - + if (!info->available()) { + return 0; + } + + module.make_resident (); + return info; } @@ -538,7 +779,7 @@ vector AudioEngine::available_backends() const { vector r; - + for (BackendMap::const_iterator i = _backends.begin(); i != _backends.end(); ++i) { r.push_back (i->second); } @@ -551,7 +792,7 @@ AudioEngine::current_backend_name() const { if (_backend) { return _backend->name(); - } + } return string(); } @@ -560,7 +801,11 @@ AudioEngine::drop_backend () { if (_backend) { _backend->stop (); + // Stopped is needed for Graph to explicitly terminate threads + Stopped (); /* EMIT SIGNAL */ + _backend->drop_device (); _backend.reset (); + _running = false; } } @@ -584,7 +829,7 @@ AudioEngine::set_backend (const std::string& name, const std::string& arg1, cons } drop_backend (); - + try { if (b->second->instantiate (arg1, arg2)) { throw failed_constructor (); @@ -615,57 +860,86 @@ AudioEngine::start (bool for_latency) _processed_frames = 0; last_monitor_check = 0; - - if (_backend->start (for_latency)) { + + int error_code = _backend->start (for_latency); + + if (error_code != 0) { + _last_backend_error_string = + AudioBackend::get_error_string((AudioBackend::ErrorCode)error_code); return -1; } _running = true; - + if (_session) { _session->set_frame_rate (_backend->sample_rate()); - + if (_session->config.get_jack_time_master()) { _backend->set_time_master (true); } + } - - start_metering_thread (); - + if (!for_latency) { Running(); /* EMIT SIGNAL */ } - + return 0; } int AudioEngine::stop (bool for_latency) { + bool stop_engine = true; + if (!_backend) { return 0; } - Glib::Threads::Mutex::Lock lm (_process_lock); + Glib::Threads::Mutex::Lock pl (_process_lock, Glib::Threads::NOT_LOCK); - if (_backend->stop ()) { - return -1; + if (running()) { + pl.acquire (); + } + + if (for_latency && _backend->can_change_systemic_latency_when_running()) { + stop_engine = false; + } else { + if (_backend->stop ()) { + pl.release (); + return -1; + } + } + + if (pl.locked ()) { + pl.release (); + } + + if (_session && _running && stop_engine && + (_session->state_of_the_state() & Session::Loading) == 0 && + (_session->state_of_the_state() & Session::Deletion) == 0) { + // it's not a halt, but should be handled the same way: + // disable record, stop transport and I/O processign but save the data. + _session->engine_halted (); + } + + if (stop_engine) { + _running = false; } - - _running = false; _processed_frames = 0; - _measuring_latency = false; + _measuring_latency = MeasureNone; _latency_output_port = 0; _latency_input_port = 0; _started_for_latency = false; - stop_metering_thread (); - - Port::PortDrop (); - if (!for_latency) { + if (stop_engine) { + Port::PortDrop (); + } + + if (!for_latency && stop_engine) { Stopped (); /* EMIT SIGNAL */ } - + return 0; } @@ -682,16 +956,16 @@ AudioEngine::freewheel (bool start_stop) } float -AudioEngine::get_dsp_load() const +AudioEngine::get_dsp_load() const { - if (!_backend) { + if (!_backend || !_running) { return 0.0; } return _backend->dsp_load (); } bool -AudioEngine::is_realtime() const +AudioEngine::is_realtime() const { if (!_backend) { return false; @@ -701,7 +975,7 @@ AudioEngine::is_realtime() const } bool -AudioEngine::connected() const +AudioEngine::connected() const { if (!_backend) { return false; @@ -791,7 +1065,7 @@ AudioEngine::raw_buffer_size (DataType t) return _backend->raw_buffer_size (t); } -pframes_t +framepos_t AudioEngine::sample_time () { if (!_backend) { @@ -800,7 +1074,7 @@ AudioEngine::sample_time () return _backend->sample_time (); } -pframes_t +framepos_t AudioEngine::sample_time_at_cycle_start () { if (!_backend) { @@ -878,6 +1152,7 @@ AudioEngine::set_sample_rate (float sr) if (!_backend) { return -1; } + return _backend->set_sample_rate (sr); } @@ -890,15 +1165,6 @@ AudioEngine::set_buffer_size (uint32_t bufsiz) return _backend->set_buffer_size (bufsiz); } -int -AudioEngine::set_sample_format (SampleFormat sf) -{ - if (!_backend) { - return -1; - } - return _backend->set_sample_format (sf); -} - int AudioEngine::set_interleaved (bool yn) { @@ -944,6 +1210,12 @@ AudioEngine::set_systemic_output_latency (uint32_t ol) return _backend->set_systemic_output_latency (ol); } +bool +AudioEngine::thread_initialised_for_audio_processing () +{ + return SessionEvent::has_per_thread_pool () && AsyncMIDIPort::is_process_thread(); +} + /* END OF BACKEND PROXY API */ void @@ -955,14 +1227,15 @@ AudioEngine::thread_init_callback (void* arg) pthread_set_name (X_("audioengine")); - PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("AudioEngine"), 4096); - PBD::notify_gui_about_thread_creation ("midiui", pthread_self(), X_("AudioEngine"), 128); - - SessionEvent::create_per_thread_pool (X_("AudioEngine"), 512); + const int thread_num = g_atomic_int_add (&audioengine_thread_cnt, 1); + const string thread_name = string_compose (X_("AudioEngine %1"), thread_num); + SessionEvent::create_per_thread_pool (thread_name, 512); + PBD::notify_event_loops_about_thread_creation (pthread_self(), thread_name, 4096); AsyncMIDIPort::set_process_thread (pthread_self()); if (arg) { + delete AudioEngine::instance()->_main_thread; /* the special thread created/managed by the backend */ AudioEngine::instance()->_main_thread = new ProcessThread; } @@ -1007,7 +1280,6 @@ AudioEngine::halted_callback (const char* why) return; } - stop_metering_thread (); _running = false; Port::PortDrop (); /* EMIT SIGNAL */ @@ -1020,49 +1292,52 @@ AudioEngine::halted_callback (const char* why) bool AudioEngine::setup_required () const { - /* If there is only a single backend and it claims to be configured - * already there is no setup to be done. - * - * Primarily for a case where there is only a JACK backend and - * JACK is already running. - */ - - if (_backends.size() == 1 && _backends.begin()->second->already_configured()) { - return false; + if (_backend) { + if (_backend->info().already_configured()) + return false; + } else { + if (_backends.size() == 1 && _backends.begin()->second->already_configured()) { + return false; + } } return true; } -MTDM* -AudioEngine::mtdm() -{ - return _mtdm; -} - int AudioEngine::prepare_for_latency_measurement () { + if (!_backend) { + return -1; + } + + if (_backend->can_change_systemic_latency_when_running()) { + if (start()) { + return -1; + } + _backend->set_systemic_input_latency (0); + _backend->set_systemic_output_latency (0); + return 0; + } + if (running()) { _stopped_for_latency = true; stop (true); } if (start (true)) { - _started_for_latency = true; return -1; } + _started_for_latency = true; return 0; } int -AudioEngine::start_latency_detection () +AudioEngine::start_latency_detection (bool for_midi) { - if (!running()) { - if (prepare_for_latency_measurement ()) { - return -1; - } + if (prepare_for_latency_measurement ()) { + return -1; } PortEngine& pe (port_engine()); @@ -1070,6 +1345,9 @@ AudioEngine::start_latency_detection () delete _mtdm; _mtdm = 0; + delete _mididm; + _mididm = 0; + /* find the ports we will connect to */ PortEngine::PortHandle out = pe.get_port_by_name (_latency_output_name); @@ -1081,27 +1359,61 @@ AudioEngine::start_latency_detection () } /* create the ports we will use to read/write data */ - - if ((_latency_output_port = pe.register_port ("latency_out", DataType::AUDIO, IsOutput)) == 0) { - stop (true); - return -1; - } - if (pe.connect (_latency_output_port, _latency_output_name)) { - pe.unregister_port (_latency_output_port); - stop (true); - return -1; - } + if (for_midi) { + if ((_latency_output_port = pe.register_port ("latency_out", DataType::MIDI, IsOutput)) == 0) { + stop (true); + return -1; + } + if (pe.connect (_latency_output_port, _latency_output_name)) { + pe.unregister_port (_latency_output_port); + stop (true); + return -1; + } + + const string portname ("latency_in"); + if ((_latency_input_port = pe.register_port (portname, DataType::MIDI, IsInput)) == 0) { + pe.unregister_port (_latency_input_port); + pe.unregister_port (_latency_output_port); + stop (true); + return -1; + } + if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) { + pe.unregister_port (_latency_input_port); + pe.unregister_port (_latency_output_port); + stop (true); + return -1; + } + + _mididm = new MIDIDM (sample_rate()); + + } else { + + if ((_latency_output_port = pe.register_port ("latency_out", DataType::AUDIO, IsOutput)) == 0) { + stop (true); + return -1; + } + if (pe.connect (_latency_output_port, _latency_output_name)) { + pe.unregister_port (_latency_output_port); + stop (true); + return -1; + } + + const string portname ("latency_in"); + if ((_latency_input_port = pe.register_port (portname, DataType::AUDIO, IsInput)) == 0) { + pe.unregister_port (_latency_input_port); + pe.unregister_port (_latency_output_port); + stop (true); + return -1; + } + if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) { + pe.unregister_port (_latency_input_port); + pe.unregister_port (_latency_output_port); + stop (true); + return -1; + } + + _mtdm = new MTDM (sample_rate()); - const string portname ("latency_in"); - if ((_latency_input_port = pe.register_port (portname, DataType::AUDIO, IsInput)) == 0) { - pe.unregister_port (_latency_output_port); - stop (true); - return -1; - } - if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) { - pe.unregister_port (_latency_output_port); - stop (true); - return -1; } LatencyRange lr; @@ -1112,10 +1424,8 @@ AudioEngine::start_latency_detection () _latency_signal_latency += lr.max; /* all created and connected, lets go */ - - _mtdm = new MTDM (sample_rate()); - _measuring_latency = true; - _latency_flush_frames = samples_per_cycle(); + _latency_flush_frames = samples_per_cycle(); + _measuring_latency = for_midi ? MeasureMIDI : MeasureAudio; return 0; } @@ -1123,7 +1433,7 @@ AudioEngine::start_latency_detection () void AudioEngine::stop_latency_detection () { - _measuring_latency = false; + _measuring_latency = MeasureNone; if (_latency_output_port) { port_engine().unregister_port (_latency_output_port); @@ -1134,7 +1444,9 @@ AudioEngine::stop_latency_detection () _latency_input_port = 0; } - stop (true); + if (!_backend->can_change_systemic_latency_when_running()) { + stop (true); + } if (_stopped_for_latency) { start ();