Add automatable click-free bypass/enable feature to a-eq
[ardour.git] / libs / ardour / audioengine.cc
index 25a55b5aaf01bb19cc9adb9ecc15891ace852be4..30c2113c06ad39fe7e23d3f695e985c3586e7db1 100644 (file)
 #include <exception>
 #include <stdexcept>
 #include <sstream>
+#include <cmath>
 
 #include <glibmm/timer.h>
+#include <glibmm/pattern.h>
+#include <glibmm/module.h>
 
+#include "pbd/epa.h"
+#include "pbd/file_utils.h"
 #include "pbd/pthread_utils.h"
 #include "pbd/stacktrace.h"
 #include "pbd/unknown_type.h"
-#include "pbd/epa.h"
-
-#include <jack/weakjack.h>
 
 #include "midi++/port.h"
-#include "midi++/jack_midi_port.h"
 #include "midi++/mmc.h"
-#include "midi++/manager.h"
 
+#include "ardour/async_midi_port.h"
 #include "ardour/audio_port.h"
+#include "ardour/audio_backend.h"
 #include "ardour/audioengine.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)
+       , _running (false)
+       , _freewheeling (false)
        , monitor_check_interval (INT32_MAX)
        , last_monitor_check (0)
        , _processed_frames (0)
-       , _freewheeling (false)
-       , _pre_freewheel_mmc_enabled (false)
-       , _usecs_per_cycle (0)
-       , port_remove_in_progress (false)
        , m_meter_thread (0)
        , _main_thread (0)
+       , _mtdm (0)
+       , _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);
-
-       Port::set_engine (this);
+       reset_silence_countdown ();
+       start_hw_event_processing();
+       discover_backends ();
 }
 
 AudioEngine::~AudioEngine ()
 {
-       config_connection.disconnect ();
-
-       {
-               Glib::Threads::Mutex::Lock tm (_process_lock);
-               session_removed.signal ();
-               stop_metering_thread ();
+       _in_destructor = true;
+       stop_hw_event_processing();
+       drop_backend ();
+       for (BackendMap::const_iterator i = _backends.begin(); i != _backends.end(); ++i) {
+               i->second->deinstantiate();
        }
+       delete _main_thread;
 }
 
 AudioEngine*
@@ -93,122 +123,71 @@ AudioEngine::create ()
        if (_instance) {
                return _instance;
        }
-       return new AudioEngine;
-}
 
-jack_client_t*
-AudioEngine::jack() const
-{
-       return _jack;
+       _instance = new AudioEngine ();
+
+       return _instance;
 }
 
 void
-_thread_init_callback (void * /*arg*/)
+AudioEngine::split_cycle (pframes_t offset)
 {
-       /* make sure that anybody who needs to know about this thread
-          knows about it.
-       */
+       /* caller must hold process lock */
 
-       pthread_set_name (X_("audioengine"));
+       Port::increment_global_port_buffer_offset (offset);
 
-       PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("Audioengine"), 4096);
-       PBD::notify_gui_about_thread_creation ("midiui", pthread_self(), X_("Audioengine"), 128);
+       /* tell all Ports that we're going to start a new (split) cycle */
 
-       SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
+       boost::shared_ptr<Ports> p = ports.reader();
 
-       MIDI::JackMIDIPort::set_process_thread (pthread_self());
+       for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+               i->second->cycle_split ();
+       }
 }
 
-
 int
-AudioEngine::start ()
+AudioEngine::sample_rate_change (pframes_t nframes)
 {
-       GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
-
-       if (!_running) {
-
-                if (!jack_port_type_get_buffer_size) {
-                        warning << _("This version of JACK is old - you should upgrade to a newer version that supports jack_port_type_get_buffer_size()") << endmsg;
-               }
+       /* check for monitor input change every 1/10th of second */
 
-               if (_session) {
-                       BootMessage (_("Connect session to engine"));
-                       _session->set_frame_rate (jack_get_sample_rate (_priv_jack));
-               }
+       monitor_check_interval = nframes / 10;
+       last_monitor_check = 0;
 
-                /* a proxy for whether jack_activate() will definitely call the buffer size
-                 * callback. with older versions of JACK, this function symbol will be null.
-                 * this is reliable, but not clean.
-                 */
+       if (_session) {
+               _session->set_frame_rate (nframes);
+       }
 
-                if (!jack_port_type_get_buffer_size) {
-                       jack_bufsize_callback (jack_get_buffer_size (_priv_jack));
-                }
-               
-               _processed_frames = 0;
-               last_monitor_check = 0;
+       SampleRateChanged (nframes); /* EMIT SIGNAL */
 
-                set_jack_callbacks ();
+#ifdef SILENCE_AFTER_SECONDS
+       _silence_countdown = nframes * SILENCE_AFTER_SECONDS;
+#endif
 
-               if (jack_activate (_priv_jack) == 0) {
-                       _running = true;
-                       _has_run = true;
-                       Running(); /* EMIT SIGNAL */
-               } else {
-                       // error << _("cannot activate JACK client") << endmsg;
-               }
-       }
-               
-       return _running ? 0 : -1;
+       return 0;
 }
 
 int
