2 Copyright (C) 2010 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <boost/scoped_ptr.hpp>
28 #include <gtkmm/messagedialog.h>
31 #include "pbd/xml++.h"
34 #include <CoreAudio/CoreAudio.h>
35 #include <CoreFoundation/CFString.h>
36 #include <sys/param.h>
37 #include <mach-o/dyld.h>
39 #include <alsa/asoundlib.h>
42 #include <jack/jack.h>
44 #include <gtkmm/stock.h>
45 #include <gtkmm2ext/utils.h>
47 #include "pbd/convert.h"
48 #include "pbd/error.h"
49 #include "pbd/pathscanner.h"
55 #include "engine_dialog.h"
60 using namespace Gtkmm2ext;
64 EngineControl::EngineControl ()
65 : periods_adjustment (2, 2, 16, 1, 2),
66 periods_spinner (periods_adjustment),
67 priority_adjustment (60, 10, 90, 1, 10),
68 priority_spinner (priority_adjustment),
69 ports_adjustment (128, 8, 1024, 1, 16),
70 ports_spinner (ports_adjustment),
71 input_latency_adjustment (0, 0, 99999, 1),
72 input_latency (input_latency_adjustment),
73 output_latency_adjustment (0, 0, 99999, 1),
74 output_latency (output_latency_adjustment),
75 realtime_button (_("Realtime")),
76 no_memory_lock_button (_("Do not lock memory")),
77 unlock_memory_button (_("Unlock memory")),
78 soft_mode_button (_("No zombies")),
79 monitor_button (_("Provide monitor ports")),
80 force16bit_button (_("Force 16 bit")),
81 hw_monitor_button (_("H/W monitoring")),
82 hw_meter_button (_("H/W metering")),
83 verbose_output_button (_("Verbose output")),
84 start_button (_("Start")),
85 stop_button (_("Stop")),
88 options_packer (4, 2),
92 options_packer (14, 2),
96 using namespace Notebook_Helpers;
98 vector<string> strings;
103 strings.push_back (_("8000Hz"));
104 strings.push_back (_("22050Hz"));
105 strings.push_back (_("44100Hz"));
106 strings.push_back (_("48000Hz"));
107 strings.push_back (_("88200Hz"));
108 strings.push_back (_("96000Hz"));
109 strings.push_back (_("192000Hz"));
110 set_popdown_strings (sample_rate_combo, strings);
111 sample_rate_combo.set_active_text ("48000Hz");
114 strings.push_back ("32");
115 strings.push_back ("64");
116 strings.push_back ("128");
117 strings.push_back ("256");
118 strings.push_back ("512");
119 strings.push_back ("1024");
120 strings.push_back ("2048");
121 strings.push_back ("4096");
122 strings.push_back ("8192");
123 set_popdown_strings (period_size_combo, strings);
124 period_size_combo.set_active_text ("1024");
127 strings.push_back (_("None"));
128 strings.push_back (_("Triangular"));
129 strings.push_back (_("Rectangular"));
130 strings.push_back (_("Shaped"));
131 set_popdown_strings (dither_mode_combo, strings);
132 dither_mode_combo.set_active_text (_("None"));
134 /* basic parameters */
136 basic_packer.set_spacings (6);
140 strings.push_back (X_("CoreAudio"));
142 strings.push_back (X_("ALSA"));
143 strings.push_back (X_("OSS"));
144 strings.push_back (X_("FreeBoB"));
145 strings.push_back (X_("FFADO"));
147 strings.push_back (X_("NetJACK"));
148 strings.push_back (X_("Dummy"));
149 set_popdown_strings (driver_combo, strings);
150 driver_combo.set_active_text (strings.front());
152 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
156 strings.push_back (_("Playback/recording on 1 device"));
157 strings.push_back (_("Playback/recording on 2 devices"));
158 strings.push_back (_("Playback only"));
159 strings.push_back (_("Recording only"));
160 set_popdown_strings (audio_mode_combo, strings);
161 audio_mode_combo.set_active_text (strings.front());
163 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
164 audio_mode_changed ();
167 strings.push_back (_("None"));
168 strings.push_back (_("seq"));
169 strings.push_back (_("raw"));
170 set_popdown_strings (midi_driver_combo, strings);
171 midi_driver_combo.set_active_text (strings.front ());
175 label = manage (left_aligned_label (_("Driver:")));
176 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
177 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
180 label = manage (left_aligned_label (_("Audio Interface:")));
181 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
182 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
185 label = manage (left_aligned_label (_("Sample rate:")));
186 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
187 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
190 label = manage (left_aligned_label (_("Buffer size:")));
191 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
192 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
196 label = manage (left_aligned_label (_("Number of buffers:")));
197 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
198 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
199 periods_spinner.set_value (2);
203 label = manage (left_aligned_label (_("Approximate latency:")));
204 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
205 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
208 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
209 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
210 period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
213 /* no audio mode with CoreAudio, its duplex or nuthin' */
216 label = manage (left_aligned_label (_("Audio mode:")));
217 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
218 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
222 interface_combo.set_size_request (250, -1);
223 input_device_combo.set_size_request (250, -1);
224 output_device_combo.set_size_request (250, -1);
228 if (engine_running()) {
229 start_button.set_sensitive (false);
231 stop_button.set_sensitive (false);
234 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
235 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
238 button_box.pack_start (start_button, false, false);
239 button_box.pack_start (stop_button, false, false);
241 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
245 options_packer.set_spacings (6);
248 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
251 realtime_button.set_active (true);
252 realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
255 #if PROVIDE_TOO_MANY_OPTIONS
258 label = manage (new Label (_("Realtime Priority")));
259 label->set_alignment (1.0, 0.5);
260 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
261 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
263 priority_spinner.set_value (60);
265 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
267 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
269 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
271 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
273 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
275 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
277 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
279 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
282 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
287 strings.push_back (_("Ignore"));
288 strings.push_back ("500 msec");
289 strings.push_back ("1 sec");
290 strings.push_back ("2 sec");
291 strings.push_back ("10 sec");
292 set_popdown_strings (timeout_combo, strings);
293 timeout_combo.set_active_text (strings.front ());
295 label = manage (new Label (_("Client timeout")));
296 label->set_alignment (1.0, 0.5);
297 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
298 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
301 #endif /* PROVIDE_TOO_MANY_OPTIONS */
302 label = manage (left_aligned_label (_("Number of ports:")));
303 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
304 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
307 label = manage (left_aligned_label (_("MIDI driver:")));
308 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
309 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
313 label = manage (left_aligned_label (_("Dither:")));
314 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
315 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
319 find_jack_servers (server_strings);
321 if (server_strings.empty()) {
322 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
326 set_popdown_strings (serverpath_combo, server_strings);
327 serverpath_combo.set_active_text (server_strings.front());
329 if (server_strings.size() > 1) {
330 label = manage (left_aligned_label (_("Server:")));
331 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
332 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
336 /* device settings */
338 device_packer.set_spacings (6);
342 label = manage (left_aligned_label (_("Input device:")));
343 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
344 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
346 label = manage (left_aligned_label (_("Output device:")));
347 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
348 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
351 label = manage (left_aligned_label (_("Hardware input latency:")));
352 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
353 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
354 label = manage (left_aligned_label (_("samples")));
355 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
357 label = manage (left_aligned_label (_("Hardware output latency:")));
358 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
359 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
360 label = manage (left_aligned_label (_("samples")));
361 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
364 basic_hbox.pack_start (basic_packer, false, false);
365 options_hbox.pack_start (options_packer, false, false);
367 device_packer.set_border_width (12);
368 options_packer.set_border_width (12);
369 basic_packer.set_border_width (12);
371 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
372 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
373 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
374 notebook.set_border_width (12);
376 set_border_width (12);
377 pack_start (notebook);
380 EngineControl::~EngineControl ()
386 EngineControl::build_command_line (vector<string>& cmd)
390 bool using_alsa = false;
391 bool using_coreaudio = false;
392 bool using_dummy = false;
393 bool using_ffado = false;
395 /* first, path to jackd */
397 cmd.push_back (serverpath_combo.get_active_text ());
399 /* now jackd arguments */
401 str = timeout_combo.get_active_text ();
403 if (str != _("Ignore")) {
408 msecs = (uint32_t) floor (secs * 1000.0);
411 cmd.push_back ("-t");
412 cmd.push_back (to_string (msecs, std::dec));
416 if (no_memory_lock_button.get_active()) {
417 cmd.push_back ("-m"); /* no munlock */
420 cmd.push_back ("-p"); /* port max */
421 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
423 if (realtime_button.get_active()) {
424 cmd.push_back ("-R");
425 cmd.push_back ("-P");
426 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
428 cmd.push_back ("-r"); /* override jackd's default --realtime */
431 if (unlock_memory_button.get_active()) {
432 cmd.push_back ("-u");
435 if (verbose_output_button.get_active()) {
436 cmd.push_back ("-v");
439 /* now add fixed arguments (not user-selectable) */
441 cmd.push_back ("-T"); // temporary */
443 /* next the driver */
445 cmd.push_back ("-d");
447 driver = driver_combo.get_active_text ();
449 if (driver == X_("ALSA")) {
451 cmd.push_back ("alsa");
452 } else if (driver == X_("OSS")) {
453 cmd.push_back ("oss");
454 } else if (driver == X_("CoreAudio")) {
455 using_coreaudio = true;
456 cmd.push_back ("coreaudio");
457 } else if (driver == X_("NetJACK")) {
458 cmd.push_back ("netjack");
459 } else if (driver == X_("FreeBoB")) {
460 cmd.push_back ("freebob");
461 } else if (driver == X_("FFADO")) {
463 cmd.push_back ("firewire");
464 } else if ( driver == X_("Dummy")) {
466 cmd.push_back ("dummy");
469 /* driver arguments */
471 if (!using_coreaudio) {
472 str = audio_mode_combo.get_active_text();
474 if (str == _("Playback/Recording on 1 Device")) {
478 } else if (str == _("Playback/Recording on 2 Devices")) {
480 string input_device = get_device_name (driver, input_device_combo.get_active_text());
481 string output_device = get_device_name (driver, output_device_combo.get_active_text());
483 if (input_device.empty() || output_device.empty()) {
488 cmd.push_back ("-C");
489 cmd.push_back (input_device);
491 cmd.push_back ("-P");
492 cmd.push_back (output_device);
494 } else if (str == _("Playback only")) {
495 cmd.push_back ("-P");
496 } else if (str == _("Recording only")) {
497 cmd.push_back ("-C");
501 cmd.push_back ("-n");
502 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
506 cmd.push_back ("-r");
507 cmd.push_back (to_string (get_rate(), std::dec));
509 cmd.push_back ("-p");
510 cmd.push_back (period_size_combo.get_active_text());
512 if (using_alsa || using_ffado || using_coreaudio) {
514 double val = input_latency_adjustment.get_value();
517 cmd.push_back ("-I");
518 cmd.push_back (to_string ((uint32_t) val, std::dec));
521 val = output_latency_adjustment.get_value();
524 cmd.push_back ("-O");
525 cmd.push_back (to_string ((uint32_t) val, std::dec));
531 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
533 string device = get_device_name (driver, interface_combo.get_active_text());
534 if (device.empty()) {
539 cmd.push_back ("-d");
540 cmd.push_back (device);
543 if (hw_meter_button.get_active()) {
544 cmd.push_back ("-M");
547 if (hw_monitor_button.get_active()) {
548 cmd.push_back ("-H");
551 str = dither_mode_combo.get_active_text();
553 if (str == _("None")) {
554 } else if (str == _("Triangular")) {
555 cmd.push_back ("-z triangular");
556 } else if (str == _("Rectangular")) {
557 cmd.push_back ("-z rectangular");
558 } else if (str == _("Shaped")) {
559 cmd.push_back ("-z shaped");
562 if (force16bit_button.get_active()) {
563 cmd.push_back ("-S");
566 if (soft_mode_button.get_active()) {
567 cmd.push_back ("-s");
570 str = midi_driver_combo.get_active_text ();
572 if (str == _("seq")) {
573 cmd.push_back ("-X seq");
574 } else if (str == _("raw")) {
575 cmd.push_back ("-X raw");
577 } else if (using_coreaudio) {
580 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
582 string device = get_device_name (driver, interface_combo.get_active_text());
583 if (device.empty()) {
588 cmd.push_back ("-d");
589 cmd.push_back (device);
596 EngineControl::engine_running ()
598 EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
599 boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
601 /* revert all environment settings back to whatever they were when ardour started
605 current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
606 global_epa->restore ();
609 jack_status_t status;
610 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
613 jack_client_close (c);
620 EngineControl::setup_engine ()
623 std::string cwd = "/tmp";
625 build_command_line (args);
628 return 1; // try again
631 std::string jackdrc_path = Glib::get_home_dir();
632 jackdrc_path += "/.jackdrc";
634 ofstream jackdrc (jackdrc_path.c_str());
636 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
639 cerr << "JACK COMMAND: ";
640 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
642 jackdrc << (*i) << ' ';
654 EngineControl::realtime_changed ()
657 priority_spinner.set_sensitive (realtime_button.get_active());
662 EngineControl::enumerate_devices (const string& driver)
664 /* note: case matters for the map keys */
666 if (driver == "CoreAudio") {
668 devices[driver] = enumerate_coreaudio_devices ();
672 } else if (driver == "ALSA") {
673 devices[driver] = enumerate_alsa_devices ();
674 } else if (driver == "FreeBOB") {
675 devices[driver] = enumerate_freebob_devices ();
676 } else if (driver == "FFADO") {
677 devices[driver] = enumerate_ffado_devices ();
678 } else if (driver == "OSS") {
679 devices[driver] = enumerate_oss_devices ();
680 } else if (driver == "Dummy") {
681 devices[driver] = enumerate_dummy_devices ();
682 } else if (driver == "NetJACK") {
683 devices[driver] = enumerate_netjack_devices ();
692 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
694 UInt32 size = sizeof(CFStringRef);
696 OSStatus res = AudioDeviceGetProperty(id, 0, false,
697 kAudioDevicePropertyDeviceUID, &size, &UI);
699 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
705 EngineControl::enumerate_coreaudio_devices ()
709 // Find out how many Core Audio devices are there, if any...
710 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
713 UInt32 outSize = sizeof(isWritable);
715 backend_devs.clear ();
717 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
718 &outSize, &isWritable);
720 // Calculate the number of device available...
721 int numCoreDevices = outSize / sizeof(AudioDeviceID);
722 // Make space for the devices we are about to get...
723 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
724 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
725 &outSize, (void *) coreDeviceIDs);
727 // Look for the CoreAudio device name...
728 char coreDeviceName[256];
731 for (int i = 0; i < numCoreDevices; i++) {
733 nameSize = sizeof (coreDeviceName);
735 /* enforce duplex devices only */
737 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
738 0, true, kAudioDevicePropertyStreams,
739 &outSize, &isWritable);
741 if (err != noErr || outSize == 0) {
745 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
746 0, false, kAudioDevicePropertyStreams,
747 &outSize, &isWritable);
749 if (err != noErr || outSize == 0) {
753 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
754 0, true, kAudioDevicePropertyDeviceName,
755 &outSize, &isWritable);
757 err = AudioDeviceGetProperty(coreDeviceIDs[i],
758 0, true, kAudioDevicePropertyDeviceName,
759 &nameSize, (void *) coreDeviceName);
761 char drivername[128];
763 // this returns the unique id for the device
764 // that must be used on the commandline for jack
766 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
767 devs.push_back (coreDeviceName);
768 backend_devs.push_back (drivername);
774 delete [] coreDeviceIDs;
778 if (devs.size() == 0) {
779 MessageDialog msg (_("\
780 You do not have any audio devices capable of\n\
781 simultaneous playback and recording.\n\n\
782 Please use Applications -> Utilities -> Audio MIDI Setup\n\
783 to create an \"aggregrate\" device, or install a suitable\n\
784 audio interface.\n\n\
785 Please send email to Apple and ask them why new Macs\n\
786 have no duplex audio device.\n\n\
787 Alternatively, if you really want just playback\n\
788 or recording but not both, start JACK before running\n\
789 Ardour and choose the relevant device then."
791 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
792 msg.set_title (_("No suitable audio devices"));
793 msg.set_position (Gtk::WIN_POS_MOUSE);
803 EngineControl::enumerate_alsa_devices ()
808 snd_ctl_card_info_t *info;
809 snd_pcm_info_t *pcminfo;
810 snd_ctl_card_info_alloca(&info);
811 snd_pcm_info_alloca(&pcminfo);
816 backend_devs.clear ();
818 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
821 devname += to_string (cardnum, std::dec);
823 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
825 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
827 snd_pcm_info_set_device (pcminfo, device);
828 snd_pcm_info_set_subdevice (pcminfo, 0);
829 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
831 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
832 devs.push_back (snd_pcm_info_get_name (pcminfo));
834 devname += to_string (device, std::dec);
835 backend_devs.push_back (devname);
839 snd_ctl_close(handle);
847 EngineControl::enumerate_ffado_devices ()
850 backend_devs.clear ();
855 EngineControl::enumerate_freebob_devices ()
862 EngineControl::enumerate_oss_devices ()
868 EngineControl::enumerate_dummy_devices ()
874 EngineControl::enumerate_netjack_devices ()
882 EngineControl::driver_changed ()
884 string driver = driver_combo.get_active_text();
885 string::size_type maxlen = 0;
888 enumerate_devices (driver);
890 vector<string>& strings = devices[driver];
892 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
896 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
897 if ((*i).length() > maxlen) {
898 maxlen = (*i).length();
902 set_popdown_strings (interface_combo, strings);
903 set_popdown_strings (input_device_combo, strings);
904 set_popdown_strings (output_device_combo, strings);
906 if (!strings.empty()) {
907 interface_combo.set_active_text (strings.front());
908 input_device_combo.set_active_text (strings.front());
909 output_device_combo.set_active_text (strings.front());
912 if (driver == "ALSA") {
913 soft_mode_button.set_sensitive (true);
914 force16bit_button.set_sensitive (true);
915 hw_monitor_button.set_sensitive (true);
916 hw_meter_button.set_sensitive (true);
917 monitor_button.set_sensitive (true);
919 soft_mode_button.set_sensitive (false);
920 force16bit_button.set_sensitive (false);
921 hw_monitor_button.set_sensitive (false);
922 hw_meter_button.set_sensitive (false);
923 monitor_button.set_sensitive (false);
928 EngineControl::get_rate ()
930 double r = atof (sample_rate_combo.get_active_text ());
931 /* the string may have been translated with an abbreviation for
932 * thousands, so use a crude heuristic to fix this.
941 EngineControl::redisplay_latency ()
943 uint32_t rate = get_rate();
947 float periods = periods_adjustment.get_value();
949 float period_size = atof (period_size_combo.get_active_text());
952 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
954 latency_label.set_text (buf);
955 latency_label.set_alignment (0, 0.5);
959 EngineControl::audio_mode_changed ()
961 std::string str = audio_mode_combo.get_active_text();
963 if (str == _("Playback/Recording on 1 Device")) {
964 input_device_combo.set_sensitive (false);
965 output_device_combo.set_sensitive (false);
966 } else if (str == _("Playback/Recording on 2 Devices")) {
967 input_device_combo.set_sensitive (true);
968 output_device_combo.set_sensitive (true);
969 } else if (str == _("Playback only")) {
970 output_device_combo.set_sensitive (true);
971 input_device_combo.set_sensitive (false);
972 } else if (str == _("Recording only")) {
973 input_device_combo.set_sensitive (true);
974 output_device_combo.set_sensitive (false);
978 static bool jack_server_filter(const string& str, void */*arg*/)
980 return str == "jackd" || str == "jackdmp";
984 EngineControl::find_jack_servers (vector<string>& strings)
987 /* this magic lets us finds the path to the OSX bundle, and then
988 we infer JACK's location from there
991 char execpath[MAXPATHLEN+1];
992 uint32_t pathsz = sizeof (execpath);
994 _NSGetExecutablePath (execpath, &pathsz);
996 string path (Glib::path_get_dirname (execpath));
999 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
1000 strings.push_back (path);
1003 if (getenv ("ARDOUR_WITH_JACK")) {
1004 /* no other options - only use the JACK we supply */
1005 if (strings.empty()) {
1006 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1015 PathScanner scanner;
1016 vector<string *> *jack_servers;
1017 std::map<string,int> un;
1019 bool need_minimal_path = false;
1021 p = getenv ("PATH");
1026 need_minimal_path = true;
1030 // many mac users don't have PATH set up to include
1031 // likely installed locations of JACK
1032 need_minimal_path = true;
1035 if (need_minimal_path) {
1037 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1039 path += ":/usr/local/bin:/opt/local/bin";
1044 // push it back into the environment so that auto-started JACK can find it.
1045 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1046 setenv ("PATH", path.c_str(), 1);
1049 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1050 if (!jack_servers) {
1054 vector<string *>::iterator iter;
1056 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1060 strings.push_back(p);
1067 EngineControl::get_device_name (const string& driver, const string& human_readable)
1069 vector<string>::iterator n;
1070 vector<string>::iterator i;
1072 if (human_readable.empty()) {
1073 /* this can happen if the user's .ardourrc file has a device name from
1074 another computer system in it
1076 MessageDialog msg (_("You need to choose an audio device first."));
1081 if (backend_devs.empty()) {
1082 return human_readable;
1085 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1086 if (human_readable == (*i)) {
1091 if (i == devices[driver].end()) {
1092 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1099 EngineControl::get_state ()
1101 XMLNode* root = new XMLNode ("AudioSetup");
1105 child = new XMLNode ("periods");
1106 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1107 root->add_child_nocopy (*child);
1109 child = new XMLNode ("priority");
1110 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1111 root->add_child_nocopy (*child);
1113 child = new XMLNode ("ports");
1114 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1115 root->add_child_nocopy (*child);
1117 child = new XMLNode ("inlatency");
1118 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1119 root->add_child_nocopy (*child);
1121 child = new XMLNode ("outlatency");
1122 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1123 root->add_child_nocopy (*child);
1125 child = new XMLNode ("realtime");
1126 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1127 root->add_child_nocopy (*child);
1129 child = new XMLNode ("nomemorylock");
1130 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1131 root->add_child_nocopy (*child);
1133 child = new XMLNode ("unlockmemory");
1134 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1135 root->add_child_nocopy (*child);
1137 child = new XMLNode ("softmode");
1138 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1139 root->add_child_nocopy (*child);
1141 child = new XMLNode ("force16bit");
1142 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1143 root->add_child_nocopy (*child);
1145 child = new XMLNode ("hwmonitor");
1146 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1147 root->add_child_nocopy (*child);
1149 child = new XMLNode ("hwmeter");
1150 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1151 root->add_child_nocopy (*child);
1153 child = new XMLNode ("verbose");
1154 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1155 root->add_child_nocopy (*child);
1157 child = new XMLNode ("samplerate");
1158 child->add_property ("val", sample_rate_combo.get_active_text());
1159 root->add_child_nocopy (*child);
1161 child = new XMLNode ("periodsize");
1162 child->add_property ("val", period_size_combo.get_active_text());
1163 root->add_child_nocopy (*child);
1165 child = new XMLNode ("serverpath");
1166 child->add_property ("val", serverpath_combo.get_active_text());
1167 root->add_child_nocopy (*child);
1169 child = new XMLNode ("driver");
1170 child->add_property ("val", driver_combo.get_active_text());
1171 root->add_child_nocopy (*child);
1173 child = new XMLNode ("interface");
1174 child->add_property ("val", interface_combo.get_active_text());
1175 root->add_child_nocopy (*child);
1177 child = new XMLNode ("timeout");
1178 child->add_property ("val", timeout_combo.get_active_text());
1179 root->add_child_nocopy (*child);
1181 child = new XMLNode ("dither");
1182 child->add_property ("val", dither_mode_combo.get_active_text());
1183 root->add_child_nocopy (*child);
1185 child = new XMLNode ("audiomode");
1186 child->add_property ("val", audio_mode_combo.get_active_text());
1187 root->add_child_nocopy (*child);
1189 child = new XMLNode ("inputdevice");
1190 child->add_property ("val", input_device_combo.get_active_text());
1191 root->add_child_nocopy (*child);
1193 child = new XMLNode ("outputdevice");
1194 child->add_property ("val", output_device_combo.get_active_text());
1195 root->add_child_nocopy (*child);
1197 child = new XMLNode ("mididriver");
1198 child->add_property ("val", midi_driver_combo.get_active_text());
1199 root->add_child_nocopy (*child);
1205 EngineControl::set_state (const XMLNode& root)
1208 XMLNodeConstIterator citer;
1210 XMLProperty* prop = NULL;
1211 bool using_dummy = false;
1212 bool using_ffado = false;
1217 if ( (child = root.child ("driver"))){
1218 prop = child->property("val");
1220 if (prop && (prop->value() == "Dummy") ) {
1223 if (prop && (prop->value() == "FFADO") ) {
1229 clist = root.children();
1231 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1235 prop = child->property ("val");
1237 if (!prop || prop->value().empty()) {
1239 if (((using_dummy || using_ffado)
1240 && ( child->name() == "interface"
1241 || child->name() == "inputdevice"
1242 || child->name() == "outputdevice"))
1243 || child->name() == "timeout")
1248 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1252 strval = prop->value();
1254 /* adjustments/spinners */
1256 if (child->name() == "periods") {
1257 val = atoi (strval);
1258 periods_adjustment.set_value(val);
1259 } else if (child->name() == "priority") {
1260 val = atoi (strval);
1261 priority_adjustment.set_value(val);
1262 } else if (child->name() == "ports") {
1263 val = atoi (strval);
1264 ports_adjustment.set_value(val);
1265 } else if (child->name() == "inlatency") {
1266 val = atoi (strval);
1267 input_latency.set_value(val);
1268 } else if (child->name() == "outlatency") {
1269 val = atoi (strval);
1270 output_latency.set_value(val);
1275 else if (child->name() == "realtime") {
1276 val = atoi (strval);
1277 realtime_button.set_active(val);
1278 } else if (child->name() == "nomemorylock") {
1279 val = atoi (strval);
1280 no_memory_lock_button.set_active(val);
1281 } else if (child->name() == "unlockmemory") {
1282 val = atoi (strval);
1283 unlock_memory_button.set_active(val);
1284 } else if (child->name() == "softmode") {
1285 val = atoi (strval);
1286 soft_mode_button.set_active(val);
1287 } else if (child->name() == "force16bit") {
1288 val = atoi (strval);
1289 force16bit_button.set_active(val);
1290 } else if (child->name() == "hwmonitor") {
1291 val = atoi (strval);
1292 hw_monitor_button.set_active(val);
1293 } else if (child->name() == "hwmeter") {
1294 val = atoi (strval);
1295 hw_meter_button.set_active(val);
1296 } else if (child->name() == "verbose") {
1297 val = atoi (strval);
1298 verbose_output_button.set_active(val);
1303 else if (child->name() == "samplerate") {
1304 sample_rate_combo.set_active_text(strval);
1305 } else if (child->name() == "periodsize") {
1306 period_size_combo.set_active_text(strval);
1307 } else if (child->name() == "serverpath") {
1309 /* only attempt to set this if we have bothered to look
1310 up server names already. otherwise this is all
1311 redundant (actually, all of this dialog/widget
1312 is redundant in that case ...)
1315 if (!server_strings.empty()) {
1316 /* do not allow us to use a server path that doesn't
1317 exist on this system. this handles cases where
1318 the user has an RC file listing a serverpath
1319 from some other machine.
1321 vector<string>::iterator x;
1322 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1327 if (x != server_strings.end()) {
1328 serverpath_combo.set_active_text (strval);
1330 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1336 } else if (child->name() == "driver") {
1337 driver_combo.set_active_text(strval);
1338 } else if (child->name() == "interface") {
1339 interface_combo.set_active_text(strval);
1340 } else if (child->name() == "timeout") {
1341 timeout_combo.set_active_text(strval);
1342 } else if (child->name() == "dither") {
1343 dither_mode_combo.set_active_text(strval);
1344 } else if (child->name() == "audiomode") {
1345 audio_mode_combo.set_active_text(strval);
1346 } else if (child->name() == "inputdevice") {
1347 input_device_combo.set_active_text(strval);
1348 } else if (child->name() == "outputdevice") {
1349 output_device_combo.set_active_text(strval);
1350 } else if (child->name() == "mididriver") {
1351 midi_driver_combo.set_active_text(strval);