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 (_("\
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 Ardour 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 EngineControl::enumerate_alsa_devices ()
806 snd_ctl_card_info_t *info;
807 snd_pcm_info_t *pcminfo;
808 snd_ctl_card_info_alloca(&info);
809 snd_pcm_info_alloca(&pcminfo);
814 backend_devs.clear ();
816 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
819 devname += to_string (cardnum, std::dec);
821 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
823 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
825 snd_pcm_info_set_device (pcminfo, device);
826 snd_pcm_info_set_subdevice (pcminfo, 0);
827 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
829 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
830 devs.push_back (snd_pcm_info_get_name (pcminfo));
832 devname += to_string (device, std::dec);
833 backend_devs.push_back (devname);
837 snd_ctl_close(handle);
846 EngineControl::enumerate_ffado_devices ()
849 backend_devs.clear ();
854 EngineControl::enumerate_freebob_devices ()
861 EngineControl::enumerate_oss_devices ()
867 EngineControl::enumerate_dummy_devices ()
873 EngineControl::enumerate_netjack_devices ()
881 EngineControl::driver_changed ()
883 string driver = driver_combo.get_active_text();
884 string::size_type maxlen = 0;
887 enumerate_devices (driver);
889 vector<string>& strings = devices[driver];
891 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
895 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
896 if ((*i).length() > maxlen) {
897 maxlen = (*i).length();
901 set_popdown_strings (interface_combo, strings);
902 set_popdown_strings (input_device_combo, strings);
903 set_popdown_strings (output_device_combo, strings);
905 if (!strings.empty()) {
906 interface_combo.set_active_text (strings.front());
907 input_device_combo.set_active_text (strings.front());
908 output_device_combo.set_active_text (strings.front());
911 if (driver == "ALSA") {
912 soft_mode_button.set_sensitive (true);
913 force16bit_button.set_sensitive (true);
914 hw_monitor_button.set_sensitive (true);
915 hw_meter_button.set_sensitive (true);
916 monitor_button.set_sensitive (true);
918 soft_mode_button.set_sensitive (false);
919 force16bit_button.set_sensitive (false);
920 hw_monitor_button.set_sensitive (false);
921 hw_meter_button.set_sensitive (false);
922 monitor_button.set_sensitive (false);
927 EngineControl::get_rate ()
929 double r = atof (sample_rate_combo.get_active_text ());
930 /* the string may have been translated with an abbreviation for
931 * thousands, so use a crude heuristic to fix this.
940 EngineControl::redisplay_latency ()
942 uint32_t rate = get_rate();
943 #if defined(__APPLE__) || defined(__FreeBSD__)
946 float periods = periods_adjustment.get_value();
948 float period_size = atof (period_size_combo.get_active_text());
951 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
953 latency_label.set_text (buf);
954 latency_label.set_alignment (0, 0.5);
958 EngineControl::audio_mode_changed ()
960 std::string str = audio_mode_combo.get_active_text();
962 if (str == _("Playback/Recording on 1 Device")) {
963 input_device_combo.set_sensitive (false);
964 output_device_combo.set_sensitive (false);
965 } else if (str == _("Playback/Recording on 2 Devices")) {
966 input_device_combo.set_sensitive (true);
967 output_device_combo.set_sensitive (true);
968 } else if (str == _("Playback only")) {
969 output_device_combo.set_sensitive (true);
970 input_device_combo.set_sensitive (false);
971 } else if (str == _("Recording only")) {
972 input_device_combo.set_sensitive (true);
973 output_device_combo.set_sensitive (false);
977 static bool jack_server_filter(const string& str, void */*arg*/)
979 return str == "jackd" || str == "jackdmp";
983 EngineControl::find_jack_servers (vector<string>& strings)
986 /* this magic lets us finds the path to the OSX bundle, and then
987 we infer JACK's location from there
990 char execpath[MAXPATHLEN+1];
991 uint32_t pathsz = sizeof (execpath);
993 _NSGetExecutablePath (execpath, &pathsz);
995 string path (Glib::path_get_dirname (execpath));
998 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
999 strings.push_back (path);
1002 if (getenv ("ARDOUR_WITH_JACK")) {
1003 /* no other options - only use the JACK we supply */
1004 if (strings.empty()) {
1005 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1014 PathScanner scanner;
1015 vector<string *> *jack_servers;
1016 std::map<string,int> un;
1018 bool need_minimal_path = false;
1020 p = getenv ("PATH");
1025 need_minimal_path = true;
1029 // many mac users don't have PATH set up to include
1030 // likely installed locations of JACK
1031 need_minimal_path = true;
1034 if (need_minimal_path) {
1036 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1038 path += ":/usr/local/bin:/opt/local/bin";
1043 // push it back into the environment so that auto-started JACK can find it.
1044 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1045 setenv ("PATH", path.c_str(), 1);
1048 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1049 if (!jack_servers) {
1053 vector<string *>::iterator iter;
1055 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1059 strings.push_back(p);
1066 EngineControl::get_device_name (const string& driver, const string& human_readable)
1068 vector<string>::iterator n;
1069 vector<string>::iterator i;
1071 if (human_readable.empty()) {
1072 /* this can happen if the user's .ardourrc file has a device name from
1073 another computer system in it
1075 MessageDialog msg (_("You need to choose an audio device first."));
1076 msg.set_position (WIN_POS_MOUSE);
1081 if (backend_devs.empty()) {
1082 return human_readable;
1085 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1086 if (human_readable == (*i)) {
1091 if (i == devices[driver].end()) {
1092 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1099 EngineControl::get_state ()
1101 XMLNode* root = new XMLNode ("AudioSetup");
1105 child = new XMLNode ("periods");
1106 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1107 root->add_child_nocopy (*child);
1109 child = new XMLNode ("ports");
1110 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1111 root->add_child_nocopy (*child);
1113 child = new XMLNode ("inlatency");
1114 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1115 root->add_child_nocopy (*child);
1117 child = new XMLNode ("outlatency");
1118 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1119 root->add_child_nocopy (*child);
1121 child = new XMLNode ("realtime");
1122 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1123 root->add_child_nocopy (*child);
1125 child = new XMLNode ("nomemorylock");
1126 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1127 root->add_child_nocopy (*child);
1129 child = new XMLNode ("unlockmemory");
1130 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1131 root->add_child_nocopy (*child);
1133 child = new XMLNode ("softmode");
1134 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1135 root->add_child_nocopy (*child);
1137 child = new XMLNode ("force16bit");
1138 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1139 root->add_child_nocopy (*child);
1141 child = new XMLNode ("hwmonitor");
1142 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1143 root->add_child_nocopy (*child);
1145 child = new XMLNode ("hwmeter");
1146 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1147 root->add_child_nocopy (*child);
1149 child = new XMLNode ("verbose");
1150 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1151 root->add_child_nocopy (*child);
1153 child = new XMLNode ("samplerate");
1154 child->add_property ("val", sample_rate_combo.get_active_text());
1155 root->add_child_nocopy (*child);
1157 child = new XMLNode ("periodsize");
1158 child->add_property ("val", period_size_combo.get_active_text());
1159 root->add_child_nocopy (*child);
1161 child = new XMLNode ("serverpath");
1162 child->add_property ("val", serverpath_combo.get_active_text());
1163 root->add_child_nocopy (*child);
1165 child = new XMLNode ("driver");
1166 child->add_property ("val", driver_combo.get_active_text());
1167 root->add_child_nocopy (*child);
1169 child = new XMLNode ("interface");
1170 child->add_property ("val", interface_combo.get_active_text());
1171 root->add_child_nocopy (*child);
1173 child = new XMLNode ("timeout");
1174 child->add_property ("val", timeout_combo.get_active_text());
1175 root->add_child_nocopy (*child);
1177 child = new XMLNode ("dither");
1178 child->add_property ("val", dither_mode_combo.get_active_text());
1179 root->add_child_nocopy (*child);
1181 child = new XMLNode ("audiomode");
1182 child->add_property ("val", audio_mode_combo.get_active_text());
1183 root->add_child_nocopy (*child);
1185 child = new XMLNode ("inputdevice");
1186 child->add_property ("val", input_device_combo.get_active_text());
1187 root->add_child_nocopy (*child);
1189 child = new XMLNode ("outputdevice");
1190 child->add_property ("val", output_device_combo.get_active_text());
1191 root->add_child_nocopy (*child);
1193 child = new XMLNode ("mididriver");
1194 child->add_property ("val", midi_driver_combo.get_active_text());
1195 root->add_child_nocopy (*child);
1201 EngineControl::set_state (const XMLNode& root)
1204 XMLNodeConstIterator citer;
1206 XMLProperty* prop = NULL;
1207 bool using_dummy = false;
1208 bool using_ffado = false;
1213 if ( (child = root.child ("driver"))){
1214 prop = child->property("val");
1216 if (prop && (prop->value() == "Dummy") ) {
1219 if (prop && (prop->value() == "FFADO") ) {
1225 clist = root.children();
1227 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1231 prop = child->property ("val");
1233 if (!prop || prop->value().empty()) {
1235 if (((using_dummy || using_ffado)
1236 && ( child->name() == "interface"
1237 || child->name() == "inputdevice"
1238 || child->name() == "outputdevice"))
1239 || child->name() == "timeout")
1244 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1248 strval = prop->value();
1250 /* adjustments/spinners */
1252 if (child->name() == "periods") {
1253 val = atoi (strval);
1254 periods_adjustment.set_value(val);
1255 } else if (child->name() == "ports") {
1256 val = atoi (strval);
1257 ports_adjustment.set_value(val);
1258 } else if (child->name() == "inlatency") {
1259 val = atoi (strval);
1260 input_latency.set_value(val);
1261 } else if (child->name() == "outlatency") {
1262 val = atoi (strval);
1263 output_latency.set_value(val);
1268 else if (child->name() == "realtime") {
1269 val = atoi (strval);
1270 realtime_button.set_active(val);
1271 } else if (child->name() == "nomemorylock") {
1272 val = atoi (strval);
1273 no_memory_lock_button.set_active(val);
1274 } else if (child->name() == "unlockmemory") {
1275 val = atoi (strval);
1276 unlock_memory_button.set_active(val);
1277 } else if (child->name() == "softmode") {
1278 val = atoi (strval);
1279 soft_mode_button.set_active(val);
1280 } else if (child->name() == "force16bit") {
1281 val = atoi (strval);
1282 force16bit_button.set_active(val);
1283 } else if (child->name() == "hwmonitor") {
1284 val = atoi (strval);
1285 hw_monitor_button.set_active(val);
1286 } else if (child->name() == "hwmeter") {
1287 val = atoi (strval);
1288 hw_meter_button.set_active(val);
1289 } else if (child->name() == "verbose") {
1290 val = atoi (strval);
1291 verbose_output_button.set_active(val);
1296 else if (child->name() == "samplerate") {
1297 sample_rate_combo.set_active_text(strval);
1298 } else if (child->name() == "periodsize") {
1299 period_size_combo.set_active_text(strval);
1300 } else if (child->name() == "serverpath") {
1302 /* only attempt to set this if we have bothered to look
1303 up server names already. otherwise this is all
1304 redundant (actually, all of this dialog/widget
1305 is redundant in that case ...)
1308 if (!server_strings.empty()) {
1309 /* do not allow us to use a server path that doesn't
1310 exist on this system. this handles cases where
1311 the user has an RC file listing a serverpath
1312 from some other machine.
1314 vector<string>::iterator x;
1315 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1320 if (x != server_strings.end()) {
1321 serverpath_combo.set_active_text (strval);
1323 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1329 } else if (child->name() == "driver") {
1330 driver_combo.set_active_text(strval);
1331 } else if (child->name() == "interface") {
1332 interface_combo.set_active_text(strval);
1333 } else if (child->name() == "timeout") {
1334 timeout_combo.set_active_text(strval);
1335 } else if (child->name() == "dither") {
1336 dither_mode_combo.set_active_text(strval);
1337 } else if (child->name() == "audiomode") {
1338 audio_mode_combo.set_active_text(strval);
1339 } else if (child->name() == "inputdevice") {
1340 input_device_combo.set_active_text(strval);
1341 } else if (child->name() == "outputdevice") {
1342 output_device_combo.set_active_text(strval);
1343 } else if (child->name() == "mididriver") {
1344 midi_driver_combo.set_active_text(strval);