Merge branch 'master' into cairocanvas
[ardour.git] / libs / backends / jack / jack_audiobackend.cc
index 3cbd0cd6ae379066c0b73e5dac8e685c6d09a4f2..1ceb794dc569558bf41224b9f9f33fc26dc21321 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <boost/scoped_ptr.hpp>
 #include <glibmm/timer.h>
+#include <glibmm/spawn.h>
 
 #include "pbd/error.h"
 
@@ -35,8 +36,8 @@
 
 #include "jack_audiobackend.h"
 #include "jack_connection.h"
-#include "jack_portengine.h"
 #include "jack_utils.h"
+#include "jack_session.h"
 
 #include "i18n.h"
 
@@ -44,8 +45,7 @@ using namespace ARDOUR;
 using namespace PBD;
 using std::string;
 using std::vector;
-using std::cerr;
-using std::endl;
+
 
 #define GET_PRIVATE_JACK_POINTER(localvar)  jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return; }
 #define GET_PRIVATE_JACK_POINTER_RET(localvar,r) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return r; }
@@ -59,13 +59,15 @@ JACKAudioBackend::JACKAudioBackend (AudioEngine& e, boost::shared_ptr<JackConnec
        , _target_buffer_size (1024)
        , _target_sample_format (FormatFloat)
        , _target_interleaved (false)
-       , _target_input_channels (-1)
-       , _target_output_channels (-1)
+       , _target_input_channels (0)
+       , _target_output_channels (0)
        , _target_systemic_input_latency (0)
        , _target_systemic_output_latency (0)
        , _current_sample_rate (0)
        , _current_buffer_size (0)
+       , _session (0)
 {
+       _jack_connection->Connected.connect_same_thread (jack_connection_connection, boost::bind (&JACKAudioBackend::when_connected_to_jack, this));
        _jack_connection->Disconnected.connect_same_thread (disconnect_connection, boost::bind (&JACKAudioBackend::disconnected, this, _1));
 }
 
@@ -86,7 +88,7 @@ JACKAudioBackend::private_handle() const
 }
 
 bool
-JACKAudioBackend::connected() const
+JACKAudioBackend::available() const
 {
        return (private_handle() != 0);
 }
@@ -153,11 +155,11 @@ JACKAudioBackend::enumerate_devices () const
 }
 
 vector<float>
