add note onset detection to the ferret, c/o the aubio-based Onset VAMP plugin (REQUIR...
[ardour.git] / gtk2_ardour / engine_dialog.cc
index c8f4ec25507ca174e4de0010b647819111f826ea..0c002374acf478851664ddd73a04d5ed2f035985 100644 (file)
@@ -1,8 +1,10 @@
 #include <vector>
 #include <cmath>
 #include <fstream>
+#include <map>
 
 #include <glibmm.h>
+#include <gtkmm/messagedialog.h>
 #include <pbd/xml++.h>
 
 #ifdef __APPLE__
@@ -22,6 +24,7 @@
 
 #include <pbd/convert.h>
 #include <pbd/error.h>
+#include <pbd/pathscanner.h>
 
 #ifdef __APPLE
 #include <CFBundle.h>
@@ -119,9 +122,6 @@ EngineControl::EngineControl ()
        set_popdown_strings (driver_combo, strings);
        driver_combo.set_active_text (strings.front());
 
-       /* figure out available devices and set up interface_combo */
-
-       enumerate_devices ();
        driver_combo.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed));
        driver_changed ();
 
@@ -186,6 +186,10 @@ EngineControl::EngineControl ()
        row++;
 #endif
 
+       interface_combo.set_size_request (125, -1);
+       input_device_combo.set_size_request (125, -1);
+       output_device_combo.set_size_request (125, -1);
+
        /*
 
        if (engine_running()) {
@@ -210,6 +214,11 @@ EngineControl::EngineControl ()
 
        options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
+
+       realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed));
+       realtime_changed ();
+
+#ifndef __APPLE__
        label = manage (new Label (_("Realtime Priority")));
        label->set_alignment (1.0, 0.5);
        options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
@@ -217,10 +226,6 @@ EngineControl::EngineControl ()
        ++row;
        priority_spinner.set_value (60);
 
-       realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed));
-       realtime_changed ();
-
-#ifndef __APPLE__
        options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
        options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
@@ -358,6 +363,7 @@ EngineControl::build_command_line (vector<string>& cmd)
        bool using_coreaudio = false;
        bool using_netjack = false;
        bool using_ffado = false;
+       bool using_dummy = false;
 
        /* first, path to jackd */
 
@@ -419,7 +425,20 @@ EngineControl::build_command_line (vector<string>& cmd)
                cmd.push_back ("netjack");
        } else if (driver == X_("FFADO")) {
                using_ffado = true;
-               cmd.push_back ("ffado");
+
+               /* do this until FFADO becomes the standard */
+
+               char* hack = getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
+
+               if (hack) {
+                       cmd.push_back (hack);
+               } else {
+                       cmd.push_back ("freebob");
+               }
+
+       } else if ( driver == X_("Dummy")) {
+               using_dummy = true;
+               cmd.push_back ("dummy");
        }
 
        /* driver arguments */
@@ -444,8 +463,10 @@ EngineControl::build_command_line (vector<string>& cmd)
                        cmd.push_back ("-C");
                }
 
-               cmd.push_back ("-n");
-               cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
+               if (! using_dummy ) {
+                       cmd.push_back ("-n");
+                       cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
+               }
        }
 
        cmd.push_back ("-r");
@@ -491,7 +512,8 @@ EngineControl::build_command_line (vector<string>& cmd)
        } else if (using_coreaudio) {
 
 #ifdef __APPLE__
-               cmd.push_back ("-n");
+               // note: older versions of the CoreAudio JACK backend use -n instead of -d here
+               cmd.push_back ("-d");
                cmd.push_back (get_device_name (driver, interface_combo.get_active_text()));
 #endif
 
@@ -516,11 +538,10 @@ EngineControl::engine_running ()
 }
 
 int
-EngineControl::start_engine ()
+EngineControl::setup_engine ()
 {
        vector<string> args;
        std::string cwd = "/tmp";
-       int ret = 0;
 
        build_command_line (args);
 
@@ -532,58 +553,52 @@ EngineControl::start_engine ()
                error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
                return -1;
        }
-
-       cerr << "will execute ...\n";
+       cerr << "JACK COMMAND: ";
        for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
-               jackdrc << (*i) << ' ';
                cerr << (*i) << ' ';
+               jackdrc << (*i) << ' ';
        }
-       jackdrc << endl;
        cerr << endl;
+       jackdrc << endl;
        jackdrc.close ();
 
        _used = true;
-       
-#if 0
-
-       try {
-               spawn_async_with_pipes (cwd, args, SpawnFlags (0), sigc::slot<void>(), &engine_pid, &engine_stdin, &engine_stdout, &engine_stderr);
-       }
-       
-       catch (Glib::Exception& err) {
-               error << _("could not start JACK server: ") << err.what() << endmsg;
-               ret = -1;
-       }
-#endif
-
-       return ret;
-}
 
