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 "ardour/profile.h"
43 #include <jack/jack.h>
45 #include <gtkmm/stock.h>
46 #include <gtkmm2ext/utils.h>
48 #include "pbd/convert.h"
49 #include "pbd/error.h"
50 #include "pbd/pathscanner.h"
56 #include "engine_dialog.h"
61 using namespace Gtkmm2ext;
65 EngineControl::EngineControl ()
66 : periods_adjustment (2, 2, 16, 1, 2),
67 periods_spinner (periods_adjustment),
68 priority_adjustment (60, 10, 90, 1, 10),
69 priority_spinner (priority_adjustment),
70 ports_adjustment (128, 8, 1024, 1, 16),
71 ports_spinner (ports_adjustment),
72 input_latency_adjustment (0, 0, 99999, 1),
73 input_latency (input_latency_adjustment),
74 output_latency_adjustment (0, 0, 99999, 1),
75 output_latency (output_latency_adjustment),
76 realtime_button (_("Realtime")),
77 no_memory_lock_button (_("Do not lock memory")),
78 unlock_memory_button (_("Unlock memory")),
79 soft_mode_button (_("No zombies")),
80 monitor_button (_("Provide monitor ports")),
81 force16bit_button (_("Force 16 bit")),
82 hw_monitor_button (_("H/W monitoring")),
83 hw_meter_button (_("H/W metering")),
84 verbose_output_button (_("Verbose output")),
85 start_button (_("Start")),
86 stop_button (_("Stop")),
89 options_packer (4, 2),
93 options_packer (14, 2),
97 using namespace Notebook_Helpers;
99 vector<string> strings;
104 strings.push_back (_("8000Hz"));
105 strings.push_back (_("22050Hz"));
106 strings.push_back (_("44100Hz"));
107 strings.push_back (_("48000Hz"));
108 strings.push_back (_("88200Hz"));
109 strings.push_back (_("96000Hz"));
110 strings.push_back (_("192000Hz"));
111 set_popdown_strings (sample_rate_combo, strings);
112 sample_rate_combo.set_active_text ("48000Hz");
115 strings.push_back ("32");
116 strings.push_back ("64");
117 strings.push_back ("128");
118 strings.push_back ("256");
119 strings.push_back ("512");
120 strings.push_back ("1024");
121 strings.push_back ("2048");
122 strings.push_back ("4096");
123 strings.push_back ("8192");
124 set_popdown_strings (period_size_combo, strings);
125 period_size_combo.set_active_text ("1024");
128 strings.push_back (_("None"));
129 strings.push_back (_("Triangular"));
130 strings.push_back (_("Rectangular"));
131 strings.push_back (_("Shaped"));
132 set_popdown_strings (dither_mode_combo, strings);
133 dither_mode_combo.set_active_text (_("None"));
135 /* basic parameters */
137 basic_packer.set_spacings (6);
141 strings.push_back (X_("CoreAudio"));
143 strings.push_back (X_("ALSA"));
144 strings.push_back (X_("OSS"));
145 strings.push_back (X_("FreeBoB"));
146 strings.push_back (X_("FFADO"));
148 strings.push_back (X_("NetJACK"));
149 strings.push_back (X_("Dummy"));
150 set_popdown_strings (driver_combo, strings);
151 driver_combo.set_active_text (strings.front());
153 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
157 strings.push_back (_("Playback/recording on 1 device"));
158 strings.push_back (_("Playback/recording on 2 devices"));
159 strings.push_back (_("Playback only"));
160 strings.push_back (_("Recording only"));
161 set_popdown_strings (audio_mode_combo, strings);
162 audio_mode_combo.set_active_text (strings.front());
164 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
165 audio_mode_changed ();
168 strings.push_back (_("None"));
169 strings.push_back (_("seq"));
170 strings.push_back (_("raw"));
171 set_popdown_strings (midi_driver_combo, strings);
172 midi_driver_combo.set_active_text (strings.front ());
176 label = manage (new Label (_("Driver:")));
177 label->set_alignment (0, 0.5);
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 (new Label (_("Audio Interface:")));
183 label->set_alignment (0, 0.5);
184 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
185 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
188 label = manage (new Label (_("Sample rate:")));
189 label->set_alignment (0, 0.5);
190 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
191 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
194 label = manage (new Label (_("Buffer size:")));
195 label->set_alignment (0, 0.5);
196 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
197 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
201 label = manage (new Label (_("Number of buffers:")));
202 label->set_alignment (0, 0.5);
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 (new Label (_("Approximate latency:")));
210 label->set_alignment (0, 0.5);
211 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
212 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
215 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
216 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
217 period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
220 /* no audio mode with CoreAudio, its duplex or nuthin' */
223 label = manage (new Label (_("Audio mode:")));
224 label->set_alignment (0, 0.5);
225 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
226 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
230 interface_combo.set_size_request (250, -1);
231 input_device_combo.set_size_request (250, -1);
232 output_device_combo.set_size_request (250, -1);
236 if (engine_running()) {
237 start_button.set_sensitive (false);
239 stop_button.set_sensitive (false);
242 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
243 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
246 button_box.pack_start (start_button, false, false);
247 button_box.pack_start (stop_button, false, false);
249 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
253 options_packer.set_spacings (6);
256 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
259 realtime_button.set_active (true);
260 realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
263 #if PROVIDE_TOO_MANY_OPTIONS
266 label = manage (new Label (_("Realtime Priority")));
267 label->set_alignment (1.0, 0.5);
268 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
269 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
271 priority_spinner.set_value (60);
273 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
275 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
277 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
279 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
281 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
283 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
285 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
287 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
290 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
295 strings.push_back (_("Ignore"));
296 strings.push_back ("500 msec");
297 strings.push_back ("1 sec");
298 strings.push_back ("2 sec");
299 strings.push_back ("10 sec");
300 set_popdown_strings (timeout_combo, strings);
301 timeout_combo.set_active_text (strings.front ());
303 label = manage (new Label (_("Client timeout")));
304 label->set_alignment (1.0, 0.5);
305 options_packer.attach (timeout_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 #endif /* PROVIDE_TOO_MANY_OPTIONS */
310 label = manage (new Label (_("Number of ports:")));
311 label->set_alignment (0, 0.5);
312 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
313 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
316 label = manage (new Label (_("MIDI driver:")));
317 label->set_alignment (0, 0.5);
318 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
319 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
323 label = manage (new Label (_("Dither:")));
324 label->set_alignment (0, 0.5);
325 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
326 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
330 find_jack_servers (server_strings);
332 if (server_strings.empty()) {
333 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
337 set_popdown_strings (serverpath_combo, server_strings);
338 serverpath_combo.set_active_text (server_strings.front());
340 if (server_strings.size() > 1) {
341 label = manage (new Label (_("Server:")));
342 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
343 label->set_alignment (0.0, 0.5);
344 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
348 /* device settings */
350 device_packer.set_spacings (6);
354 label = manage (new Label (_("Input device:")));
355 label->set_alignment (0, 0.5);
356 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
357 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
359 label = manage (new Label (_("Output device:")));
360 label->set_alignment (0, 0.5);
361 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
362 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
365 label = manage (new Label (_("Hardware input latency:")));
366 label->set_alignment (0, 0.5);
367 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
368 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
369 label = manage (new Label (_("samples")));
370 label->set_alignment (0, 0.5);
371 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
373 label = manage (new Label (_("Hardware output latency:")));
374 label->set_alignment (0, 0.5);
375 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
376 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
377 label = manage (new Label (_("samples")));
378 label->set_alignment (0, 0.5);
379 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
382 basic_hbox.pack_start (basic_packer, false, false);
383 options_hbox.pack_start (options_packer, false, false);
385 device_packer.set_border_width (12);
386 options_packer.set_border_width (12);
387 basic_packer.set_border_width (12);
389 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
390 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
391 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
392 notebook.set_border_width (12);
394 set_border_width (12);
395 pack_start (notebook);
398 EngineControl::~EngineControl ()
404 EngineControl::build_command_line (vector<string>& cmd)
408 bool using_alsa = false;
409 bool using_coreaudio = false;
410 bool using_dummy = false;
411 bool using_ffado = false;
413 /* first, path to jackd */
415 cmd.push_back (serverpath_combo.get_active_text ());
417 /* now jackd arguments */
419 str = timeout_combo.get_active_text ();
421 if (str != _("Ignore")) {
426 msecs = (uint32_t) floor (secs * 1000.0);
429 cmd.push_back ("-t");
430 cmd.push_back (to_string (msecs, std::dec));
434 if (no_memory_lock_button.get_active()) {
435 cmd.push_back ("-m"); /* no munlock */
438 cmd.push_back ("-p"); /* port max */
439 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
441 if (realtime_button.get_active()) {
442 cmd.push_back ("-R");
443 cmd.push_back ("-P");
444 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
446 cmd.push_back ("-r"); /* override jackd's default --realtime */
449 if (unlock_memory_button.get_active()) {
450 cmd.push_back ("-u");
453 if (verbose_output_button.get_active()) {
454 cmd.push_back ("-v");
457 /* now add fixed arguments (not user-selectable) */
459 cmd.push_back ("-T"); // temporary */
461 /* next the driver */
463 cmd.push_back ("-d");
465 driver = driver_combo.get_active_text ();
467 if (driver == X_("ALSA")) {
469 cmd.push_back ("alsa");
470 } else if (driver == X_("OSS")) {
471 cmd.push_back ("oss");
472 } else if (driver == X_("CoreAudio")) {
473 using_coreaudio = true;
474 cmd.push_back ("coreaudio");
475 } else if (driver == X_("NetJACK")) {
476 cmd.push_back ("netjack");
477 } else if (driver == X_("FreeBoB")) {
478 cmd.push_back ("freebob");
479 } else if (driver == X_("FFADO")) {
481 cmd.push_back ("firewire");
482 } else if ( driver == X_("Dummy")) {
484 cmd.push_back ("dummy");
487 /* driver arguments */
489 if (!using_coreaudio) {
490 str = audio_mode_combo.get_active_text();
492 if (str == _("Playback/Recording on 1 Device")) {
496 } else if (str == _("Playback/Recording on 2 Devices")) {
498 string input_device = get_device_name (driver, input_device_combo.get_active_text());
499 string output_device = get_device_name (driver, output_device_combo.get_active_text());
501 if (input_device.empty() || output_device.empty()) {
506 cmd.push_back ("-C");
507 cmd.push_back (input_device);
509 cmd.push_back ("-P");
510 cmd.push_back (output_device);
512 } else if (str == _("Playback only")) {
513 cmd.push_back ("-P");
514 } else if (str == _("Recording only")) {
515 cmd.push_back ("-C");
519 cmd.push_back ("-n");
520 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
524 cmd.push_back ("-r");
525 cmd.push_back (to_string (get_rate(), std::dec));
527 cmd.push_back ("-p");
528 cmd.push_back (period_size_combo.get_active_text());
530 if (using_alsa || using_ffado || using_coreaudio) {
532 double val = input_latency_adjustment.get_value();
535 cmd.push_back ("-I");
536 cmd.push_back (to_string ((uint32_t) val, std::dec));
539 val = output_latency_adjustment.get_value();
542 cmd.push_back ("-O");
543 cmd.push_back (to_string ((uint32_t) val, std::dec));
549 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
551 string device = get_device_name (driver, interface_combo.get_active_text());
552 if (device.empty()) {
557 cmd.push_back ("-d");
558 cmd.push_back (device);
561 if (hw_meter_button.get_active()) {
562 cmd.push_back ("-M");
565 if (hw_monitor_button.get_active()) {
566 cmd.push_back ("-H");
569 str = dither_mode_combo.get_active_text();
571 if (str == _("None")) {
572 } else if (str == _("Triangular")) {
573 cmd.push_back ("-z triangular");
574 } else if (str == _("Rectangular")) {
575 cmd.push_back ("-z rectangular");
576 } else if (str == _("Shaped")) {
577 cmd.push_back ("-z shaped");
580 if (force16bit_button.get_active()) {
581 cmd.push_back ("-S");
584 if (soft_mode_button.get_active()) {
585 cmd.push_back ("-s");
588 str = midi_driver_combo.get_active_text ();
590 if (str == _("seq")) {
591 cmd.push_back ("-X seq");
592 } else if (str == _("raw")) {
593 cmd.push_back ("-X raw");
595 } else if (using_coreaudio) {
598 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
600 string device = get_device_name (driver, interface_combo.get_active_text());
601 if (device.empty()) {
606 cmd.push_back ("-d");
607 cmd.push_back (device);
614 EngineControl::engine_running ()
616 EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
617 boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
619 /* revert all environment settings back to whatever they were when ardour started
623 current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
624 global_epa->restore ();
627 jack_status_t status;
628 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
631 jack_client_close (c);
638 EngineControl::setup_engine ()
641 std::string cwd = "/tmp";
643 build_command_line (args);
646 return 1; // try again
649 std::string jackdrc_path = Glib::get_home_dir();
650 jackdrc_path += "/.jackdrc";
652 ofstream jackdrc (jackdrc_path.c_str());
654 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
657 cerr << "JACK COMMAND: ";
658 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
660 jackdrc << (*i) << ' ';
672 EngineControl::realtime_changed ()
675 priority_spinner.set_sensitive (realtime_button.get_active());
680 EngineControl::enumerate_devices (const string& driver)
682 /* note: case matters for the map keys */
684 if (driver == "CoreAudio") {
686 devices[driver] = enumerate_coreaudio_devices ();
690 } else if (driver == "ALSA") {
691 devices[driver] = enumerate_alsa_devices ();
692 } else if (driver == "FreeBOB") {
693 devices[driver] = enumerate_freebob_devices ();
694 } else if (driver == "FFADO") {
695 devices[driver] = enumerate_ffado_devices ();
696 } else if (driver == "OSS") {
697 devices[driver] = enumerate_oss_devices ();
698 } else if (driver == "Dummy") {
699 devices[driver] = enumerate_dummy_devices ();
700 } else if (driver == "NetJACK") {
701 devices[driver] = enumerate_netjack_devices ();
710 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
712 UInt32 size = sizeof(CFStringRef);
714 OSStatus res = AudioDeviceGetProperty(id, 0, false,
715 kAudioDevicePropertyDeviceUID, &size, &UI);
717 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
723 EngineControl::enumerate_coreaudio_devices ()
727 // Find out how many Core Audio devices are there, if any...
728 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
731 UInt32 outSize = sizeof(isWritable);
733 backend_devs.clear ();
735 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
736 &outSize, &isWritable);
738 // Calculate the number of device available...
739 int numCoreDevices = outSize / sizeof(AudioDeviceID);
740 // Make space for the devices we are about to get...
741 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
742 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
743 &outSize, (void *) coreDeviceIDs);
745 // Look for the CoreAudio device name...
746 char coreDeviceName[256];
749 for (int i = 0; i < numCoreDevices; i++) {
751 nameSize = sizeof (coreDeviceName);
753 /* enforce duplex devices only */
755 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
756 0, true, kAudioDevicePropertyStreams,
757 &outSize, &isWritable);
759 if (err != noErr || outSize == 0) {
763 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
764 0, false, kAudioDevicePropertyStreams,
765 &outSize, &isWritable);
767 if (err != noErr || outSize == 0) {
771 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
772 0, true, kAudioDevicePropertyDeviceName,
773 &outSize, &isWritable);
775 err = AudioDeviceGetProperty(coreDeviceIDs[i],
776 0, true, kAudioDevicePropertyDeviceName,
777 &nameSize, (void *) coreDeviceName);
779 char drivername[128];
781 // this returns the unique id for the device
782 // that must be used on the commandline for jack
784 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
785 devs.push_back (coreDeviceName);
786 backend_devs.push_back (drivername);
792 delete [] coreDeviceIDs;
796 if (devs.size() == 0) {
797 MessageDialog msg (_("\
798 You do not have any audio devices capable of\n\
799 simultaneous playback and recording.\n\n\
800 Please use Applications -> Utilities -> Audio MIDI Setup\n\
801 to create an \"aggregrate\" device, or install a suitable\n\
802 audio interface.\n\n\
803 Please send email to Apple and ask them why new Macs\n\
804 have no duplex audio device.\n\n\
805 Alternatively, if you really want just playback\n\
806 or recording but not both, start JACK before running\n\
807 Ardour and choose the relevant device then."
809 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
810 msg.set_title (_("No suitable audio devices"));
811 msg.set_position (Gtk::WIN_POS_MOUSE);
821 EngineControl::enumerate_alsa_devices ()
826 snd_ctl_card_info_t *info;
827 snd_pcm_info_t *pcminfo;
828 snd_ctl_card_info_alloca(&info);
829 snd_pcm_info_alloca(&pcminfo);
834 backend_devs.clear ();
836 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
839 devname += to_string (cardnum, std::dec);
841 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
843 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
845 snd_pcm_info_set_device (pcminfo, device);
846 snd_pcm_info_set_subdevice (pcminfo, 0);
847 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
849 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
850 devs.push_back (snd_pcm_info_get_name (pcminfo));
852 devname += to_string (device, std::dec);
853 backend_devs.push_back (devname);
857 snd_ctl_close(handle);
865 EngineControl::enumerate_ffado_devices ()
868 backend_devs.clear ();
873 EngineControl::enumerate_freebob_devices ()
880 EngineControl::enumerate_oss_devices ()
886 EngineControl::enumerate_dummy_devices ()
892 EngineControl::enumerate_netjack_devices ()
900 EngineControl::driver_changed ()
902 string driver = driver_combo.get_active_text();
903 string::size_type maxlen = 0;
906 enumerate_devices (driver);
908 vector<string>& strings = devices[driver];
910 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
914 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
915 if ((*i).length() > maxlen) {
916 maxlen = (*i).length();
920 set_popdown_strings (interface_combo, strings);
921 set_popdown_strings (input_device_combo, strings);
922 set_popdown_strings (output_device_combo, strings);
924 if (!strings.empty()) {
925 interface_combo.set_active_text (strings.front());
926 input_device_combo.set_active_text (strings.front());
927 output_device_combo.set_active_text (strings.front());
930 if (driver == "ALSA") {
931 soft_mode_button.set_sensitive (true);
932 force16bit_button.set_sensitive (true);
933 hw_monitor_button.set_sensitive (true);
934 hw_meter_button.set_sensitive (true);
935 monitor_button.set_sensitive (true);
937 soft_mode_button.set_sensitive (false);
938 force16bit_button.set_sensitive (false);
939 hw_monitor_button.set_sensitive (false);
940 hw_meter_button.set_sensitive (false);
941 monitor_button.set_sensitive (false);
946 EngineControl::get_rate ()
948 double r = atof (sample_rate_combo.get_active_text ());
949 /* the string may have been translated with an abbreviation for
950 * thousands, so use a crude heuristic to fix this.
959 EngineControl::redisplay_latency ()
961 uint32_t rate = get_rate();
965 float periods = periods_adjustment.get_value();
967 float period_size = atof (period_size_combo.get_active_text());
970 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
972 latency_label.set_text (buf);
973 latency_label.set_alignment (0, 0.5);
977 EngineControl::audio_mode_changed ()
979 std::string str = audio_mode_combo.get_active_text();
981 if (str == _("Playback/Recording on 1 Device")) {
982 input_device_combo.set_sensitive (false);
983 output_device_combo.set_sensitive (false);
984 } else if (str == _("Playback/Recording on 2 Devices")) {
985 input_device_combo.set_sensitive (true);
986 output_device_combo.set_sensitive (true);
987 } else if (str == _("Playback only")) {
988 output_device_combo.set_sensitive (true);
989 input_device_combo.set_sensitive (false);
990 } else if (str == _("Recording only")) {
991 input_device_combo.set_sensitive (true);
992 output_device_combo.set_sensitive (false);
996 static bool jack_server_filter(const string& str, void */*arg*/)
998 return str == "jackd" || str == "jackdmp";
1002 EngineControl::find_jack_servers (vector<string>& strings)
1005 /* this magic lets us finds the path to the OSX bundle, and then
1006 we infer JACK's location from there
1009 char execpath[MAXPATHLEN+1];
1010 uint32_t pathsz = sizeof (execpath);
1012 _NSGetExecutablePath (execpath, &pathsz);
1014 string path (Glib::path_get_dirname (execpath));
1017 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
1018 strings.push_back (path);
1021 if (getenv ("ARDOUR_WITH_JACK")) {
1022 /* no other options - only use the JACK we supply */
1023 if (strings.empty()) {
1024 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1033 PathScanner scanner;
1034 vector<string *> *jack_servers;
1035 std::map<string,int> un;
1037 bool need_minimal_path = false;
1039 p = getenv ("PATH");
1044 need_minimal_path = true;
1048 // many mac users don't have PATH set up to include
1049 // likely installed locations of JACK
1050 need_minimal_path = true;
1053 if (need_minimal_path) {
1055 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1057 path += ":/usr/local/bin:/opt/local/bin";
1062 // push it back into the environment so that auto-started JACK can find it.
1063 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1064 setenv ("PATH", path.c_str(), 1);
1067 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1068 if (!jack_servers) {
1072 vector<string *>::iterator iter;
1074 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1078 strings.push_back(p);
1085 EngineControl::get_device_name (const string& driver, const string& human_readable)
1087 vector<string>::iterator n;
1088 vector<string>::iterator i;
1090 if (human_readable.empty()) {
1091 /* this can happen if the user's .ardourrc file has a device name from
1092 another computer system in it
1094 MessageDialog msg (_("You need to choose an audio device first."));
1099 if (backend_devs.empty()) {
1100 return human_readable;
1103 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1104 if (human_readable == (*i)) {
1109 if (i == devices[driver].end()) {
1110 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1117 EngineControl::get_state ()
1119 XMLNode* root = new XMLNode ("AudioSetup");
1123 child = new XMLNode ("periods");
1124 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1125 root->add_child_nocopy (*child);
1127 child = new XMLNode ("priority");
1128 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1129 root->add_child_nocopy (*child);
1131 child = new XMLNode ("ports");
1132 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1133 root->add_child_nocopy (*child);
1135 child = new XMLNode ("inlatency");
1136 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1137 root->add_child_nocopy (*child);
1139 child = new XMLNode ("outlatency");
1140 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1141 root->add_child_nocopy (*child);
1143 child = new XMLNode ("realtime");
1144 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1145 root->add_child_nocopy (*child);
1147 child = new XMLNode ("nomemorylock");
1148 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1149 root->add_child_nocopy (*child);
1151 child = new XMLNode ("unlockmemory");
1152 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1153 root->add_child_nocopy (*child);
1155 child = new XMLNode ("softmode");
1156 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1157 root->add_child_nocopy (*child);
1159 child = new XMLNode ("force16bit");
1160 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1161 root->add_child_nocopy (*child);
1163 child = new XMLNode ("hwmonitor");
1164 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1165 root->add_child_nocopy (*child);
1167 child = new XMLNode ("hwmeter");
1168 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1169 root->add_child_nocopy (*child);
1171 child = new XMLNode ("verbose");
1172 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1173 root->add_child_nocopy (*child);
1175 child = new XMLNode ("samplerate");
1176 child->add_property ("val", sample_rate_combo.get_active_text());
1177 root->add_child_nocopy (*child);
1179 child = new XMLNode ("periodsize");
1180 child->add_property ("val", period_size_combo.get_active_text());
1181 root->add_child_nocopy (*child);
1183 child = new XMLNode ("serverpath");
1184 child->add_property ("val", serverpath_combo.get_active_text());
1185 root->add_child_nocopy (*child);
1187 child = new XMLNode ("driver");
1188 child->add_property ("val", driver_combo.get_active_text());
1189 root->add_child_nocopy (*child);
1191 child = new XMLNode ("interface");
1192 child->add_property ("val", interface_combo.get_active_text());
1193 root->add_child_nocopy (*child);
1195 child = new XMLNode ("timeout");
1196 child->add_property ("val", timeout_combo.get_active_text());
1197 root->add_child_nocopy (*child);
1199 child = new XMLNode ("dither");
1200 child->add_property ("val", dither_mode_combo.get_active_text());
1201 root->add_child_nocopy (*child);
1203 child = new XMLNode ("audiomode");
1204 child->add_property ("val", audio_mode_combo.get_active_text());
1205 root->add_child_nocopy (*child);
1207 child = new XMLNode ("inputdevice");
1208 child->add_property ("val", input_device_combo.get_active_text());
1209 root->add_child_nocopy (*child);
1211 child = new XMLNode ("outputdevice");
1212 child->add_property ("val", output_device_combo.get_active_text());
1213 root->add_child_nocopy (*child);
1215 child = new XMLNode ("mididriver");
1216 child->add_property ("val", midi_driver_combo.get_active_text());
1217 root->add_child_nocopy (*child);
1223 EngineControl::set_state (const XMLNode& root)
1226 XMLNodeConstIterator citer;
1228 XMLProperty* prop = NULL;
1229 bool using_dummy = false;
1230 bool using_ffado = false;
1235 if ( (child = root.child ("driver"))){
1236 prop = child->property("val");
1238 if (prop && (prop->value() == "Dummy") ) {
1241 if (prop && (prop->value() == "FFADO") ) {
1247 clist = root.children();
1249 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1253 prop = child->property ("val");
1255 if (!prop || prop->value().empty()) {
1257 if (((using_dummy || using_ffado)
1258 && ( child->name() == "interface"
1259 || child->name() == "inputdevice"
1260 || child->name() == "outputdevice"))
1261 || child->name() == "timeout")
1266 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1270 strval = prop->value();
1272 /* adjustments/spinners */
1274 if (child->name() == "periods") {
1275 val = atoi (strval);
1276 periods_adjustment.set_value(val);
1277 } else if (child->name() == "priority") {
1278 val = atoi (strval);
1279 priority_adjustment.set_value(val);
1280 } else if (child->name() == "ports") {
1281 val = atoi (strval);
1282 ports_adjustment.set_value(val);
1283 } else if (child->name() == "inlatency") {
1284 val = atoi (strval);
1285 input_latency.set_value(val);
1286 } else if (child->name() == "outlatency") {
1287 val = atoi (strval);
1288 output_latency.set_value(val);
1293 else if (child->name() == "realtime") {
1294 val = atoi (strval);
1295 realtime_button.set_active(val);
1296 } else if (child->name() == "nomemorylock") {
1297 val = atoi (strval);
1298 no_memory_lock_button.set_active(val);
1299 } else if (child->name() == "unlockmemory") {
1300 val = atoi (strval);
1301 unlock_memory_button.set_active(val);
1302 } else if (child->name() == "softmode") {
1303 val = atoi (strval);
1304 soft_mode_button.set_active(val);
1305 } else if (child->name() == "force16bit") {
1306 val = atoi (strval);
1307 force16bit_button.set_active(val);
1308 } else if (child->name() == "hwmonitor") {
1309 val = atoi (strval);
1310 hw_monitor_button.set_active(val);
1311 } else if (child->name() == "hwmeter") {
1312 val = atoi (strval);
1313 hw_meter_button.set_active(val);
1314 } else if (child->name() == "verbose") {
1315 val = atoi (strval);
1316 verbose_output_button.set_active(val);
1321 else if (child->name() == "samplerate") {
1322 sample_rate_combo.set_active_text(strval);
1323 } else if (child->name() == "periodsize") {
1324 period_size_combo.set_active_text(strval);
1325 } else if (child->name() == "serverpath") {
1327 /* only attempt to set this if we have bothered to look
1328 up server names already. otherwise this is all
1329 redundant (actually, all of this dialog/widget
1330 is redundant in that case ...)
1333 if (!server_strings.empty()) {
1334 /* do not allow us to use a server path that doesn't
1335 exist on this system. this handles cases where
1336 the user has an RC file listing a serverpath
1337 from some other machine.
1339 vector<string>::iterator x;
1340 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1345 if (x != server_strings.end()) {
1346 serverpath_combo.set_active_text (strval);
1348 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1354 } else if (child->name() == "driver") {
1355 driver_combo.set_active_text(strval);
1356 } else if (child->name() == "interface") {
1357 interface_combo.set_active_text(strval);
1358 } else if (child->name() == "timeout") {
1359 timeout_combo.set_active_text(strval);
1360 } else if (child->name() == "dither") {
1361 dither_mode_combo.set_active_text(strval);
1362 } else if (child->name() == "audiomode") {
1363 audio_mode_combo.set_active_text(strval);
1364 } else if (child->name() == "inputdevice") {
1365 input_device_combo.set_active_text(strval);
1366 } else if (child->name() == "outputdevice") {
1367 output_device_combo.set_active_text(strval);
1368 } else if (child->name() == "mididriver") {
1369 midi_driver_combo.set_active_text(strval);