-AudioEngine::stop (bool forever)
+AudioEngine::buffer_size_change (pframes_t bufsiz)
 {
-       GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
-
-       if (_priv_jack) {
-               if (forever) {
-                       disconnect_from_jack ();
-               } else {
-                       jack_deactivate (_priv_jack);
-                       MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */
-                       Stopped(); /* EMIT SIGNAL */
-               }
+       if (_session) {
+               _session->set_block_size (bufsiz);
+               last_monitor_check = 0;
        }
 
-        if (forever) {
-                stop_metering_thread ();
-        }
-
-       return _running ? -1 : 0;
-}
-
+       BufferSizeChanged (bufsiz); /* EMIT SIGNAL */
 
-void
-AudioEngine::split_cycle (pframes_t offset)
-{
-       /* caller must hold process lock */
-
-       Port::increment_global_port_buffer_offset (offset);
-
-       /* tell all Ports that we're going to start a new (split) cycle */
-
-       boost::shared_ptr<Ports> p = ports.reader();
-
-       for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-               i->second->cycle_split ();
-       }
+       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)
 {
-       GET_PRIVATE_JACK_POINTER_RET(_jack,0);
        Glib::Threads::Mutex::Lock tm (_process_lock, Glib::Threads::TRY_LOCK);
 
        PT_TIMING_REF;
@@ -227,10 +206,104 @@ 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 == 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);
+
+               if (_latency_input_port && _latency_output_port) {
+                       PortEngine& pe (port_engine());
+
+                       Sample* in = (Sample*) pe.get_buffer (_latency_input_port, nframes);
+                       Sample* out = (Sample*) pe.get_buffer (_latency_output_port, nframes);
+
+                       _mtdm->process (nframes, in, out);
+               }
+
+               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.
+                */
+
+               PortManager::cycle_start (nframes);
+               PortManager::silence (nframes);
+               PortManager::cycle_end (nframes);
+
+                if (_latency_flush_frames > nframes) {
+                        _latency_flush_frames -= nframes;
+                } else {
+                        _latency_flush_frames = 0;
+                }
+
+               return_after_remove_check = true;
+       }
+
        if (session_remove_pending) {
 
                /* perform the actual session removal */
@@ -238,19 +311,19 @@ AudioEngine::process_callback (pframes_t nframes)
                if (session_removal_countdown < 0) {
 
                        /* fade out over 1 second */
-                       session_removal_countdown = _frame_rate/2;
-                       session_removal_gain = 1.0;
+                       session_removal_countdown = sample_rate()/2;
+                       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.
                        */
 
@@ -267,11 +340,15 @@ AudioEngine::process_callback (pframes_t nframes)
                }
        }
 
+       if (return_after_remove_check) {
+               return 0;
+       }
+
        if (_session == 0) {
 
                if (!_freewheeling) {
-                       MIDI::Manager::instance()->cycle_start(nframes);
-                       MIDI::Manager::instance()->cycle_end();
+                       PortManager::cycle_start (nframes);
+                       PortManager::cycle_end (nframes);
                }
 
                _processed_frames = next_processed_frames;
@@ -282,37 +359,24 @@ AudioEngine::process_callback (pframes_t nframes)
        /* tell all relevant objects that we're starting a new cycle */
 
        InternalSend::CycleStart (nframes);
-       Port::set_global_port_buffer_offset (0);
-        Port::set_cycle_framecnt (nframes);
 
        /* tell all Ports that we're starting a new cycle */
 
-       boost::shared_ptr<Ports> p = ports.reader();
-
-       for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-               i->second->cycle_start (nframes);
-       }
+       PortManager::cycle_start (nframes);
 
        /* test if we are freewheeling and there are freewheel signals connected.
            ardour should act normally even when freewheeling unless /it/ is
-           exporting 
+           exporting (which is what Freewheel.empty() tests for).
        */
 
        if (_freewheeling && !Freewheel.empty()) {
-
-                Freewheel (nframes);
-
+               Freewheel (nframes);
        } else {
-               MIDI::Manager::instance()->cycle_start(nframes);
-
-               if (_session) {
-                       _session->process (nframes);
-               }
-
-               MIDI::Manager::instance()->cycle_end();
+               _session->process (nframes);
        }
 
        if (_freewheeling) {
+               PortManager::cycle_end (nframes);
                return 0;
        }
 
@@ -323,52 +387,39 @@ AudioEngine::process_callback (pframes_t nframes)
 
        if (last_monitor_check + monitor_check_interval < next_processed_frames) {
 
-               boost::shared_ptr<Ports> p = ports.reader();
+               PortManager::check_monitoring ();
+               last_monitor_check = next_processed_frames;
+       }
 
-               for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+#ifdef SILENCE_AFTER_SECONDS
 
-                       bool x;
+       bool was_silent = (_silence_countdown == 0);
 
-                       if (i->second->last_monitor() != (x = i->second->monitoring_input ())) {
-                               i->second->set_last_monitor (x);
-                               /* XXX I think this is dangerous, due to
-                                  a likely mutex in the signal handlers ...
-                               */
-                               i->second->MonitorInputChanged (x); /* EMIT SIGNAL */
-                       }
-               }
-               last_monitor_check = next_processed_frames;
+       if (_silence_countdown >= nframes) {
+               _silence_countdown -= nframes;
+       } else {
+               _silence_countdown = 0;
        }
 
