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_("FreeBoB"));
142 strings.push_back (X_("FFADO"));
144 strings.push_back (X_("NetJACK"));
145 strings.push_back (X_("Dummy"));
146 set_popdown_strings (driver_combo, strings);
147 driver_combo.set_active_text (strings.front());
149 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
153 strings.push_back (_("Playback/recording on 1 device"));
154 strings.push_back (_("Playback/recording on 2 devices"));
155 strings.push_back (_("Playback only"));
156 strings.push_back (_("Recording only"));
157 set_popdown_strings (audio_mode_combo, strings);
158 audio_mode_combo.set_active_text (strings.front());
160 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
161 audio_mode_changed ();
164 strings.push_back (_("None"));
165 strings.push_back (_("seq"));
166 strings.push_back (_("raw"));
167 set_popdown_strings (midi_driver_combo, strings);
168 midi_driver_combo.set_active_text (strings.front ());
172 label = manage (new Label (_("Driver:")));
173 label->set_alignment (0, 0.5);
174 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
175 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
178 label = manage (new Label (_("Interface:")));
179 label->set_alignment (0, 0.5);
180 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
181 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
184 label = manage (new Label (_("Sample rate:")));
185 label->set_alignment (0, 0.5);
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 (new Label (_("Buffer size:")));
191 label->set_alignment (0, 0.5);
192 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
193 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
197 label = manage (new Label (_("Number of buffers:")));
198 label->set_alignment (0, 0.5);
199 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
200 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
201 periods_spinner.set_value (2);
205 label = manage (new Label (_("Approximate latency:")));
206 label->set_alignment (0, 0.5);
207 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
208 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
211 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
212 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
213 period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
216 /* no audio mode with CoreAudio, its duplex or nuthin' */
219 label = manage (new Label (_("Audio mode:")));
220 label->set_alignment (0, 0.5);
221 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
222 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
226 interface_combo.set_size_request (250, -1);
227 input_device_combo.set_size_request (250, -1);
228 output_device_combo.set_size_request (250, -1);
232 if (engine_running()) {
233 start_button.set_sensitive (false);
235 stop_button.set_sensitive (false);
238 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
239 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
242 button_box.pack_start (start_button, false, false);
243 button_box.pack_start (stop_button, false, false);
245 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
249 options_packer.set_spacings (6);
252 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
255 realtime_button.set_active (true);
256 realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
259 #if PROVIDE_TOO_MANY_OPTIONS
262 label = manage (new Label (_("Realtime Priority")));
263 label->set_alignment (1.0, 0.5);
264 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
265 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
267 priority_spinner.set_value (60);
269 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
271 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
273 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
275 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
277 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
279 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
281 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
283 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
286 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
291 strings.push_back (_("Ignore"));
292 strings.push_back ("500 msec");
293 strings.push_back ("1 sec");
294 strings.push_back ("2 sec");
295 strings.push_back ("10 sec");
296 set_popdown_strings (timeout_combo, strings);
297 timeout_combo.set_active_text (strings.front ());
299 label = manage (new Label (_("Client timeout")));
300 label->set_alignment (1.0, 0.5);
301 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
302 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
305 #endif /* PROVIDE_TOO_MANY_OPTIONS */
306 label = manage (new Label (_("Number of ports:")));
307 label->set_alignment (0, 0.5);
308 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
309 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
312 label = manage (new Label (_("MIDI driver:")));
313 label->set_alignment (0, 0.5);
314 options_packer.attach (midi_driver_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 label = manage (new Label (_("Dither:")));
320 label->set_alignment (0, 0.5);
321 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
322 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
326 find_jack_servers (server_strings);
328 if (server_strings.empty()) {
329 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
333 set_popdown_strings (serverpath_combo, server_strings);
334 serverpath_combo.set_active_text (server_strings.front());
336 if (server_strings.size() > 1) {
337 label = manage (new Label (_("Server:")));
338 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
339 label->set_alignment (0.0, 0.5);
340 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
344 /* device settings */
346 device_packer.set_spacings (6);
350 label = manage (new Label (_("Input 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 (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
355 label = manage (new Label (_("Output device:")));
356 label->set_alignment (0, 0.5);
357 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
358 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
361 label = manage (new Label (_("Input 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 (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
366 label = manage (new Label (_("Output channels:")));
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 (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
371 label = manage (new Label (_("Hardware input latency:")));
372 label->set_alignment (0, 0.5);
373 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
374 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
375 label = manage (new Label (_("samples")));
376 label->set_alignment (0, 0.5);
377 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
379 label = manage (new Label (_("Hardware output latency:")));
380 label->set_alignment (0, 0.5);
381 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
382 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
383 label = manage (new Label (_("samples")));
384 label->set_alignment (0, 0.5);
385 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
388 basic_hbox.pack_start (basic_packer, false, false);
389 options_hbox.pack_start (options_packer, false, false);
391 device_packer.set_border_width (12);
392 options_packer.set_border_width (12);
393 basic_packer.set_border_width (12);
395 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
396 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
397 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
398 notebook.set_border_width (12);
400 set_border_width (12);
401 pack_start (notebook);
404 EngineControl::~EngineControl ()
410 EngineControl::build_command_line (vector<string>& cmd)
414 bool using_alsa = false;
415 bool using_coreaudio = false;
416 bool using_dummy = false;
418 /* first, path to jackd */
420 cmd.push_back (serverpath_combo.get_active_text ());
422 /* now jackd arguments */
424 str = timeout_combo.get_active_text ();
425 if (str != _("Ignore")) {
429 msecs = (uint32_t) floor (secs * 1000.0);
431 cmd.push_back ("-t");
432 cmd.push_back (to_string (msecs, std::dec));
436 if (no_memory_lock_button.get_active()) {
437 cmd.push_back ("-m"); /* no munlock */
440 cmd.push_back ("-p"); /* port max */
441 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
443 if (realtime_button.get_active()) {
444 cmd.push_back ("-R");
445 cmd.push_back ("-P");
446 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
448 cmd.push_back ("-r"); /* override jackd's default --realtime */
451 if (unlock_memory_button.get_active()) {
452 cmd.push_back ("-u");
455 if (verbose_output_button.get_active()) {
456 cmd.push_back ("-v");
459 /* now add fixed arguments (not user-selectable) */
461 cmd.push_back ("-T"); // temporary */
463 /* next the driver */
465 cmd.push_back ("-d");
467 driver = driver_combo.get_active_text ();
468 if (driver == X_("ALSA")) {
470 cmd.push_back ("alsa");
471 } else if (driver == X_("OSS")) {
472 cmd.push_back ("oss");
473 } else if (driver == X_("CoreAudio")) {
474 using_coreaudio = true;
475 cmd.push_back ("coreaudio");
476 } else if (driver == X_("NetJACK")) {
477 cmd.push_back ("netjack");
478 } else if (driver == X_("FreeBoB")) {
479 cmd.push_back ("freebob");
480 } else if (driver == X_("FFADO")) {
481 cmd.push_back ("firewire");
482 } else if ( driver == X_("Dummy")) {
484 cmd.push_back ("dummy");
487 /* driver arguments */
489 if (!using_coreaudio) {
490 str = audio_mode_combo.get_active_text();
492 if (str == _("Playback/Recording on 1 Device")) {
496 } else if (str == _("Playback/Recording on 2 Devices")) {
498 string input_device = get_device_name (driver, input_device_combo.get_active_text());
499 string output_device = get_device_name (driver, output_device_combo.get_active_text());
501 if (input_device.empty() || output_device.empty()) {
506 cmd.push_back ("-C");
507 cmd.push_back (input_device);
508 cmd.push_back ("-P");
509 cmd.push_back (output_device);
511 } else if (str == _("Playback only")) {
512 cmd.push_back ("-P");
513 } else if (str == _("Recording only")) {
514 cmd.push_back ("-C");
518 cmd.push_back ("-n");
519 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
523 cmd.push_back ("-r");
524 cmd.push_back (to_string (get_rate(), std::dec));
526 cmd.push_back ("-p");
527 cmd.push_back (period_size_combo.get_active_text());
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");
578 double val = input_latency_adjustment.get_value();
581 cmd.push_back ("-I");
582 cmd.push_back (to_string ((uint32_t) val, std::dec));
585 val = output_latency_adjustment.get_value();
587 cmd.push_back ("-O");
588 cmd.push_back (to_string ((uint32_t) val, std::dec));
591 } else if (using_coreaudio) {
594 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
596 string device = get_device_name (driver, interface_combo.get_active_text());
597 if (device.empty()) {
602 cmd.push_back ("-d");
603 cmd.push_back (device);
605 double val = input_latency_adjustment.get_value();
608 cmd.push_back ("-I");
609 cmd.push_back (to_string ((uint32_t) val, std::dec));
612 double val = output_latency_adjustment.get_value();
614 cmd.push_back ("-O");
615 cmd.push_back (to_string ((uint32_t) val, std::dec));
623 EngineControl::engine_running ()
625 jack_status_t status;
626 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
629 jack_client_close (c);
636 EngineControl::setup_engine ()
639 std::string cwd = "/tmp";
641 build_command_line (args);
644 return 1; // try again
647 std::string jackdrc_path = Glib::get_home_dir();
648 jackdrc_path += "/.jackdrc";
650 ofstream jackdrc (jackdrc_path.c_str());
652 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
655 cerr << "JACK COMMAND: ";
656 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
658 jackdrc << (*i) << ' ';
670 EngineControl::realtime_changed ()
673 priority_spinner.set_sensitive (realtime_button.get_active());
678 EngineControl::enumerate_devices (const string& driver)
680 /* note: case matters for the map keys */
682 if (driver == "CoreAudio") {
684 devices[driver] = enumerate_coreaudio_devices ();
688 } else if (driver == "ALSA") {
689 devices[driver] = enumerate_alsa_devices ();
690 } else if (driver == "FreeBOB") {
691 devices[driver] = enumerate_freebob_devices ();
692 } else if (driver == "FFADO") {
693 devices[driver] = enumerate_ffado_devices ();
694 } else if (driver == "OSS") {
695 devices[driver] = enumerate_oss_devices ();
696 } else if (driver == "Dummy") {
697 devices[driver] = enumerate_dummy_devices ();
698 } else if (driver == "NetJACK") {
699 devices[driver] = enumerate_netjack_devices ();
708 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
710 UInt32 size = sizeof(CFStringRef);
712 OSStatus res = AudioDeviceGetProperty(id, 0, false,
713 kAudioDevicePropertyDeviceUID, &size, &UI);
715 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
721 EngineControl::enumerate_coreaudio_devices ()
725 // Find out how many Core Audio devices are there, if any...
726 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
729 size_t outSize = sizeof(isWritable);
731 backend_devs.clear ();
733 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
734 &outSize, &isWritable);
736 // Calculate the number of device available...
737 int numCoreDevices = outSize / sizeof(AudioDeviceID);
738 // Make space for the devices we are about to get...
739 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
740 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
741 &outSize, (void *) coreDeviceIDs);
743 // Look for the CoreAudio device name...
744 char coreDeviceName[256];
747 for (int i = 0; i < numCoreDevices; i++) {
749 nameSize = sizeof (coreDeviceName);
751 /* enforce duplex devices only */
753 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
754 0, true, kAudioDevicePropertyStreams,
755 &outSize, &isWritable);
757 if (err != noErr || outSize == 0) {
761 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
762 0, false, kAudioDevicePropertyStreams,
763 &outSize, &isWritable);
765 if (err != noErr || outSize == 0) {
769 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
770 0, true, kAudioDevicePropertyDeviceName,
771 &outSize, &isWritable);
773 err = AudioDeviceGetProperty(coreDeviceIDs[i],
774 0, true, kAudioDevicePropertyDeviceName,
775 &nameSize, (void *) coreDeviceName);
777 char drivername[128];
779 // this returns the unique id for the device
780 // that must be used on the commandline for jack
782 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
783 devs.push_back (coreDeviceName);
784 backend_devs.push_back (drivername);
790 delete [] coreDeviceIDs;
794 if (devs.size() == 0) {
795 MessageDialog msg (_("\
796 You do not have any audio devices capable of\n\
797 simultaneous playback and recording.\n\n\
798 Please use Applications -> Utilities -> Audio MIDI Setup\n\
799 to create an \"aggregrate\" device, or install a suitable\n\
800 audio interface.\n\n\
801 Please send email to Apple and ask them why new Macs\n\
802 have no duplex audio device.\n\n\
803 Alternatively, if you really want just playback\n\
804 or recording but not both, start JACK before running\n\
805 Ardour and choose the relevant device then."
807 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
808 msg.set_title (_("No suitable audio devices"));
809 msg.set_position (Gtk::WIN_POS_MOUSE);
819 EngineControl::enumerate_alsa_devices ()
824 snd_ctl_card_info_t *info;
825 snd_pcm_info_t *pcminfo;
826 snd_ctl_card_info_alloca(&info);
827 snd_pcm_info_alloca(&pcminfo);
832 backend_devs.clear ();
834 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
837 devname += to_string (cardnum, std::dec);
839 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
841 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
843 snd_pcm_info_set_device (pcminfo, device);
844 snd_pcm_info_set_subdevice (pcminfo, 0);
845 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
847 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
848 devs.push_back (snd_pcm_info_get_name (pcminfo));
850 devname += to_string (device, std::dec);
851 backend_devs.push_back (devname);
855 snd_ctl_close(handle);
863 EngineControl::enumerate_ffado_devices ()
866 backend_devs.clear ();
871 EngineControl::enumerate_freebob_devices ()
878 EngineControl::enumerate_oss_devices ()
884 EngineControl::enumerate_dummy_devices ()
890 EngineControl::enumerate_netjack_devices ()
898 EngineControl::driver_changed ()
900 string driver = driver_combo.get_active_text();
901 string::size_type maxlen = 0;
905 enumerate_devices (driver);
907 vector<string>& strings = devices[driver];
909 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
910 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
914 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
915 if ((*i).length() > maxlen) {
916 maxlen = (*i).length();
921 set_popdown_strings (interface_combo, strings);
922 set_popdown_strings (input_device_combo, strings);
923 set_popdown_strings (output_device_combo, strings);
925 if (!strings.empty()) {
926 interface_combo.set_active_text (strings.front());
927 input_device_combo.set_active_text (strings.front());
928 output_device_combo.set_active_text (strings.front());
931 if (driver == "ALSA") {
932 soft_mode_button.set_sensitive (true);
933 force16bit_button.set_sensitive (true);
934 hw_monitor_button.set_sensitive (true);
935 hw_meter_button.set_sensitive (true);
936 monitor_button.set_sensitive (true);
938 soft_mode_button.set_sensitive (false);
939 force16bit_button.set_sensitive (false);
940 hw_monitor_button.set_sensitive (false);
941 hw_meter_button.set_sensitive (false);
942 monitor_button.set_sensitive (false);
947 EngineControl::get_rate ()
949 return atoi (sample_rate_combo.get_active_text ());
953 EngineControl::redisplay_latency ()
955 uint32_t rate = get_rate();
959 float periods = periods_adjustment.get_value();
961 float period_size = atof (period_size_combo.get_active_text());
964 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
966 latency_label.set_text (buf);
967 latency_label.set_alignment (0, 0.5);
971 EngineControl::audio_mode_changed ()
973 std::string str = audio_mode_combo.get_active_text();
975 if (str == _("Playback/Recording on 1 Device")) {
976 input_device_combo.set_sensitive (false);
977 output_device_combo.set_sensitive (false);
978 } else if (str == _("Playback/Recording on 2 Devices")) {
979 input_device_combo.set_sensitive (true);
980 output_device_combo.set_sensitive (true);
981 } else if (str == _("Playback only")) {
982 output_device_combo.set_sensitive (true);
983 } else if (str == _("Recording only")) {
984 input_device_combo.set_sensitive (true);
988 static bool jack_server_filter(const string& str, void */*arg*/)
990 return str == "jackd" || str == "jackdmp";
994 EngineControl::find_jack_servers (vector<string>& strings)
997 /* this magic lets us finds the path to the OSX bundle, and then
998 we infer JACK's location from there
1001 char execpath[MAXPATHLEN+1];
1002 uint32_t pathsz = sizeof (execpath);
1004 _NSGetExecutablePath (execpath, &pathsz);
1006 string path (Glib::path_get_dirname (execpath));
1009 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
1010 strings.push_back (path);
1013 if (getenv ("ARDOUR_WITH_JACK")) {
1014 /* no other options - only use the JACK we supply */
1015 if (strings.empty()) {
1016 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1025 PathScanner scanner;
1026 vector<string *> *jack_servers;
1027 std::map<string,int> un;
1029 bool need_minimal_path = false;
1031 p = getenv ("PATH");
1036 need_minimal_path = true;
1040 // many mac users don't have PATH set up to include
1041 // likely installed locations of JACK
1042 need_minimal_path = true;
1045 if (need_minimal_path) {
1047 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1049 path += ":/usr/local/bin:/opt/local/bin";
1054 // push it back into the environment so that auto-started JACK can find it.
1055 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1056 setenv ("PATH", path.c_str(), 1);
1059 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1061 vector<string *>::iterator iter;
1063 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1067 strings.push_back(p);
1074 EngineControl::get_device_name (const string& driver, const string& human_readable)
1076 vector<string>::iterator n;
1077 vector<string>::iterator i;
1079 if (human_readable.empty()) {
1080 /* this can happen if the user's .ardourrc file has a device name from
1081 another computer system in it
1083 MessageDialog msg (_("You need to choose an audio device first."));
1088 if (backend_devs.empty()) {
1089 return human_readable;
1092 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1093 if (human_readable == (*i)) {
1098 if (i == devices[driver].end()) {
1099 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1106 EngineControl::get_state ()
1108 XMLNode* root = new XMLNode ("AudioSetup");
1112 child = new XMLNode ("periods");
1113 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1114 root->add_child_nocopy (*child);
1116 child = new XMLNode ("priority");
1117 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1118 root->add_child_nocopy (*child);
1120 child = new XMLNode ("ports");
1121 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1122 root->add_child_nocopy (*child);
1124 child = new XMLNode ("inchannels");
1125 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1126 root->add_child_nocopy (*child);
1128 child = new XMLNode ("outchannels");
1129 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1130 root->add_child_nocopy (*child);
1132 child = new XMLNode ("inlatency");
1133 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1134 root->add_child_nocopy (*child);
1136 child = new XMLNode ("outlatency");
1137 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1138 root->add_child_nocopy (*child);
1140 child = new XMLNode ("realtime");
1141 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1142 root->add_child_nocopy (*child);
1144 child = new XMLNode ("nomemorylock");
1145 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1146 root->add_child_nocopy (*child);
1148 child = new XMLNode ("unlockmemory");
1149 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1150 root->add_child_nocopy (*child);
1152 child = new XMLNode ("softmode");
1153 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1154 root->add_child_nocopy (*child);
1156 child = new XMLNode ("force16bit");
1157 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1158 root->add_child_nocopy (*child);
1160 child = new XMLNode ("hwmonitor");
1161 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1162 root->add_child_nocopy (*child);
1164 child = new XMLNode ("hwmeter");
1165 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1166 root->add_child_nocopy (*child);
1168 child = new XMLNode ("verbose");
1169 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1170 root->add_child_nocopy (*child);
1172 child = new XMLNode ("samplerate");
1173 child->add_property ("val", sample_rate_combo.get_active_text());
1174 root->add_child_nocopy (*child);
1176 child = new XMLNode ("periodsize");
1177 child->add_property ("val", period_size_combo.get_active_text());
1178 root->add_child_nocopy (*child);
1180 child = new XMLNode ("serverpath");
1181 child->add_property ("val", serverpath_combo.get_active_text());
1182 root->add_child_nocopy (*child);
1184 child = new XMLNode ("driver");
1185 child->add_property ("val", driver_combo.get_active_text());
1186 root->add_child_nocopy (*child);
1188 child = new XMLNode ("interface");
1189 child->add_property ("val", interface_combo.get_active_text());
1190 root->add_child_nocopy (*child);
1192 child = new XMLNode ("timeout");
1193 child->add_property ("val", timeout_combo.get_active_text());
1194 root->add_child_nocopy (*child);
1196 child = new XMLNode ("dither");
1197 child->add_property ("val", dither_mode_combo.get_active_text());
1198 root->add_child_nocopy (*child);
1200 child = new XMLNode ("audiomode");
1201 child->add_property ("val", audio_mode_combo.get_active_text());
1202 root->add_child_nocopy (*child);
1204 child = new XMLNode ("inputdevice");
1205 child->add_property ("val", input_device_combo.get_active_text());
1206 root->add_child_nocopy (*child);
1208 child = new XMLNode ("outputdevice");
1209 child->add_property ("val", output_device_combo.get_active_text());
1210 root->add_child_nocopy (*child);
1212 child = new XMLNode ("mididriver");
1213 child->add_property ("val", midi_driver_combo.get_active_text());
1214 root->add_child_nocopy (*child);
1220 EngineControl::set_state (const XMLNode& root)
1223 XMLNodeConstIterator citer;
1225 XMLProperty* prop = NULL;
1226 bool using_dummy = false;
1231 if ( (child = root.child ("driver"))){
1232 prop = child->property("val");
1233 if (prop && (prop->value() == "Dummy") ) {
1238 clist = root.children();
1240 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1244 prop = child->property ("val");
1246 if (!prop || prop->value().empty()) {
1248 if ((using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" )) ||
1249 child->name() == "timeout")
1251 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1255 strval = prop->value();
1257 /* adjustments/spinners */
1259 if (child->name() == "periods") {
1260 val = atoi (strval);
1261 periods_adjustment.set_value(val);
1262 } else if (child->name() == "priority") {
1263 val = atoi (strval);
1264 priority_adjustment.set_value(val);
1265 } else if (child->name() == "ports") {
1266 val = atoi (strval);
1267 ports_adjustment.set_value(val);
1268 } else if (child->name() == "inchannels") {
1269 val = atoi (strval);
1270 input_channels.set_value(val);
1271 } else if (child->name() == "outchannels") {
1272 val = atoi (strval);
1273 output_channels.set_value(val);
1274 } else if (child->name() == "inlatency") {
1275 val = atoi (strval);
1276 input_latency.set_value(val);
1277 } else if (child->name() == "outlatency") {
1278 val = atoi (strval);
1279 output_latency.set_value(val);
1284 else if (child->name() == "realtime") {
1285 val = atoi (strval);
1286 realtime_button.set_active(val);
1287 } else if (child->name() == "nomemorylock") {
1288 val = atoi (strval);
1289 no_memory_lock_button.set_active(val);
1290 } else if (child->name() == "unlockmemory") {
1291 val = atoi (strval);
1292 unlock_memory_button.set_active(val);
1293 } else if (child->name() == "softmode") {
1294 val = atoi (strval);
1295 soft_mode_button.set_active(val);
1296 } else if (child->name() == "force16bit") {
1297 val = atoi (strval);
1298 force16bit_button.set_active(val);
1299 } else if (child->name() == "hwmonitor") {
1300 val = atoi (strval);
1301 hw_monitor_button.set_active(val);
1302 } else if (child->name() == "hwmeter") {
1303 val = atoi (strval);
1304 hw_meter_button.set_active(val);
1305 } else if (child->name() == "verbose") {
1306 val = atoi (strval);
1307 verbose_output_button.set_active(val);
1312 else if (child->name() == "samplerate") {
1313 sample_rate_combo.set_active_text(strval);
1314 } else if (child->name() == "periodsize") {
1315 period_size_combo.set_active_text(strval);
1316 } else if (child->name() == "serverpath") {
1318 /* only attempt to set this if we have bothered to look
1319 up server names already. otherwise this is all
1320 redundant (actually, all of this dialog/widget
1321 is redundant in that case ...)
1324 if (!server_strings.empty()) {
1325 /* do not allow us to use a server path that doesn't
1326 exist on this system. this handles cases where
1327 the user has an RC file listing a serverpath
1328 from some other machine.
1330 vector<string>::iterator x;
1331 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1336 if (x != server_strings.end()) {
1337 serverpath_combo.set_active_text (strval);
1339 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1345 } else if (child->name() == "driver") {
1346 driver_combo.set_active_text(strval);
1347 } else if (child->name() == "interface") {
1348 interface_combo.set_active_text(strval);
1349 } else if (child->name() == "timeout") {
1350 timeout_combo.set_active_text(strval);
1351 } else if (child->name() == "dither") {
1352 dither_mode_combo.set_active_text(strval);
1353 } else if (child->name() == "audiomode") {
1354 audio_mode_combo.set_active_text(strval);
1355 } else if (child->name() == "inputdevice") {
1356 input_device_combo.set_active_text(strval);
1357 } else if (child->name() == "outputdevice") {
1358 output_device_combo.set_active_text(strval);
1359 } else if (child->name() == "mididriver") {
1360 midi_driver_combo.set_active_text(strval);