-JACKAudioBackend::available_sample_rates (const string& /*device*/) const
+JACKAudioBackend::available_sample_rates (const string& device) const
 {
        vector<float> f;
        
-       if (connected()) {
+       if (device == _target_device && available()) {
                f.push_back (sample_rate());
                return f;
        }
@@ -181,11 +183,11 @@ JACKAudioBackend::available_sample_rates (const string& /*device*/) const
 }
 
 vector<uint32_t>
-JACKAudioBackend::available_buffer_sizes (const string& /*device*/) const
+JACKAudioBackend::available_buffer_sizes (const string& device) const
 {
        vector<uint32_t> s;
-       
-       if (connected()) {
+               
+       if (device == _target_device && available()) {
                s.push_back (buffer_size());
                return s;
        }
@@ -222,7 +224,7 @@ JACKAudioBackend::available_output_channel_count (const string& /*device*/) cons
 int
 JACKAudioBackend::set_device_name (const string& dev)
 {
-       if (connected()) {
+       if (available()) {
                /* need to stop and restart JACK for this to work, at present */
                return -1;
        }
@@ -234,7 +236,7 @@ JACKAudioBackend::set_device_name (const string& dev)
 int
 JACKAudioBackend::set_sample_rate (float sr)
 {
-       if (!connected()) {
+       if (!available()) {
                _target_sample_rate = sr;
                return 0;
        }
@@ -251,7 +253,7 @@ JACKAudioBackend::set_sample_rate (float sr)
 int
 JACKAudioBackend::set_buffer_size (uint32_t nframes)
 {
-       if (!connected()) {
+       if (!available()) {
                _target_buffer_size = nframes;
                return 0;
        }
@@ -292,8 +294,11 @@ JACKAudioBackend::set_interleaved (bool yn)
 int
 JACKAudioBackend::set_input_channels (uint32_t cnt)
 {
-       if (connected()) {
-               return -1;
+       if (available()) {
+               if (cnt != 0) {
+                       /* can't set a real value for this while JACK runs */
+                       return -1;
+               }
        }
 
        _target_input_channels = cnt;
@@ -304,8 +309,11 @@ JACKAudioBackend::set_input_channels (uint32_t cnt)
 int
 JACKAudioBackend::set_output_channels (uint32_t cnt)
 {
-       if (connected()) {
-               return -1;
+       if (available()) {
+               if (cnt != 0) {
+                       /* can't set a real value for this while JACK runs */
+                       return -1;
+               }
        }
 
        _target_output_channels = cnt;
@@ -316,7 +324,8 @@ JACKAudioBackend::set_output_channels (uint32_t cnt)
 int
 JACKAudioBackend::set_systemic_input_latency (uint32_t l)
 {
-       if (connected()) {
+       if (available()) {
+               /* can't do this while JACK runs */
                return -1;
        }
 
@@ -328,7 +337,8 @@ JACKAudioBackend::set_systemic_input_latency (uint32_t l)
 int
 JACKAudioBackend::set_systemic_output_latency (uint32_t l)
 {
-       if (connected()) {
+       if (available()) {
+               /* can't do this while JACK runs */
                return -1;
        }
 
@@ -342,18 +352,34 @@ JACKAudioBackend::set_systemic_output_latency (uint32_t l)
 std::string
 JACKAudioBackend::device_name () const
 {
-       if (connected()) {
-               return "???";
+       if (!_jack_connection->in_control()) {
+               return "???"; // JACK has no way (as of fall 2013) to return
+                             // the device name
        } 
 
        return _target_device;
 }
 
+std::string
+JACKAudioBackend::driver_name() const
+{
+       if (!_jack_connection->in_control()) {
+               return "???"; // JACK has no way (as of fall 2013) to return
+                             // the driver name
+       }
+
+       return _target_driver;
+}
+
 float
 JACKAudioBackend::sample_rate () const
 {
-       if (connected()) {
-               return _current_sample_rate;
+       if (!_jack_connection->in_control()) {
+               if (available()) {
+                       return _current_sample_rate;
+               } else {
+                       return 0;
+               }
        }
        return _target_sample_rate;
 }
@@ -361,8 +387,12 @@ JACKAudioBackend::sample_rate () const
 uint32_t
 JACKAudioBackend::buffer_size () const
 {
-       if (connected()) {
-               return _current_buffer_size;
+       if (!_jack_connection->in_control()) {
+               if (available()) {
+                       return _current_buffer_size;
+               } else {
+                       return 0;
+               }
        }
        return _target_buffer_size;
 }
@@ -379,22 +409,46 @@ JACKAudioBackend::interleaved () const
        return false;
 }
 
+string
+JACKAudioBackend::midi_option () const
+{
+       return _target_midi_option;
+}
+
 uint32_t
 JACKAudioBackend::input_channels () const
 {
-       if (connected()) {
-               return n_physical (JackPortIsInput).n_audio();
-       } 
-       return _target_input_channels;
+       if (!_jack_connection->in_control()) {
+               if (available()) {
+                       return n_physical (JackPortIsInput).n_audio();
+               } else {
+                       return 0;
+               }
+       } else {
+               if (available()) {
+                       return n_physical (JackPortIsInput).n_audio();
+               } else {
+                       return _target_input_channels;
+               }
+       }
 }
 
 uint32_t
 JACKAudioBackend::output_channels () const
 {
-       if (connected()) {
-               return n_physical (JackPortIsOutput).n_audio();
-       } 
-       return _target_output_channels;
+       if (!_jack_connection->in_control()) {
+               if (available()) {
+                       return n_physical (JackPortIsOutput).n_audio();
+               } else {
+                       return 0;
+               }
+       } else {
+               if (available()) {
+                       return n_physical (JackPortIsOutput).n_audio();
+               } else {
+                       return _target_output_channels;
+               }
+       }
 }
 
 uint32_t
@@ -417,7 +471,7 @@ JACKAudioBackend::raw_buffer_size(DataType t)
 }
 
 void
-JACKAudioBackend::setup_jack_startup_command ()
+JACKAudioBackend::setup_jack_startup_command (bool for_latency_measurement)
 {
        /* first we map the parameters that have been set onto a
         * JackCommandLineOptions object.
@@ -442,6 +496,8 @@ JACKAudioBackend::setup_jack_startup_command ()
        options.realtime = true;
        options.ports_max = 2048;
        
+       ARDOUR::set_midi_option (options, _target_midi_option);
+
        /* this must always be true for any server instance we start ourselves
         */
 
@@ -449,8 +505,11 @@ JACKAudioBackend::setup_jack_startup_command ()
 
        string cmdline;
 
-       if (!get_jack_command_line_string (options, cmdline)) {
-               /* error, somehow */
+       if (!get_jack_command_line_string (options, cmdline, for_latency_measurement)) {
+               /* error, somehow - we will still try to start JACK
+                * automatically but it will be without our preferred options
+                */
+               std::cerr << "get_jack_command_line_string () failed: using default settings." << std::endl;
                return;
        }
 
@@ -462,12 +521,15 @@ JACKAudioBackend::setup_jack_startup_command ()
 /* ---- BASIC STATE CONTROL API: start/stop/pause/freewheel --- */
 
 int
-JACKAudioBackend::start ()
+JACKAudioBackend::_start (bool for_latency_measurement)
 {
-       if (!connected()) {
+       if (!available()) {
 
-               if (!_jack_connection->server_running()) {
-                       setup_jack_startup_command ();
+               if (_jack_connection->in_control()) {
+                       /* we will be starting JACK, so set up the 
+                          command that JACK will use when it (auto-)starts
+                       */
+                       setup_jack_startup_command (for_latency_measurement);
                }
 
                if (_jack_connection->open ()) {
@@ -485,8 +547,11 @@ JACKAudioBackend::start ()
        /* Now that we have buffer size and sample rate established, the engine 
           can go ahead and do its stuff
        */
-       
-       engine.reestablish_ports ();
+
+       if (engine.reestablish_ports ()) {
+               error << _("Could not re-establish ports after connecting to JACK") << endmsg;
+               return -1;
+       }
 
        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;
@@ -520,18 +585,6 @@ JACKAudioBackend::stop ()
        return 0;
 }
 
-int
-JACKAudioBackend::pause ()
-{
-       GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
-
-       if (_priv_jack) {
-               jack_deactivate (_priv_jack);
-       }
-
-       return 0;
-}
-
 int
 JACKAudioBackend::freewheel (bool onoff)
 {
@@ -544,7 +597,7 @@ JACKAudioBackend::freewheel (bool onoff)
        }
 
        if (jack_set_freewheel (_priv_jack, onoff) == 0) {
-               _freewheeling = true;
+               _freewheeling = onoff;
                return 0;
        }
 
@@ -697,7 +750,8 @@ JACKAudioBackend::jack_timebase_callback (jack_transport_state_t state, pframes_
        ARDOUR::Session* session = engine.session();
 
        if (session) {
-               session->jack_timebase_callback (state, nframes, pos, new_position);
+               JACKSession jsession (session);
+               jsession.timebase_callback (state, nframes, pos, new_position);
        }
 }
 
@@ -713,9 +767,6 @@ JACKAudioBackend::jack_sync_callback (jack_transport_state_t state, jack_positio
        TransportState tstate;
 
        switch (state) {
-       case JackTransportStopped:
-               tstate = TransportStopped;
-               break;
        case JackTransportRolling:
                tstate = TransportRolling;
                break;
@@ -725,6 +776,10 @@ JACKAudioBackend::jack_sync_callback (jack_transport_state_t state, jack_positio
        case JackTransportStarting:
                tstate = TransportStarting;
                break;
+       case JackTransportStopped:
+       default:
+               tstate = TransportStopped;
+               break;
        }
 
        return engine.sync_callback (tstate, pos->frame);
@@ -735,25 +790,24 @@ JACKAudioBackend::jack_sync_callback (jack_transport_state_t state, jack_positio
 int
 JACKAudioBackend::_xrun_callback (void *arg)
 {
-       JACKAudioBackend* ae = static_cast<JACKAudioBackend*> (arg);
-       if (ae->connected()) {
-               ae->engine.Xrun (); /* EMIT SIGNAL */
+       JACKAudioBackend* jab = static_cast<JACKAudioBackend*> (arg);
+       if (jab->available()) {
+               jab->engine.Xrun (); /* EMIT SIGNAL */
        }
        return 0;
 }
 
-#ifdef HAVE_JACK_SESSION
 void
 JACKAudioBackend::_session_callback (jack_session_event_t *event, void *arg)
 {
-       JACKAudioBackend* ae = static_cast<JACKAudioBackend*> (arg);
-       ARDOUR::Session* session = ae->engine.session();
+       JACKAudioBackend* jab = static_cast<JACKAudioBackend*> (arg);
+       ARDOUR::Session* session = jab->engine.session();
 
        if (session) {
-               session->jack_session_event (event);
+               JACKSession jsession (session);
+               jsession.session_event (event);
        }
 }
-#endif
 
 void
 JACKAudioBackend::_freewheel_callback (int onoff, void *arg)
@@ -775,17 +829,72 @@ JACKAudioBackend::_latency_callback (jack_latency_callback_mode_t mode, void* ar
 }
 
 int
-JACKAudioBackend::create_process_thread (boost::function<void()> f, pthread_t* thread, size_t stacksize)
+JACKAudioBackend::create_process_thread (boost::function<void()> f)
 {
-        GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
-        ThreadData* td = new ThreadData (this, f, stacksize);
+        GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+       jack_native_thread_t thread_id;
+        ThreadData* td = new ThreadData (this, f, thread_stack_size());
 
-        if (jack_client_create_thread (_priv_jack, thread, jack_client_real_time_priority (_priv_jack),
+        if (jack_client_create_thread (_priv_jack, &thread_id, jack_client_real_time_priority (_priv_jack),
                                        jack_is_realtime (_priv_jack), _start_process_thread, td)) {
                 return -1;
         }
 
-        return 0;
+       _jack_threads.push_back(thread_id);
+       return 0;
+}
+
+int
+JACKAudioBackend::join_process_threads ()
+{
+        GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+       int ret = 0;
+
+       for (std::vector<jack_native_thread_t>::const_iterator i = _jack_threads.begin ();
+            i != _jack_threads.end(); i++) {
+
+#if defined(USING_JACK2_EXPANSION_OF_JACK_API) || defined(PLATFORM_WINDOWS)
+               if (jack_client_stop_thread (_priv_jack, *i) != 0) {
+#else
+               void* status;
+               if (pthread_join (*i, &status) != 0) {
+#endif
+                       error << "AudioEngine: cannot stop process thread" << endmsg;
+                       ret += -1;
+               }
+       }
+
+       _jack_threads.clear();
+
+       return ret;
+}
+
+bool
+JACKAudioBackend::in_process_thread ()
+{
+       for (std::vector<jack_native_thread_t>::const_iterator i = _jack_threads.begin ();
+            i != _jack_threads.end(); i++) {
+
+#ifdef COMPILER_MINGW
+               if (*i == GetCurrentThread()) {
+                       return true;
+               }
+#else // pthreads
+               if (pthread_equal (*i, pthread_self()) != 0) {
+                       return true;
+               }
+#endif
+       }
+
+       return false;
+}
+
+uint32_t
+JACKAudioBackend::process_thread_count ()
+{
+       return _jack_threads.size();
 }
 
 void*
@@ -905,8 +1014,9 @@ JACKAudioBackend::disconnected (const char* why)
                engine.halted_callback (why); /* EMIT SIGNAL */
         }
 }
+
 float 
-JACKAudioBackend::cpu_load() const 
+JACKAudioBackend::dsp_load() const 
 {
        GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0);
        return jack_cpu_load (_priv_jack);
@@ -931,8 +1041,10 @@ JACKAudioBackend::n_physical (unsigned long flags) const
        if (ports) {
                for (uint32_t i = 0; ports[i]; ++i) {
                        if (!strstr (ports[i], "Midi-Through")) {
-                               DataType t (jack_port_type (jack_port_by_name (_priv_jack, ports[i])));
-                               c.set (t, c.get (t) + 1);
+                               DataType t = port_data_type (jack_port_by_name (_priv_jack, ports[i]));
+                               if (t != DataType::NIL) {
+                                       c.set (t, c.get (t) + 1);
+                               }
                        }
                }
                
@@ -957,25 +1069,101 @@ JACKAudioBackend::can_change_buffer_size_when_running () const
 string
 JACKAudioBackend::control_app_name () const
 {
-       string appname;
+       /* Since JACK/ALSA really don't provide particularly integrated support
+          for the idea of a control app to be used to control a device,
+          allow the user to take some control themselves if necessary.
+       */
 
-       std::cerr << "td = " << _target_driver << " tdev = " << _target_device << std::endl;
+       const char* env_value  = g_getenv ("ARDOUR_DEVICE_CONTROL_APP");
+       string appname;
 
-       if (_target_driver.empty() || _target_device.empty()) {
-               return appname;
+       if (!env_value) {
+               if (_target_driver.empty() || _target_device.empty()) {
+                       return appname;
+               }
+               
+               if (_target_driver == "ALSA") {
+                       
+                       if (_target_device == "Hammerfall DSP") {
+                               appname = "hdspconf";
+                       } else if (_target_device == "M Audio Delta 1010") {
+                               appname = "mudita24";
+                       }
+               }
+       } else {
+               appname = env_value;
        }
 
-       if (_target_driver == "ALSA") {
+       return appname;
+}
 
-               if (_target_device == "Hammerfall DSP") {
-                       appname = "hdspconf";
-               } else if (_target_device == "M Audio Delta 1010") {
-                       appname = "mudita";
-               }
+void
+JACKAudioBackend::launch_control_app ()
+{
+       string appname = control_app_name();
+
+       if (appname.empty()) {
+               error << string_compose (_("There is no control application for the device \"%1\""), _target_device) << endmsg;
+               return;
        }
-       
-       std::cerr << "appname retrurned as " << appname << std::endl;
 
-       return appname;
+       std::list<string> args;
+       args.push_back (appname);
+       Glib::spawn_async ("", args, Glib::SPAWN_SEARCH_PATH);
+}
+
+vector<string>
+JACKAudioBackend::enumerate_midi_options () const
+{
+       return ARDOUR::enumerate_midi_options ();
 }
 
+int
+JACKAudioBackend::set_midi_option (const string& opt)
+{
+       _target_midi_option = opt;
+       return 0;
+}
+
+bool
+JACKAudioBackend::speed_and_position (double& speed, framepos_t& position)
+{
+       jack_position_t pos;
+       jack_transport_state_t state;
+       bool starting;
+
+       /* this won't be called if the port engine in use is not JACK, so we do 
+          not have to worry about the type of PortEngine::private_handle()
+       */
+
+       speed = 0;
+       position = 0;
+
+       GET_PRIVATE_JACK_POINTER_RET (_priv_jack, true);
+
+       state = jack_transport_query (_priv_jack, &pos);
+
+       switch (state) {
+       case JackTransportStopped:
+               speed = 0;
+               starting = false;
+               break;
+       case JackTransportRolling:
+               speed = 1.0;
+               starting = false;
+               break;
+       case JackTransportLooping:
+               speed = 1.0;
+               starting = false;
+               break;
+       case JackTransportStarting:
+               starting = true;
+               // don't adjust speed here, just leave it as it was
+               break;
+       default:
+               std::cerr << "WARNING: Unknown JACK transport state: " << state << std::endl;
+       }
+
+       position = pos.frame;
+       return starting;
+}