-       if (_session->silent()) {
+       if (!was_silent && _silence_countdown == 0) {
+               _silence_hit_cnt++;
+               BecameSilent (); /* EMIT SIGNAL */
+       }
 
-               for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+       if (_silence_countdown == 0 || _session->silent()) {
+               PortManager::silence (nframes);
+       }
 
-                       if (i->second->sends_output()) {
-                               i->second->get_buffer(nframes).silence(nframes);
-                       }
-               }
+#else
+       if (_session->silent()) {
+               PortManager::silence (nframes, _session);
        }
+#endif
 
        if (session_remove_pending && session_removal_countdown) {
 
-               for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-
-                       if (i->second->sends_output()) {
+               PortManager::fade_out (session_removal_gain, session_removal_gain_step, nframes);
 
-                               boost::shared_ptr<AudioPort> ap = boost::dynamic_pointer_cast<AudioPort> (i->second);
-                               if (ap) {
-                                       Sample* s = ap->engine_get_whole_audio_buffer ();
-                                       gain_t g = session_removal_gain;
-                                       
-                                       for (pframes_t n = 0; n < nframes; ++n) {
-                                               *s++ *= g;
-                                               g -= session_removal_gain_step;
-                                       }
-                               }
-                       }
-               }
-               
                if (session_removal_countdown > nframes) {
                        session_removal_countdown -= nframes;
                } else {
@@ -378,52 +429,178 @@ AudioEngine::process_callback (pframes_t nframes)
                session_removal_gain -= (nframes * session_removal_gain_step);
        }
 
-       // Finalize ports
-
-       for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-               i->second->cycle_end (nframes);
-       }
+       PortManager::cycle_end (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)
@@ -434,19 +611,9 @@ AudioEngine::set_session (Session *s)
 
        if (_session) {
 
-               start_metering_thread ();
-
-               pframes_t blocksize = jack_get_buffer_size (_jack);
+               pframes_t blocksize = samples_per_cycle ();
 
-               /* page in as much of the session process code as we
-                  can before we really start running.
-               */
-
-               boost::shared_ptr<Ports> p = ports.reader();
-
-               for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-                       i->second->cycle_start (blocksize);
-               }
+               PortManager::cycle_start (blocksize);
 
                _session->process (blocksize);
                _session->process (blocksize);
@@ -457,9 +624,7 @@ AudioEngine::set_session (Session *s)
                _session->process (blocksize);
                _session->process (blocksize);
 
-               for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-                       i->second->cycle_end (blocksize);
-               }
+               PortManager::cycle_end (blocksize);
        }
 }
 
@@ -470,10 +635,10 @@ AudioEngine::remove_session ()
 
        if (_running) {
 
-               stop_metering_thread ();
-
                if (_session) {
                        session_remove_pending = true;
+                       /* signal the start of the fade out countdown */
+                       session_removal_countdown = -1;
                        session_removed.wait(_process_lock);
                }
 
@@ -484,549 +649,821 @@ AudioEngine::remove_session ()
        remove_all_ports ();
 }
 
+
 void
