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>
38 #elif !defined(__FreeBSD__)
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"));
143 strings.push_back (X_("ALSA"));
145 strings.push_back (X_("OSS"));
146 strings.push_back (X_("FreeBoB"));
147 strings.push_back (X_("FFADO"));
149 strings.push_back (X_("NetJACK"));
150 strings.push_back (X_("Dummy"));
151 set_popdown_strings (driver_combo, strings);
152 driver_combo.set_active_text (strings.front());
154 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
158 strings.push_back (_("Playback/recording on 1 device"));
159 strings.push_back (_("Playback/recording on 2 devices"));
160 strings.push_back (_("Playback only"));
161 strings.push_back (_("Recording only"));
162 set_popdown_strings (audio_mode_combo, strings);
163 audio_mode_combo.set_active_text (strings.front());
165 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
166 audio_mode_changed ();
169 strings.push_back (_("None"));
170 strings.push_back (_("seq"));
171 strings.push_back (_("raw"));
172 set_popdown_strings (midi_driver_combo, strings);
173 midi_driver_combo.set_active_text (strings.front ());
177 label = manage (left_aligned_label (_("Driver:")));
178 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
179 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
182 label = manage (left_aligned_label (_("Audio Interface:")));
183 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
184 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
187 label = manage (left_aligned_label (_("Sample rate:")));
188 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
189 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
192 label = manage (left_aligned_label (_("Buffer size:")));
193 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
194 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
197 #if !defined(__APPLE__) && !defined(__FreeBSD__)
198 label = manage (left_aligned_label (_("Number of buffers:")));
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 (left_aligned_label (_("Approximate latency:")));
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' */
217 #if !defined(__APPLE__) && !defined(__FreeBSD__)
218 label = manage (left_aligned_label (_("Audio mode:")));
219 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
220 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
224 interface_combo.set_size_request (250, -1);
225 input_device_combo.set_size_request (250, -1);
226 output_device_combo.set_size_request (250, -1);
230 if (engine_running()) {
231 start_button.set_sensitive (false);
233 stop_button.set_sensitive (false);
236 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
237 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
240 button_box.pack_start (start_button, false, false);
241 button_box.pack_start (stop_button, false, false);
243 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
247 options_packer.set_spacings (6);
250 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
253 realtime_button.set_active (true);
255 #if PROVIDE_TOO_MANY_OPTIONS
257 #if !defined(__APPLE__) && !defined(__FreeBSD__)
258 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
260 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
262 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
264 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
268 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
270 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
272 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
275 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
280 strings.push_back (_("Ignore"));
281 strings.push_back ("500 msec");
282 strings.push_back ("1 sec");
283 strings.push_back ("2 sec");
284 strings.push_back ("10 sec");
285 set_popdown_strings (timeout_combo, strings);
286 timeout_combo.set_active_text (strings.front ());
288 label = manage (new Label (_("Client timeout")));
289 label->set_alignment (1.0, 0.5);
290 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
291 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
294 #endif /* PROVIDE_TOO_MANY_OPTIONS */
295 label = manage (left_aligned_label (_("Number of ports:")));
296 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
297 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
300 label = manage (left_aligned_label (_("MIDI driver:")));
301 options_packer.attach (midi_driver_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 #if !defined(__APPLE__) && !defined(__FreeBSD__)
306 label = manage (left_aligned_label (_("Dither:")));
307 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
308 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
312 find_jack_servers (server_strings);
314 if (server_strings.empty()) {
315 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
319 set_popdown_strings (serverpath_combo, server_strings);
320 serverpath_combo.set_active_text (server_strings.front());
322 if (server_strings.size() > 1) {
323 label = manage (left_aligned_label (_("Server:")));
324 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
325 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
329 /* device settings */
331 device_packer.set_spacings (6);
334 #if !defined(__APPLE__) && !defined(__FreeBSD__)
335 label = manage (left_aligned_label (_("Input device:")));
336 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
337 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
339 label = manage (left_aligned_label (_("Output device:")));
340 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
341 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
344 label = manage (left_aligned_label (_("Hardware input latency:")));
345 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
346 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
347 label = manage (left_aligned_label (_("samples")));
348 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
350 label = manage (left_aligned_label (_("Hardware output latency:")));
351 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
352 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
353 label = manage (left_aligned_label (_("samples")));
354 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
357 basic_hbox.pack_start (basic_packer, false, false);
358 options_hbox.pack_start (options_packer, false, false);
360 device_packer.set_border_width (12);
361 options_packer.set_border_width (12);
362 basic_packer.set_border_width (12);
364 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
365 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
366 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
367 notebook.set_border_width (12);
369 set_border_width (12);
370 pack_start (notebook);
372 /* Pick up any existing audio setup configuration, if appropriate */
374 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioSetup");
377 set_state (*audio_setup);
381 EngineControl::~EngineControl ()
387 EngineControl::build_command_line (vector<string>& cmd)
391 bool using_alsa = false;
392 bool using_coreaudio = false;
393 bool using_dummy = false;
394 bool using_ffado = false;
396 /* first, path to jackd */
398 cmd.push_back (serverpath_combo.get_active_text ());
400 /* now jackd arguments */
402 str = timeout_combo.get_active_text ();
404 if (str != _("Ignore")) {
409 msecs = (uint32_t) floor (secs * 1000.0);
412 cmd.push_back ("-t");
413 cmd.push_back (to_string (msecs, std::dec));
417 if (no_memory_lock_button.get_active()) {
418 cmd.push_back ("-m"); /* no munlock */
421 cmd.push_back ("-p"); /* port max */
422 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
424 if (realtime_button.get_active()) {
425 cmd.push_back ("-R");
427 cmd.push_back ("-r"); /* override jackd's default --realtime */
430 if (unlock_memory_button.get_active()) {
431 cmd.push_back ("-u");
434 if (verbose_output_button.get_active()) {
435 cmd.push_back ("-v");
438 /* now add fixed arguments (not user-selectable) */
440 cmd.push_back ("-T"); // temporary */
442 /* next the driver */
444 cmd.push_back ("-d");
446 driver = driver_combo.get_active_text ();
448 if (driver == X_("ALSA")) {
450 cmd.push_back ("alsa");
451 } else if (driver == X_("OSS")) {
452 cmd.push_back ("oss");
453 } else if (driver == X_("CoreAudio")) {
454 using_coreaudio = true;
455 cmd.push_back ("coreaudio");
456 } else if (driver == X_("NetJACK")) {
457 cmd.push_back ("netjack");
458 } else if (driver == X_("FreeBoB")) {
459 cmd.push_back ("freebob");
460 } else if (driver == X_("FFADO")) {
462 cmd.push_back ("firewire");
463 } else if ( driver == X_("Dummy")) {
465 cmd.push_back ("dummy");
468 /* driver arguments */
470 if (!using_coreaudio) {
471 str = audio_mode_combo.get_active_text();
473 if (str == _("Playback/recording on 1 device")) {
477 } else if (str == _("Playback/recording on 2 devices")) {
479 string input_device = get_device_name (driver, input_device_combo.get_active_text());
480 string output_device = get_device_name (driver, output_device_combo.get_active_text());
482 if (input_device.empty() || output_device.empty()) {
487 cmd.push_back ("-C");
488 cmd.push_back (input_device);
490 cmd.push_back ("-P");
491 cmd.push_back (output_device);
493 } else if (str == _("Playback only")) {
494 cmd.push_back ("-P");
495 } else if (str == _("Recording only")) {
496 cmd.push_back ("-C");
500 cmd.push_back ("-n");
501 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
505 cmd.push_back ("-r");
506 cmd.push_back (to_string (get_rate(), std::dec));
508 cmd.push_back ("-p");
509 cmd.push_back (period_size_combo.get_active_text());
511 if (using_alsa || using_ffado || using_coreaudio) {
513 double val = input_latency_adjustment.get_value();
516 cmd.push_back ("-I");
517 cmd.push_back (to_string ((uint32_t) val, std::dec));
520 val = output_latency_adjustment.get_value();
523 cmd.push_back ("-O");
524 cmd.push_back (to_string ((uint32_t) val, std::dec));
530 if (audio_mode_combo.get_active_text() != _("Playback/recording on 2 devices")) {
532 string device = get_device_name (driver, interface_combo.get_active_text());
533 if (device.empty()) {
538 cmd.push_back ("-d");
539 cmd.push_back (device);
542 if (hw_meter_button.get_active()) {
543 cmd.push_back ("-M");
546 if (hw_monitor_button.get_active()) {
547 cmd.push_back ("-H");
550 str = dither_mode_combo.get_active_text();
552 if (str == _("None")) {
553 } else if (str == _("Triangular")) {
554 cmd.push_back ("-z triangular");
555 } else if (str == _("Rectangular")) {
556 cmd.push_back ("-z rectangular");
557 } else if (str == _("Shaped")) {
558 cmd.push_back ("-z shaped");
561 if (force16bit_button.get_active()) {
562 cmd.push_back ("-S");
565 if (soft_mode_button.get_active()) {
566 cmd.push_back ("-s");
569 str = midi_driver_combo.get_active_text ();
571 if (str == _("seq")) {
572 cmd.push_back ("-X seq");
573 } else if (str == _("raw")) {
574 cmd.push_back ("-X raw");
576 } else if (using_coreaudio) {
579 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
581 string device = get_device_name (driver, interface_combo.get_active_text());
582 if (device.empty()) {
587 cmd.push_back ("-d");
588 cmd.push_back (device);
595 EngineControl::need_setup ()
597 return !engine_running();
601 EngineControl::engine_running ()
603 EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
604 boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
606 /* revert all environment settings back to whatever they were when
607 * ardour started, because ardour's startup script may have reset
608 * something in ways that interfere with finding/starting JACK.
612 current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
613 global_epa->restore ();
616 jack_status_t status;
617 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
620 jack_client_close (c);
627 EngineControl::setup_engine ()
630 std::string cwd = "/tmp";
632 build_command_line (args);
635 return 1; // try again
638 std::string jackdrc_path = Glib::get_home_dir();
639 jackdrc_path += "/.jackdrc";
641 ofstream jackdrc (jackdrc_path.c_str());
643 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
647 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
648 jackdrc << (*i) << ' ';
660 EngineControl::enumerate_devices (const string& driver)
662 /* note: case matters for the map keys */
664 if (driver == "CoreAudio") {
666 devices[driver] = enumerate_coreaudio_devices ();
669 #if !defined(__APPLE__) && !defined(__FreeBSD__)
670 } else if (driver == "ALSA") {
671 devices[driver] = enumerate_alsa_devices ();
672 } else if (driver == "FreeBOB") {
673 devices[driver] = enumerate_freebob_devices ();
674 } else if (driver == "FFADO") {
675 devices[driver] = enumerate_ffado_devices ();
676 } else if (driver == "OSS") {
677 devices[driver] = enumerate_oss_devices ();
678 } else if (driver == "Dummy") {
679 devices[driver] = enumerate_dummy_devices ();
680 } else if (driver == "NetJACK") {
681 devices[driver] = enumerate_netjack_devices ();
690 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
692 UInt32 size = sizeof(CFStringRef);
694 OSStatus res = AudioDeviceGetProperty(id, 0, false,
695 kAudioDevicePropertyDeviceUID, &size, &UI);
697 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
703 EngineControl::enumerate_coreaudio_devices ()
707 // Find out how many Core Audio devices are there, if any...
708 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
711 UInt32 outSize = sizeof(isWritable);
713 backend_devs.clear ();
715 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
716 &outSize, &isWritable);
718 // Calculate the number of device available...
719 int numCoreDevices = outSize / sizeof(AudioDeviceID);
720 // Make space for the devices we are about to get...
721 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
722 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
723 &outSize, (void *) coreDeviceIDs);
725 // Look for the CoreAudio device name...
726 char coreDeviceName[256];
729 for (int i = 0; i < numCoreDevices; i++) {
731 nameSize = sizeof (coreDeviceName);
733 /* enforce duplex devices only */
735 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
736 0, true, kAudioDevicePropertyStreams,
737 &outSize, &isWritable);
739 if (err != noErr || outSize == 0) {
743 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
744 0, false, kAudioDevicePropertyStreams,
745 &outSize, &isWritable);
747 if (err != noErr || outSize == 0) {
751 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
752 0, true, kAudioDevicePropertyDeviceName,
753 &outSize, &isWritable);
755 err = AudioDeviceGetProperty(coreDeviceIDs[i],
756 0, true, kAudioDevicePropertyDeviceName,
757 &nameSize, (void *) coreDeviceName);
759 char drivername[128];
761 // this returns the unique id for the device
762 // that must be used on the commandline for jack
764 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
765 devs.push_back (coreDeviceName);
766 backend_devs.push_back (drivername);
772 delete [] coreDeviceIDs;
776 if (devs.size() == 0) {
777 MessageDialog msg (string_compose (_("\
778 You do not have any audio devices capable of\n\
779 simultaneous playback and recording.\n\n\
780 Please use Applications -> Utilities -> Audio MIDI Setup\n\
781 to create an \"aggregrate\" device, or install a suitable\n\
782 audio interface.\n\n\
783 Please send email to Apple and ask them why new Macs\n\
784 have no duplex audio device.\n\n\
785 Alternatively, if you really want just playback\n\
786 or recording but not both, start JACK before running\n\
787 %1 and choose the relevant device then."
789 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
790 msg.set_title (_("No suitable audio devices"));
791 msg.set_position (Gtk::WIN_POS_MOUSE);
801 #if !defined(__FreeBSD__)
803 EngineControl::enumerate_alsa_devices ()
808 snd_ctl_card_info_t *info;
809 snd_pcm_info_t *pcminfo;
810 snd_ctl_card_info_alloca(&info);
811 snd_pcm_info_alloca(&pcminfo);
816 backend_devs.clear ();
818 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
821 devname += to_string (cardnum, std::dec);
823 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
825 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
827 snd_pcm_info_set_device (pcminfo, device);
828 snd_pcm_info_set_subdevice (pcminfo, 0);
829 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
831 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
832 devs.push_back (snd_pcm_info_get_name (pcminfo));
834 devname += to_string (device, std::dec);
835 backend_devs.push_back (devname);
839 snd_ctl_close(handle);
848 EngineControl::enumerate_ffado_devices ()
851 backend_devs.clear ();
856 EngineControl::enumerate_freebob_devices ()
863 EngineControl::enumerate_oss_devices ()
869 EngineControl::enumerate_dummy_devices ()
875 EngineControl::enumerate_netjack_devices ()
883 EngineControl::driver_changed ()
885 string driver = driver_combo.get_active_text();
886 string::size_type maxlen = 0;
889 enumerate_devices (driver);
891 vector<string>& strings = devices[driver];
893 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
897 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
898 if ((*i).length() > maxlen) {
899 maxlen = (*i).length();
903 set_popdown_strings (interface_combo, strings);
904 set_popdown_strings (input_device_combo, strings);
905 set_popdown_strings (output_device_combo, strings);
907 if (!strings.empty()) {
908 interface_combo.set_active_text (strings.front());
909 input_device_combo.set_active_text (strings.front());
910 output_device_combo.set_active_text (strings.front());
913 if (driver == "ALSA") {
914 soft_mode_button.set_sensitive (true);
915 force16bit_button.set_sensitive (true);
916 hw_monitor_button.set_sensitive (true);
917 hw_meter_button.set_sensitive (true);
918 monitor_button.set_sensitive (true);
920 soft_mode_button.set_sensitive (false);
921 force16bit_button.set_sensitive (false);
922 hw_monitor_button.set_sensitive (false);
923 hw_meter_button.set_sensitive (false);
924 monitor_button.set_sensitive (false);
929 EngineControl::get_rate ()
931 double r = atof (sample_rate_combo.get_active_text ());
932 /* the string may have been translated with an abbreviation for
933 * thousands, so use a crude heuristic to fix this.
942 EngineControl::redisplay_latency ()
944 uint32_t rate = get_rate();
945 #if defined(__APPLE__) || defined(__FreeBSD__)
948 float periods = periods_adjustment.get_value();
950 float period_size = atof (period_size_combo.get_active_text());
953 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
955 latency_label.set_text (buf);
956 latency_label.set_alignment (0, 0.5);
960 EngineControl::audio_mode_changed ()
962 std::string str = audio_mode_combo.get_active_text();
964 if (str == _("Playback/recording on 1 device")) {
965 input_device_combo.set_sensitive (false);
966 output_device_combo.set_sensitive (false);
967 } else if (str == _("Playback/recording on 2 devices")) {
968 input_device_combo.set_sensitive (true);
969 output_device_combo.set_sensitive (true);
970 } else if (str == _("Playback only")) {
971 output_device_combo.set_sensitive (true);
972 input_device_combo.set_sensitive (false);
973 } else if (str == _("Recording only")) {
974 input_device_combo.set_sensitive (true);
975 output_device_combo.set_sensitive (false);
979 static bool jack_server_filter(const string& str, void */*arg*/)
981 return str == "jackd" || str == "jackdmp";
985 EngineControl::find_jack_servers (vector<string>& strings)
988 /* this magic lets us finds the path to the OSX bundle, and then
989 we infer JACK's location from there
992 char execpath[MAXPATHLEN+1];
993 uint32_t pathsz = sizeof (execpath);
995 _NSGetExecutablePath (execpath, &pathsz);
997 string path (Glib::path_get_dirname (execpath));
1000 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
1001 strings.push_back (path);
1004 if (getenv ("ARDOUR_WITH_JACK")) {
1005 /* no other options - only use the JACK we supply */
1006 if (strings.empty()) {
1007 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1016 PathScanner scanner;
1017 vector<string *> *jack_servers;
1018 std::map<string,int> un;
1020 bool need_minimal_path = false;
1022 p = getenv ("PATH");
1027 need_minimal_path = true;
1031 // many mac users don't have PATH set up to include
1032 // likely installed locations of JACK
1033 need_minimal_path = true;
1036 if (need_minimal_path) {
1038 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1040 path += ":/usr/local/bin:/opt/local/bin";
1045 // push it back into the environment so that auto-started JACK can find it.
1046 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1047 setenv ("PATH", path.c_str(), 1);
1050 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1051 if (!jack_servers) {
1055 vector<string *>::iterator iter;
1057 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1061 strings.push_back(p);
1068 EngineControl::get_device_name (const string& driver, const string& human_readable)
1070 vector<string>::iterator n;
1071 vector<string>::iterator i;
1073 if (human_readable.empty()) {
1074 /* this can happen if the user's .ardourrc file has a device name from
1075 another computer system in it
1077 MessageDialog msg (_("You need to choose an audio device first."));
1078 msg.set_position (WIN_POS_MOUSE);
1083 if (backend_devs.empty()) {
1084 return human_readable;
1087 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1088 if (human_readable == (*i)) {
1093 if (i == devices[driver].end()) {
1094 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1101 EngineControl::get_state ()
1103 XMLNode* root = new XMLNode ("AudioSetup");
1107 child = new XMLNode ("periods");
1108 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1109 root->add_child_nocopy (*child);
1111 child = new XMLNode ("ports");
1112 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1113 root->add_child_nocopy (*child);
1115 child = new XMLNode ("inlatency");
1116 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1117 root->add_child_nocopy (*child);
1119 child = new XMLNode ("outlatency");
1120 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1121 root->add_child_nocopy (*child);
1123 child = new XMLNode ("realtime");
1124 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1125 root->add_child_nocopy (*child);
1127 child = new XMLNode ("nomemorylock");
1128 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1129 root->add_child_nocopy (*child);
1131 child = new XMLNode ("unlockmemory");
1132 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1133 root->add_child_nocopy (*child);
1135 child = new XMLNode ("softmode");
1136 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1137 root->add_child_nocopy (*child);
1139 child = new XMLNode ("force16bit");
1140 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1141 root->add_child_nocopy (*child);
1143 child = new XMLNode ("hwmonitor");
1144 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1145 root->add_child_nocopy (*child);
1147 child = new XMLNode ("hwmeter");
1148 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1149 root->add_child_nocopy (*child);
1151 child = new XMLNode ("verbose");
1152 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1153 root->add_child_nocopy (*child);
1155 child = new XMLNode ("samplerate");
1156 child->add_property ("val", sample_rate_combo.get_active_text());
1157 root->add_child_nocopy (*child);
1159 child = new XMLNode ("periodsize");
1160 child->add_property ("val", period_size_combo.get_active_text());
1161 root->add_child_nocopy (*child);
1163 child = new XMLNode ("serverpath");
1164 child->add_property ("val", serverpath_combo.get_active_text());
1165 root->add_child_nocopy (*child);
1167 child = new XMLNode ("driver");
1168 child->add_property ("val", driver_combo.get_active_text());
1169 root->add_child_nocopy (*child);
1171 child = new XMLNode ("interface");
1172 child->add_property ("val", interface_combo.get_active_text());
1173 root->add_child_nocopy (*child);
1175 child = new XMLNode ("timeout");
1176 child->add_property ("val", timeout_combo.get_active_text());
1177 root->add_child_nocopy (*child);
1179 child = new XMLNode ("dither");
1180 child->add_property ("val", dither_mode_combo.get_active_text());
1181 root->add_child_nocopy (*child);
1183 child = new XMLNode ("audiomode");
1184 child->add_property ("val", audio_mode_combo.get_active_text());
1185 root->add_child_nocopy (*child);
1187 child = new XMLNode ("inputdevice");
1188 child->add_property ("val", input_device_combo.get_active_text());
1189 root->add_child_nocopy (*child);
1191 child = new XMLNode ("outputdevice");
1192 child->add_property ("val", output_device_combo.get_active_text());
1193 root->add_child_nocopy (*child);
1195 child = new XMLNode ("mididriver");
1196 child->add_property ("val", midi_driver_combo.get_active_text());
1197 root->add_child_nocopy (*child);
1203 EngineControl::set_state (const XMLNode& root)
1206 XMLNodeConstIterator citer;
1208 XMLProperty* prop = NULL;
1209 bool using_dummy = false;
1210 bool using_ffado = false;
1215 if ( (child = root.child ("driver"))){
1216 prop = child->property("val");
1218 if (prop && (prop->value() == "Dummy") ) {
1221 if (prop && (prop->value() == "FFADO") ) {
1227 clist = root.children();
1229 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1233 prop = child->property ("val");
1235 if (!prop || prop->value().empty()) {
1237 if (((using_dummy || using_ffado)
1238 && ( child->name() == "interface"
1239 || child->name() == "inputdevice"
1240 || child->name() == "outputdevice"))
1241 || child->name() == "timeout")
1246 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1250 strval = prop->value();
1252 /* adjustments/spinners */
1254 if (child->name() == "periods") {
1255 val = atoi (strval);
1256 periods_adjustment.set_value(val);
1257 } else if (child->name() == "ports") {
1258 val = atoi (strval);
1259 ports_adjustment.set_value(val);
1260 } else if (child->name() == "inlatency") {
1261 val = atoi (strval);
1262 input_latency.set_value(val);
1263 } else if (child->name() == "outlatency") {
1264 val = atoi (strval);
1265 output_latency.set_value(val);
1270 else if (child->name() == "realtime") {
1271 val = atoi (strval);
1272 realtime_button.set_active(val);
1273 } else if (child->name() == "nomemorylock") {
1274 val = atoi (strval);
1275 no_memory_lock_button.set_active(val);
1276 } else if (child->name() == "unlockmemory") {
1277 val = atoi (strval);
1278 unlock_memory_button.set_active(val);
1279 } else if (child->name() == "softmode") {
1280 val = atoi (strval);
1281 soft_mode_button.set_active(val);
1282 } else if (child->name() == "force16bit") {
1283 val = atoi (strval);
1284 force16bit_button.set_active(val);
1285 } else if (child->name() == "hwmonitor") {
1286 val = atoi (strval);
1287 hw_monitor_button.set_active(val);
1288 } else if (child->name() == "hwmeter") {
1289 val = atoi (strval);
1290 hw_meter_button.set_active(val);
1291 } else if (child->name() == "verbose") {
1292 val = atoi (strval);
1293 verbose_output_button.set_active(val);
1298 else if (child->name() == "samplerate") {
1299 sample_rate_combo.set_active_text(strval);
1300 } else if (child->name() == "periodsize") {
1301 period_size_combo.set_active_text(strval);
1302 } else if (child->name() == "serverpath") {
1304 /* only attempt to set this if we have bothered to look
1305 up server names already. otherwise this is all
1306 redundant (actually, all of this dialog/widget
1307 is redundant in that case ...)
1310 if (!server_strings.empty()) {
1311 /* do not allow us to use a server path that doesn't
1312 exist on this system. this handles cases where
1313 the user has an RC file listing a serverpath
1314 from some other machine.
1316 vector<string>::iterator x;
1317 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1322 if (x != server_strings.end()) {
1323 serverpath_combo.set_active_text (strval);
1325 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1331 } else if (child->name() == "driver") {
1332 driver_combo.set_active_text(strval);
1333 } else if (child->name() == "interface") {
1334 interface_combo.set_active_text(strval);
1335 } else if (child->name() == "timeout") {
1336 timeout_combo.set_active_text(strval);
1337 } else if (child->name() == "dither") {
1338 dither_mode_combo.set_active_text(strval);
1339 } else if (child->name() == "audiomode") {
1340 audio_mode_combo.set_active_text(strval);
1341 } else if (child->name() == "inputdevice") {
1342 input_device_combo.set_active_text(strval);
1343 } else if (child->name() == "outputdevice") {
1344 output_device_combo.set_active_text(strval);
1345 } else if (child->name() == "mididriver") {
1346 midi_driver_combo.set_active_text(strval);