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.
26 #include <gtkmm/messagedialog.h>
27 #include "pbd/xml++.h"
30 #include <CoreAudio/CoreAudio.h>
31 #include <CoreFoundation/CFString.h>
32 #include <sys/param.h>
33 #include <mach-o/dyld.h>
35 #include <alsa/asoundlib.h>
38 #include "ardour/profile.h"
39 #include <jack/jack.h>
41 #include <gtkmm/stock.h>
42 #include <gtkmm2ext/utils.h>
44 #include "pbd/convert.h"
45 #include "pbd/error.h"
46 #include "pbd/pathscanner.h"
52 #include "engine_dialog.h"
57 using namespace Gtkmm2ext;
61 EngineControl::EngineControl ()
62 : periods_adjustment (2, 2, 16, 1, 2),
63 periods_spinner (periods_adjustment),
64 priority_adjustment (60, 10, 90, 1, 10),
65 priority_spinner (priority_adjustment),
66 ports_adjustment (128, 8, 1024, 1, 16),
67 ports_spinner (ports_adjustment),
68 realtime_button (_("Realtime")),
69 no_memory_lock_button (_("Do not lock memory")),
70 unlock_memory_button (_("Unlock memory")),
71 soft_mode_button (_("No zombies")),
72 monitor_button (_("Provide monitor ports")),
73 force16bit_button (_("Force 16 bit")),
74 hw_monitor_button (_("H/W monitoring")),
75 hw_meter_button (_("H/W metering")),
76 verbose_output_button (_("Verbose output")),
77 start_button (_("Start")),
78 stop_button (_("Stop")),
81 options_packer (4, 2),
85 options_packer (14, 2),
89 using namespace Notebook_Helpers;
91 vector<string> strings;
96 strings.push_back (_("8000Hz"));
97 strings.push_back (_("22050Hz"));
98 strings.push_back (_("44100Hz"));
99 strings.push_back (_("48000Hz"));
100 strings.push_back (_("88200Hz"));
101 strings.push_back (_("96000Hz"));
102 strings.push_back (_("192000Hz"));
103 set_popdown_strings (sample_rate_combo, strings);
104 sample_rate_combo.set_active_text ("48000Hz");
107 strings.push_back ("32");
108 strings.push_back ("64");
109 strings.push_back ("128");
110 strings.push_back ("256");
111 strings.push_back ("512");
112 strings.push_back ("1024");
113 strings.push_back ("2048");
114 strings.push_back ("4096");
115 strings.push_back ("8192");
116 set_popdown_strings (period_size_combo, strings);
117 period_size_combo.set_active_text ("1024");
120 strings.push_back (_("None"));
121 strings.push_back (_("Triangular"));
122 strings.push_back (_("Rectangular"));
123 strings.push_back (_("Shaped"));
124 set_popdown_strings (dither_mode_combo, strings);
125 dither_mode_combo.set_active_text (_("None"));
127 /* basic parameters */
129 basic_packer.set_spacings (6);
133 strings.push_back (X_("CoreAudio"));
135 strings.push_back (X_("ALSA"));
136 strings.push_back (X_("OSS"));
137 strings.push_back (X_("FFADO"));
139 strings.push_back (X_("NetJACK"));
140 strings.push_back (X_("Dummy"));
141 set_popdown_strings (driver_combo, strings);
142 driver_combo.set_active_text (strings.front());
144 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
148 strings.push_back (_("Playback/recording on 1 device"));
149 strings.push_back (_("Playback/recording on 2 devices"));
150 strings.push_back (_("Playback only"));
151 strings.push_back (_("Recording only"));
152 set_popdown_strings (audio_mode_combo, strings);
153 audio_mode_combo.set_active_text (strings.front());
155 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
156 audio_mode_changed ();
159 strings.push_back (_("None"));
160 strings.push_back (_("seq"));
161 strings.push_back (_("raw"));
162 set_popdown_strings (midi_driver_combo, strings);
163 midi_driver_combo.set_active_text (strings.front ());
167 label = manage (new Label (_("Driver:")));
168 label->set_alignment (0, 0.5);
169 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
170 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
173 label = manage (new Label (_("Interface:")));
174 label->set_alignment (0, 0.5);
175 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
176 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
179 label = manage (new Label (_("Sample rate:")));
180 label->set_alignment (0, 0.5);
181 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
182 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
185 label = manage (new Label (_("Buffer size:")));
186 label->set_alignment (0, 0.5);
187 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
188 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
192 label = manage (new Label (_("Number of buffers:")));
193 label->set_alignment (0, 0.5);
194 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
195 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
196 periods_spinner.set_value (2);
200 label = manage (new Label (_("Approximate latency:")));
201 label->set_alignment (0, 0.5);
202 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
203 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
206 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
207 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
208 period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
211 /* no audio mode with CoreAudio, its duplex or nuthin' */
214 label = manage (new Label (_("Audio mode:")));
215 label->set_alignment (0, 0.5);
216 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
217 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
221 interface_combo.set_size_request (250, -1);
222 input_device_combo.set_size_request (250, -1);
223 output_device_combo.set_size_request (250, -1);
227 if (engine_running()) {
228 start_button.set_sensitive (false);
230 stop_button.set_sensitive (false);
233 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
234 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
237 button_box.pack_start (start_button, false, false);
238 button_box.pack_start (stop_button, false, false);
240 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
244 options_packer.set_spacings (6);
247 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
250 realtime_button.set_active (true);
251 realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
254 #if PROVIDE_TOO_MANY_OPTIONS
257 label = manage (new Label (_("Realtime Priority")));
258 label->set_alignment (1.0, 0.5);
259 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
260 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
262 priority_spinner.set_value (60);
264 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
268 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
270 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
272 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
274 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
276 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
278 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
281 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
286 strings.push_back (_("Ignore"));
287 strings.push_back ("500 msec");
288 strings.push_back ("1 sec");
289 strings.push_back ("2 sec");
290 strings.push_back ("10 sec");
291 set_popdown_strings (timeout_combo, strings);
292 timeout_combo.set_active_text (strings.front ());
294 label = manage (new Label (_("Client timeout")));
295 label->set_alignment (1.0, 0.5);
296 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
297 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
300 #endif /* PROVIDE_TOO_MANY_OPTIONS */
301 label = manage (new Label (_("Number of ports:")));
302 label->set_alignment (0, 0.5);
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 (new Label (_("MIDI driver:")));
308 label->set_alignment (0, 0.5);
309 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
310 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
314 label = manage (new Label (_("Dither:")));
315 label->set_alignment (0, 0.5);
316 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
317 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
321 find_jack_servers (server_strings);
323 if (server_strings.empty()) {
324 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
328 set_popdown_strings (serverpath_combo, server_strings);
329 serverpath_combo.set_active_text (server_strings.front());
331 if (server_strings.size() > 1) {
332 label = manage (new Label (_("Server:")));
333 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
334 label->set_alignment (0.0, 0.5);
335 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
339 /* device settings */
341 device_packer.set_spacings (6);
345 label = manage (new Label (_("Input device:")));
346 label->set_alignment (0, 0.5);
347 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
348 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
350 label = manage (new Label (_("Output device:")));
351 label->set_alignment (0, 0.5);
352 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
353 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
356 label = manage (new Label (_("Input channels:")));
357 label->set_alignment (0, 0.5);
358 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
359 device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
361 label = manage (new Label (_("Output channels:")));
362 label->set_alignment (0, 0.5);
363 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
364 device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
366 label = manage (new Label (_("Hardware input latency:")));
367 label->set_alignment (0, 0.5);
368 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
369 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
370 label = manage (new Label (_("samples")));
371 label->set_alignment (0, 0.5);
372 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
374 label = manage (new Label (_("Hardware output latency:")));
375 label->set_alignment (0, 0.5);
376 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
377 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
378 label = manage (new Label (_("samples")));
379 label->set_alignment (0, 0.5);
380 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
383 basic_hbox.pack_start (basic_packer, false, false);
384 options_hbox.pack_start (options_packer, false, false);
386 device_packer.set_border_width (12);
387 options_packer.set_border_width (12);
388 basic_packer.set_border_width (12);
390 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
391 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
392 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
393 notebook.set_border_width (12);
395 set_border_width (12);
396 pack_start (notebook);
399 EngineControl::~EngineControl ()
405 EngineControl::build_command_line (vector<string>& cmd)
409 bool using_oss = false;
410 bool using_alsa = false;
411 bool using_coreaudio = false;
412 bool using_netjack = false;
413 bool using_ffado = false;
414 bool using_dummy = false;
416 /* first, path to jackd */
418 cmd.push_back (serverpath_combo.get_active_text ());
420 /* now jackd arguments */
422 str = timeout_combo.get_active_text ();
423 if (str != _("Ignore")) {
427 msecs = (uint32_t) floor (secs * 1000.0);
429 cmd.push_back ("-t");
430 cmd.push_back (to_string (msecs, std::dec));
434 if (no_memory_lock_button.get_active()) {
435 cmd.push_back ("-m"); /* no munlock */
438 cmd.push_back ("-p"); /* port max */
439 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
441 if (realtime_button.get_active()) {
442 cmd.push_back ("-R");
443 cmd.push_back ("-P");
444 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
446 cmd.push_back ("-r"); /* override jackd's default --realtime */
449 if (unlock_memory_button.get_active()) {
450 cmd.push_back ("-u");
453 if (verbose_output_button.get_active()) {
454 cmd.push_back ("-v");
457 /* now add fixed arguments (not user-selectable) */
459 cmd.push_back ("-T"); // temporary */
461 /* next the driver */
463 cmd.push_back ("-d");
465 driver = driver_combo.get_active_text ();
466 if (driver == X_("ALSA")) {
468 cmd.push_back ("alsa");
469 } else if (driver == X_("OSS")) {
471 cmd.push_back ("oss");
472 } else if (driver == X_("CoreAudio")) {
473 using_coreaudio = true;
474 cmd.push_back ("coreaudio");
475 } else if (driver == X_("NetJACK")) {
476 using_netjack = true;
477 cmd.push_back ("netjack");
478 } else if (driver == X_("FFADO")) {
481 /* do this until FFADO becomes the standard */
483 char* hack = getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
486 cmd.push_back (hack);
488 cmd.push_back ("freebob");
491 } else if ( driver == X_("Dummy")) {
493 cmd.push_back ("dummy");
496 /* driver arguments */
498 if (!using_coreaudio) {
499 str = audio_mode_combo.get_active_text();
501 if (str == _("Playback/Recording on 1 Device")) {
505 } else if (str == _("Playback/Recording on 2 Devices")) {
507 string input_device = get_device_name (driver, input_device_combo.get_active_text());
508 string output_device = get_device_name (driver, output_device_combo.get_active_text());
510 if (input_device.empty() || output_device.empty()) {
515 cmd.push_back ("-C");
516 cmd.push_back (input_device);
517 cmd.push_back ("-P");
518 cmd.push_back (output_device);
520 } else if (str == _("Playback only")) {
521 cmd.push_back ("-P");
522 } else if (str == _("Recording only")) {
523 cmd.push_back ("-C");
526 if (! using_dummy ) {
527 cmd.push_back ("-n");
528 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
532 cmd.push_back ("-r");
533 cmd.push_back (to_string (get_rate(), std::dec));
535 cmd.push_back ("-p");
536 cmd.push_back (period_size_combo.get_active_text());
540 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
542 string device = get_device_name (driver, interface_combo.get_active_text());
543 if (device.empty()) {
548 cmd.push_back ("-d");
549 cmd.push_back (device);
552 if (hw_meter_button.get_active()) {
553 cmd.push_back ("-M");
556 if (hw_monitor_button.get_active()) {
557 cmd.push_back ("-H");
560 str = dither_mode_combo.get_active_text();
562 if (str == _("None")) {
563 } else if (str == _("Triangular")) {
564 cmd.push_back ("-z triangular");
565 } else if (str == _("Rectangular")) {
566 cmd.push_back ("-z rectangular");
567 } else if (str == _("Shaped")) {
568 cmd.push_back ("-z shaped");
571 if (force16bit_button.get_active()) {
572 cmd.push_back ("-S");
575 if (soft_mode_button.get_active()) {
576 cmd.push_back ("-s");
579 str = midi_driver_combo.get_active_text ();
581 if (str == _("seq")) {
582 cmd.push_back ("-X seq");
583 } else if (str == _("raw")) {
584 cmd.push_back ("-X raw");
587 } else if (using_coreaudio) {
590 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
592 string device = get_device_name (driver, interface_combo.get_active_text());
593 if (device.empty()) {
598 cmd.push_back ("-d");
599 cmd.push_back (device);
602 } else if (using_oss) {
604 } else if (using_netjack) {
610 EngineControl::engine_running ()
612 jack_status_t status;
613 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
616 jack_client_close (c);
623 EngineControl::setup_engine ()
626 std::string cwd = "/tmp";
628 build_command_line (args);
631 return 1; // try again
634 std::string jackdrc_path = Glib::get_home_dir();
635 jackdrc_path += "/.jackdrc";
637 ofstream jackdrc (jackdrc_path.c_str());
639 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
642 cerr << "JACK COMMAND: ";
643 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
645 jackdrc << (*i) << ' ';
657 EngineControl::realtime_changed ()
660 priority_spinner.set_sensitive (realtime_button.get_active());
665 EngineControl::enumerate_devices (const string& driver)
667 /* note: case matters for the map keys */
669 if (driver == "CoreAudio") {
671 devices[driver] = enumerate_coreaudio_devices ();
675 } else if (driver == "ALSA") {
676 devices[driver] = enumerate_alsa_devices ();
677 } else if (driver == "FFADO") {
678 devices[driver] = enumerate_ffado_devices ();
679 } else if (driver == "OSS") {
680 devices[driver] = enumerate_oss_devices ();
681 } else if (driver == "Dummy") {
682 devices[driver] = enumerate_dummy_devices ();
683 } else if (driver == "NetJACK") {
684 devices[driver] = enumerate_netjack_devices ();
693 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
695 UInt32 size = sizeof(CFStringRef);
697 OSStatus res = AudioDeviceGetProperty(id, 0, false,
698 kAudioDevicePropertyDeviceUID, &size, &UI);
700 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
706 EngineControl::enumerate_coreaudio_devices ()
710 // Find out how many Core Audio devices are there, if any...
711 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
714 size_t outSize = sizeof(isWritable);
716 backend_devs.clear ();
718 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
719 &outSize, &isWritable);
721 // Calculate the number of device available...
722 int numCoreDevices = outSize / sizeof(AudioDeviceID);
723 // Make space for the devices we are about to get...
724 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
725 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
726 &outSize, (void *) coreDeviceIDs);
728 // Look for the CoreAudio device name...
729 char coreDeviceName[256];
732 for (int i = 0; i < numCoreDevices; i++) {
734 nameSize = sizeof (coreDeviceName);
736 /* enforce duplex devices only */
738 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
739 0, true, kAudioDevicePropertyStreams,
740 &outSize, &isWritable);
742 if (err != noErr || outSize == 0) {
746 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
747 0, false, kAudioDevicePropertyStreams,
748 &outSize, &isWritable);
750 if (err != noErr || outSize == 0) {
754 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
755 0, true, kAudioDevicePropertyDeviceName,
756 &outSize, &isWritable);
758 err = AudioDeviceGetProperty(coreDeviceIDs[i],
759 0, true, kAudioDevicePropertyDeviceName,
760 &nameSize, (void *) coreDeviceName);
762 char drivername[128];
764 // this returns the unique id for the device
765 // that must be used on the commandline for jack
767 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
768 devs.push_back (coreDeviceName);
769 backend_devs.push_back (drivername);
775 delete [] coreDeviceIDs;
779 if (devs.size() == 0) {
780 MessageDialog msg (_("\
781 You do not have any audio devices capable of\n\
782 simultaneous playback and recording.\n\n\
783 Please use Applications -> Utilities -> Audio MIDI Setup\n\
784 to create an \"aggregrate\" device, or install a suitable\n\
785 audio interface.\n\n\
786 Please send email to Apple and ask them why new Macs\n\
787 have no duplex audio device.\n\n\
788 Alternatively, if you really want just playback\n\
789 or recording but not both, start JACK before running\n\
790 Ardour and choose the relevant device then."
792 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
793 msg.set_title (_("No suitable audio devices"));
794 msg.set_position (Gtk::WIN_POS_MOUSE);
804 EngineControl::enumerate_alsa_devices ()
809 snd_ctl_card_info_t *info;
810 snd_pcm_info_t *pcminfo;
811 snd_ctl_card_info_alloca(&info);
812 snd_pcm_info_alloca(&pcminfo);
817 backend_devs.clear ();
819 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
822 devname += to_string (cardnum, std::dec);
824 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
826 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
828 snd_pcm_info_set_device (pcminfo, device);
829 snd_pcm_info_set_subdevice (pcminfo, 0);
830 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
832 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
833 devs.push_back (snd_pcm_info_get_name (pcminfo));
835 devname += to_string (device, std::dec);
836 backend_devs.push_back (devname);
840 snd_ctl_close(handle);
848 EngineControl::enumerate_ffado_devices ()
851 backend_devs.clear ();
856 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;
889 enumerate_devices (driver);
891 vector<string>& strings = devices[driver];
893 if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
894 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
898 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
899 if ((*i).length() > maxlen) {
900 maxlen = (*i).length();
905 set_popdown_strings (interface_combo, strings);
906 set_popdown_strings (input_device_combo, strings);
907 set_popdown_strings (output_device_combo, strings);
909 if (!strings.empty()) {
910 interface_combo.set_active_text (strings.front());
911 input_device_combo.set_active_text (strings.front());
912 output_device_combo.set_active_text (strings.front());
915 if (driver == "ALSA") {
916 soft_mode_button.set_sensitive (true);
917 force16bit_button.set_sensitive (true);
918 hw_monitor_button.set_sensitive (true);
919 hw_meter_button.set_sensitive (true);
920 monitor_button.set_sensitive (true);
922 soft_mode_button.set_sensitive (false);
923 force16bit_button.set_sensitive (false);
924 hw_monitor_button.set_sensitive (false);
925 hw_meter_button.set_sensitive (false);
926 monitor_button.set_sensitive (false);
931 EngineControl::get_rate ()
933 return atoi (sample_rate_combo.get_active_text ());
937 EngineControl::redisplay_latency ()
939 uint32_t rate = get_rate();
943 float periods = periods_adjustment.get_value();
945 float period_size = atof (period_size_combo.get_active_text());
948 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
950 latency_label.set_text (buf);
951 latency_label.set_alignment (0, 0.5);
955 EngineControl::audio_mode_changed ()
957 std::string str = audio_mode_combo.get_active_text();
959 if (str == _("Playback/Recording on 1 Device")) {
960 input_device_combo.set_sensitive (false);
961 output_device_combo.set_sensitive (false);
962 } else if (str == _("Playback/Recording on 2 Devices")) {
963 input_device_combo.set_sensitive (true);
964 output_device_combo.set_sensitive (true);
965 } else if (str == _("Playback only")) {
966 output_device_combo.set_sensitive (true);
967 } else if (str == _("Recording only")) {
968 input_device_combo.set_sensitive (true);
972 static bool jack_server_filter(const string& str, void */*arg*/)
974 return str == "jackd" || str == "jackdmp";
978 EngineControl::find_jack_servers (vector<string>& strings)
981 /* this magic lets us finds the path to the OSX bundle, and then
982 we infer JACK's location from there
985 char execpath[MAXPATHLEN+1];
986 uint32_t pathsz = sizeof (execpath);
988 _NSGetExecutablePath (execpath, &pathsz);
990 string path (Glib::path_get_dirname (execpath));
993 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
994 strings.push_back (path);
997 if (getenv ("ARDOUR_WITH_JACK")) {
998 /* no other options - only use the JACK we supply */
999 if (strings.empty()) {
1000 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1009 PathScanner scanner;
1010 vector<string *> *jack_servers;
1011 std::map<string,int> un;
1013 bool need_minimal_path = false;
1015 p = getenv ("PATH");
1020 need_minimal_path = true;
1024 // many mac users don't have PATH set up to include
1025 // likely installed locations of JACK
1026 need_minimal_path = true;
1029 if (need_minimal_path) {
1031 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1033 path += ":/usr/local/bin:/opt/local/bin";
1038 // push it back into the environment so that auto-started JACK can find it.
1039 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1040 setenv ("PATH", path.c_str(), 1);
1043 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1045 vector<string *>::iterator iter;
1047 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1051 strings.push_back(p);
1058 EngineControl::get_device_name (const string& driver, const string& human_readable)
1060 vector<string>::iterator n;
1061 vector<string>::iterator i;
1063 if (human_readable.empty()) {
1064 /* this can happen if the user's .ardourrc file has a device name from
1065 another computer system in it
1067 MessageDialog msg (_("You need to choose an audio device first."));
1072 if (backend_devs.empty()) {
1073 return human_readable;
1076 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1077 if (human_readable == (*i)) {
1082 if (i == devices[driver].end()) {
1083 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1090 EngineControl::get_state ()
1092 XMLNode* root = new XMLNode ("AudioSetup");
1096 child = new XMLNode ("periods");
1097 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1098 root->add_child_nocopy (*child);
1100 child = new XMLNode ("priority");
1101 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1102 root->add_child_nocopy (*child);
1104 child = new XMLNode ("ports");
1105 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1106 root->add_child_nocopy (*child);
1108 child = new XMLNode ("inchannels");
1109 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1110 root->add_child_nocopy (*child);
1112 child = new XMLNode ("outchannels");
1113 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1114 root->add_child_nocopy (*child);
1116 child = new XMLNode ("inlatency");
1117 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1118 root->add_child_nocopy (*child);
1120 child = new XMLNode ("outlatency");
1121 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1122 root->add_child_nocopy (*child);
1124 child = new XMLNode ("realtime");
1125 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1126 root->add_child_nocopy (*child);
1128 child = new XMLNode ("nomemorylock");
1129 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1130 root->add_child_nocopy (*child);
1132 child = new XMLNode ("unlockmemory");
1133 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1134 root->add_child_nocopy (*child);
1136 child = new XMLNode ("softmode");
1137 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1138 root->add_child_nocopy (*child);
1140 child = new XMLNode ("force16bit");
1141 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1142 root->add_child_nocopy (*child);
1144 child = new XMLNode ("hwmonitor");
1145 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1146 root->add_child_nocopy (*child);
1148 child = new XMLNode ("hwmeter");
1149 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1150 root->add_child_nocopy (*child);
1152 child = new XMLNode ("verbose");
1153 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1154 root->add_child_nocopy (*child);
1156 child = new XMLNode ("samplerate");
1157 child->add_property ("val", sample_rate_combo.get_active_text());
1158 root->add_child_nocopy (*child);
1160 child = new XMLNode ("periodsize");
1161 child->add_property ("val", period_size_combo.get_active_text());
1162 root->add_child_nocopy (*child);
1164 child = new XMLNode ("serverpath");
1165 child->add_property ("val", serverpath_combo.get_active_text());
1166 root->add_child_nocopy (*child);
1168 child = new XMLNode ("driver");
1169 child->add_property ("val", driver_combo.get_active_text());
1170 root->add_child_nocopy (*child);
1172 child = new XMLNode ("interface");
1173 child->add_property ("val", interface_combo.get_active_text());
1174 root->add_child_nocopy (*child);
1176 child = new XMLNode ("timeout");
1177 child->add_property ("val", timeout_combo.get_active_text());
1178 root->add_child_nocopy (*child);
1180 child = new XMLNode ("dither");
1181 child->add_property ("val", dither_mode_combo.get_active_text());
1182 root->add_child_nocopy (*child);
1184 child = new XMLNode ("audiomode");
1185 child->add_property ("val", audio_mode_combo.get_active_text());
1186 root->add_child_nocopy (*child);
1188 child = new XMLNode ("inputdevice");
1189 child->add_property ("val", input_device_combo.get_active_text());
1190 root->add_child_nocopy (*child);
1192 child = new XMLNode ("outputdevice");
1193 child->add_property ("val", output_device_combo.get_active_text());
1194 root->add_child_nocopy (*child);
1196 child = new XMLNode ("mididriver");
1197 child->add_property ("val", midi_driver_combo.get_active_text());
1198 root->add_child_nocopy (*child);
1204 EngineControl::set_state (const XMLNode& root)
1207 XMLNodeConstIterator citer;
1209 XMLProperty* prop = NULL;
1210 bool using_dummy = false;
1215 if ( (child = root.child ("driver"))){
1216 prop = child->property("val");
1217 if (prop && (prop->value() == "Dummy") ) {
1222 clist = root.children();
1224 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1228 prop = child->property ("val");
1230 if (!prop || prop->value().empty()) {
1232 if ((using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" )) ||
1233 child->name() == "timeout")
1235 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1239 strval = prop->value();
1241 /* adjustments/spinners */
1243 if (child->name() == "periods") {
1244 val = atoi (strval);
1245 periods_adjustment.set_value(val);
1246 } else if (child->name() == "priority") {
1247 val = atoi (strval);
1248 priority_adjustment.set_value(val);
1249 } else if (child->name() == "ports") {
1250 val = atoi (strval);
1251 ports_adjustment.set_value(val);
1252 } else if (child->name() == "inchannels") {
1253 val = atoi (strval);
1254 input_channels.set_value(val);
1255 } else if (child->name() == "outchannels") {
1256 val = atoi (strval);
1257 output_channels.set_value(val);
1258 } else if (child->name() == "inlatency") {
1259 val = atoi (strval);
1260 input_latency.set_value(val);
1261 } else if (child->name() == "outlatency") {
1262 val = atoi (strval);
1263 output_latency.set_value(val);
1268 else if (child->name() == "realtime") {
1269 val = atoi (strval);
1270 realtime_button.set_active(val);
1271 } else if (child->name() == "nomemorylock") {
1272 val = atoi (strval);
1273 no_memory_lock_button.set_active(val);
1274 } else if (child->name() == "unlockmemory") {
1275 val = atoi (strval);
1276 unlock_memory_button.set_active(val);
1277 } else if (child->name() == "softmode") {
1278 val = atoi (strval);
1279 soft_mode_button.set_active(val);
1280 } else if (child->name() == "force16bit") {
1281 val = atoi (strval);
1282 force16bit_button.set_active(val);
1283 } else if (child->name() == "hwmonitor") {
1284 val = atoi (strval);
1285 hw_monitor_button.set_active(val);
1286 } else if (child->name() == "hwmeter") {
1287 val = atoi (strval);
1288 hw_meter_button.set_active(val);
1289 } else if (child->name() == "verbose") {
1290 val = atoi (strval);
1291 verbose_output_button.set_active(val);
1296 else if (child->name() == "samplerate") {
1297 sample_rate_combo.set_active_text(strval);
1298 } else if (child->name() == "periodsize") {
1299 period_size_combo.set_active_text(strval);
1300 } else if (child->name() == "serverpath") {
1302 /* only attempt to set this if we have bothered to look
1303 up server names already. otherwise this is all
1304 redundant (actually, all of this dialog/widget
1305 is redundant in that case ...)
1308 if (!server_strings.empty()) {
1309 /* do not allow us to use a server path that doesn't
1310 exist on this system. this handles cases where
1311 the user has an RC file listing a serverpath
1312 from some other machine.
1314 vector<string>::iterator x;
1315 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1320 if (x != server_strings.end()) {
1321 serverpath_combo.set_active_text (strval);
1323 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1329 } else if (child->name() == "driver") {
1330 driver_combo.set_active_text(strval);
1331 } else if (child->name() == "interface") {
1332 interface_combo.set_active_text(strval);
1333 } else if (child->name() == "timeout") {
1334 timeout_combo.set_active_text(strval);
1335 } else if (child->name() == "dither") {
1336 dither_mode_combo.set_active_text(strval);
1337 } else if (child->name() == "audiomode") {
1338 audio_mode_combo.set_active_text(strval);
1339 } else if (child->name() == "inputdevice") {
1340 input_device_combo.set_active_text(strval);
1341 } else if (child->name() == "outputdevice") {
1342 output_device_combo.set_active_text(strval);
1343 } else if (child->name() == "mididriver") {
1344 midi_driver_combo.set_active_text(strval);