2 Copyright (C) 2010 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <boost/scoped_ptr.hpp>
28 #include <gtkmm/messagedialog.h>
31 #include "pbd/xml++.h"
34 #include <CoreAudio/CoreAudio.h>
35 #include <CoreFoundation/CFString.h>
36 #include <sys/param.h>
37 #include <mach-o/dyld.h>
39 #include <alsa/asoundlib.h>
42 #include <jack/jack.h>
44 #include <gtkmm/stock.h>
45 #include <gtkmm2ext/utils.h>
47 #include "pbd/convert.h"
48 #include "pbd/error.h"
49 #include "pbd/pathscanner.h"
55 #include "engine_dialog.h"
60 using namespace Gtkmm2ext;
64 EngineControl::EngineControl ()
65 : periods_adjustment (2, 2, 16, 1, 2),
66 periods_spinner (periods_adjustment),
67 priority_adjustment (60, 10, 90, 1, 10),
68 priority_spinner (priority_adjustment),
69 ports_adjustment (128, 8, 1024, 1, 16),
70 ports_spinner (ports_adjustment),
71 input_latency_adjustment (0, 0, 99999, 1),
72 input_latency (input_latency_adjustment),
73 output_latency_adjustment (0, 0, 99999, 1),
74 output_latency (output_latency_adjustment),
75 realtime_button (_("Realtime")),
76 no_memory_lock_button (_("Do not lock memory")),
77 unlock_memory_button (_("Unlock memory")),
78 soft_mode_button (_("No zombies")),
79 monitor_button (_("Provide monitor ports")),
80 force16bit_button (_("Force 16 bit")),
81 hw_monitor_button (_("H/W monitoring")),
82 hw_meter_button (_("H/W metering")),
83 verbose_output_button (_("Verbose output")),
84 start_button (_("Start")),
85 stop_button (_("Stop")),
88 options_packer (4, 2),
92 options_packer (14, 2),
96 using namespace Notebook_Helpers;
98 vector<string> strings;
103 strings.push_back (_("8000Hz"));
104 strings.push_back (_("22050Hz"));
105 strings.push_back (_("44100Hz"));
106 strings.push_back (_("48000Hz"));
107 strings.push_back (_("88200Hz"));
108 strings.push_back (_("96000Hz"));
109 strings.push_back (_("192000Hz"));
110 set_popdown_strings (sample_rate_combo, strings);
111 sample_rate_combo.set_active_text ("48000Hz");
114 strings.push_back ("32");
115 strings.push_back ("64");
116 strings.push_back ("128");
117 strings.push_back ("256");
118 strings.push_back ("512");
119 strings.push_back ("1024");
120 strings.push_back ("2048");
121 strings.push_back ("4096");
122 strings.push_back ("8192");
123 set_popdown_strings (period_size_combo, strings);
124 period_size_combo.set_active_text ("1024");
127 strings.push_back (_("None"));
128 strings.push_back (_("Triangular"));
129 strings.push_back (_("Rectangular"));
130 strings.push_back (_("Shaped"));
131 set_popdown_strings (dither_mode_combo, strings);
132 dither_mode_combo.set_active_text (_("None"));
134 /* basic parameters */
136 basic_packer.set_spacings (6);
140 strings.push_back (X_("CoreAudio"));
142 strings.push_back (X_("ALSA"));
143 strings.push_back (X_("OSS"));
144 strings.push_back (X_("FreeBoB"));
145 strings.push_back (X_("FFADO"));
147 strings.push_back (X_("NetJACK"));
148 strings.push_back (X_("Dummy"));
149 set_popdown_strings (driver_combo, strings);
150 driver_combo.set_active_text (strings.front());
152 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
156 strings.push_back (_("Playback/recording on 1 device"));
157 strings.push_back (_("Playback/recording on 2 devices"));
158 strings.push_back (_("Playback only"));
159 strings.push_back (_("Recording only"));
160 set_popdown_strings (audio_mode_combo, strings);
161 audio_mode_combo.set_active_text (strings.front());
163 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
164 audio_mode_changed ();
167 strings.push_back (_("None"));
168 strings.push_back (_("seq"));
169 strings.push_back (_("raw"));
170 set_popdown_strings (midi_driver_combo, strings);
171 midi_driver_combo.set_active_text (strings.front ());
175 label = manage (new Label (_("Driver:")));
176 label->set_alignment (0, 0.5);
177 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
178 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
181 label = manage (new Label (_("Audio Interface:")));
182 label->set_alignment (0, 0.5);
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 (new Label (_("Sample rate:")));
188 label->set_alignment (0, 0.5);
189 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
190 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
193 label = manage (new Label (_("Buffer size:")));
194 label->set_alignment (0, 0.5);
195 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
196 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
200 label = manage (new Label (_("Number of buffers:")));
201 label->set_alignment (0, 0.5);
202 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
203 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
204 periods_spinner.set_value (2);
208 label = manage (new Label (_("Approximate latency:")));
209 label->set_alignment (0, 0.5);
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' */
222 label = manage (new Label (_("Audio mode:")));
223 label->set_alignment (0, 0.5);
224 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
225 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
229 interface_combo.set_size_request (250, -1);
230 input_device_combo.set_size_request (250, -1);
231 output_device_combo.set_size_request (250, -1);
235 if (engine_running()) {
236 start_button.set_sensitive (false);
238 stop_button.set_sensitive (false);
241 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
242 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
245 button_box.pack_start (start_button, false, false);
246 button_box.pack_start (stop_button, false, false);
248 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
252 options_packer.set_spacings (6);
255 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
258 realtime_button.set_active (true);
259 realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
262 #if PROVIDE_TOO_MANY_OPTIONS
265 label = manage (new Label (_("Realtime Priority")));
266 label->set_alignment (1.0, 0.5);
267 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
268 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
270 priority_spinner.set_value (60);
272 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
274 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
276 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
278 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
280 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
282 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
284 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
286 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
289 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
294 strings.push_back (_("Ignore"));
295 strings.push_back ("500 msec");
296 strings.push_back ("1 sec");
297 strings.push_back ("2 sec");
298 strings.push_back ("10 sec");
299 set_popdown_strings (timeout_combo, strings);
300 timeout_combo.set_active_text (strings.front ());
302 label = manage (new Label (_("Client timeout")));
303 label->set_alignment (1.0, 0.5);
304 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
305 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
308 #endif /* PROVIDE_TOO_MANY_OPTIONS */
309 label = manage (new Label (_("Number of ports:")));
310 label->set_alignment (0, 0.5);
311 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
312 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
315 label = manage (new Label (_("MIDI driver:")));
316 label->set_alignment (0, 0.5);
317 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
318 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
322 label = manage (new Label (_("Dither:")));
323 label->set_alignment (0, 0.5);
324 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
325 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
329 find_jack_servers (server_strings);
331 if (server_strings.empty()) {
332 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
336 set_popdown_strings (serverpath_combo, server_strings);
337 serverpath_combo.set_active_text (server_strings.front());
339 if (server_strings.size() > 1) {
340 label = manage (new Label (_("Server:")));
341 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
342 label->set_alignment (0.0, 0.5);
343 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
347 /* device settings */
349 device_packer.set_spacings (6);
353 label = manage (new Label (_("Input device:")));
354 label->set_alignment (0, 0.5);
355 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
356 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
358 label = manage (new Label (_("Output device:")));
359 label->set_alignment (0, 0.5);
360 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
361 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
364 label = manage (new Label (_("Hardware input latency:")));
365 label->set_alignment (0, 0.5);
366 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
367 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
368 label = manage (new Label (_("samples")));
369 label->set_alignment (0, 0.5);
370 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
372 label = manage (new Label (_("Hardware output latency:")));
373 label->set_alignment (0, 0.5);
374 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
375 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
376 label = manage (new Label (_("samples")));
377 label->set_alignment (0, 0.5);
378 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
381 basic_hbox.pack_start (basic_packer, false, false);
382 options_hbox.pack_start (options_packer, false, false);
384 device_packer.set_border_width (12);
385 options_packer.set_border_width (12);
386 basic_packer.set_border_width (12);
388 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
389 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
390 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
391 notebook.set_border_width (12);
393 set_border_width (12);
394 pack_start (notebook);
397 EngineControl::~EngineControl ()
403 EngineControl::build_command_line (vector<string>& cmd)
407 bool using_alsa = false;
408 bool using_coreaudio = false;
409 bool using_dummy = false;
410 bool using_ffado = false;
412 /* first, path to jackd */
414 cmd.push_back (serverpath_combo.get_active_text ());
416 /* now jackd arguments */
418 str = timeout_combo.get_active_text ();
420 if (str != _("Ignore")) {
425 msecs = (uint32_t) floor (secs * 1000.0);
428 cmd.push_back ("-t");
429 cmd.push_back (to_string (msecs, std::dec));
433 if (no_memory_lock_button.get_active()) {
434 cmd.push_back ("-m"); /* no munlock */
437 cmd.push_back ("-p"); /* port max */
438 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
440 if (realtime_button.get_active()) {
441 cmd.push_back ("-R");
442 cmd.push_back ("-P");
443 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
445 cmd.push_back ("-r"); /* override jackd's default --realtime */
448 if (unlock_memory_button.get_active()) {
449 cmd.push_back ("-u");
452 if (verbose_output_button.get_active()) {
453 cmd.push_back ("-v");
456 /* now add fixed arguments (not user-selectable) */
458 cmd.push_back ("-T"); // temporary */
460 /* next the driver */
462 cmd.push_back ("-d");
464 driver = driver_combo.get_active_text ();
466 if (driver == X_("ALSA")) {
468 cmd.push_back ("alsa");
469 } else if (driver == X_("OSS")) {
470 cmd.push_back ("oss");
471 } else if (driver == X_("CoreAudio")) {
472 using_coreaudio = true;
473 cmd.push_back ("coreaudio");
474 } else if (driver == X_("NetJACK")) {
475 cmd.push_back ("netjack");
476 } else if (driver == X_("FreeBoB")) {
477 cmd.push_back ("freebob");
478 } else if (driver == X_("FFADO")) {
480 cmd.push_back ("firewire");
481 } else if ( driver == X_("Dummy")) {
483 cmd.push_back ("dummy");
486 /* driver arguments */
488 if (!using_coreaudio) {
489 str = audio_mode_combo.get_active_text();
491 if (str == _("Playback/Recording on 1 Device")) {
495 } else if (str == _("Playback/Recording on 2 Devices")) {
497 string input_device = get_device_name (driver, input_device_combo.get_active_text());
498 string output_device = get_device_name (driver, output_device_combo.get_active_text());
500 if (input_device.empty() || output_device.empty()) {
505 cmd.push_back ("-C");
506 cmd.push_back (input_device);
508 cmd.push_back ("-P");
509 cmd.push_back (output_device);
511 } else if (str == _("Playback only")) {
512 cmd.push_back ("-P");
513 } else if (str == _("Recording only")) {
514 cmd.push_back ("-C");
518 cmd.push_back ("-n");
519 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
523 cmd.push_back ("-r");
524 cmd.push_back (to_string (get_rate(), std::dec));
526 cmd.push_back ("-p");
527 cmd.push_back (period_size_combo.get_active_text());
529 if (using_alsa || using_ffado || using_coreaudio) {
531 double val = input_latency_adjustment.get_value();
534 cmd.push_back ("-I");
535 cmd.push_back (to_string ((uint32_t) val, std::dec));
538 val = output_latency_adjustment.get_value();
541 cmd.push_back ("-O");
542 cmd.push_back (to_string ((uint32_t) val, std::dec));
548 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
550 string device = get_device_name (driver, interface_combo.get_active_text());
551 if (device.empty()) {
556 cmd.push_back ("-d");
557 cmd.push_back (device);
560 if (hw_meter_button.get_active()) {
561 cmd.push_back ("-M");
564 if (hw_monitor_button.get_active()) {
565 cmd.push_back ("-H");
568 str = dither_mode_combo.get_active_text();
570 if (str == _("None")) {
571 } else if (str == _("Triangular")) {
572 cmd.push_back ("-z triangular");
573 } else if (str == _("Rectangular")) {
574 cmd.push_back ("-z rectangular");
575 } else if (str == _("Shaped")) {
576 cmd.push_back ("-z shaped");
579 if (force16bit_button.get_active()) {
580 cmd.push_back ("-S");
583 if (soft_mode_button.get_active()) {
584 cmd.push_back ("-s");
587 str = midi_driver_combo.get_active_text ();
589 if (str == _("seq")) {
590 cmd.push_back ("-X seq");
591 } else if (str == _("raw")) {
592 cmd.push_back ("-X raw");
594 } else if (using_coreaudio) {
597 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
599 string device = get_device_name (driver, interface_combo.get_active_text());
600 if (device.empty()) {
605 cmd.push_back ("-d");
606 cmd.push_back (device);
613 EngineControl::engine_running ()
615 EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
616 boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
618 /* revert all environment settings back to whatever they were when ardour started
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;
656 cerr << "JACK COMMAND: ";
657 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
659 jackdrc << (*i) << ' ';
671 EngineControl::realtime_changed ()
674 priority_spinner.set_sensitive (realtime_button.get_active());
679 EngineControl::enumerate_devices (const string& driver)
681 /* note: case matters for the map keys */
683 if (driver == "CoreAudio") {
685 devices[driver] = enumerate_coreaudio_devices ();
689 } else if (driver == "ALSA") {
690 devices[driver] = enumerate_alsa_devices ();
691 } else if (driver == "FreeBOB") {
692 devices[driver] = enumerate_freebob_devices ();
693 } else if (driver == "FFADO") {
694 devices[driver] = enumerate_ffado_devices ();
695 } else if (driver == "OSS") {
696 devices[driver] = enumerate_oss_devices ();
697 } else if (driver == "Dummy") {
698 devices[driver] = enumerate_dummy_devices ();
699 } else if (driver == "NetJACK") {
700 devices[driver] = enumerate_netjack_devices ();
709 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
711 UInt32 size = sizeof(CFStringRef);
713 OSStatus res = AudioDeviceGetProperty(id, 0, false,
714 kAudioDevicePropertyDeviceUID, &size, &UI);
716 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
722 EngineControl::enumerate_coreaudio_devices ()
726 // Find out how many Core Audio devices are there, if any...
727 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
730 UInt32 outSize = sizeof(isWritable);
732 backend_devs.clear ();
734 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
735 &outSize, &isWritable);
737 // Calculate the number of device available...
738 int numCoreDevices = outSize / sizeof(AudioDeviceID);
739 // Make space for the devices we are about to get...
740 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
741 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
742 &outSize, (void *) coreDeviceIDs);
744 // Look for the CoreAudio device name...
745 char coreDeviceName[256];
748 for (int i = 0; i < numCoreDevices; i++) {
750 nameSize = sizeof (coreDeviceName);
752 /* enforce duplex devices only */
754 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
755 0, true, kAudioDevicePropertyStreams,
756 &outSize, &isWritable);
758 if (err != noErr || outSize == 0) {
762 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
763 0, false, kAudioDevicePropertyStreams,
764 &outSize, &isWritable);
766 if (err != noErr || outSize == 0) {
770 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
771 0, true, kAudioDevicePropertyDeviceName,
772 &outSize, &isWritable);
774 err = AudioDeviceGetProperty(coreDeviceIDs[i],
775 0, true, kAudioDevicePropertyDeviceName,
776 &nameSize, (void *) coreDeviceName);
778 char drivername[128];
780 // this returns the unique id for the device
781 // that must be used on the commandline for jack
783 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
784 devs.push_back (coreDeviceName);
785 backend_devs.push_back (drivername);
791 delete [] coreDeviceIDs;
795 if (devs.size() == 0) {
796 MessageDialog msg (_("\
797 You do not have any audio devices capable of\n\
798 simultaneous playback and recording.\n\n\
799 Please use Applications -> Utilities -> Audio MIDI Setup\n\
800 to create an \"aggregrate\" device, or install a suitable\n\
801 audio interface.\n\n\
802 Please send email to Apple and ask them why new Macs\n\
803 have no duplex audio device.\n\n\
804 Alternatively, if you really want just playback\n\
805 or recording but not both, start JACK before running\n\
806 Ardour and choose the relevant device then."
808 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
809 msg.set_title (_("No suitable audio devices"));
810 msg.set_position (Gtk::WIN_POS_MOUSE);
820 EngineControl::enumerate_alsa_devices ()
825 snd_ctl_card_info_t *info;
826 snd_pcm_info_t *pcminfo;
827 snd_ctl_card_info_alloca(&info);
828 snd_pcm_info_alloca(&pcminfo);
833 backend_devs.clear ();
835 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
838 devname += to_string (cardnum, std::dec);
840 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
842 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
844 snd_pcm_info_set_device (pcminfo, device);
845 snd_pcm_info_set_subdevice (pcminfo, 0);
846 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
848 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
849 devs.push_back (snd_pcm_info_get_name (pcminfo));
851 devname += to_string (device, std::dec);
852 backend_devs.push_back (devname);
856 snd_ctl_close(handle);
864 EngineControl::enumerate_ffado_devices ()
867 backend_devs.clear ();
872 EngineControl::enumerate_freebob_devices ()
879 EngineControl::enumerate_oss_devices ()
885 EngineControl::enumerate_dummy_devices ()
891 EngineControl::enumerate_netjack_devices ()
899 EngineControl::driver_changed ()
901 string driver = driver_combo.get_active_text();
902 string::size_type maxlen = 0;
905 enumerate_devices (driver);
907 vector<string>& strings = devices[driver];
909 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
913 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
914 if ((*i).length() > maxlen) {
915 maxlen = (*i).length();
919 set_popdown_strings (interface_combo, strings);
920 set_popdown_strings (input_device_combo, strings);
921 set_popdown_strings (output_device_combo, strings);
923 if (!strings.empty()) {
924 interface_combo.set_active_text (strings.front());
925 input_device_combo.set_active_text (strings.front());
926 output_device_combo.set_active_text (strings.front());
929 if (driver == "ALSA") {
930 soft_mode_button.set_sensitive (true);
931 force16bit_button.set_sensitive (true);
932 hw_monitor_button.set_sensitive (true);
933 hw_meter_button.set_sensitive (true);
934 monitor_button.set_sensitive (true);
936 soft_mode_button.set_sensitive (false);
937 force16bit_button.set_sensitive (false);
938 hw_monitor_button.set_sensitive (false);
939 hw_meter_button.set_sensitive (false);
940 monitor_button.set_sensitive (false);
945 EngineControl::get_rate ()
947 double r = atof (sample_rate_combo.get_active_text ());
948 /* the string may have been translated with an abbreviation for
949 * thousands, so use a crude heuristic to fix this.
958 EngineControl::redisplay_latency ()
960 uint32_t rate = get_rate();
964 float periods = periods_adjustment.get_value();
966 float period_size = atof (period_size_combo.get_active_text());
969 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
971 latency_label.set_text (buf);
972 latency_label.set_alignment (0, 0.5);
976 EngineControl::audio_mode_changed ()
978 std::string str = audio_mode_combo.get_active_text();
980 if (str == _("Playback/Recording on 1 Device")) {
981 input_device_combo.set_sensitive (false);
982 output_device_combo.set_sensitive (false);
983 } else if (str == _("Playback/Recording on 2 Devices")) {
984 input_device_combo.set_sensitive (true);
985 output_device_combo.set_sensitive (true);
986 } else if (str == _("Playback only")) {
987 output_device_combo.set_sensitive (true);
988 input_device_combo.set_sensitive (false);
989 } else if (str == _("Recording only")) {
990 input_device_combo.set_sensitive (true);
991 output_device_combo.set_sensitive (false);
995 static bool jack_server_filter(const string& str, void */*arg*/)
997 return str == "jackd" || str == "jackdmp";
1001 EngineControl::find_jack_servers (vector<string>& strings)
1004 /* this magic lets us finds the path to the OSX bundle, and then
1005 we infer JACK's location from there
1008 char execpath[MAXPATHLEN+1];
1009 uint32_t pathsz = sizeof (execpath);
1011 _NSGetExecutablePath (execpath, &pathsz);
1013 string path (Glib::path_get_dirname (execpath));
1016 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
1017 strings.push_back (path);
1020 if (getenv ("ARDOUR_WITH_JACK")) {
1021 /* no other options - only use the JACK we supply */
1022 if (strings.empty()) {
1023 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1032 PathScanner scanner;
1033 vector<string *> *jack_servers;
1034 std::map<string,int> un;
1036 bool need_minimal_path = false;
1038 p = getenv ("PATH");
1043 need_minimal_path = true;
1047 // many mac users don't have PATH set up to include
1048 // likely installed locations of JACK
1049 need_minimal_path = true;
1052 if (need_minimal_path) {
1054 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1056 path += ":/usr/local/bin:/opt/local/bin";
1061 // push it back into the environment so that auto-started JACK can find it.
1062 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1063 setenv ("PATH", path.c_str(), 1);
1066 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1067 if (!jack_servers) {
1071 vector<string *>::iterator iter;
1073 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1077 strings.push_back(p);
1084 EngineControl::get_device_name (const string& driver, const string& human_readable)
1086 vector<string>::iterator n;
1087 vector<string>::iterator i;
1089 if (human_readable.empty()) {
1090 /* this can happen if the user's .ardourrc file has a device name from
1091 another computer system in it
1093 MessageDialog msg (_("You need to choose an audio device first."));
1098 if (backend_devs.empty()) {
1099 return human_readable;
1102 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1103 if (human_readable == (*i)) {
1108 if (i == devices[driver].end()) {
1109 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1116 EngineControl::get_state ()
1118 XMLNode* root = new XMLNode ("AudioSetup");
1122 child = new XMLNode ("periods");
1123 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1124 root->add_child_nocopy (*child);
1126 child = new XMLNode ("priority");
1127 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1128 root->add_child_nocopy (*child);
1130 child = new XMLNode ("ports");
1131 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1132 root->add_child_nocopy (*child);
1134 child = new XMLNode ("inlatency");
1135 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1136 root->add_child_nocopy (*child);
1138 child = new XMLNode ("outlatency");
1139 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1140 root->add_child_nocopy (*child);
1142 child = new XMLNode ("realtime");
1143 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1144 root->add_child_nocopy (*child);
1146 child = new XMLNode ("nomemorylock");
1147 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1148 root->add_child_nocopy (*child);
1150 child = new XMLNode ("unlockmemory");
1151 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1152 root->add_child_nocopy (*child);
1154 child = new XMLNode ("softmode");
1155 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1156 root->add_child_nocopy (*child);
1158 child = new XMLNode ("force16bit");
1159 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1160 root->add_child_nocopy (*child);
1162 child = new XMLNode ("hwmonitor");
1163 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1164 root->add_child_nocopy (*child);
1166 child = new XMLNode ("hwmeter");
1167 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1168 root->add_child_nocopy (*child);
1170 child = new XMLNode ("verbose");
1171 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1172 root->add_child_nocopy (*child);
1174 child = new XMLNode ("samplerate");
1175 child->add_property ("val", sample_rate_combo.get_active_text());
1176 root->add_child_nocopy (*child);
1178 child = new XMLNode ("periodsize");
1179 child->add_property ("val", period_size_combo.get_active_text());
1180 root->add_child_nocopy (*child);
1182 child = new XMLNode ("serverpath");
1183 child->add_property ("val", serverpath_combo.get_active_text());
1184 root->add_child_nocopy (*child);
1186 child = new XMLNode ("driver");
1187 child->add_property ("val", driver_combo.get_active_text());
1188 root->add_child_nocopy (*child);
1190 child = new XMLNode ("interface");
1191 child->add_property ("val", interface_combo.get_active_text());
1192 root->add_child_nocopy (*child);
1194 child = new XMLNode ("timeout");
1195 child->add_property ("val", timeout_combo.get_active_text());
1196 root->add_child_nocopy (*child);
1198 child = new XMLNode ("dither");
1199 child->add_property ("val", dither_mode_combo.get_active_text());
1200 root->add_child_nocopy (*child);
1202 child = new XMLNode ("audiomode");
1203 child->add_property ("val", audio_mode_combo.get_active_text());
1204 root->add_child_nocopy (*child);
1206 child = new XMLNode ("inputdevice");
1207 child->add_property ("val", input_device_combo.get_active_text());
1208 root->add_child_nocopy (*child);
1210 child = new XMLNode ("outputdevice");
1211 child->add_property ("val", output_device_combo.get_active_text());
1212 root->add_child_nocopy (*child);
1214 child = new XMLNode ("mididriver");
1215 child->add_property ("val", midi_driver_combo.get_active_text());
1216 root->add_child_nocopy (*child);
1222 EngineControl::set_state (const XMLNode& root)
1225 XMLNodeConstIterator citer;
1227 XMLProperty* prop = NULL;
1228 bool using_dummy = false;
1229 bool using_ffado = false;
1234 if ( (child = root.child ("driver"))){
1235 prop = child->property("val");
1237 if (prop && (prop->value() == "Dummy") ) {
1240 if (prop && (prop->value() == "FFADO") ) {
1246 clist = root.children();
1248 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1252 prop = child->property ("val");
1254 if (!prop || prop->value().empty()) {
1256 if (((using_dummy || using_ffado)
1257 && ( child->name() == "interface"
1258 || child->name() == "inputdevice"
1259 || child->name() == "outputdevice"))
1260 || child->name() == "timeout")
1265 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1269 strval = prop->value();
1271 /* adjustments/spinners */
1273 if (child->name() == "periods") {
1274 val = atoi (strval);
1275 periods_adjustment.set_value(val);
1276 } else if (child->name() == "priority") {
1277 val = atoi (strval);
1278 priority_adjustment.set_value(val);
1279 } else if (child->name() == "ports") {
1280 val = atoi (strval);
1281 ports_adjustment.set_value(val);
1282 } else if (child->name() == "inlatency") {
1283 val = atoi (strval);
1284 input_latency.set_value(val);
1285 } else if (child->name() == "outlatency") {
1286 val = atoi (strval);
1287 output_latency.set_value(val);
1292 else if (child->name() == "realtime") {
1293 val = atoi (strval);
1294 realtime_button.set_active(val);
1295 } else if (child->name() == "nomemorylock") {
1296 val = atoi (strval);
1297 no_memory_lock_button.set_active(val);
1298 } else if (child->name() == "unlockmemory") {
1299 val = atoi (strval);
1300 unlock_memory_button.set_active(val);
1301 } else if (child->name() == "softmode") {
1302 val = atoi (strval);
1303 soft_mode_button.set_active(val);
1304 } else if (child->name() == "force16bit") {
1305 val = atoi (strval);
1306 force16bit_button.set_active(val);
1307 } else if (child->name() == "hwmonitor") {
1308 val = atoi (strval);
1309 hw_monitor_button.set_active(val);
1310 } else if (child->name() == "hwmeter") {
1311 val = atoi (strval);
1312 hw_meter_button.set_active(val);
1313 } else if (child->name() == "verbose") {
1314 val = atoi (strval);
1315 verbose_output_button.set_active(val);
1320 else if (child->name() == "samplerate") {
1321 sample_rate_combo.set_active_text(strval);
1322 } else if (child->name() == "periodsize") {
1323 period_size_combo.set_active_text(strval);
1324 } else if (child->name() == "serverpath") {
1326 /* only attempt to set this if we have bothered to look
1327 up server names already. otherwise this is all
1328 redundant (actually, all of this dialog/widget
1329 is redundant in that case ...)
1332 if (!server_strings.empty()) {
1333 /* do not allow us to use a server path that doesn't
1334 exist on this system. this handles cases where
1335 the user has an RC file listing a serverpath
1336 from some other machine.
1338 vector<string>::iterator x;
1339 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1344 if (x != server_strings.end()) {
1345 serverpath_combo.set_active_text (strval);
1347 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1353 } else if (child->name() == "driver") {
1354 driver_combo.set_active_text(strval);
1355 } else if (child->name() == "interface") {
1356 interface_combo.set_active_text(strval);
1357 } else if (child->name() == "timeout") {
1358 timeout_combo.set_active_text(strval);
1359 } else if (child->name() == "dither") {
1360 dither_mode_combo.set_active_text(strval);
1361 } else if (child->name() == "audiomode") {
1362 audio_mode_combo.set_active_text(strval);
1363 } else if (child->name() == "inputdevice") {
1364 input_device_combo.set_active_text(strval);
1365 } else if (child->name() == "outputdevice") {
1366 output_device_combo.set_active_text(strval);
1367 } else if (child->name() == "mididriver") {
1368 midi_driver_combo.set_active_text(strval);