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"));
171 strings.push_back (_("coremidi"));
173 strings.push_back (_("seq"));
174 strings.push_back (_("raw"));
176 set_popdown_strings (midi_driver_combo, strings);
177 midi_driver_combo.set_active_text (strings.front ());
181 label = manage (left_aligned_label (_("Driver:")));
182 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
183 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
186 label = manage (left_aligned_label (_("Audio Interface:")));
187 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
188 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
191 label = manage (left_aligned_label (_("Sample rate:")));
192 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
193 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
196 label = manage (left_aligned_label (_("Buffer size:")));
197 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
198 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
201 #if !defined(__APPLE__) && !defined(__FreeBSD__)
202 label = manage (left_aligned_label (_("Number of buffers:")));
203 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
204 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
205 periods_spinner.set_value (2);
209 label = manage (left_aligned_label (_("Approximate latency:")));
210 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
211 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
214 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
215 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
216 period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
219 /* no audio mode with CoreAudio, its duplex or nuthin' */
221 #if !defined(__APPLE__) && !defined(__FreeBSD__)
222 label = manage (left_aligned_label (_("Audio mode:")));
223 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
224 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
228 interface_combo.set_size_request (250, -1);
229 input_device_combo.set_size_request (250, -1);
230 output_device_combo.set_size_request (250, -1);
234 if (engine_running()) {
235 start_button.set_sensitive (false);
237 stop_button.set_sensitive (false);
240 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
241 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
244 button_box.pack_start (start_button, false, false);
245 button_box.pack_start (stop_button, false, false);
247 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
251 options_packer.set_spacings (6);
254 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
257 realtime_button.set_active (true);
259 #if PROVIDE_TOO_MANY_OPTIONS
261 #if !defined(__APPLE__) && !defined(__FreeBSD__)
262 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
264 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
268 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
270 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
272 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
274 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
276 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
279 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
284 strings.push_back (_("Ignore"));
285 strings.push_back ("500 msec");
286 strings.push_back ("1 sec");
287 strings.push_back ("2 sec");
288 strings.push_back ("10 sec");
289 set_popdown_strings (timeout_combo, strings);
290 timeout_combo.set_active_text (strings.front ());
292 label = manage (new Label (_("Client timeout")));
293 label->set_alignment (1.0, 0.5);
294 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
295 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
298 #endif /* PROVIDE_TOO_MANY_OPTIONS */
299 label = manage (left_aligned_label (_("Number of ports:")));
300 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
301 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
304 label = manage (left_aligned_label (_("MIDI driver:")));
305 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
306 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
309 #if !defined(__APPLE__) && !defined(__FreeBSD__)
310 label = manage (left_aligned_label (_("Dither:")));
311 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
312 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
316 find_jack_servers (server_strings);
318 if (server_strings.empty()) {
319 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
323 set_popdown_strings (serverpath_combo, server_strings);
324 serverpath_combo.set_active_text (server_strings.front());
326 if (server_strings.size() > 1) {
327 label = manage (left_aligned_label (_("Server:")));
328 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
329 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
333 /* device settings */
335 device_packer.set_spacings (6);
338 #if !defined(__APPLE__) && !defined(__FreeBSD__)
339 label = manage (left_aligned_label (_("Input device:")));
340 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
341 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
343 label = manage (left_aligned_label (_("Output device:")));
344 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
345 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
348 label = manage (left_aligned_label (_("Hardware input latency:")));
349 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
350 device_packer.attach (input_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);
354 label = manage (left_aligned_label (_("Hardware output latency:")));
355 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
356 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
357 label = manage (left_aligned_label (_("samples")));
358 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
361 basic_hbox.pack_start (basic_packer, false, false);
362 options_hbox.pack_start (options_packer, false, false);
364 device_packer.set_border_width (12);
365 options_packer.set_border_width (12);
366 basic_packer.set_border_width (12);
368 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
369 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
370 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
371 notebook.set_border_width (12);
373 set_border_width (12);
374 pack_start (notebook);
376 /* Pick up any existing audio setup configuration, if appropriate */
378 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioSetup");
381 set_state (*audio_setup);
385 EngineControl::~EngineControl ()
391 EngineControl::build_command_line (vector<string>& cmd)
395 bool using_alsa = false;
396 bool using_coreaudio = false;
397 bool using_dummy = false;
398 bool using_ffado = false;
400 /* first, path to jackd */
402 cmd.push_back (serverpath_combo.get_active_text ());
404 /* now jackd arguments */
406 str = timeout_combo.get_active_text ();
408 if (str != _("Ignore")) {
413 msecs = (uint32_t) floor (secs * 1000.0);
416 cmd.push_back ("-t");
417 cmd.push_back (to_string (msecs, std::dec));
421 if (no_memory_lock_button.get_active()) {
422 cmd.push_back ("-m"); /* no munlock */
425 cmd.push_back ("-p"); /* port max */
426 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
428 if (realtime_button.get_active()) {
429 cmd.push_back ("-R");
431 cmd.push_back ("-r"); /* override jackd's default --realtime */
434 if (unlock_memory_button.get_active()) {
435 cmd.push_back ("-u");
438 if (verbose_output_button.get_active()) {
439 cmd.push_back ("-v");
442 /* now add fixed arguments (not user-selectable) */
444 cmd.push_back ("-T"); // temporary */
446 /* setup coremidi before the driver, otherwise jack won't start */
448 if (midi_driver_combo.get_active_text() == _("coremidi")) {
449 cmd.push_back ("-X coremidi");
452 /* next the driver */
454 cmd.push_back ("-d");
456 driver = driver_combo.get_active_text ();
458 if (driver == X_("ALSA")) {
460 cmd.push_back ("alsa");
461 } else if (driver == X_("OSS")) {
462 cmd.push_back ("oss");
463 } else if (driver == X_("CoreAudio")) {
464 using_coreaudio = true;
465 cmd.push_back ("coreaudio");
466 } else if (driver == X_("NetJACK")) {
467 cmd.push_back ("netjack");
468 } else if (driver == X_("FreeBoB")) {
469 cmd.push_back ("freebob");
470 } else if (driver == X_("FFADO")) {
472 cmd.push_back ("firewire");
473 } else if ( driver == X_("Dummy")) {
475 cmd.push_back ("dummy");
478 /* driver arguments */
480 if (!using_coreaudio) {
481 str = audio_mode_combo.get_active_text();
483 if (str == _("Playback/recording on 1 device")) {
487 } else if (str == _("Playback/recording on 2 devices")) {
489 string input_device = get_device_name (driver, input_device_combo.get_active_text());
490 string output_device = get_device_name (driver, output_device_combo.get_active_text());
492 if (input_device.empty() || output_device.empty()) {
497 cmd.push_back ("-C");
498 cmd.push_back (input_device);
500 cmd.push_back ("-P");
501 cmd.push_back (output_device);
503 } else if (str == _("Playback only")) {
504 cmd.push_back ("-P");
505 } else if (str == _("Recording only")) {
506 cmd.push_back ("-C");
510 cmd.push_back ("-n");
511 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
515 cmd.push_back ("-r");
516 cmd.push_back (to_string (get_rate(), std::dec));
518 cmd.push_back ("-p");
519 cmd.push_back (period_size_combo.get_active_text());
521 if (using_alsa || using_ffado || using_coreaudio) {
523 double val = input_latency_adjustment.get_value();
526 cmd.push_back ("-I");
527 cmd.push_back (to_string ((uint32_t) val, std::dec));
530 val = output_latency_adjustment.get_value();
533 cmd.push_back ("-O");
534 cmd.push_back (to_string ((uint32_t) val, std::dec));
540 if (audio_mode_combo.get_active_text() != _("Playback/recording on 2 devices")) {
542 string device = get_device_name (driver, interface_combo.get_active_text());
543 if (device.empty()) {
548 cmd.push_back ("-d");
549 cmd.push_back (device);
552 if (hw_meter_button.get_active()) {
553 cmd.push_back ("-M");
556 if (hw_monitor_button.get_active()) {
557 cmd.push_back ("-H");
560 str = dither_mode_combo.get_active_text();
562 if (str == _("None")) {
563 } else if (str == _("Triangular")) {
564 cmd.push_back ("-z triangular");
565 } else if (str == _("Rectangular")) {
566 cmd.push_back ("-z rectangular");
567 } else if (str == _("Shaped")) {
568 cmd.push_back ("-z shaped");
571 if (force16bit_button.get_active()) {
572 cmd.push_back ("-S");
575 if (soft_mode_button.get_active()) {
576 cmd.push_back ("-s");
579 str = midi_driver_combo.get_active_text ();
581 if (str == _("seq")) {
582 cmd.push_back ("-X seq");
583 } else if (str == _("raw")) {
584 cmd.push_back ("-X raw");
586 } else if (using_coreaudio) {
589 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
591 string device = get_device_name (driver, interface_combo.get_active_text());
592 if (device.empty()) {
597 cmd.push_back ("-d");
598 cmd.push_back (device);
605 EngineControl::need_setup ()
607 return !engine_running();
611 EngineControl::engine_running ()
613 EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
614 boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
616 /* revert all environment settings back to whatever they were when
617 * ardour started, because ardour's startup script may have reset
618 * something in ways that interfere with finding/starting JACK.
622 current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
623 global_epa->restore ();
626 jack_status_t status;
627 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
630 jack_client_close (c);
637 EngineControl::setup_engine ()
640 std::string cwd = "/tmp";
642 build_command_line (args);
645 return 1; // try again
648 std::string jackdrc_path = Glib::get_home_dir();
649 jackdrc_path += "/.jackdrc";
651 ofstream jackdrc (jackdrc_path.c_str());
653 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
657 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
658 jackdrc << (*i) << ' ';
670 EngineControl::enumerate_devices (const string& driver)
672 /* note: case matters for the map keys */
674 if (driver == "CoreAudio") {
676 devices[driver] = enumerate_coreaudio_devices ();
679 #if !defined(__APPLE__) && !defined(__FreeBSD__)
680 } else if (driver == "ALSA") {
681 devices[driver] = enumerate_alsa_devices ();
682 } else if (driver == "FreeBOB") {
683 devices[driver] = enumerate_freebob_devices ();
684 } else if (driver == "FFADO") {
685 devices[driver] = enumerate_ffado_devices ();
686 } else if (driver == "OSS") {
687 devices[driver] = enumerate_oss_devices ();
688 } else if (driver == "Dummy") {
689 devices[driver] = enumerate_dummy_devices ();
690 } else if (driver == "NetJACK") {
691 devices[driver] = enumerate_netjack_devices ();
700 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
702 UInt32 size = sizeof(CFStringRef);
704 OSStatus res = AudioDeviceGetProperty(id, 0, false,
705 kAudioDevicePropertyDeviceUID, &size, &UI);
707 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
713 EngineControl::enumerate_coreaudio_devices ()
717 // Find out how many Core Audio devices are there, if any...
718 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
721 UInt32 outSize = sizeof(isWritable);
723 backend_devs.clear ();
725 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
726 &outSize, &isWritable);
728 // Calculate the number of device available...
729 int numCoreDevices = outSize / sizeof(AudioDeviceID);
730 // Make space for the devices we are about to get...
731 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
732 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
733 &outSize, (void *) coreDeviceIDs);
735 // Look for the CoreAudio device name...
736 char coreDeviceName[256];
739 for (int i = 0; i < numCoreDevices; i++) {
741 nameSize = sizeof (coreDeviceName);
743 /* enforce duplex devices only */
745 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
746 0, true, kAudioDevicePropertyStreams,
747 &outSize, &isWritable);
749 if (err != noErr || outSize == 0) {
753 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
754 0, false, kAudioDevicePropertyStreams,
755 &outSize, &isWritable);
757 if (err != noErr || outSize == 0) {
761 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
762 0, true, kAudioDevicePropertyDeviceName,
763 &outSize, &isWritable);
765 err = AudioDeviceGetProperty(coreDeviceIDs[i],
766 0, true, kAudioDevicePropertyDeviceName,
767 &nameSize, (void *) coreDeviceName);
769 char drivername[128];
771 // this returns the unique id for the device
772 // that must be used on the commandline for jack
774 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
775 devs.push_back (coreDeviceName);
776 backend_devs.push_back (drivername);
782 delete [] coreDeviceIDs;
786 if (devs.size() == 0) {
787 MessageDialog msg (string_compose (_("\
788 You do not have any audio devices capable of\n\
789 simultaneous playback and recording.\n\n\
790 Please use Applications -> Utilities -> Audio MIDI Setup\n\
791 to create an \"aggregrate\" device, or install a suitable\n\
792 audio interface.\n\n\
793 Please send email to Apple and ask them why new Macs\n\
794 have no duplex audio device.\n\n\
795 Alternatively, if you really want just playback\n\
796 or recording but not both, start JACK before running\n\
797 %1 and choose the relevant device then."
799 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
800 msg.set_title (_("No suitable audio devices"));
801 msg.set_position (Gtk::WIN_POS_MOUSE);
811 #if !defined(__FreeBSD__)
813 EngineControl::enumerate_alsa_devices ()
818 snd_ctl_card_info_t *info;
819 snd_pcm_info_t *pcminfo;
820 snd_ctl_card_info_alloca(&info);
821 snd_pcm_info_alloca(&pcminfo);
826 backend_devs.clear ();
828 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
831 devname += to_string (cardnum, std::dec);
833 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
835 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
837 snd_pcm_info_set_device (pcminfo, device);
838 snd_pcm_info_set_subdevice (pcminfo, 0);
839 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
841 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
842 devs.push_back (snd_pcm_info_get_name (pcminfo));
844 devname += to_string (device, std::dec);
845 backend_devs.push_back (devname);
849 snd_ctl_close(handle);
858 EngineControl::enumerate_ffado_devices ()
861 backend_devs.clear ();
866 EngineControl::enumerate_freebob_devices ()
873 EngineControl::enumerate_oss_devices ()
879 EngineControl::enumerate_dummy_devices ()
885 EngineControl::enumerate_netjack_devices ()
893 EngineControl::driver_changed ()
895 string driver = driver_combo.get_active_text();
896 string::size_type maxlen = 0;
899 enumerate_devices (driver);
901 vector<string>& strings = devices[driver];
903 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
907 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
908 if ((*i).length() > maxlen) {
909 maxlen = (*i).length();
913 set_popdown_strings (interface_combo, strings);
914 set_popdown_strings (input_device_combo, strings);
915 set_popdown_strings (output_device_combo, strings);
917 if (!strings.empty()) {
918 interface_combo.set_active_text (strings.front());
919 input_device_combo.set_active_text (strings.front());
920 output_device_combo.set_active_text (strings.front());
923 if (driver == "ALSA") {
924 soft_mode_button.set_sensitive (true);
925 force16bit_button.set_sensitive (true);
926 hw_monitor_button.set_sensitive (true);
927 hw_meter_button.set_sensitive (true);
928 monitor_button.set_sensitive (true);
930 soft_mode_button.set_sensitive (false);
931 force16bit_button.set_sensitive (false);
932 hw_monitor_button.set_sensitive (false);
933 hw_meter_button.set_sensitive (false);
934 monitor_button.set_sensitive (false);
939 EngineControl::get_rate ()
941 double r = atof (sample_rate_combo.get_active_text ());
942 /* the string may have been translated with an abbreviation for
943 * thousands, so use a crude heuristic to fix this.
952 EngineControl::redisplay_latency ()
954 uint32_t rate = get_rate();
955 #if defined(__APPLE__) || defined(__FreeBSD__)
958 float periods = periods_adjustment.get_value();
960 float period_size = atof (period_size_combo.get_active_text());
963 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
965 latency_label.set_text (buf);
966 latency_label.set_alignment (0, 0.5);
970 EngineControl::audio_mode_changed ()
972 std::string str = audio_mode_combo.get_active_text();
974 if (str == _("Playback/recording on 1 device")) {
975 input_device_combo.set_sensitive (false);
976 output_device_combo.set_sensitive (false);
977 } else if (str == _("Playback/recording on 2 devices")) {
978 input_device_combo.set_sensitive (true);
979 output_device_combo.set_sensitive (true);
980 } else if (str == _("Playback only")) {
981 output_device_combo.set_sensitive (true);
982 input_device_combo.set_sensitive (false);
983 } else if (str == _("Recording only")) {
984 input_device_combo.set_sensitive (true);
985 output_device_combo.set_sensitive (false);
989 static bool jack_server_filter(const string& str, void */*arg*/)
991 return str == "jackd" || str == "jackdmp";
995 EngineControl::find_jack_servers (vector<string>& strings)
998 /* this magic lets us finds the path to the OSX bundle, and then
999 we infer JACK's location from there
1002 char execpath[MAXPATHLEN+1];
1003 uint32_t pathsz = sizeof (execpath);
1005 _NSGetExecutablePath (execpath, &pathsz);
1007 string path (Glib::path_get_dirname (execpath));
1010 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
1011 strings.push_back (path);
1014 if (getenv ("ARDOUR_WITH_JACK")) {
1015 /* no other options - only use the JACK we supply */
1016 if (strings.empty()) {
1017 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1026 PathScanner scanner;
1027 vector<string *> *jack_servers;
1028 std::map<string,int> un;
1030 bool need_minimal_path = false;
1032 p = getenv ("PATH");
1037 need_minimal_path = true;
1041 // many mac users don't have PATH set up to include
1042 // likely installed locations of JACK
1043 need_minimal_path = true;
1046 if (need_minimal_path) {
1048 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1050 path += ":/usr/local/bin:/opt/local/bin";
1055 // push it back into the environment so that auto-started JACK can find it.
1056 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1057 setenv ("PATH", path.c_str(), 1);
1060 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1061 if (!jack_servers) {
1065 vector<string *>::iterator iter;
1067 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1071 strings.push_back(p);
1078 EngineControl::get_device_name (const string& driver, const string& human_readable)
1080 vector<string>::iterator n;
1081 vector<string>::iterator i;
1083 if (human_readable.empty()) {
1084 /* this can happen if the user's .ardourrc file has a device name from
1085 another computer system in it
1087 MessageDialog msg (_("You need to choose an audio device first."));
1088 msg.set_position (WIN_POS_MOUSE);
1093 if (backend_devs.empty()) {
1094 return human_readable;
1097 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1098 if (human_readable == (*i)) {
1103 if (i == devices[driver].end()) {
1104 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1111 EngineControl::get_state ()
1113 XMLNode* root = new XMLNode ("AudioSetup");
1117 child = new XMLNode ("periods");
1118 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1119 root->add_child_nocopy (*child);
1121 child = new XMLNode ("ports");
1122 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1123 root->add_child_nocopy (*child);
1125 child = new XMLNode ("inlatency");
1126 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1127 root->add_child_nocopy (*child);
1129 child = new XMLNode ("outlatency");
1130 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1131 root->add_child_nocopy (*child);
1133 child = new XMLNode ("realtime");
1134 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1135 root->add_child_nocopy (*child);
1137 child = new XMLNode ("nomemorylock");
1138 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1139 root->add_child_nocopy (*child);
1141 child = new XMLNode ("unlockmemory");
1142 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1143 root->add_child_nocopy (*child);
1145 child = new XMLNode ("softmode");
1146 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1147 root->add_child_nocopy (*child);
1149 child = new XMLNode ("force16bit");
1150 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1151 root->add_child_nocopy (*child);
1153 child = new XMLNode ("hwmonitor");
1154 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1155 root->add_child_nocopy (*child);
1157 child = new XMLNode ("hwmeter");
1158 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1159 root->add_child_nocopy (*child);
1161 child = new XMLNode ("verbose");
1162 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1163 root->add_child_nocopy (*child);
1165 child = new XMLNode ("samplerate");
1166 child->add_property ("val", sample_rate_combo.get_active_text());
1167 root->add_child_nocopy (*child);
1169 child = new XMLNode ("periodsize");
1170 child->add_property ("val", period_size_combo.get_active_text());
1171 root->add_child_nocopy (*child);
1173 child = new XMLNode ("serverpath");
1174 child->add_property ("val", serverpath_combo.get_active_text());
1175 root->add_child_nocopy (*child);
1177 child = new XMLNode ("driver");
1178 child->add_property ("val", driver_combo.get_active_text());
1179 root->add_child_nocopy (*child);
1181 child = new XMLNode ("interface");
1182 child->add_property ("val", interface_combo.get_active_text());
1183 root->add_child_nocopy (*child);
1185 child = new XMLNode ("timeout");
1186 child->add_property ("val", timeout_combo.get_active_text());
1187 root->add_child_nocopy (*child);
1189 child = new XMLNode ("dither");
1190 child->add_property ("val", dither_mode_combo.get_active_text());
1191 root->add_child_nocopy (*child);
1193 child = new XMLNode ("audiomode");
1194 child->add_property ("val", audio_mode_combo.get_active_text());
1195 root->add_child_nocopy (*child);
1197 child = new XMLNode ("inputdevice");
1198 child->add_property ("val", input_device_combo.get_active_text());
1199 root->add_child_nocopy (*child);
1201 child = new XMLNode ("outputdevice");
1202 child->add_property ("val", output_device_combo.get_active_text());
1203 root->add_child_nocopy (*child);
1205 child = new XMLNode ("mididriver");
1206 child->add_property ("val", midi_driver_combo.get_active_text());
1207 root->add_child_nocopy (*child);
1213 EngineControl::set_state (const XMLNode& root)
1216 XMLNodeConstIterator citer;
1218 XMLProperty* prop = NULL;
1219 bool using_dummy = false;
1220 bool using_ffado = false;
1225 if ( (child = root.child ("driver"))){
1226 prop = child->property("val");
1228 if (prop && (prop->value() == "Dummy") ) {
1231 if (prop && (prop->value() == "FFADO") ) {
1237 clist = root.children();
1239 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1243 prop = child->property ("val");
1245 if (!prop || prop->value().empty()) {
1247 if (((using_dummy || using_ffado)
1248 && ( child->name() == "interface"
1249 || child->name() == "inputdevice"
1250 || child->name() == "outputdevice"))
1251 || child->name() == "timeout")
1256 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1260 strval = prop->value();
1262 /* adjustments/spinners */
1264 if (child->name() == "periods") {
1265 val = atoi (strval);
1266 periods_adjustment.set_value(val);
1267 } else if (child->name() == "ports") {
1268 val = atoi (strval);
1269 ports_adjustment.set_value(val);
1270 } else if (child->name() == "inlatency") {
1271 val = atoi (strval);
1272 input_latency.set_value(val);
1273 } else if (child->name() == "outlatency") {
1274 val = atoi (strval);
1275 output_latency.set_value(val);
1280 else if (child->name() == "realtime") {
1281 val = atoi (strval);
1282 realtime_button.set_active(val);
1283 } else if (child->name() == "nomemorylock") {
1284 val = atoi (strval);
1285 no_memory_lock_button.set_active(val);
1286 } else if (child->name() == "unlockmemory") {
1287 val = atoi (strval);
1288 unlock_memory_button.set_active(val);
1289 } else if (child->name() == "softmode") {
1290 val = atoi (strval);
1291 soft_mode_button.set_active(val);
1292 } else if (child->name() == "force16bit") {
1293 val = atoi (strval);
1294 force16bit_button.set_active(val);
1295 } else if (child->name() == "hwmonitor") {
1296 val = atoi (strval);
1297 hw_monitor_button.set_active(val);
1298 } else if (child->name() == "hwmeter") {
1299 val = atoi (strval);
1300 hw_meter_button.set_active(val);
1301 } else if (child->name() == "verbose") {
1302 val = atoi (strval);
1303 verbose_output_button.set_active(val);
1308 else if (child->name() == "samplerate") {
1309 sample_rate_combo.set_active_text(strval);
1310 } else if (child->name() == "periodsize") {
1311 period_size_combo.set_active_text(strval);
1312 } else if (child->name() == "serverpath") {
1314 /* only attempt to set this if we have bothered to look
1315 up server names already. otherwise this is all
1316 redundant (actually, all of this dialog/widget
1317 is redundant in that case ...)
1320 if (!server_strings.empty()) {
1321 /* do not allow us to use a server path that doesn't
1322 exist on this system. this handles cases where
1323 the user has an RC file listing a serverpath
1324 from some other machine.
1326 vector<string>::iterator x;
1327 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1332 if (x != server_strings.end()) {
1333 serverpath_combo.set_active_text (strval);
1335 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1341 } else if (child->name() == "driver") {
1342 driver_combo.set_active_text(strval);
1343 } else if (child->name() == "interface") {
1344 interface_combo.set_active_text(strval);
1345 } else if (child->name() == "timeout") {
1346 timeout_combo.set_active_text(strval);
1347 } else if (child->name() == "dither") {
1348 dither_mode_combo.set_active_text(strval);
1349 } else if (child->name() == "audiomode") {
1350 audio_mode_combo.set_active_text(strval);
1351 } else if (child->name() == "inputdevice") {
1352 input_device_combo.set_active_text(strval);
1353 } else if (child->name() == "outputdevice") {
1354 output_device_combo.set_active_text(strval);
1355 } else if (child->name() == "mididriver") {
1356 midi_driver_combo.set_active_text(strval);