2 Copyright (C) 2010 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <boost/scoped_ptr.hpp>
28 #include <gtkmm/messagedialog.h>
31 #include "pbd/xml++.h"
34 #include <CoreAudio/CoreAudio.h>
35 #include <CoreFoundation/CFString.h>
36 #include <sys/param.h>
37 #include <mach-o/dyld.h>
39 #include <alsa/asoundlib.h>
42 #include <jack/jack.h>
44 #include <gtkmm/stock.h>
45 #include <gtkmm2ext/utils.h>
47 #include "ardour/rc_configuration.h"
49 #include "pbd/convert.h"
50 #include "pbd/error.h"
51 #include "pbd/pathscanner.h"
57 #include "engine_dialog.h"
62 using namespace Gtkmm2ext;
66 EngineControl::EngineControl ()
67 : periods_adjustment (2, 2, 16, 1, 2),
68 periods_spinner (periods_adjustment),
69 ports_adjustment (128, 8, 1024, 1, 16),
70 ports_spinner (ports_adjustment),
71 input_latency_adjustment (0, 0, 99999, 1),
72 input_latency (input_latency_adjustment),
73 output_latency_adjustment (0, 0, 99999, 1),
74 output_latency (output_latency_adjustment),
75 realtime_button (_("Realtime")),
76 no_memory_lock_button (_("Do not lock memory")),
77 unlock_memory_button (_("Unlock memory")),
78 soft_mode_button (_("No zombies")),
79 monitor_button (_("Provide monitor ports")),
80 force16bit_button (_("Force 16 bit")),
81 hw_monitor_button (_("H/W monitoring")),
82 hw_meter_button (_("H/W metering")),
83 verbose_output_button (_("Verbose output")),
84 start_button (_("Start")),
85 stop_button (_("Stop")),
88 options_packer (4, 2),
92 options_packer (14, 2),
96 using namespace Notebook_Helpers;
98 vector<string> strings;
103 strings.push_back (_("8000Hz"));
104 strings.push_back (_("22050Hz"));
105 strings.push_back (_("44100Hz"));
106 strings.push_back (_("48000Hz"));
107 strings.push_back (_("88200Hz"));
108 strings.push_back (_("96000Hz"));
109 strings.push_back (_("192000Hz"));
110 set_popdown_strings (sample_rate_combo, strings);
111 sample_rate_combo.set_active_text ("48000Hz");
114 strings.push_back ("32");
115 strings.push_back ("64");
116 strings.push_back ("128");
117 strings.push_back ("256");
118 strings.push_back ("512");
119 strings.push_back ("1024");
120 strings.push_back ("2048");
121 strings.push_back ("4096");
122 strings.push_back ("8192");
123 set_popdown_strings (period_size_combo, strings);
124 period_size_combo.set_active_text ("1024");
127 strings.push_back (_("None"));
128 strings.push_back (_("Triangular"));
129 strings.push_back (_("Rectangular"));
130 strings.push_back (_("Shaped"));
131 set_popdown_strings (dither_mode_combo, strings);
132 dither_mode_combo.set_active_text (_("None"));
134 /* basic parameters */
136 basic_packer.set_spacings (6);
140 strings.push_back (X_("CoreAudio"));
142 strings.push_back (X_("ALSA"));
143 strings.push_back (X_("OSS"));
144 strings.push_back (X_("FreeBoB"));
145 strings.push_back (X_("FFADO"));
147 strings.push_back (X_("NetJACK"));
148 strings.push_back (X_("Dummy"));
149 set_popdown_strings (driver_combo, strings);
150 driver_combo.set_active_text (strings.front());
152 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
156 strings.push_back (_("Playback/recording on 1 device"));
157 strings.push_back (_("Playback/recording on 2 devices"));
158 strings.push_back (_("Playback only"));
159 strings.push_back (_("Recording only"));
160 set_popdown_strings (audio_mode_combo, strings);
161 audio_mode_combo.set_active_text (strings.front());
163 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
164 audio_mode_changed ();
167 strings.push_back (_("None"));
168 strings.push_back (_("seq"));
169 strings.push_back (_("raw"));
170 set_popdown_strings (midi_driver_combo, strings);
171 midi_driver_combo.set_active_text (strings.front ());
175 label = manage (left_aligned_label (_("Driver:")));
176 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
177 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
180 label = manage (left_aligned_label (_("Audio Interface:")));
181 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
182 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
185 label = manage (left_aligned_label (_("Sample rate:")));
186 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
187 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
190 label = manage (left_aligned_label (_("Buffer size:")));
191 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
192 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
196 label = manage (left_aligned_label (_("Number of buffers:")));
197 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
198 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
199 periods_spinner.set_value (2);
203 label = manage (left_aligned_label (_("Approximate latency:")));
204 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
205 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
208 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
209 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
210 period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
213 /* no audio mode with CoreAudio, its duplex or nuthin' */
216 label = manage (left_aligned_label (_("Audio mode:")));
217 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
218 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
222 interface_combo.set_size_request (250, -1);
223 input_device_combo.set_size_request (250, -1);
224 output_device_combo.set_size_request (250, -1);
228 if (engine_running()) {
229 start_button.set_sensitive (false);
231 stop_button.set_sensitive (false);
234 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
235 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
238 button_box.pack_start (start_button, false, false);
239 button_box.pack_start (stop_button, false, false);
241 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
245 options_packer.set_spacings (6);
248 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
251 realtime_button.set_active (true);
253 #if PROVIDE_TOO_MANY_OPTIONS
256 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
258 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
260 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
262 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
264 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
268 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
270 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
273 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
278 strings.push_back (_("Ignore"));
279 strings.push_back ("500 msec");
280 strings.push_back ("1 sec");
281 strings.push_back ("2 sec");
282 strings.push_back ("10 sec");
283 set_popdown_strings (timeout_combo, strings);
284 timeout_combo.set_active_text (strings.front ());
286 label = manage (new Label (_("Client timeout")));
287 label->set_alignment (1.0, 0.5);
288 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
289 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
292 #endif /* PROVIDE_TOO_MANY_OPTIONS */
293 label = manage (left_aligned_label (_("Number of ports:")));
294 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
295 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
298 label = manage (left_aligned_label (_("MIDI driver:")));
299 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
300 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
304 label = manage (left_aligned_label (_("Dither:")));
305 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
306 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
310 find_jack_servers (server_strings);
312 if (server_strings.empty()) {
313 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
317 set_popdown_strings (serverpath_combo, server_strings);
318 serverpath_combo.set_active_text (server_strings.front());
320 if (server_strings.size() > 1) {
321 label = manage (left_aligned_label (_("Server:")));
322 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
323 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
327 /* device settings */
329 device_packer.set_spacings (6);
333 label = manage (left_aligned_label (_("Input device:")));
334 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
335 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
337 label = manage (left_aligned_label (_("Output device:")));
338 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
339 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
342 label = manage (left_aligned_label (_("Hardware input latency:")));
343 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
344 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
345 label = manage (left_aligned_label (_("samples")));
346 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
348 label = manage (left_aligned_label (_("Hardware output latency:")));
349 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
350 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
351 label = manage (left_aligned_label (_("samples")));
352 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
355 basic_hbox.pack_start (basic_packer, false, false);
356 options_hbox.pack_start (options_packer, false, false);
358 device_packer.set_border_width (12);
359 options_packer.set_border_width (12);
360 basic_packer.set_border_width (12);
362 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
363 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
364 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
365 notebook.set_border_width (12);
367 set_border_width (12);
368 pack_start (notebook);
370 /* Pick up any existing audio setup configuration, if appropriate */
372 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioSetup");
375 set_state (*audio_setup);
379 EngineControl::~EngineControl ()
385 EngineControl::build_command_line (vector<string>& cmd)
389 bool using_alsa = false;
390 bool using_coreaudio = false;
391 bool using_dummy = false;
392 bool using_ffado = false;
394 /* first, path to jackd */
396 cmd.push_back (serverpath_combo.get_active_text ());
398 /* now jackd arguments */
400 str = timeout_combo.get_active_text ();
402 if (str != _("Ignore")) {
407 msecs = (uint32_t) floor (secs * 1000.0);
410 cmd.push_back ("-t");
411 cmd.push_back (to_string (msecs, std::dec));
415 if (no_memory_lock_button.get_active()) {
416 cmd.push_back ("-m"); /* no munlock */
419 cmd.push_back ("-p"); /* port max */
420 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
422 if (realtime_button.get_active()) {
423 cmd.push_back ("-R");
425 cmd.push_back ("-r"); /* override jackd's default --realtime */
428 if (unlock_memory_button.get_active()) {
429 cmd.push_back ("-u");
432 if (verbose_output_button.get_active()) {
433 cmd.push_back ("-v");
436 /* now add fixed arguments (not user-selectable) */
438 cmd.push_back ("-T"); // temporary */
440 /* next the driver */
442 cmd.push_back ("-d");
444 driver = driver_combo.get_active_text ();
446 if (driver == X_("ALSA")) {
448 cmd.push_back ("alsa");
449 } else if (driver == X_("OSS")) {
450 cmd.push_back ("oss");
451 } else if (driver == X_("CoreAudio")) {
452 using_coreaudio = true;
453 cmd.push_back ("coreaudio");
454 } else if (driver == X_("NetJACK")) {
455 cmd.push_back ("netjack");
456 } else if (driver == X_("FreeBoB")) {
457 cmd.push_back ("freebob");
458 } else if (driver == X_("FFADO")) {
460 cmd.push_back ("firewire");
461 } else if ( driver == X_("Dummy")) {
463 cmd.push_back ("dummy");
466 /* driver arguments */
468 if (!using_coreaudio) {
469 str = audio_mode_combo.get_active_text();
471 if (str == _("Playback/Recording on 1 Device")) {
475 } else if (str == _("Playback/Recording on 2 Devices")) {
477 string input_device = get_device_name (driver, input_device_combo.get_active_text());
478 string output_device = get_device_name (driver, output_device_combo.get_active_text());
480 if (input_device.empty() || output_device.empty()) {
485 cmd.push_back ("-C");
486 cmd.push_back (input_device);
488 cmd.push_back ("-P");
489 cmd.push_back (output_device);
491 } else if (str == _("Playback only")) {
492 cmd.push_back ("-P");
493 } else if (str == _("Recording only")) {
494 cmd.push_back ("-C");
498 cmd.push_back ("-n");
499 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
503 cmd.push_back ("-r");
504 cmd.push_back (to_string (get_rate(), std::dec));
506 cmd.push_back ("-p");
507 cmd.push_back (period_size_combo.get_active_text());
509 if (using_alsa || using_ffado || using_coreaudio) {
511 double val = input_latency_adjustment.get_value();
514 cmd.push_back ("-I");
515 cmd.push_back (to_string ((uint32_t) val, std::dec));
518 val = output_latency_adjustment.get_value();
521 cmd.push_back ("-O");
522 cmd.push_back (to_string ((uint32_t) val, std::dec));
528 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
530 string device = get_device_name (driver, interface_combo.get_active_text());
531 if (device.empty()) {
536 cmd.push_back ("-d");
537 cmd.push_back (device);
540 if (hw_meter_button.get_active()) {
541 cmd.push_back ("-M");
544 if (hw_monitor_button.get_active()) {
545 cmd.push_back ("-H");
548 str = dither_mode_combo.get_active_text();
550 if (str == _("None")) {
551 } else if (str == _("Triangular")) {
552 cmd.push_back ("-z triangular");
553 } else if (str == _("Rectangular")) {
554 cmd.push_back ("-z rectangular");
555 } else if (str == _("Shaped")) {
556 cmd.push_back ("-z shaped");
559 if (force16bit_button.get_active()) {
560 cmd.push_back ("-S");
563 if (soft_mode_button.get_active()) {
564 cmd.push_back ("-s");
567 str = midi_driver_combo.get_active_text ();
569 if (str == _("seq")) {
570 cmd.push_back ("-X seq");
571 } else if (str == _("raw")) {
572 cmd.push_back ("-X raw");
574 } else if (using_coreaudio) {
577 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
579 string device = get_device_name (driver, interface_combo.get_active_text());
580 if (device.empty()) {
585 cmd.push_back ("-d");
586 cmd.push_back (device);
593 EngineControl::need_setup ()
595 return !engine_running();
599 EngineControl::engine_running ()
601 EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
602 boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
604 /* revert all environment settings back to whatever they were when
605 * ardour started, because ardour's startup script may have reset
606 * something in ways that interfere with finding/starting JACK.
610 current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
611 global_epa->restore ();
614 jack_status_t status;
615 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
618 jack_client_close (c);
625 EngineControl::setup_engine ()
628 std::string cwd = "/tmp";
630 build_command_line (args);
633 return 1; // try again
636 std::string jackdrc_path = Glib::get_home_dir();
637 jackdrc_path += "/.jackdrc";
639 ofstream jackdrc (jackdrc_path.c_str());
641 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
645 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
646 jackdrc << (*i) << ' ';
658 EngineControl::enumerate_devices (const string& driver)
660 /* note: case matters for the map keys */
662 if (driver == "CoreAudio") {
664 devices[driver] = enumerate_coreaudio_devices ();
668 } else if (driver == "ALSA") {
669 devices[driver] = enumerate_alsa_devices ();
670 } else if (driver == "FreeBOB") {
671 devices[driver] = enumerate_freebob_devices ();
672 } else if (driver == "FFADO") {
673 devices[driver] = enumerate_ffado_devices ();
674 } else if (driver == "OSS") {
675 devices[driver] = enumerate_oss_devices ();
676 } else if (driver == "Dummy") {
677 devices[driver] = enumerate_dummy_devices ();
678 } else if (driver == "NetJACK") {
679 devices[driver] = enumerate_netjack_devices ();
688 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
690 UInt32 size = sizeof(CFStringRef);
692 OSStatus res = AudioDeviceGetProperty(id, 0, false,
693 kAudioDevicePropertyDeviceUID, &size, &UI);
695 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
701 EngineControl::enumerate_coreaudio_devices ()
705 // Find out how many Core Audio devices are there, if any...
706 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
709 UInt32 outSize = sizeof(isWritable);
711 backend_devs.clear ();
713 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
714 &outSize, &isWritable);
716 // Calculate the number of device available...
717 int numCoreDevices = outSize / sizeof(AudioDeviceID);
718 // Make space for the devices we are about to get...
719 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
720 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
721 &outSize, (void *) coreDeviceIDs);
723 // Look for the CoreAudio device name...
724 char coreDeviceName[256];
727 for (int i = 0; i < numCoreDevices; i++) {
729 nameSize = sizeof (coreDeviceName);
731 /* enforce duplex devices only */
733 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
734 0, true, kAudioDevicePropertyStreams,
735 &outSize, &isWritable);
737 if (err != noErr || outSize == 0) {
741 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
742 0, false, kAudioDevicePropertyStreams,
743 &outSize, &isWritable);
745 if (err != noErr || outSize == 0) {
749 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
750 0, true, kAudioDevicePropertyDeviceName,
751 &outSize, &isWritable);
753 err = AudioDeviceGetProperty(coreDeviceIDs[i],
754 0, true, kAudioDevicePropertyDeviceName,
755 &nameSize, (void *) coreDeviceName);
757 char drivername[128];
759 // this returns the unique id for the device
760 // that must be used on the commandline for jack
762 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
763 devs.push_back (coreDeviceName);
764 backend_devs.push_back (drivername);
770 delete [] coreDeviceIDs;
774 if (devs.size() == 0) {
775 MessageDialog msg (_("\
776 You do not have any audio devices capable of\n\
777 simultaneous playback and recording.\n\n\
778 Please use Applications -> Utilities -> Audio MIDI Setup\n\
779 to create an \"aggregrate\" device, or install a suitable\n\
780 audio interface.\n\n\
781 Please send email to Apple and ask them why new Macs\n\
782 have no duplex audio device.\n\n\
783 Alternatively, if you really want just playback\n\
784 or recording but not both, start JACK before running\n\
785 Ardour and choose the relevant device then."
787 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
788 msg.set_title (_("No suitable audio devices"));
789 msg.set_position (Gtk::WIN_POS_MOUSE);
799 EngineControl::enumerate_alsa_devices ()
804 snd_ctl_card_info_t *info;
805 snd_pcm_info_t *pcminfo;
806 snd_ctl_card_info_alloca(&info);
807 snd_pcm_info_alloca(&pcminfo);
812 backend_devs.clear ();
814 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
817 devname += to_string (cardnum, std::dec);
819 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
821 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
823 snd_pcm_info_set_device (pcminfo, device);
824 snd_pcm_info_set_subdevice (pcminfo, 0);
825 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
827 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
828 devs.push_back (snd_pcm_info_get_name (pcminfo));
830 devname += to_string (device, std::dec);
831 backend_devs.push_back (devname);
835 snd_ctl_close(handle);
843 EngineControl::enumerate_ffado_devices ()
846 backend_devs.clear ();
851 EngineControl::enumerate_freebob_devices ()
858 EngineControl::enumerate_oss_devices ()
864 EngineControl::enumerate_dummy_devices ()
870 EngineControl::enumerate_netjack_devices ()
878 EngineControl::driver_changed ()
880 string driver = driver_combo.get_active_text();
881 string::size_type maxlen = 0;
884 enumerate_devices (driver);
886 vector<string>& strings = devices[driver];
888 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
892 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
893 if ((*i).length() > maxlen) {
894 maxlen = (*i).length();
898 set_popdown_strings (interface_combo, strings);
899 set_popdown_strings (input_device_combo, strings);
900 set_popdown_strings (output_device_combo, strings);
902 if (!strings.empty()) {
903 interface_combo.set_active_text (strings.front());
904 input_device_combo.set_active_text (strings.front());
905 output_device_combo.set_active_text (strings.front());
908 if (driver == "ALSA") {
909 soft_mode_button.set_sensitive (true);
910 force16bit_button.set_sensitive (true);
911 hw_monitor_button.set_sensitive (true);
912 hw_meter_button.set_sensitive (true);
913 monitor_button.set_sensitive (true);
915 soft_mode_button.set_sensitive (false);
916 force16bit_button.set_sensitive (false);
917 hw_monitor_button.set_sensitive (false);
918 hw_meter_button.set_sensitive (false);
919 monitor_button.set_sensitive (false);
924 EngineControl::get_rate ()
926 double r = atof (sample_rate_combo.get_active_text ());
927 /* the string may have been translated with an abbreviation for
928 * thousands, so use a crude heuristic to fix this.
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 input_device_combo.set_sensitive (false);
968 } else if (str == _("Recording only")) {
969 input_device_combo.set_sensitive (true);
970 output_device_combo.set_sensitive (false);
974 static bool jack_server_filter(const string& str, void */*arg*/)
976 return str == "jackd" || str == "jackdmp";
980 EngineControl::find_jack_servers (vector<string>& strings)
983 /* this magic lets us finds the path to the OSX bundle, and then
984 we infer JACK's location from there
987 char execpath[MAXPATHLEN+1];
988 uint32_t pathsz = sizeof (execpath);
990 _NSGetExecutablePath (execpath, &pathsz);
992 string path (Glib::path_get_dirname (execpath));
995 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
996 strings.push_back (path);
999 if (getenv ("ARDOUR_WITH_JACK")) {
1000 /* no other options - only use the JACK we supply */
1001 if (strings.empty()) {
1002 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1011 PathScanner scanner;
1012 vector<string *> *jack_servers;
1013 std::map<string,int> un;
1015 bool need_minimal_path = false;
1017 p = getenv ("PATH");
1022 need_minimal_path = true;
1026 // many mac users don't have PATH set up to include
1027 // likely installed locations of JACK
1028 need_minimal_path = true;
1031 if (need_minimal_path) {
1033 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1035 path += ":/usr/local/bin:/opt/local/bin";
1040 // push it back into the environment so that auto-started JACK can find it.
1041 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1042 setenv ("PATH", path.c_str(), 1);
1045 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1046 if (!jack_servers) {
1050 vector<string *>::iterator iter;
1052 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1056 strings.push_back(p);
1063 EngineControl::get_device_name (const string& driver, const string& human_readable)
1065 vector<string>::iterator n;
1066 vector<string>::iterator i;
1068 if (human_readable.empty()) {
1069 /* this can happen if the user's .ardourrc file has a device name from
1070 another computer system in it
1072 MessageDialog msg (_("You need to choose an audio device first."));
1073 msg.set_position (WIN_POS_MOUSE);
1078 if (backend_devs.empty()) {
1079 return human_readable;
1082 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1083 if (human_readable == (*i)) {
1088 if (i == devices[driver].end()) {
1089 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1096 EngineControl::get_state ()
1098 XMLNode* root = new XMLNode ("AudioSetup");
1102 child = new XMLNode ("periods");
1103 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1104 root->add_child_nocopy (*child);
1106 child = new XMLNode ("ports");
1107 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1108 root->add_child_nocopy (*child);
1110 child = new XMLNode ("inlatency");
1111 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1112 root->add_child_nocopy (*child);
1114 child = new XMLNode ("outlatency");
1115 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1116 root->add_child_nocopy (*child);
1118 child = new XMLNode ("realtime");
1119 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1120 root->add_child_nocopy (*child);
1122 child = new XMLNode ("nomemorylock");
1123 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1124 root->add_child_nocopy (*child);
1126 child = new XMLNode ("unlockmemory");
1127 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1128 root->add_child_nocopy (*child);
1130 child = new XMLNode ("softmode");
1131 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1132 root->add_child_nocopy (*child);
1134 child = new XMLNode ("force16bit");
1135 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1136 root->add_child_nocopy (*child);
1138 child = new XMLNode ("hwmonitor");
1139 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1140 root->add_child_nocopy (*child);
1142 child = new XMLNode ("hwmeter");
1143 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1144 root->add_child_nocopy (*child);
1146 child = new XMLNode ("verbose");
1147 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1148 root->add_child_nocopy (*child);
1150 child = new XMLNode ("samplerate");
1151 child->add_property ("val", sample_rate_combo.get_active_text());
1152 root->add_child_nocopy (*child);
1154 child = new XMLNode ("periodsize");
1155 child->add_property ("val", period_size_combo.get_active_text());
1156 root->add_child_nocopy (*child);
1158 child = new XMLNode ("serverpath");
1159 child->add_property ("val", serverpath_combo.get_active_text());
1160 root->add_child_nocopy (*child);
1162 child = new XMLNode ("driver");
1163 child->add_property ("val", driver_combo.get_active_text());
1164 root->add_child_nocopy (*child);
1166 child = new XMLNode ("interface");
1167 child->add_property ("val", interface_combo.get_active_text());
1168 root->add_child_nocopy (*child);
1170 child = new XMLNode ("timeout");
1171 child->add_property ("val", timeout_combo.get_active_text());
1172 root->add_child_nocopy (*child);
1174 child = new XMLNode ("dither");
1175 child->add_property ("val", dither_mode_combo.get_active_text());
1176 root->add_child_nocopy (*child);
1178 child = new XMLNode ("audiomode");
1179 child->add_property ("val", audio_mode_combo.get_active_text());
1180 root->add_child_nocopy (*child);
1182 child = new XMLNode ("inputdevice");
1183 child->add_property ("val", input_device_combo.get_active_text());
1184 root->add_child_nocopy (*child);
1186 child = new XMLNode ("outputdevice");
1187 child->add_property ("val", output_device_combo.get_active_text());
1188 root->add_child_nocopy (*child);
1190 child = new XMLNode ("mididriver");
1191 child->add_property ("val", midi_driver_combo.get_active_text());
1192 root->add_child_nocopy (*child);
1198 EngineControl::set_state (const XMLNode& root)
1201 XMLNodeConstIterator citer;
1203 XMLProperty* prop = NULL;
1204 bool using_dummy = false;
1205 bool using_ffado = false;
1210 if ( (child = root.child ("driver"))){
1211 prop = child->property("val");
1213 if (prop && (prop->value() == "Dummy") ) {
1216 if (prop && (prop->value() == "FFADO") ) {
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 || using_ffado)
1233 && ( child->name() == "interface"
1234 || child->name() == "inputdevice"
1235 || child->name() == "outputdevice"))
1236 || child->name() == "timeout")
1241 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1245 strval = prop->value();
1247 /* adjustments/spinners */
1249 if (child->name() == "periods") {
1250 val = atoi (strval);
1251 periods_adjustment.set_value(val);
1252 } else if (child->name() == "ports") {
1253 val = atoi (strval);
1254 ports_adjustment.set_value(val);
1255 } else if (child->name() == "inlatency") {
1256 val = atoi (strval);
1257 input_latency.set_value(val);
1258 } else if (child->name() == "outlatency") {
1259 val = atoi (strval);
1260 output_latency.set_value(val);
1265 else if (child->name() == "realtime") {
1266 val = atoi (strval);
1267 realtime_button.set_active(val);
1268 } else if (child->name() == "nomemorylock") {
1269 val = atoi (strval);
1270 no_memory_lock_button.set_active(val);
1271 } else if (child->name() == "unlockmemory") {
1272 val = atoi (strval);
1273 unlock_memory_button.set_active(val);
1274 } else if (child->name() == "softmode") {
1275 val = atoi (strval);
1276 soft_mode_button.set_active(val);
1277 } else if (child->name() == "force16bit") {
1278 val = atoi (strval);
1279 force16bit_button.set_active(val);
1280 } else if (child->name() == "hwmonitor") {
1281 val = atoi (strval);
1282 hw_monitor_button.set_active(val);
1283 } else if (child->name() == "hwmeter") {
1284 val = atoi (strval);
1285 hw_meter_button.set_active(val);
1286 } else if (child->name() == "verbose") {
1287 val = atoi (strval);
1288 verbose_output_button.set_active(val);
1293 else if (child->name() == "samplerate") {
1294 sample_rate_combo.set_active_text(strval);
1295 } else if (child->name() == "periodsize") {
1296 period_size_combo.set_active_text(strval);
1297 } else if (child->name() == "serverpath") {
1299 /* only attempt to set this if we have bothered to look
1300 up server names already. otherwise this is all
1301 redundant (actually, all of this dialog/widget
1302 is redundant in that case ...)
1305 if (!server_strings.empty()) {
1306 /* do not allow us to use a server path that doesn't
1307 exist on this system. this handles cases where
1308 the user has an RC file listing a serverpath
1309 from some other machine.
1311 vector<string>::iterator x;
1312 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1317 if (x != server_strings.end()) {
1318 serverpath_combo.set_active_text (strval);
1320 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1326 } else if (child->name() == "driver") {
1327 driver_combo.set_active_text(strval);
1328 } else if (child->name() == "interface") {
1329 interface_combo.set_active_text(strval);
1330 } else if (child->name() == "timeout") {
1331 timeout_combo.set_active_text(strval);
1332 } else if (child->name() == "dither") {
1333 dither_mode_combo.set_active_text(strval);
1334 } else if (child->name() == "audiomode") {
1335 audio_mode_combo.set_active_text(strval);
1336 } else if (child->name() == "inputdevice") {
1337 input_device_combo.set_active_text(strval);
1338 } else if (child->name() == "outputdevice") {
1339 output_device_combo.set_active_text(strval);
1340 } else if (child->name() == "mididriver") {
1341 midi_driver_combo.set_active_text(strval);