X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fengine_dialog.cc;h=d6e6a52f4606e2cc6e5168b1f78b255a3ed9dc59;hb=153ee4e441eeebc9aceaa3121e4a785c6011a962;hp=e511e59af2f8e1037b111008be929f2623ffcf98;hpb=8a2e30816ee392b871c5918f769f68cc0196a893;p=ardour.git diff --git a/gtk2_ardour/engine_dialog.cc b/gtk2_ardour/engine_dialog.cc index e511e59af2..d6e6a52f46 100644 --- a/gtk2_ardour/engine_dialog.cc +++ b/gtk2_ardour/engine_dialog.cc @@ -1,10 +1,34 @@ +/* + Copyright (C) 2010 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + #include #include #include #include +#include + #include -#include +#include + +#include "pbd/epa.h" +#include "pbd/xml++.h" #ifdef __APPLE__ #include @@ -15,15 +39,15 @@ #include #endif -#include +#include "ardour/profile.h" #include #include #include -#include -#include -#include +#include "pbd/convert.h" +#include "pbd/error.h" +#include "pbd/pathscanner.h" #ifdef __APPLE #include @@ -45,6 +69,10 @@ EngineControl::EngineControl () priority_spinner (priority_adjustment), ports_adjustment (128, 8, 1024, 1, 16), ports_spinner (ports_adjustment), + input_latency_adjustment (0, 0, 99999, 1), + input_latency (input_latency_adjustment), + output_latency_adjustment (0, 0, 99999, 1), + output_latency (output_latency_adjustment), realtime_button (_("Realtime")), no_memory_lock_button (_("Do not lock memory")), unlock_memory_button (_("Unlock memory")), @@ -64,7 +92,7 @@ EngineControl::EngineControl () basic_packer (8, 2), options_packer (14, 2), device_packer (6, 2) -#endif +#endif { using namespace Notebook_Helpers; Label* label; @@ -114,6 +142,7 @@ EngineControl::EngineControl () #else strings.push_back (X_("ALSA")); strings.push_back (X_("OSS")); + strings.push_back (X_("FreeBoB")); strings.push_back (X_("FFADO")); #endif strings.push_back (X_("NetJACK")); @@ -121,76 +150,86 @@ 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_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed)); driver_changed (); strings.clear (); - strings.push_back (_("Playback/Recording on 1 Device")); - strings.push_back (_("Playback/Recording on 2 Devices")); + strings.push_back (_("Playback/recording on 1 device")); + strings.push_back (_("Playback/recording on 2 devices")); strings.push_back (_("Playback only")); strings.push_back (_("Recording only")); set_popdown_strings (audio_mode_combo, strings); audio_mode_combo.set_active_text (strings.front()); - audio_mode_combo.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed)); + audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed)); audio_mode_changed (); + strings.clear (); + strings.push_back (_("None")); + strings.push_back (_("seq")); + strings.push_back (_("raw")); + set_popdown_strings (midi_driver_combo, strings); + midi_driver_combo.set_active_text (strings.front ()); + row = 0; - label = manage (new Label (_("Driver"))); + label = manage (new Label (_("Driver:"))); + label->set_alignment (0, 0.5); basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); row++; - label = manage (new Label (_("Interface"))); + label = manage (new Label (_("Interface:"))); + label->set_alignment (0, 0.5); basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); row++; - label = manage (new Label (_("Sample Rate"))); + label = manage (new Label (_("Sample rate:"))); + label->set_alignment (0, 0.5); basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); row++; - label = manage (new Label (_("Buffer size"))); + label = manage (new Label (_("Buffer size:"))); + label->set_alignment (0, 0.5); basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); row++; #ifndef __APPLE__ - label = manage (new Label (_("Number of buffers"))); + label = manage (new Label (_("Number of buffers:"))); + label->set_alignment (0, 0.5); basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); periods_spinner.set_value (2); row++; #endif - label = manage (new Label (_("Approximate latency"))); - label->set_alignment (0.0, 0.5); + label = manage (new Label (_("Approximate latency:"))); + label->set_alignment (0, 0.5); basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); row++; - sample_rate_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency)); - periods_adjustment.signal_value_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency)); - period_size_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency)); + sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency)); + periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency)); + period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency)); redisplay_latency(); row++; /* no audio mode with CoreAudio, its duplex or nuthin' */ #ifndef __APPLE__ - label = manage (new Label (_("Audio Mode"))); + label = manage (new Label (_("Audio mode:"))); + label->set_alignment (0, 0.5); basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); 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); + interface_combo.set_size_request (250, -1); + input_device_combo.set_size_request (250, -1); + output_device_combo.set_size_request (250, -1); /* @@ -200,8 +239,8 @@ EngineControl::EngineControl () stop_button.set_sensitive (false); } - start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine)); - stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine)); + start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine)); + stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine)); */ button_box.pack_start (start_button, false, false); @@ -217,9 +256,12 @@ 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_button.set_active (true); + realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed)); realtime_changed (); +#if PROVIDE_TOO_MANY_OPTIONS + #ifndef __APPLE__ label = manage (new Label (_("Realtime Priority"))); label->set_alignment (1.0, 0.5); @@ -244,7 +286,7 @@ EngineControl::EngineControl () ++row; options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); ++row; -#else +#else options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); ++row; #endif @@ -264,15 +306,22 @@ EngineControl::EngineControl () options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); ++row; - label = manage (new Label (_("Number of ports"))); - label->set_alignment (1.0, 0.5); +#endif /* PROVIDE_TOO_MANY_OPTIONS */ + label = manage (new Label (_("Number of ports:"))); + label->set_alignment (0, 0.5); options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0)); options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); ++row; + label = manage (new Label (_("MIDI driver:"))); + label->set_alignment (0, 0.5); + options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0)); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + #ifndef __APPLE__ - label = manage (new Label (_("Dither"))); - label->set_alignment (1.0, 0.5); + label = manage (new Label (_("Dither:"))); + label->set_alignment (0, 0.5); options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0)); options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); ++row; @@ -284,7 +333,7 @@ EngineControl::EngineControl () fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg; /*NOTREACHED*/ } - + set_popdown_strings (serverpath_combo, server_strings); serverpath_combo.set_active_text (server_strings.front()); @@ -302,36 +351,42 @@ EngineControl::EngineControl () row = 0; #ifndef __APPLE__ - label = manage (new Label (_("Input device"))); - label->set_alignment (1.0, 0.5); + label = manage (new Label (_("Input device:"))); + label->set_alignment (0, 0.5); device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); ++row; - label = manage (new Label (_("Output device"))); - label->set_alignment (1.0, 0.5); + label = manage (new Label (_("Output device:"))); + label->set_alignment (0, 0.5); device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); - device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); ++row; #endif - label = manage (new Label (_("Input channels"))); - label->set_alignment (1.0, 0.5); + label = manage (new Label (_("Input channels:"))); + label->set_alignment (0, 0.5); device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); ++row; - label = manage (new Label (_("Output channels"))); - label->set_alignment (1.0, 0.5); + label = manage (new Label (_("Output channels:"))); + label->set_alignment (0, 0.5); device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); ++row; - label = manage (new Label (_("Hardware input latency (samples)"))); - label->set_alignment (1.0, 0.5); + label = manage (new Label (_("Hardware input latency:"))); + label->set_alignment (0, 0.5); device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + label = manage (new Label (_("samples"))); + label->set_alignment (0, 0.5); + device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0); ++row; - label = manage (new Label (_("Hardware output latency (samples)"))); - label->set_alignment (1.0, 0.5); + label = manage (new Label (_("Hardware output latency:"))); + label->set_alignment (0, 0.5); device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + label = manage (new Label (_("samples"))); + label->set_alignment (0, 0.5); + device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0); ++row; basic_hbox.pack_start (basic_packer, false, false); @@ -360,33 +415,36 @@ EngineControl::build_command_line (vector& cmd) { string str; string driver; - bool using_oss = false; bool using_alsa = false; bool using_coreaudio = false; - bool using_netjack = false; - bool using_ffado = false; bool using_dummy = false; + bool using_ffado = false; /* first, path to jackd */ cmd.push_back (serverpath_combo.get_active_text ()); - + /* now jackd arguments */ str = timeout_combo.get_active_text (); + if (str != _("Ignore")) { - double secs; + + double secs = 0; uint32_t msecs; - atof (str); + secs = atof (str); msecs = (uint32_t) floor (secs * 1000.0); - cmd.push_back ("-t"); - cmd.push_back (to_string (msecs, std::dec)); + + if (msecs > 0) { + cmd.push_back ("-t"); + cmd.push_back (to_string (msecs, std::dec)); + } } if (no_memory_lock_button.get_active()) { cmd.push_back ("-m"); /* no munlock */ } - + cmd.push_back ("-p"); /* port max */ cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec)); @@ -394,6 +452,8 @@ EngineControl::build_command_line (vector& cmd) cmd.push_back ("-R"); cmd.push_back ("-P"); cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec)); + } else { + cmd.push_back ("-r"); /* override jackd's default --realtime */ } if (unlock_memory_button.get_active()) { @@ -403,7 +463,7 @@ EngineControl::build_command_line (vector& cmd) if (verbose_output_button.get_active()) { cmd.push_back ("-v"); } - + /* now add fixed arguments (not user-selectable) */ cmd.push_back ("-T"); // temporary */ @@ -413,71 +473,105 @@ EngineControl::build_command_line (vector& cmd) cmd.push_back ("-d"); driver = driver_combo.get_active_text (); + if (driver == X_("ALSA")) { using_alsa = true; cmd.push_back ("alsa"); } else if (driver == X_("OSS")) { - using_oss = true; cmd.push_back ("oss"); } else if (driver == X_("CoreAudio")) { using_coreaudio = true; cmd.push_back ("coreaudio"); } else if (driver == X_("NetJACK")) { - using_netjack = true; cmd.push_back ("netjack"); + } else if (driver == X_("FreeBoB")) { + cmd.push_back ("freebob"); } else if (driver == X_("FFADO")) { using_ffado = true; cmd.push_back ("firewire"); } else if ( driver == X_("Dummy")) { - using_dummy = true; - cmd.push_back ("dummy"); - } + using_dummy = true; + cmd.push_back ("dummy"); + } /* driver arguments */ if (!using_coreaudio) { str = audio_mode_combo.get_active_text(); - + if (str == _("Playback/Recording on 1 Device")) { - + /* relax */ - + } else if (str == _("Playback/Recording on 2 Devices")) { - + + string input_device = get_device_name (driver, input_device_combo.get_active_text()); + string output_device = get_device_name (driver, output_device_combo.get_active_text()); + + if (input_device.empty() || output_device.empty()) { + cmd.clear (); + return; + } + cmd.push_back ("-C"); - cmd.push_back (get_device_name (driver, input_device_combo.get_active_text())); + cmd.push_back (input_device); + cmd.push_back ("-P"); - cmd.push_back (get_device_name (driver, output_device_combo.get_active_text())); - + cmd.push_back (output_device); + } else if (str == _("Playback only")) { cmd.push_back ("-P"); } else if (str == _("Recording only")) { cmd.push_back ("-C"); } - if (! using_dummy ) { - 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"); cmd.push_back (to_string (get_rate(), std::dec)); - + cmd.push_back ("-p"); cmd.push_back (period_size_combo.get_active_text()); + if (using_alsa || using_ffado || using_coreaudio) { + + double val = input_latency_adjustment.get_value(); + + if (val) { + cmd.push_back ("-I"); + cmd.push_back (to_string ((uint32_t) val, std::dec)); + } + + val = output_latency_adjustment.get_value(); + + if (val) { + cmd.push_back ("-O"); + cmd.push_back (to_string ((uint32_t) val, std::dec)); + } + } + if (using_alsa) { - + if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) { + + string device = get_device_name (driver, interface_combo.get_active_text()); + if (device.empty()) { + cmd.clear (); + return; + } + cmd.push_back ("-d"); - cmd.push_back (get_device_name (driver, interface_combo.get_active_text())); - } + cmd.push_back (device); + } if (hw_meter_button.get_active()) { cmd.push_back ("-M"); } - + if (hw_monitor_button.get_active()) { cmd.push_back ("-H"); } @@ -496,22 +590,32 @@ EngineControl::build_command_line (vector& cmd) if (force16bit_button.get_active()) { cmd.push_back ("-S"); } - + if (soft_mode_button.get_active()) { cmd.push_back ("-s"); } + str = midi_driver_combo.get_active_text (); + + if (str == _("seq")) { + cmd.push_back ("-X seq"); + } else if (str == _("raw")) { + cmd.push_back ("-X raw"); + } } else if (using_coreaudio) { #ifdef __APPLE__ // 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 - } else if (using_oss) { + string device = get_device_name (driver, interface_combo.get_active_text()); + if (device.empty()) { + cmd.clear (); + return; + } - } else if (using_netjack) { + cmd.push_back ("-d"); + cmd.push_back (device); +#endif } } @@ -519,6 +623,17 @@ EngineControl::build_command_line (vector& cmd) bool EngineControl::engine_running () { + EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa (); + boost::scoped_ptr current_epa; + + /* revert all environment settings back to whatever they were when ardour started + */ + + if (global_epa) { + current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */ + global_epa->restore (); + } + jack_status_t status; jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status); @@ -537,7 +652,11 @@ EngineControl::setup_engine () build_command_line (args); - Glib::ustring jackdrc_path = Glib::get_home_dir(); + if (args.empty()) { + return 1; // try again + } + + std::string jackdrc_path = Glib::get_home_dir(); jackdrc_path += "/.jackdrc"; ofstream jackdrc (jackdrc_path.c_str()); @@ -545,10 +664,12 @@ EngineControl::setup_engine () error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg; return -1; } - + cerr << "JACK COMMAND: "; for (vector::iterator i = args.begin(); i != args.end(); ++i) { + cerr << (*i) << ' '; jackdrc << (*i) << ' '; } + cerr << endl; jackdrc << endl; jackdrc.close (); @@ -566,30 +687,43 @@ EngineControl::realtime_changed () } void -EngineControl::enumerate_devices () +EngineControl::enumerate_devices (const string& driver) { /* note: case matters for the map keys */ + if (driver == "CoreAudio") { #ifdef __APPLE__ - devices["CoreAudio"] = enumerate_coreaudio_devices (); + devices[driver] = enumerate_coreaudio_devices (); +#endif + +#ifndef __APPLE__ + } else if (driver == "ALSA") { + devices[driver] = enumerate_alsa_devices (); + } else if (driver == "FreeBOB") { + devices[driver] = enumerate_freebob_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 } #ifdef __APPLE__ -static OSStatus +static OSStatus getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize) { UInt32 size = sizeof(CFStringRef); CFStringRef UI; OSStatus res = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UI); - if (res == noErr) + if (res == noErr) CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding()); CFRelease(UI); return res; @@ -599,7 +733,7 @@ vector EngineControl::enumerate_coreaudio_devices () { vector devs; - + // Find out how many Core Audio devices are there, if any... // (code snippet gently "borrowed" from St?hane Letz jackdmp;) OSStatus err; @@ -621,10 +755,29 @@ EngineControl::enumerate_coreaudio_devices () // Look for the CoreAudio device name... char coreDeviceName[256]; 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); @@ -637,11 +790,11 @@ EngineControl::enumerate_coreaudio_devices () // this returns the unique id for the device // that must be used on the commandline for jack - + if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) { devs.push_back (coreDeviceName); backend_devs.push_back (drivername); - } + } } } } @@ -649,6 +802,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 @@ -677,28 +852,11 @@ EngineControl::enumerate_alsa_devices () while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) { - bool have_playback = false; - bool have_capture = false; - - /* find duplex devices only */ - - snd_pcm_info_set_device (pcminfo, device); - snd_pcm_info_set_subdevice (pcminfo, 0); - snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE); - - if (snd_ctl_pcm_info (handle, pcminfo) >= 0) { - have_capture = true; - } - snd_pcm_info_set_device (pcminfo, device); snd_pcm_info_set_subdevice (pcminfo, 0); snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK); if (snd_ctl_pcm_info (handle, pcminfo) >= 0) { - have_playback = true; - } - - if (have_capture && have_playback) { devs.push_back (snd_pcm_info_get_name (pcminfo)); devname += ','; devname += to_string (device, std::dec); @@ -717,14 +875,17 @@ vector EngineControl::enumerate_ffado_devices () { vector devs; + backend_devs.clear (); return devs; } + vector EngineControl::enumerate_freebob_devices () { vector devs; return devs; } + vector EngineControl::enumerate_oss_devices () { @@ -749,15 +910,20 @@ void EngineControl::driver_changed () { string driver = driver_combo.get_active_text(); - vector& strings = devices[driver]; string::size_type maxlen = 0; - int maxindex = -1; int n = 0; + enumerate_devices (driver); + + vector& strings = devices[driver]; + + if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") { + return; + } + for (vector::iterator i = strings.begin(); i != strings.end(); ++i, ++n) { if ((*i).length() > maxlen) { maxlen = (*i).length(); - maxindex = n; } } @@ -770,7 +936,7 @@ EngineControl::driver_changed () 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); force16bit_button.set_sensitive (true); @@ -807,12 +973,13 @@ EngineControl::redisplay_latency () snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0)); latency_label.set_text (buf); + latency_label.set_alignment (0, 0.5); } void EngineControl::audio_mode_changed () { - Glib::ustring str = audio_mode_combo.get_active_text(); + std::string str = audio_mode_combo.get_active_text(); if (str == _("Playback/Recording on 1 Device")) { input_device_combo.set_sensitive (false); @@ -827,7 +994,7 @@ EngineControl::audio_mode_changed () } } -static bool jack_server_filter(const string& str, void *arg) +static bool jack_server_filter(const string& str, void */*arg*/) { return str == "jackd" || str == "jackdmp"; } @@ -839,23 +1006,23 @@ EngineControl::find_jack_servers (vector& strings) /* this magic lets us finds the path to the OSX bundle, and then we infer JACK's location from there */ - + char execpath[MAXPATHLEN+1]; uint32_t pathsz = sizeof (execpath); _NSGetExecutablePath (execpath, &pathsz); - - 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); - } + } if (getenv ("ARDOUR_WITH_JACK")) { /* no other options - only use the JACK we supply */ if (strings.empty()) { - fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg; + fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg; /*NOTREACHED*/ } return; @@ -863,20 +1030,48 @@ EngineControl::find_jack_servers (vector& strings) #else string path; #endif - + PathScanner scanner; vector *jack_servers; std::map un; - - path = getenv ("PATH"); - + char *p; + bool need_minimal_path = false; + + p = getenv ("PATH"); + + if (p && *p) { + path = p; + } else { + need_minimal_path = true; + } + +#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::iterator iter; - + for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) { string p = **iter; - + if (un[p]++ == 0) { strings.push_back(p); } @@ -890,23 +1085,29 @@ EngineControl::get_device_name (const string& driver, const string& human_readab vector::iterator n; vector::iterator i; + if (human_readable.empty()) { + /* this can happen if the user's .ardourrc file has a device name from + another computer system in it + */ + MessageDialog msg (_("You need to choose an audio device first.")); + msg.run (); + return string(); + } + if (backend_devs.empty()) { return human_readable; } - + for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) { if (human_readable == (*i)) { return (*n); } } - + if (i == devices[driver].end()) { - fatal << string_compose (_("programming error: %1"), "true hardware name for ID missing") << endmsg; - /*NOTREACHED*/ + warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg; } - /* keep gcc happy */ - return string(); } @@ -915,7 +1116,7 @@ EngineControl::get_state () { XMLNode* root = new XMLNode ("AudioSetup"); XMLNode* child; - Glib::ustring path; + std::string path; child = new XMLNode ("periods"); child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec)); @@ -1016,7 +1217,11 @@ EngineControl::get_state () child = new XMLNode ("outputdevice"); child->add_property ("val", output_device_combo.get_active_text()); root->add_child_nocopy (*child); - + + child = new XMLNode ("mididriver"); + child->add_property ("val", midi_driver_combo.get_active_text()); + root->add_child_nocopy (*child); + return *root; } @@ -1026,19 +1231,24 @@ EngineControl::set_state (const XMLNode& root) XMLNodeList clist; XMLNodeConstIterator citer; XMLNode* child; - XMLProperty* prop; - - bool using_dummy = false; + XMLProperty* prop = NULL; + bool using_dummy = false; + bool using_ffado = false; int val; string strval; - if ( (child = root.child("driver"))){ - prop = child->property("val"); - if (prop && (prop->value() == "Dummy") ) { - using_dummy = true; - } - } + if ( (child = root.child ("driver"))){ + prop = child->property("val"); + + if (prop && (prop->value() == "Dummy") ) { + using_dummy = true; + } + if (prop && (prop->value() == "FFADO") ) { + using_ffado = true; + } + + } clist = root.children(); @@ -1049,12 +1259,20 @@ EngineControl::set_state (const XMLNode& root) prop = child->property ("val"); if (!prop || prop->value().empty()) { - if ( using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" )) - continue; + + if (((using_dummy || using_ffado) + && ( child->name() == "interface" + || child->name() == "inputdevice" + || child->name() == "outputdevice")) + || child->name() == "timeout") + { + continue; + } + error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg; continue; } - + strval = prop->value(); /* adjustments/spinners */ @@ -1117,24 +1335,34 @@ EngineControl::set_state (const XMLNode& root) } else if (child->name() == "periodsize") { period_size_combo.set_active_text(strval); } else if (child->name() == "serverpath") { - /* do not allow us to use a server path that doesn't - exist on this system. this handles cases where - the user has an RC file listing a serverpath - from some other machine. - */ - vector::iterator x; - for (x = server_strings.begin(); x != server_strings.end(); ++x) { - if (*x == strval) { - break; - } - } - if (x != server_strings.end()) { - serverpath_combo.set_active_text (strval); - } else { - warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"), - strval) - << endmsg; - } + + /* only attempt to set this if we have bothered to look + up server names already. otherwise this is all + redundant (actually, all of this dialog/widget + is redundant in that case ...) + */ + + if (!server_strings.empty()) { + /* do not allow us to use a server path that doesn't + exist on this system. this handles cases where + the user has an RC file listing a serverpath + from some other machine. + */ + vector::iterator x; + for (x = server_strings.begin(); x != server_strings.end(); ++x) { + if (*x == strval) { + break; + } + } + if (x != server_strings.end()) { + serverpath_combo.set_active_text (strval); + } else { + warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"), + strval) + << endmsg; + } + } + } else if (child->name() == "driver") { driver_combo.set_active_text(strval); } else if (child->name() == "interface") { @@ -1149,6 +1377,8 @@ EngineControl::set_state (const XMLNode& root) input_device_combo.set_active_text(strval); } else if (child->name() == "outputdevice") { output_device_combo.set_active_text(strval); + } else if (child->name() == "mididriver") { + midi_driver_combo.set_active_text(strval); } } }