-int
-EngineControl::stop_engine ()
-{
        return 0;
 }
 
 void
 EngineControl::realtime_changed ()
 {
+#ifndef __APPLE__
        priority_spinner.set_sensitive (realtime_button.get_active());
+#endif
 }
 
 void
-EngineControl::enumerate_devices ()
+EngineControl::enumerate_devices (const string& driver)
 {
        /* note: case matters for the map keys */
 
-#ifdef __APPLE__
-       devices["CoreAudio"] = enumerate_coreaudio_devices ();
+       if (driver == "CoreAudio") {
+#ifdef __APPLE__               
+               devices[driver] = enumerate_coreaudio_devices ();
+#endif
+
+#ifndef __APPLE__
+       } else if (driver == "ALSA") {
+               devices[driver] = enumerate_alsa_devices ();
+       } else if (driver == "FFADO") {
+               devices[driver] = enumerate_ffado_devices ();
+       } else if (driver == "OSS") {
+               devices[driver] = enumerate_oss_devices ();
+       } else if (driver == "Dummy") {
+               devices[driver] = enumerate_dummy_devices ();
+       } else if (driver == "NetJACK") {
+               devices[driver] = enumerate_netjack_devices ();
+       }
 #else
-       devices["ALSA"] = enumerate_alsa_devices ();
-       devices["FFADO"] = enumerate_ffado_devices ();
-       devices["OSS"] = enumerate_oss_devices ();
-       devices["Dummy"] = enumerate_dummy_devices ();
-       devices["NetJACK"] = enumerate_netjack_devices ();
+        }
 #endif
 }
 
@@ -626,8 +641,30 @@ EngineControl::enumerate_coreaudio_devices ()
                if (err == noErr) {
                        // Look for the CoreAudio device name...
                        char coreDeviceName[256];
-                       size_t nameSize = sizeof (coreDeviceName);
+                       size_t nameSize;
+
                        for (int i = 0; i < numCoreDevices; i++) {
+
+                               nameSize = sizeof (coreDeviceName);
+
+                               /* enforce duplex devices only */
+
+                               err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
+                                                                0, true, kAudioDevicePropertyStreams,
+                                                                &outSize, &isWritable);
+
+                               if (err != noErr || outSize == 0) {
+                                       continue;
+                               }
+
+                               err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
+                                                                0, false, kAudioDevicePropertyStreams,
+                                                                &outSize, &isWritable);
+
+                               if (err != noErr || outSize == 0) {
+                                       continue;
+                               }
+
                                err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
                                                                 0, true, kAudioDevicePropertyDeviceName,
                                                                 &outSize, &isWritable);
@@ -636,7 +673,6 @@ EngineControl::enumerate_coreaudio_devices ()
                                                                     0, true, kAudioDevicePropertyDeviceName,
                                                                     &nameSize, (void *) coreDeviceName);
                                        if (err == noErr) {
-
                                                char drivername[128];
 
                                                // this returns the unique id for the device
@@ -653,6 +689,28 @@ EngineControl::enumerate_coreaudio_devices ()
                delete [] coreDeviceIDs;
        }
 
+
+       if (devs.size() == 0) {
+               MessageDialog msg (_("\
+You do not have any audio devices capable of\n\
+simultaneous playback and recording.\n\n\
+Please use Applications -> Utilities -> Audio MIDI Setup\n\
+to create an \"aggregrate\" device, or install a suitable\n\
+audio interface.\n\n\
+Please send email to Apple and ask them why new Macs\n\
+have no duplex audio device.\n\n\
+Alternatively, if you really want just playback\n\
+or recording but not both, start JACK before running\n\
+Ardour and choose the relevant device then."
+                                          ), 
+                                  true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
+               msg.set_title (_("No suitable audio devices"));
+               msg.set_position (Gtk::WIN_POS_MOUSE);
+               msg.run ();
+               exit (1);
+       }
+
+
        return devs;
 }
 #else
@@ -721,8 +779,10 @@ vector<string>
 EngineControl::enumerate_ffado_devices ()
 {
        vector<string> devs;
+       backend_devs.clear ();
        return devs;
 }
+
 vector<string>
 EngineControl::enumerate_oss_devices ()
 {
@@ -747,11 +807,19 @@ void
 EngineControl::driver_changed ()
 {
        string driver = driver_combo.get_active_text();
-       vector<string>& strings = devices[driver];
        string::size_type maxlen = 0;
        int maxindex = -1;
        int n = 0;
 
+       enumerate_devices (driver);
+
+       vector<string>& strings = devices[driver];
+
+       if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
+               error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
+               return;
+       }
+       
        for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
                if ((*i).length() > maxlen) {
                        maxlen = (*i).length();
@@ -763,16 +831,11 @@ EngineControl::driver_changed ()
        set_popdown_strings (input_device_combo, strings);
        set_popdown_strings (output_device_combo, strings);
 
-       const guint32 FUDGE = 18; // Combo's are stupid - they steal space from the entry for the button interface_combo
-       set_size_request_to_display_given_text (interface_combo, strings[maxindex].c_str(), 5+FUDGE, 5);
-       set_size_request_to_display_given_text (input_device_combo, strings[maxindex].c_str(), 5+FUDGE, 5);
-       set_size_request_to_display_given_text (output_device_combo, strings[maxindex].c_str(), 5+FUDGE, 5);
-
        if (!strings.empty()) {
                interface_combo.set_active_text (strings.front());
                input_device_combo.set_active_text (strings.front());
                output_device_combo.set_active_text (strings.front());
-       }
+       } 
        
        if (driver == "ALSA") {
                soft_mode_button.set_sensitive (true);
@@ -830,6 +893,11 @@ EngineControl::audio_mode_changed ()
        }
 }
 
