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 input_latency_adjustment (0, 0, 99999, 1),
69 input_latency (input_latency_adjustment),
70 output_latency_adjustment (0, 0, 99999, 1),
71 output_latency (output_latency_adjustment),
72 realtime_button (_("Realtime")),
73 no_memory_lock_button (_("Do not lock memory")),
74 unlock_memory_button (_("Unlock memory")),
75 soft_mode_button (_("No zombies")),
76 monitor_button (_("Provide monitor ports")),
77 force16bit_button (_("Force 16 bit")),
78 hw_monitor_button (_("H/W monitoring")),
79 hw_meter_button (_("H/W metering")),
80 verbose_output_button (_("Verbose output")),
81 start_button (_("Start")),
82 stop_button (_("Stop")),
85 options_packer (4, 2),
89 options_packer (14, 2),
93 using namespace Notebook_Helpers;
95 vector<string> strings;
100 strings.push_back (_("8000Hz"));
101 strings.push_back (_("22050Hz"));
102 strings.push_back (_("44100Hz"));
103 strings.push_back (_("48000Hz"));
104 strings.push_back (_("88200Hz"));
105 strings.push_back (_("96000Hz"));
106 strings.push_back (_("192000Hz"));
107 set_popdown_strings (sample_rate_combo, strings);
108 sample_rate_combo.set_active_text ("48000Hz");
111 strings.push_back ("32");
112 strings.push_back ("64");
113 strings.push_back ("128");
114 strings.push_back ("256");
115 strings.push_back ("512");
116 strings.push_back ("1024");
117 strings.push_back ("2048");
118 strings.push_back ("4096");
119 strings.push_back ("8192");
120 set_popdown_strings (period_size_combo, strings);
121 period_size_combo.set_active_text ("1024");
124 strings.push_back (_("None"));
125 strings.push_back (_("Triangular"));
126 strings.push_back (_("Rectangular"));
127 strings.push_back (_("Shaped"));
128 set_popdown_strings (dither_mode_combo, strings);
129 dither_mode_combo.set_active_text (_("None"));
131 /* basic parameters */
133 basic_packer.set_spacings (6);
137 strings.push_back (X_("CoreAudio"));
139 strings.push_back (X_("ALSA"));
140 strings.push_back (X_("OSS"));
141 strings.push_back (X_("FFADO"));
143 strings.push_back (X_("NetJACK"));
144 strings.push_back (X_("Dummy"));
145 set_popdown_strings (driver_combo, strings);
146 driver_combo.set_active_text (strings.front());
148 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
152 strings.push_back (_("Playback/recording on 1 device"));
153 strings.push_back (_("Playback/recording on 2 devices"));
154 strings.push_back (_("Playback only"));
155 strings.push_back (_("Recording only"));
156 set_popdown_strings (audio_mode_combo, strings);
157 audio_mode_combo.set_active_text (strings.front());
159 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
160 audio_mode_changed ();
163 strings.push_back (_("None"));
164 strings.push_back (_("seq"));
165 strings.push_back (_("raw"));
166 set_popdown_strings (midi_driver_combo, strings);
167 midi_driver_combo.set_active_text (strings.front ());
171 label = manage (new Label (_("Driver:")));
172 label->set_alignment (0, 0.5);
173 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
174 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
177 label = manage (new Label (_("Interface:")));
178 label->set_alignment (0, 0.5);
179 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
180 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
183 label = manage (new Label (_("Sample rate:")));
184 label->set_alignment (0, 0.5);
185 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
186 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
189 label = manage (new Label (_("Buffer size:")));
190 label->set_alignment (0, 0.5);
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 (new Label (_("Number of buffers:")));
197 label->set_alignment (0, 0.5);
198 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
199 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
200 periods_spinner.set_value (2);
204 label = manage (new Label (_("Approximate latency:")));
205 label->set_alignment (0, 0.5);
206 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
207 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
210 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
211 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
212 period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
215 /* no audio mode with CoreAudio, its duplex or nuthin' */
218 label = manage (new Label (_("Audio mode:")));
219 label->set_alignment (0, 0.5);
220 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
221 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
225 interface_combo.set_size_request (250, -1);
226 input_device_combo.set_size_request (250, -1);
227 output_device_combo.set_size_request (250, -1);
231 if (engine_running()) {
232 start_button.set_sensitive (false);
234 stop_button.set_sensitive (false);
237 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
238 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
241 button_box.pack_start (start_button, false, false);
242 button_box.pack_start (stop_button, false, false);
244 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
248 options_packer.set_spacings (6);
251 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
254 realtime_button.set_active (true);
255 realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
258 #if PROVIDE_TOO_MANY_OPTIONS
261 label = manage (new Label (_("Realtime Priority")));
262 label->set_alignment (1.0, 0.5);
263 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
264 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266 priority_spinner.set_value (60);
268 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
270 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
272 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
274 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
276 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
278 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
280 options_packer.attach (hw_meter_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);
285 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
290 strings.push_back (_("Ignore"));
291 strings.push_back ("500 msec");
292 strings.push_back ("1 sec");
293 strings.push_back ("2 sec");
294 strings.push_back ("10 sec");
295 set_popdown_strings (timeout_combo, strings);
296 timeout_combo.set_active_text (strings.front ());
298 label = manage (new Label (_("Client timeout")));
299 label->set_alignment (1.0, 0.5);
300 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
301 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
304 #endif /* PROVIDE_TOO_MANY_OPTIONS */
305 label = manage (new Label (_("Number of ports:")));
306 label->set_alignment (0, 0.5);
307 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
308 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
311 label = manage (new Label (_("MIDI driver:")));
312 label->set_alignment (0, 0.5);
313 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
314 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
318 label = manage (new Label (_("Dither:")));
319 label->set_alignment (0, 0.5);
320 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
321 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
325 find_jack_servers (server_strings);
327 if (server_strings.empty()) {
328 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
332 set_popdown_strings (serverpath_combo, server_strings);
333 serverpath_combo.set_active_text (server_strings.front());
335 if (server_strings.size() > 1) {
336 label = manage (new Label (_("Server:")));
337 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
338 label->set_alignment (0.0, 0.5);
339 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
343 /* device settings */
345 device_packer.set_spacings (6);
349 label = manage (new Label (_("Input device:")));
350 label->set_alignment (0, 0.5);
351 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
352 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
354 label = manage (new Label (_("Output device:")));
355 label->set_alignment (0, 0.5);
356 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
357 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
360 label = manage (new Label (_("Input channels:")));
361 label->set_alignment (0, 0.5);
362 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
363 device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
365 label = manage (new Label (_("Output channels:")));
366 label->set_alignment (0, 0.5);
367 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
368 device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
370 label = manage (new Label (_("Hardware input latency:")));
371 label->set_alignment (0, 0.5);
372 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
373 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
374 label = manage (new Label (_("samples")));
375 label->set_alignment (0, 0.5);
376 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
378 label = manage (new Label (_("Hardware output latency:")));
379 label->set_alignment (0, 0.5);
380 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
381 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
382 label = manage (new Label (_("samples")));
383 label->set_alignment (0, 0.5);
384 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
387 basic_hbox.pack_start (basic_packer, false, false);
388 options_hbox.pack_start (options_packer, false, false);
390 device_packer.set_border_width (12);
391 options_packer.set_border_width (12);
392 basic_packer.set_border_width (12);
394 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
395 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
396 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
397 notebook.set_border_width (12);
399 set_border_width (12);
400 pack_start (notebook);
403 EngineControl::~EngineControl ()
409 EngineControl::build_command_line (vector<string>& cmd)
413 bool using_oss = false;
414 bool using_alsa = false;
415 bool using_coreaudio = false;
416 bool using_netjack = false;
417 bool using_ffado = false;
418 bool using_dummy = false;
420 /* first, path to jackd */
422 cmd.push_back (serverpath_combo.get_active_text ());
424 /* now jackd arguments */
426 str = timeout_combo.get_active_text ();
427 if (str != _("Ignore")) {
431 msecs = (uint32_t) floor (secs * 1000.0);
433 cmd.push_back ("-t");
434 cmd.push_back (to_string (msecs, std::dec));
438 if (no_memory_lock_button.get_active()) {
439 cmd.push_back ("-m"); /* no munlock */
442 cmd.push_back ("-p"); /* port max */
443 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
445 if (realtime_button.get_active()) {
446 cmd.push_back ("-R");
447 cmd.push_back ("-P");
448 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
450 cmd.push_back ("-r"); /* override jackd's default --realtime */
453 if (unlock_memory_button.get_active()) {
454 cmd.push_back ("-u");
457 if (verbose_output_button.get_active()) {
458 cmd.push_back ("-v");
461 /* now add fixed arguments (not user-selectable) */
463 cmd.push_back ("-T"); // temporary */
465 /* next the driver */
467 cmd.push_back ("-d");
469 driver = driver_combo.get_active_text ();
470 if (driver == X_("ALSA")) {
472 cmd.push_back ("alsa");
473 } else if (driver == X_("OSS")) {
475 cmd.push_back ("oss");
476 } else if (driver == X_("CoreAudio")) {
477 using_coreaudio = true;
478 cmd.push_back ("coreaudio");
479 } else if (driver == X_("NetJACK")) {
480 using_netjack = true;
481 cmd.push_back ("netjack");
482 } else if (driver == X_("FFADO")) {
485 /* do this until FFADO becomes the standard */
487 char* hack = getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
490 cmd.push_back (hack);
492 cmd.push_back ("freebob");
495 } else if ( driver == X_("Dummy")) {
497 cmd.push_back ("dummy");
500 /* driver arguments */
502 if (!using_coreaudio) {
503 str = audio_mode_combo.get_active_text();
505 if (str == _("Playback/Recording on 1 Device")) {
509 } else if (str == _("Playback/Recording on 2 Devices")) {
511 string input_device = get_device_name (driver, input_device_combo.get_active_text());
512 string output_device = get_device_name (driver, output_device_combo.get_active_text());
514 if (input_device.empty() || output_device.empty()) {
519 cmd.push_back ("-C");
520 cmd.push_back (input_device);
521 cmd.push_back ("-P");
522 cmd.push_back (output_device);
524 } else if (str == _("Playback only")) {
525 cmd.push_back ("-P");
526 } else if (str == _("Recording only")) {
527 cmd.push_back ("-C");
530 if (! using_dummy ) {
531 cmd.push_back ("-n");
532 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
536 cmd.push_back ("-r");
537 cmd.push_back (to_string (get_rate(), std::dec));
539 cmd.push_back ("-p");
540 cmd.push_back (period_size_combo.get_active_text());
544 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
546 string device = get_device_name (driver, interface_combo.get_active_text());
547 if (device.empty()) {
552 cmd.push_back ("-d");
553 cmd.push_back (device);
556 if (hw_meter_button.get_active()) {
557 cmd.push_back ("-M");
560 if (hw_monitor_button.get_active()) {
561 cmd.push_back ("-H");
564 str = dither_mode_combo.get_active_text();
566 if (str == _("None")) {
567 } else if (str == _("Triangular")) {
568 cmd.push_back ("-z triangular");
569 } else if (str == _("Rectangular")) {
570 cmd.push_back ("-z rectangular");
571 } else if (str == _("Shaped")) {
572 cmd.push_back ("-z shaped");
575 if (force16bit_button.get_active()) {
576 cmd.push_back ("-S");
579 if (soft_mode_button.get_active()) {
580 cmd.push_back ("-s");
583 str = midi_driver_combo.get_active_text ();
585 if (str == _("seq")) {
586 cmd.push_back ("-X seq");
587 } else if (str == _("raw")) {
588 cmd.push_back ("-X raw");
591 double val = input_latency_adjustment.get_value();
594 cmd.push_back ("-I");
595 cmd.push_back (to_string ((uint32_t) val, std::dec));
598 val = output_latency_adjustment.get_value();
600 cmd.push_back ("-O");
601 cmd.push_back (to_string ((uint32_t) val, std::dec));
604 } else if (using_coreaudio) {
607 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
609 string device = get_device_name (driver, interface_combo.get_active_text());
610 if (device.empty()) {
615 cmd.push_back ("-d");
616 cmd.push_back (device);
618 double val = input_latency_adjustment.get_value();
621 cmd.push_back ("-I");
622 cmd.push_back (to_string ((uint32_t) val, std::dec));
625 double val = output_latency_adjustment.get_value();
627 cmd.push_back ("-O");
628 cmd.push_back (to_string ((uint32_t) val, std::dec));
632 } else if (using_oss) {
634 } else if (using_netjack) {
640 EngineControl::engine_running ()
642 jack_status_t status;
643 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
646 jack_client_close (c);
653 EngineControl::setup_engine ()
656 std::string cwd = "/tmp";
658 build_command_line (args);
661 return 1; // try again
664 std::string jackdrc_path = Glib::get_home_dir();
665 jackdrc_path += "/.jackdrc";
667 ofstream jackdrc (jackdrc_path.c_str());
669 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
672 cerr << "JACK COMMAND: ";
673 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
675 jackdrc << (*i) << ' ';
687 EngineControl::realtime_changed ()
690 priority_spinner.set_sensitive (realtime_button.get_active());
695 EngineControl::enumerate_devices (const string& driver)
697 /* note: case matters for the map keys */
699 if (driver == "CoreAudio") {
701 devices[driver] = enumerate_coreaudio_devices ();
705 } else if (driver == "ALSA") {
706 devices[driver] = enumerate_alsa_devices ();
707 } else if (driver == "FFADO") {
708 devices[driver] = enumerate_ffado_devices ();
709 } else if (driver == "OSS") {
710 devices[driver] = enumerate_oss_devices ();
711 } else if (driver == "Dummy") {
712 devices[driver] = enumerate_dummy_devices ();
713 } else if (driver == "NetJACK") {
714 devices[driver] = enumerate_netjack_devices ();
723 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
725 UInt32 size = sizeof(CFStringRef);
727 OSStatus res = AudioDeviceGetProperty(id, 0, false,
728 kAudioDevicePropertyDeviceUID, &size, &UI);
730 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
736 EngineControl::enumerate_coreaudio_devices ()
740 // Find out how many Core Audio devices are there, if any...
741 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
744 size_t outSize = sizeof(isWritable);
746 backend_devs.clear ();
748 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
749 &outSize, &isWritable);
751 // Calculate the number of device available...
752 int numCoreDevices = outSize / sizeof(AudioDeviceID);
753 // Make space for the devices we are about to get...
754 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
755 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
756 &outSize, (void *) coreDeviceIDs);
758 // Look for the CoreAudio device name...
759 char coreDeviceName[256];
762 for (int i = 0; i < numCoreDevices; i++) {
764 nameSize = sizeof (coreDeviceName);
766 /* enforce duplex devices only */
768 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
769 0, true, kAudioDevicePropertyStreams,
770 &outSize, &isWritable);
772 if (err != noErr || outSize == 0) {
776 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
777 0, false, kAudioDevicePropertyStreams,
778 &outSize, &isWritable);
780 if (err != noErr || outSize == 0) {
784 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
785 0, true, kAudioDevicePropertyDeviceName,
786 &outSize, &isWritable);
788 err = AudioDeviceGetProperty(coreDeviceIDs[i],
789 0, true, kAudioDevicePropertyDeviceName,
790 &nameSize, (void *) coreDeviceName);
792 char drivername[128];
794 // this returns the unique id for the device
795 // that must be used on the commandline for jack
797 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
798 devs.push_back (coreDeviceName);
799 backend_devs.push_back (drivername);
805 delete [] coreDeviceIDs;
809 if (devs.size() == 0) {
810 MessageDialog msg (_("\
811 You do not have any audio devices capable of\n\
812 simultaneous playback and recording.\n\n\
813 Please use Applications -> Utilities -> Audio MIDI Setup\n\
814 to create an \"aggregrate\" device, or install a suitable\n\
815 audio interface.\n\n\
816 Please send email to Apple and ask them why new Macs\n\
817 have no duplex audio device.\n\n\
818 Alternatively, if you really want just playback\n\
819 or recording but not both, start JACK before running\n\
820 Ardour and choose the relevant device then."
822 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
823 msg.set_title (_("No suitable audio devices"));
824 msg.set_position (Gtk::WIN_POS_MOUSE);
834 EngineControl::enumerate_alsa_devices ()
839 snd_ctl_card_info_t *info;
840 snd_pcm_info_t *pcminfo;
841 snd_ctl_card_info_alloca(&info);
842 snd_pcm_info_alloca(&pcminfo);
847 backend_devs.clear ();
849 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
852 devname += to_string (cardnum, std::dec);
854 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
856 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
858 snd_pcm_info_set_device (pcminfo, device);
859 snd_pcm_info_set_subdevice (pcminfo, 0);
860 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
862 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
863 devs.push_back (snd_pcm_info_get_name (pcminfo));
865 devname += to_string (device, std::dec);
866 backend_devs.push_back (devname);
870 snd_ctl_close(handle);
878 EngineControl::enumerate_ffado_devices ()
881 backend_devs.clear ();
886 EngineControl::enumerate_freebob_devices ()
892 EngineControl::enumerate_oss_devices ()
898 EngineControl::enumerate_dummy_devices ()
904 EngineControl::enumerate_netjack_devices ()
912 EngineControl::driver_changed ()
914 string driver = driver_combo.get_active_text();
915 string::size_type maxlen = 0;
919 enumerate_devices (driver);
921 vector<string>& strings = devices[driver];
923 if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
924 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
928 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
929 if ((*i).length() > maxlen) {
930 maxlen = (*i).length();
935 set_popdown_strings (interface_combo, strings);
936 set_popdown_strings (input_device_combo, strings);
937 set_popdown_strings (output_device_combo, strings);
939 if (!strings.empty()) {
940 interface_combo.set_active_text (strings.front());
941 input_device_combo.set_active_text (strings.front());
942 output_device_combo.set_active_text (strings.front());
945 if (driver == "ALSA") {
946 soft_mode_button.set_sensitive (true);
947 force16bit_button.set_sensitive (true);
948 hw_monitor_button.set_sensitive (true);
949 hw_meter_button.set_sensitive (true);
950 monitor_button.set_sensitive (true);
952 soft_mode_button.set_sensitive (false);
953 force16bit_button.set_sensitive (false);
954 hw_monitor_button.set_sensitive (false);
955 hw_meter_button.set_sensitive (false);
956 monitor_button.set_sensitive (false);
961 EngineControl::get_rate ()
963 return atoi (sample_rate_combo.get_active_text ());
967 EngineControl::redisplay_latency ()
969 uint32_t rate = get_rate();
973 float periods = periods_adjustment.get_value();
975 float period_size = atof (period_size_combo.get_active_text());
978 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
980 latency_label.set_text (buf);
981 latency_label.set_alignment (0, 0.5);
985 EngineControl::audio_mode_changed ()
987 std::string str = audio_mode_combo.get_active_text();
989 if (str == _("Playback/Recording on 1 Device")) {
990 input_device_combo.set_sensitive (false);
991 output_device_combo.set_sensitive (false);
992 } else if (str == _("Playback/Recording on 2 Devices")) {
993 input_device_combo.set_sensitive (true);
994 output_device_combo.set_sensitive (true);
995 } else if (str == _("Playback only")) {
996 output_device_combo.set_sensitive (true);
997 } else if (str == _("Recording only")) {
998 input_device_combo.set_sensitive (true);
1002 static bool jack_server_filter(const string& str, void */*arg*/)
1004 return str == "jackd" || str == "jackdmp";
1008 EngineControl::find_jack_servers (vector<string>& strings)
1011 /* this magic lets us finds the path to the OSX bundle, and then
1012 we infer JACK's location from there
1015 char execpath[MAXPATHLEN+1];
1016 uint32_t pathsz = sizeof (execpath);
1018 _NSGetExecutablePath (execpath, &pathsz);
1020 string path (Glib::path_get_dirname (execpath));
1023 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
1024 strings.push_back (path);
1027 if (getenv ("ARDOUR_WITH_JACK")) {
1028 /* no other options - only use the JACK we supply */
1029 if (strings.empty()) {
1030 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1039 PathScanner scanner;
1040 vector<string *> *jack_servers;
1041 std::map<string,int> un;
1043 bool need_minimal_path = false;
1045 p = getenv ("PATH");
1050 need_minimal_path = true;
1054 // many mac users don't have PATH set up to include
1055 // likely installed locations of JACK
1056 need_minimal_path = true;
1059 if (need_minimal_path) {
1061 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1063 path += ":/usr/local/bin:/opt/local/bin";
1068 // push it back into the environment so that auto-started JACK can find it.
1069 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1070 setenv ("PATH", path.c_str(), 1);
1073 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1075 vector<string *>::iterator iter;
1077 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1081 strings.push_back(p);
1088 EngineControl::get_device_name (const string& driver, const string& human_readable)
1090 vector<string>::iterator n;
1091 vector<string>::iterator i;
1093 if (human_readable.empty()) {
1094 /* this can happen if the user's .ardourrc file has a device name from
1095 another computer system in it
1097 MessageDialog msg (_("You need to choose an audio device first."));
1102 if (backend_devs.empty()) {
1103 return human_readable;
1106 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1107 if (human_readable == (*i)) {
1112 if (i == devices[driver].end()) {
1113 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1120 EngineControl::get_state ()
1122 XMLNode* root = new XMLNode ("AudioSetup");
1126 child = new XMLNode ("periods");
1127 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1128 root->add_child_nocopy (*child);
1130 child = new XMLNode ("priority");
1131 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1132 root->add_child_nocopy (*child);
1134 child = new XMLNode ("ports");
1135 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1136 root->add_child_nocopy (*child);
1138 child = new XMLNode ("inchannels");
1139 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1140 root->add_child_nocopy (*child);
1142 child = new XMLNode ("outchannels");
1143 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1144 root->add_child_nocopy (*child);
1146 child = new XMLNode ("inlatency");
1147 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1148 root->add_child_nocopy (*child);
1150 child = new XMLNode ("outlatency");
1151 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1152 root->add_child_nocopy (*child);
1154 child = new XMLNode ("realtime");
1155 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1156 root->add_child_nocopy (*child);
1158 child = new XMLNode ("nomemorylock");
1159 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1160 root->add_child_nocopy (*child);
1162 child = new XMLNode ("unlockmemory");
1163 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1164 root->add_child_nocopy (*child);
1166 child = new XMLNode ("softmode");
1167 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1168 root->add_child_nocopy (*child);
1170 child = new XMLNode ("force16bit");
1171 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1172 root->add_child_nocopy (*child);
1174 child = new XMLNode ("hwmonitor");
1175 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1176 root->add_child_nocopy (*child);
1178 child = new XMLNode ("hwmeter");
1179 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1180 root->add_child_nocopy (*child);
1182 child = new XMLNode ("verbose");
1183 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1184 root->add_child_nocopy (*child);
1186 child = new XMLNode ("samplerate");
1187 child->add_property ("val", sample_rate_combo.get_active_text());
1188 root->add_child_nocopy (*child);
1190 child = new XMLNode ("periodsize");
1191 child->add_property ("val", period_size_combo.get_active_text());
1192 root->add_child_nocopy (*child);
1194 child = new XMLNode ("serverpath");
1195 child->add_property ("val", serverpath_combo.get_active_text());
1196 root->add_child_nocopy (*child);
1198 child = new XMLNode ("driver");
1199 child->add_property ("val", driver_combo.get_active_text());
1200 root->add_child_nocopy (*child);
1202 child = new XMLNode ("interface");
1203 child->add_property ("val", interface_combo.get_active_text());
1204 root->add_child_nocopy (*child);
1206 child = new XMLNode ("timeout");
1207 child->add_property ("val", timeout_combo.get_active_text());
1208 root->add_child_nocopy (*child);
1210 child = new XMLNode ("dither");
1211 child->add_property ("val", dither_mode_combo.get_active_text());
1212 root->add_child_nocopy (*child);
1214 child = new XMLNode ("audiomode");
1215 child->add_property ("val", audio_mode_combo.get_active_text());
1216 root->add_child_nocopy (*child);
1218 child = new XMLNode ("inputdevice");
1219 child->add_property ("val", input_device_combo.get_active_text());
1220 root->add_child_nocopy (*child);
1222 child = new XMLNode ("outputdevice");
1223 child->add_property ("val", output_device_combo.get_active_text());
1224 root->add_child_nocopy (*child);
1226 child = new XMLNode ("mididriver");
1227 child->add_property ("val", midi_driver_combo.get_active_text());
1228 root->add_child_nocopy (*child);
1234 EngineControl::set_state (const XMLNode& root)
1237 XMLNodeConstIterator citer;
1239 XMLProperty* prop = NULL;
1240 bool using_dummy = false;
1245 if ( (child = root.child ("driver"))){
1246 prop = child->property("val");
1247 if (prop && (prop->value() == "Dummy") ) {
1252 clist = root.children();
1254 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1258 prop = child->property ("val");
1260 if (!prop || prop->value().empty()) {
1262 if ((using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" )) ||
1263 child->name() == "timeout")
1265 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1269 strval = prop->value();
1271 /* adjustments/spinners */
1273 if (child->name() == "periods") {
1274 val = atoi (strval);
1275 periods_adjustment.set_value(val);
1276 } else if (child->name() == "priority") {
1277 val = atoi (strval);
1278 priority_adjustment.set_value(val);
1279 } else if (child->name() == "ports") {
1280 val = atoi (strval);
1281 ports_adjustment.set_value(val);
1282 } else if (child->name() == "inchannels") {
1283 val = atoi (strval);
1284 input_channels.set_value(val);
1285 } else if (child->name() == "outchannels") {
1286 val = atoi (strval);
1287 output_channels.set_value(val);
1288 } else if (child->name() == "inlatency") {
1289 val = atoi (strval);
1290 input_latency.set_value(val);
1291 } else if (child->name() == "outlatency") {
1292 val = atoi (strval);
1293 output_latency.set_value(val);
1298 else if (child->name() == "realtime") {
1299 val = atoi (strval);
1300 realtime_button.set_active(val);
1301 } else if (child->name() == "nomemorylock") {
1302 val = atoi (strval);
1303 no_memory_lock_button.set_active(val);
1304 } else if (child->name() == "unlockmemory") {
1305 val = atoi (strval);
1306 unlock_memory_button.set_active(val);
1307 } else if (child->name() == "softmode") {
1308 val = atoi (strval);
1309 soft_mode_button.set_active(val);
1310 } else if (child->name() == "force16bit") {
1311 val = atoi (strval);
1312 force16bit_button.set_active(val);
1313 } else if (child->name() == "hwmonitor") {
1314 val = atoi (strval);
1315 hw_monitor_button.set_active(val);
1316 } else if (child->name() == "hwmeter") {
1317 val = atoi (strval);
1318 hw_meter_button.set_active(val);
1319 } else if (child->name() == "verbose") {
1320 val = atoi (strval);
1321 verbose_output_button.set_active(val);
1326 else if (child->name() == "samplerate") {
1327 sample_rate_combo.set_active_text(strval);
1328 } else if (child->name() == "periodsize") {
1329 period_size_combo.set_active_text(strval);
1330 } else if (child->name() == "serverpath") {
1332 /* only attempt to set this if we have bothered to look
1333 up server names already. otherwise this is all
1334 redundant (actually, all of this dialog/widget
1335 is redundant in that case ...)
1338 if (!server_strings.empty()) {
1339 /* do not allow us to use a server path that doesn't
1340 exist on this system. this handles cases where
1341 the user has an RC file listing a serverpath
1342 from some other machine.
1344 vector<string>::iterator x;
1345 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1350 if (x != server_strings.end()) {
1351 serverpath_combo.set_active_text (strval);
1353 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1359 } else if (child->name() == "driver") {
1360 driver_combo.set_active_text(strval);
1361 } else if (child->name() == "interface") {
1362 interface_combo.set_active_text(strval);
1363 } else if (child->name() == "timeout") {
1364 timeout_combo.set_active_text(strval);
1365 } else if (child->name() == "dither") {
1366 dither_mode_combo.set_active_text(strval);
1367 } else if (child->name() == "audiomode") {
1368 audio_mode_combo.set_active_text(strval);
1369 } else if (child->name() == "inputdevice") {
1370 input_device_combo.set_active_text(strval);
1371 } else if (child->name() == "outputdevice") {
1372 output_device_combo.set_active_text(strval);
1373 } else if (child->name() == "mididriver") {
1374 midi_driver_combo.set_active_text(strval);