-AudioEngine::port_registration_failure (const std::string& portname)
+AudioEngine::reconnect_session_routes (bool reconnect_inputs, bool reconnect_outputs)
 {
-       GET_PRIVATE_JACK_POINTER (_jack);
-       string full_portname = jack_client_name;
-       full_portname += ':';
-       full_portname += portname;
-
-
-       jack_port_t* p = jack_port_by_name (_priv_jack, full_portname.c_str());
-       string reason;
-
-       if (p) {
-               reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
-       } else {
-               reason = string_compose (_("No more JACK ports are available. You will need to stop %1 and restart JACK with more ports if you need this many tracks."), PROGRAM_NAME);
+#ifdef USE_TRACKS_CODE_FEATURES
+       if (_session) {
+               _session->reconnect_existing_routes(true, true, reconnect_inputs, reconnect_outputs);
        }
-
-       throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
+#endif
 }
 
-boost::shared_ptr<Port>
-AudioEngine::register_port (DataType dtype, const string& portname, bool input)
+
+void
+AudioEngine::died ()
 {
-       boost::shared_ptr<Port> newport;
+       /* called from a signal handler for SIGPIPE */
+       _running = false;
+}
 
-       try {
-               if (dtype == DataType::AUDIO) {
-                       newport.reset (new AudioPort (portname, (input ? Port::IsInput : Port::IsOutput)));
-               } else if (dtype == DataType::MIDI) {
-                       newport.reset (new MidiPort (portname, (input ? Port::IsInput : Port::IsOutput)));
+int
+AudioEngine::reset_timebase ()
+{
+       if (_session) {
+               if (_session->config.get_jack_time_master()) {
+                       _backend->set_time_master (true);
                } else {
-                       throw PortRegistrationFailure("unable to create port (unknown type)");
+                       _backend->set_time_master (false);
                }
-
-               RCUWriter<Ports> writer (ports);
-               boost::shared_ptr<Ports> ps = writer.get_copy ();
-               ps->insert (make_pair (make_port_name_relative (portname), newport));
-
-               /* writer goes out of scope, forces update */
-
-               return newport;
-       }
-
-       catch (PortRegistrationFailure& err) {
-               throw err;
-       } catch (std::exception& e) {
-               throw PortRegistrationFailure(string_compose(
-                               _("unable to create port: %1"), e.what()).c_str());
-       } catch (...) {
-               throw PortRegistrationFailure("unable to create port (unknown error)");
        }
+       return 0;
 }
 
-boost::shared_ptr<Port>
-AudioEngine::register_input_port (DataType type, const string& portname)
-{
-       return register_port (type, portname, true);
-}
 
-boost::shared_ptr<Port>
-AudioEngine::register_output_port (DataType type, const string& portname)
+void
+AudioEngine::destroy ()
 {
-       return register_port (type, portname, false);
+       delete _instance;
+       _instance = 0;
 }
 
 int
-AudioEngine::unregister_port (boost::shared_ptr<Port> port)
+AudioEngine::discover_backends ()
 {
-       /* caller must hold process lock */
-
-       if (!_running) {
-               /* probably happening when the engine has been halted by JACK,
-                  in which case, there is nothing we can do here.
-                  */
-               return 0;
-       }
+       vector<std::string> backend_modules;
 
-       {
-               RCUWriter<Ports> writer (ports);
-               boost::shared_ptr<Ports> ps = writer.get_copy ();
-               Ports::iterator x = ps->find (make_port_name_relative (port->name()));
+       _backends.clear ();
 
-               if (x != ps->end()) {
-                       ps->erase (x);
-               }
+       Glib::PatternSpec so_extension_pattern("*backend.so");
+       Glib::PatternSpec dylib_extension_pattern("*backend.dylib");
 
-               /* writer goes out of scope, forces update */
-       }
+#if defined(PLATFORM_WINDOWS) && defined(DEBUGGABLE_BACKENDS)
+       #if defined(DEBUG) || defined(_DEBUG)
+               Glib::PatternSpec dll_extension_pattern("*backendD.dll");
+       #else
+               Glib::PatternSpec dll_extension_pattern("*backendRDC.dll");
+       #endif
+#else
+       Glib::PatternSpec dll_extension_pattern("*backend.dll");
+#endif
 
-       ports.flush ();
+       find_files_matching_pattern (backend_modules, backend_search_path (),
+                                    so_extension_pattern);
 
-       return 0;
-}
+       find_files_matching_pattern (backend_modules, backend_search_path (),
+                                    dylib_extension_pattern);
 
-int
-AudioEngine::connect (const string& source, const string& destination)
-{
-       int ret;
+       find_files_matching_pattern (backend_modules, backend_search_path (),
+                                    dll_extension_pattern);
 
-       if (!_running) {
-               if (!_has_run) {
-                       fatal << _("connect called before engine was started") << endmsg;
-                       /*NOTREACHED*/
-               } else {
-                       return -1;
-               }
-       }
+       DEBUG_TRACE (DEBUG::AudioEngine, string_compose ("looking for backends in %1\n", backend_search_path().to_string()));
 
-       string s = make_port_name_non_relative (source);
-       string d = make_port_name_non_relative (destination);
+       for (vector<std::string>::iterator i = backend_modules.begin(); i != backend_modules.end(); ++i) {
 
+               AudioBackendInfo* info;
 
-       boost::shared_ptr<Port> src = get_port_by_name (s);
-       boost::shared_ptr<Port> dst = get_port_by_name (d);
+               DEBUG_TRACE (DEBUG::AudioEngine, string_compose ("Checking possible backend in %1\n", *i));
 
-       if (src) {
-               ret = src->connect (d);
-       } else if (dst) {
-               ret = dst->connect (s);
-       } else {
-               /* neither port is known to us, and this API isn't intended for use as a general patch bay */
-               ret = -1;
+               if ((info = backend_discover (*i)) != 0) {
+                       _backends.insert (make_pair (info->name, info));
+               }
        }
 
-       if (ret > 0) {
-               /* already exists - no error, no warning */
-       } else if (ret < 0) {
-               error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
-                                       source, s, destination, d)
-                     << endmsg;
-       }
+       DEBUG_TRACE (DEBUG::AudioEngine, string_compose ("Found %1 backends\n", _backends.size()));
 
-       return ret;
+       return _backends.size();
 }
 
-int
-AudioEngine::disconnect (const string& source, const string& destination)
+AudioBackendInfo*
+AudioEngine::backend_discover (const string& path)
 {
-       int ret;
+#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;
 
-       if (!_running) {
-               if (!_has_run) {
-                       fatal << _("disconnect called before engine was started") << endmsg;
-                       /*NOTREACHED*/
-               } else {
-                       return -1;
-               }
+       if (!module) {
+               error << string_compose(_("AudioEngine: cannot load module \"%1\" (%2)"), path,
+                                       Glib::Module::get_last_error()) << endmsg;
+               return 0;
        }
 
-       string s = make_port_name_non_relative (source);
-       string d = make_port_name_non_relative (destination);
-
-       boost::shared_ptr<Port> src = get_port_by_name (s);
-       boost::shared_ptr<Port> dst = get_port_by_name (d);
+       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;
+       }
 
-       if (src) {
-                       ret = src->disconnect (d);
-       } else if (dst) {
-                       ret = dst->disconnect (s);
-       } else {
-               /* neither port is known to us, and this API isn't intended for use as a general patch bay */
-               ret = -1;
+       dfunc = (AudioBackendInfo* (*)(void))func;
+       info = dfunc();
+       if (!info->available()) {
+               return 0;
        }
-       return ret;
+
+       module.make_resident ();
+
+       return info;
 }
 
-int
-AudioEngine::disconnect (boost::shared_ptr<Port> port)
+vector<const AudioBackendInfo*>
+AudioEngine::available_backends() const
 {
-       GET_PRIVATE_JACK_POINTER_RET (_jack,-1);
+       vector<const AudioBackendInfo*> r;
 
-       if (!_running) {
-               if (!_has_run) {
-                       fatal << _("disconnect called before engine was started") << endmsg;
-                       /*NOTREACHED*/
-               } else {
-                       return -1;
-               }
+       for (BackendMap::const_iterator i = _backends.begin(); i != _backends.end(); ++i) {
+               r.push_back (i->second);
        }
 
-       return port->disconnect_all ();
+       return r;
 }
 
-ARDOUR::framecnt_t
-AudioEngine::frame_rate () const
+string
+AudioEngine::current_backend_name() const
 {
-       GET_PRIVATE_JACK_POINTER_RET (_jack, 0);
-       if (_frame_rate == 0) {
-               return (_frame_rate = jack_get_sample_rate (_priv_jack));
-       } else {
-               return _frame_rate;
+       if (_backend) {
+               return _backend->name();
        }
+       return string();
 }
 
-size_t
-AudioEngine::raw_buffer_size (DataType t)
+void
+AudioEngine::drop_backend ()
 {
-       std::map<DataType,size_t>::const_iterator s = _raw_buffer_sizes.find(t);
-       return (s != _raw_buffer_sizes.end()) ? s->second : 0;
+       if (_backend) {
+               _backend->stop ();
+               // Stopped is needed for Graph to explicitly terminate threads
+               Stopped (); /* EMIT SIGNAL */
+               _backend->drop_device ();
+               _backend.reset ();
+               _running = false;
+       }
 }
 
-ARDOUR::pframes_t
-AudioEngine::frames_per_cycle () const
+boost::shared_ptr<AudioBackend>
+AudioEngine::set_default_backend ()
 {
-       GET_PRIVATE_JACK_POINTER_RET (_jack,0);
-       if (_buffer_size == 0) {
-               return jack_get_buffer_size (_jack);
-       } else {
-               return _buffer_size;
+       if (_backends.empty()) {
+               return boost::shared_ptr<AudioBackend>();
        }
-}
 
-/** @param name Full or short name of port
- *  @return Corresponding Port or 0.
- */
+       return set_backend (_backends.begin()->first, "", "");
+}
 
-boost::shared_ptr<Port>
-AudioEngine::get_port_by_name (const string& portname)
+boost::shared_ptr<AudioBackend>
+AudioEngine::set_backend (const std::string& name, const std::string& arg1, const std::string& arg2)
 {
-       if (!_running) {
-               if (!_has_run) {
-                       fatal << _("get_port_by_name() called before engine was started") << endmsg;
-                       /*NOTREACHED*/
-               } else {
-                       boost::shared_ptr<Port> ();
-               }
-       }
+       BackendMap::iterator b = _backends.find (name);
 
-        if (!port_is_mine (portname)) {
-                /* not an ardour port */
-                return boost::shared_ptr<Port> ();
-        }
+       if (b == _backends.end()) {
+               return boost::shared_ptr<AudioBackend>();
+       }
 
-       boost::shared_ptr<Ports> pr = ports.reader();
-       std::string rel = make_port_name_relative (portname);
-       Ports::iterator x = pr->find (rel);
+       drop_backend ();
 
-       if (x != pr->end()) {
-               /* its possible that the port was renamed by some 3rd party and
-                  we don't know about it. check for this (the check is quick
-                  and cheap), and if so, rename the port (which will alter
-                  the port map as a side effect).
-               */
-               const std::string check = make_port_name_relative (jack_port_name (x->second->jack_port()));
-               if (check != rel) {
-                       x->second->set_name (check);
+       try {
+               if (b->second->instantiate (arg1, arg2)) {
+                       throw failed_constructor ();
                }
-               return x->second;
+
+               _backend = b->second->factory (*this);
+
+       } catch (exception& e) {
+               error << string_compose (_("Could not create backend for %1: %2"), name, e.what()) << endmsg;
+               return boost::shared_ptr<AudioBackend>();
        }
 
-        return boost::shared_ptr<Port> ();
+       return _backend;
 }
 
-void
-AudioEngine::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
+/* BACKEND PROXY WRAPPERS */
+
+int
+AudioEngine::start (bool for_latency)
 {
-       RCUWriter<Ports> writer (ports);
-       boost::shared_ptr<Ports> p = writer.get_copy();
-       Ports::iterator x = p->find (old_relative_name);
-       
-       if (x != p->end()) {
-               boost::shared_ptr<Port> port = x->second;
-               p->erase (x);
-               p->insert (make_pair (new_relative_name, port));
+       if (!_backend) {
+               return -1;
+       }
+
+       if (_running) {
+               return 0;
+       }
+
+       _processed_frames = 0;
+       last_monitor_check = 0;
+
+       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);
+               }
+
+       }
+
+       if (!for_latency) {
+               Running(); /* EMIT SIGNAL */
        }
+
+       return 0;
 }
 
-const char **
-AudioEngine::get_ports (const string& port_name_pattern, const string& type_name_pattern, uint32_t flags)
+int
+AudioEngine::stop (bool for_latency)
 {
-       GET_PRIVATE_JACK_POINTER_RET (_jack,0);
-       if (!_running) {
-               if (!_has_run) {
-                       fatal << _("get_ports called before engine was started") << endmsg;
-                       /*NOTREACHED*/
-               } else {
-                       return 0;
+       bool stop_engine = true;
+
+       if (!_backend) {
+               return 0;
+       }
+
+       Glib::Threads::Mutex::Lock pl (_process_lock, Glib::Threads::NOT_LOCK);
+
+       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;
                }
        }
-       return jack_get_ports (_priv_jack, port_name_pattern.c_str(), type_name_pattern.c_str(), flags);
+
+       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;
+       }
+       _processed_frames = 0;
+       _measuring_latency = MeasureNone;
+       _latency_output_port = 0;
+       _latency_input_port = 0;
+       _started_for_latency = false;
+
+       if (stop_engine) {
+               Port::PortDrop ();
+       }
+
+       if (!for_latency && stop_engine) {
+               Stopped (); /* EMIT SIGNAL */
+       }
+
+       return 0;
 }
 
-void
-AudioEngine::died ()
+int
+AudioEngine::freewheel (bool start_stop)
 {
-        /* called from a signal handler for SIGPIPE */
+       if (!_backend) {
+               return -1;
+       }
 
-       stop_metering_thread ();
+       /* _freewheeling will be set when first Freewheel signal occurs */
 
-        _running = false;
-       _buffer_size = 0;
-       _frame_rate = 0;
-       _jack = 0;
+       return _backend->freewheel (start_stop);
 }
 
-bool
-AudioEngine::can_request_hardware_monitoring ()
+float
+AudioEngine::get_dsp_load() const
 {
-       GET_PRIVATE_JACK_POINTER_RET (_jack,false);
-       const char ** ports;
+       if (!_backend || !_running) {
+               return 0.0;
+       }
+       return _backend->dsp_load ();
+}
 
-       if ((ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) {
+bool
+AudioEngine::is_realtime() const
+{
+       if (!_backend) {
                return false;
        }
 
-       free (ports);
-
-       return true;
+       return _backend->is_realtime();
 }
 
-ChanCount
-AudioEngine::n_physical (unsigned long flags) const
+bool
+AudioEngine::connected() const
 {
-       ChanCount c;
+       if (!_backend) {
+               return false;
+       }
 
-       GET_PRIVATE_JACK_POINTER_RET (_jack, c);
+       return _backend->available();
+}
 
-       const char ** ports = jack_get_ports (_priv_jack, NULL, NULL, JackPortIsPhysical | flags);
-       if (ports == 0) {
-               return c;
+void
+AudioEngine::transport_start ()
+{
+       if (!_backend) {
+               return;
        }
+       return _backend->transport_start ();
+}
 
-       for (uint32_t i = 0; ports[i]; ++i) {
-               if (!strstr (ports[i], "Midi-Through")) {
-                       DataType t (jack_port_type (jack_port_by_name (_jack, ports[i])));
-                       c.set (t, c.get (t) + 1);
-               }
+void
+AudioEngine::transport_stop ()
+{
+       if (!_backend) {
+               return;
        }
+       return _backend->transport_stop ();
+}
 
-       free (ports);
+TransportState
+AudioEngine::transport_state ()
+{
+       if (!_backend) {
+               return TransportStopped;
+       }
+       return _backend->transport_state ();
+}
 
-       return c;
+void
+AudioEngine::transport_locate (framepos_t pos)
+{
+       if (!_backend) {
+               return;
+       }
+       return _backend->transport_locate (pos);
 }
 
-ChanCount
-AudioEngine::n_physical_inputs () const
+framepos_t
+AudioEngine::transport_frame()
 {
-       return n_physical (JackPortIsInput);
+       if (!_backend) {
+               return 0;
+       }
+       return _backend->transport_frame ();
 }
 
-ChanCount
-AudioEngine::n_physical_outputs () const
+framecnt_t
+AudioEngine::sample_rate () const
 {
-       return n_physical (JackPortIsOutput);
+       if (!_backend) {
+               return 0;
+       }
+       return _backend->sample_rate ();
 }
 
-void
-AudioEngine::get_physical (DataType type, unsigned long flags, vector<string>& phy)
+pframes_t
+AudioEngine::samples_per_cycle () const
 {
-       GET_PRIVATE_JACK_POINTER (_jack);
-       const char ** ports;
+       if (!_backend) {
+               return 0;
+       }
+       return _backend->buffer_size ();
+}
 
-       if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical | flags)) == 0) {
-               return;
+int
+AudioEngine::usecs_per_cycle () const
+{
+       if (!_backend) {
+               return -1;
        }
+       return _backend->usecs_per_cycle ();
+}
 
-       if (ports) {
-               for (uint32_t i = 0; ports[i]; ++i) {
-                        if (strstr (ports[i], "Midi-Through")) {
-                                continue;
-                        }
-                       phy.push_back (ports[i]);
-               }
-               free (ports);
+size_t
+AudioEngine::raw_buffer_size (DataType t)
+{
+       if (!_backend) {
+               return -1;
        }
+       return _backend->raw_buffer_size (t);
 }
 
-/** Get physical ports for which JackPortIsOutput is set; ie those that correspond to
- *  a physical input connector.
- */
-void
-AudioEngine::get_physical_inputs (DataType type, vector<string>& ins)
+framepos_t
+AudioEngine::sample_time ()
 {
-       get_physical (type, JackPortIsOutput, ins);
+       if (!_backend) {
+               return 0;
+       }
+       return _backend->sample_time ();
 }
 
-/** Get physical ports for which JackPortIsInput is set; ie those that correspond to
- *  a physical output connector.
- */
-void
-AudioEngine::get_physical_outputs (DataType type, vector<string>& outs)
+framepos_t
+AudioEngine::sample_time_at_cycle_start ()
+{
+       if (!_backend) {
+               return 0;
+       }
+       return _backend->sample_time_at_cycle_start ();
+}
+
+pframes_t
+AudioEngine::samples_since_cycle_start ()
 {
-       get_physical (type, JackPortIsInput, outs);
+       if (!_backend) {
+               return 0;
+       }
+       return _backend->samples_since_cycle_start ();
 }
 
+bool
+AudioEngine::get_sync_offset (pframes_t& offset) const
+{
+       if (!_backend) {
+               return false;
+       }
+       return _backend->get_sync_offset (offset);
+}
 
 int
-AudioEngine::reset_timebase ()
+AudioEngine::create_process_thread (boost::function<void()> func)
 {
-       GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
-       if (_session) {
-               if (_session->config.get_jack_time_master()) {
-                       _backend->set_time_master (true);
-               } else {
-                       _backend->set_time_master (false);
-               }
+       if (!_backend) {
+               return -1;
        }
-       return 0;
+       return _backend->create_process_thread (func);
 }
 
-void
-AudioEngine::remove_all_ports ()
+int
+AudioEngine::join_process_threads ()
 {
-       /* make sure that JACK callbacks that will be invoked as we cleanup
-        * ports know that they have nothing to do.
-        */
+       if (!_backend) {
+               return -1;
+       }
+       return _backend->join_process_threads ();
+}
 
-       port_remove_in_progress = true;
+bool
+AudioEngine::in_process_thread ()
+{
+       if (!_backend) {
+               return false;
+       }
+       return _backend->in_process_thread ();
+}
 
-       /* process lock MUST be held by caller
-       */
+uint32_t
+AudioEngine::process_thread_count ()
+{
+       if (!_backend) {
+               return 0;
+       }
+       return _backend->process_thread_count ();
+}
 
-       {
-               RCUWriter<Ports> writer (ports);
-               boost::shared_ptr<Ports> ps = writer.get_copy ();
-               ps->clear ();
+int
+AudioEngine::set_device_name (const std::string& name)
+{
+       if (!_backend) {
+               return -1;
        }
+       return _backend->set_device_name  (name);
+}
 
-       /* clear dead wood list in RCU */
+int
+AudioEngine::set_sample_rate (float sr)
+{
+       if (!_backend) {
+               return -1;
+       }
 
-       ports.flush ();
+       return _backend->set_sample_rate  (sr);
+}
 
-       port_remove_in_progress = false;
+int
+AudioEngine::set_buffer_size (uint32_t bufsiz)
+{
+       if (!_backend) {
+               return -1;
+       }
+       return _backend->set_buffer_size  (bufsiz);
 }
 
+int
+AudioEngine::set_interleaved (bool yn)
+{
+       if (!_backend) {
+               return -1;
+       }
+       return _backend->set_interleaved  (yn);
+}
 
-string
-AudioEngine::make_port_name_relative (string portname) const
+int
+AudioEngine::set_input_channels (uint32_t ic)
 {
-       string::size_type len;
-       string::size_type n;
+       if (!_backend) {
+               return -1;
+       }
+       return _backend->set_input_channels  (ic);
+}
 
-       len = portname.length();
+int
+AudioEngine::set_output_channels (uint32_t oc)
+{
+       if (!_backend) {
+               return -1;
+       }
+       return _backend->set_output_channels (oc);
+}
 
-       for (n = 0; n < len; ++n) {
-               if (portname[n] == ':') {
-                       break;
-               }
+int
+AudioEngine::set_systemic_input_latency (uint32_t il)
+{
+       if (!_backend) {
+               return -1;
        }
+       return _backend->set_systemic_input_latency  (il);
+}
 
-       if ((n != len) && (portname.substr (0, n) == jack_client_name)) {
-               return portname.substr (n+1);
+int
+AudioEngine::set_systemic_output_latency (uint32_t ol)
+{
+       if (!_backend) {
+               return -1;
        }
+       return _backend->set_systemic_output_latency  (ol);
+}
 
-       return portname;
+bool
+AudioEngine::thread_initialised_for_audio_processing ()
+{
+    return SessionEvent::has_per_thread_pool () && AsyncMIDIPort::is_process_thread();
 }
 
-string
-AudioEngine::make_port_name_non_relative (string portname) const
+/* END OF BACKEND PROXY API */
+
+void
+AudioEngine::thread_init_callback (void* arg)
 {
-       string str;
+       /* make sure that anybody who needs to know about this thread
+          knows about it.
+       */
+
+       pthread_set_name (X_("audioengine"));
+
+       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 (portname.find_first_of (':') != string::npos) {
-               return portname;
+       if (arg) {
+               delete AudioEngine::instance()->_main_thread;
+               /* the special thread created/managed by the backend */
+               AudioEngine::instance()->_main_thread = new ProcessThread;
        }
+}
 
-       str  = jack_client_name;
-       str += ':';
-       str += portname;
+int
+AudioEngine::sync_callback (TransportState state, framepos_t position)
+{
+       if (_session) {
+               return _session->backend_sync_callback (state, position);
+       }
+       return 0;
+}
 
-       return str;
+void
+AudioEngine::freewheel_callback (bool onoff)
+{
+       _freewheeling = onoff;
 }
 
-bool
-AudioEngine::port_is_mine (const string& portname) const
+void
+AudioEngine::latency_callback (bool for_playback)
 {
-       if (portname.find_first_of (':') != string::npos) {
-               if (portname.substr (0, jack_client_name.length ()) != jack_client_name) {
-                        return false;
-                }
+        if (_session) {
+                _session->update_latency (for_playback);
         }
-        return true;
 }
 
-bool
-AudioEngine::port_is_physical (const std::string& portname) const
+void
+AudioEngine::update_latencies ()
 {
-        GET_PRIVATE_JACK_POINTER_RET(_jack, false);
+       if (_backend) {
+               _backend->update_latencies ();
+       }
+}
 
-        jack_port_t *port = jack_port_by_name (_priv_jack, portname.c_str());
+void
+AudioEngine::halted_callback (const char* why)
+{
+       if (_in_destructor) {
+               /* everything is under control */
+               return;
+       }
 
-        if (!port) {
-                return false;
-        }
+       _running = false;
+
+       Port::PortDrop (); /* EMIT SIGNAL */
+
+       if (!_started_for_latency) {
+               Halted (why);      /* EMIT SIGNAL */
+       }
+}
+
+bool
+AudioEngine::setup_required () const
+{
+       if (_backend) {
+               if (_backend->info().already_configured())
+                       return false;
+       } else {
+               if (_backends.size() == 1 && _backends.begin()->second->already_configured()) {
+                       return false;
+               }
+       }
 
-        return jack_port_flags (port) & JackPortIsPhysical;
+       return true;
 }
 
-void
-AudioEngine::destroy ()
+int
+AudioEngine::prepare_for_latency_measurement ()
 {
-       delete _instance;
-       _instance = 0;
+       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)) {
+               return -1;
+       }
+       _started_for_latency = true;
+
+       return 0;
 }
 
 int
-AudioEngine::discover_backends ()
+AudioEngine::start_latency_detection (bool for_midi)
 {
-       vector<std::string> backend_modules;
-       AudioBackend* backend;
+       if (prepare_for_latency_measurement ()) {
+               return -1;
+       }
 
-       Glib::PatternSpec so_extension_pattern("*.so");
-       Glib::PatternSpec dylib_extension_pattern("*.dylib");
+       PortEngine& pe (port_engine());
 
-       find_matching_files_in_search_path (backend_search_path (),
-                                           so_extension_pattern, backend_modules);
+       delete _mtdm;
+       _mtdm = 0;
 
-       find_matching_files_in_search_path (backend_search_path (),
-                                           dylib_extension_pattern, backend_modules);
+       delete _mididm;
+       _mididm = 0;
 
-       DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for backends in %1"), backend_search_path().to_string()));
+       /* find the ports we will connect to */
 
-       for (vector<std::string>::iterator i = backend_modules.begin(); i != backend_modules.end(); ++i) {
-               if ((backend = backend_discover (*i)) != 0) {
-                       _backends.insert (make_pair (backend->name(), backend));
+       PortEngine::PortHandle out = pe.get_port_by_name (_latency_output_name);
+       PortEngine::PortHandle in = pe.get_port_by_name (_latency_input_name);
+
+       if (!out || !in) {
+               stop (true);
+               return -1;
+       }
+
+       /* create the ports we will use to read/write data */
+       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());
+
        }
+
+       LatencyRange lr;
+       _latency_signal_latency = 0;
+       lr = pe.get_latency_range (in, false);
+       _latency_signal_latency = lr.max;
+       lr = pe.get_latency_range (out, true);
+       _latency_signal_latency += lr.max;
+
+       /* all created and connected, lets go */
+       _latency_flush_frames = samples_per_cycle();
+       _measuring_latency = for_midi ? MeasureMIDI : MeasureAudio;
+
+       return 0;
 }
 
-AudioBackend*
-AudioEngine::backend_discover (string path)
+void
+AudioEngine::stop_latency_detection ()
 {
-       Glib::Module* module = new Glib::Module(path);
-       AudioBackend* (*dfunc)(void);
-       void* func = 0;
+       _measuring_latency = MeasureNone;
 
-       if (!module) {
-               error << string_compose(_("AudioEngine: cannot load module \"%1\" (%2)"), path,
-                               Glib::Module::get_last_error()) << endmsg;
-               delete module;
-               return 0;
+       if (_latency_output_port) {
+               port_engine().unregister_port (_latency_output_port);
+               _latency_output_port = 0;
+       }
+       if (_latency_input_port) {
+               port_engine().unregister_port (_latency_input_port);
+               _latency_input_port = 0;
        }
 
-       if (!module->get_symbol("backend_factory", func)) {
-               error << string_compose(_("AudioEngine: module \"%1\" has no factory function."), path) << endmsg;
-               error << Glib::Module::get_last_error() << endmsg;
-               delete module;
-               return 0;
+       if (!_backend->can_change_systemic_latency_when_running()) {
+               stop (true);
        }
 
-       dfunc = (AudioBackend* (*)(void))func;
-       AudioBackend* backend = dfunc();
+       if (_stopped_for_latency) {
+               start ();
+       }
+
+       _stopped_for_latency = false;
+       _started_for_latency = false;
+}
 
-       return backend;
+void
+AudioEngine::set_latency_output_port (const string& name)
+{
+       _latency_output_name = name;
+}
+
+void
+AudioEngine::set_latency_input_port (const string& name)
+{
+       _latency_input_name = name;
 }