+static bool jack_server_filter(const string& str, void *arg)
+{
+   return str == "jackd" || str == "jackdmp";
+}
+
 void
 EngineControl::find_jack_servers (vector<string>& strings)
 {
@@ -843,46 +911,70 @@ EngineControl::find_jack_servers (vector<string>& strings)
 
        _NSGetExecutablePath (execpath, &pathsz);
        
-       cerr << " execpath = " << execpath << endl;
-
-       Glib::ustring path (Glib::path_get_dirname (execpath));
+       string path (Glib::path_get_dirname (execpath));
        path += "/jackd";
 
        if (Glib::file_test (path, FILE_TEST_EXISTS)) {
                strings.push_back (path);
-               cerr << "Found jack in " << path << endl;
        } 
 
-       if (ARDOUR::Profile->get_single_package()) {
+       if (getenv ("ARDOUR_WITH_JACK")) {
                /* no other options - only use the JACK we supply */
                if (strings.empty()) {
-                       // cerr << "OOPS!\n";
-                       // fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
+                       fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
                        /*NOTREACHED*/
                }
                return;
        }
+#else
+       string path;
 #endif
        
-       if (Glib::file_test ("/usr/bin/jackd", FILE_TEST_EXISTS)) {
-               strings.push_back ("/usr/bin/jackd");
-       }
-       if (Glib::file_test ("/usr/local/bin/jackd", FILE_TEST_EXISTS)) {
-               strings.push_back ("/usr/local/bin/jackd");
-       }
-       if (Glib::file_test ("/opt/bin/jackd", FILE_TEST_EXISTS)) {
-               strings.push_back ("/opt/bin/jackd");
-       }
-       if (Glib::file_test ("/usr/bin/jackdmp", FILE_TEST_EXISTS)) {
-               strings.push_back ("/usr/bin/jackd");
-       }
-       if (Glib::file_test ("/usr/local/bin/jackdmp", FILE_TEST_EXISTS)) {
-               strings.push_back ("/usr/local/bin/jackd");
+       PathScanner scanner;
+       vector<string *> *jack_servers;
+       std::map<string,int> un;
+       char *p;
+       bool need_minimal_path = false;
+
+       p = getenv ("PATH");
+
+       if (p && *p) {
+               path = p;
+       } else {
+               need_minimal_path = true;
        }
-       if (Glib::file_test ("/opt/bin/jackdmp", FILE_TEST_EXISTS)) {
-               strings.push_back ("/opt/bin/jackd");
+
+#ifdef __APPLE__
+       // many mac users don't have PATH set up to include
+       // likely installed locations of JACK
+       need_minimal_path = true;
+#endif
+
+       if (need_minimal_path) {
+               if (path.empty()) {
+                       path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
+               } else {
+                       path += ":/usr/local/bin:/opt/local/bin";
+               }
        }
 
+#ifdef __APPLE__
+       // push it back into the environment so that auto-started JACK can find it.
+       // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
+       setenv ("PATH", path.c_str(), 1);
+#endif
+
+       jack_servers = scanner (path, jack_server_filter, 0, false, true);
+       
+       vector<string *>::iterator iter;
+       
+       for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
+               string p = **iter;
+               
+               if (un[p]++ == 0) {
+                       strings.push_back(p);
+               }
+       }
 }
 
 string
@@ -1028,19 +1120,31 @@ EngineControl::set_state (const XMLNode& root)
        XMLNodeConstIterator citer;
        XMLNode* child;
        XMLProperty* prop;
-
+       bool using_dummy = false;
+       
        int val;
        string strval;
-
+       
+       if ( (child = root.child ("driver"))){
+               prop = child->property("val");
+               if (prop && (prop->value() == "Dummy") ) {
+                       using_dummy = true;
+               }
+       }
+       
        clist = root.children();
 
        for (citer = clist.begin(); citer != clist.end(); ++citer) {
-
+               if ( prop && (prop->value() == "FFADO" ))
+                               continue;
                child = *citer;
 
                prop = child->property ("val");
 
                if (!prop || prop->value().empty()) {
+
+                       if ( using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" ))
+                               continue;
                        error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
                        continue;
                }