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 ports_adjustment (128, 8, 1024, 1, 16),
68 ports_spinner (ports_adjustment),
69 input_latency_adjustment (0, 0, 99999, 1),
70 input_latency (input_latency_adjustment),
71 output_latency_adjustment (0, 0, 99999, 1),
72 output_latency (output_latency_adjustment),
73 realtime_button (_("Realtime")),
74 no_memory_lock_button (_("Do not lock memory")),
75 unlock_memory_button (_("Unlock memory")),
76 soft_mode_button (_("No zombies")),
77 monitor_button (_("Provide monitor ports")),
78 force16bit_button (_("Force 16 bit")),
79 hw_monitor_button (_("H/W monitoring")),
80 hw_meter_button (_("H/W metering")),
81 verbose_output_button (_("Verbose output")),
82 start_button (_("Start")),
83 stop_button (_("Stop")),
86 options_packer (4, 2),
90 options_packer (14, 2),
94 using namespace Notebook_Helpers;
96 vector<string> strings;
101 strings.push_back (_("8000Hz"));
102 strings.push_back (_("22050Hz"));
103 strings.push_back (_("44100Hz"));
104 strings.push_back (_("48000Hz"));
105 strings.push_back (_("88200Hz"));
106 strings.push_back (_("96000Hz"));
107 strings.push_back (_("192000Hz"));
108 set_popdown_strings (sample_rate_combo, strings);
109 sample_rate_combo.set_active_text ("48000Hz");
112 strings.push_back ("32");
113 strings.push_back ("64");
114 strings.push_back ("128");
115 strings.push_back ("256");
116 strings.push_back ("512");
117 strings.push_back ("1024");
118 strings.push_back ("2048");
119 strings.push_back ("4096");
120 strings.push_back ("8192");
121 set_popdown_strings (period_size_combo, strings);
122 period_size_combo.set_active_text ("1024");
125 strings.push_back (_("None"));
126 strings.push_back (_("Triangular"));
127 strings.push_back (_("Rectangular"));
128 strings.push_back (_("Shaped"));
129 set_popdown_strings (dither_mode_combo, strings);
130 dither_mode_combo.set_active_text (_("None"));
132 /* basic parameters */
134 basic_packer.set_spacings (6);
138 strings.push_back (X_("CoreAudio"));
140 strings.push_back (X_("ALSA"));
141 strings.push_back (X_("OSS"));
142 strings.push_back (X_("FreeBoB"));
143 strings.push_back (X_("FFADO"));
145 strings.push_back (X_("NetJACK"));
146 strings.push_back (X_("Dummy"));
147 set_popdown_strings (driver_combo, strings);
148 driver_combo.set_active_text (strings.front());
150 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
154 strings.push_back (_("Playback/recording on 1 device"));
155 strings.push_back (_("Playback/recording on 2 devices"));
156 strings.push_back (_("Playback only"));
157 strings.push_back (_("Recording only"));
158 set_popdown_strings (audio_mode_combo, strings);
159 audio_mode_combo.set_active_text (strings.front());
161 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
162 audio_mode_changed ();
165 strings.push_back (_("None"));
166 strings.push_back (_("seq"));
167 strings.push_back (_("raw"));
168 set_popdown_strings (midi_driver_combo, strings);
169 midi_driver_combo.set_active_text (strings.front ());
173 label = manage (left_aligned_label (_("Driver:")));
174 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
175 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
178 label = manage (left_aligned_label (_("Audio Interface:")));
179 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
180 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
183 label = manage (left_aligned_label (_("Sample rate:")));
184 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
185 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
188 label = manage (left_aligned_label (_("Buffer size:")));
189 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
190 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
194 label = manage (left_aligned_label (_("Number of buffers:")));
195 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
196 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
197 periods_spinner.set_value (2);
201 label = manage (left_aligned_label (_("Approximate latency:")));
202 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
203 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
206 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
207 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
208 period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
211 /* no audio mode with CoreAudio, its duplex or nuthin' */
214 label = manage (left_aligned_label (_("Audio mode:")));
215 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
216 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
220 interface_combo.set_size_request (250, -1);
221 input_device_combo.set_size_request (250, -1);
222 output_device_combo.set_size_request (250, -1);
226 if (engine_running()) {
227 start_button.set_sensitive (false);
229 stop_button.set_sensitive (false);
232 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
233 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
236 button_box.pack_start (start_button, false, false);
237 button_box.pack_start (stop_button, false, false);
239 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
243 options_packer.set_spacings (6);
246 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
249 realtime_button.set_active (true);
251 #if PROVIDE_TOO_MANY_OPTIONS
254 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
256 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
258 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
260 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
262 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
264 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
268 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
271 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
276 strings.push_back (_("Ignore"));
277 strings.push_back ("500 msec");
278 strings.push_back ("1 sec");
279 strings.push_back ("2 sec");
280 strings.push_back ("10 sec");
281 set_popdown_strings (timeout_combo, strings);
282 timeout_combo.set_active_text (strings.front ());
284 label = manage (new Label (_("Client timeout")));
285 label->set_alignment (1.0, 0.5);
286 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
287 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
290 #endif /* PROVIDE_TOO_MANY_OPTIONS */
291 label = manage (left_aligned_label (_("Number of ports:")));
292 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
293 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
296 label = manage (left_aligned_label (_("MIDI driver:")));
297 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
298 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
302 label = manage (left_aligned_label (_("Dither:")));
303 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
304 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
308 find_jack_servers (server_strings);
310 if (server_strings.empty()) {
311 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
315 set_popdown_strings (serverpath_combo, server_strings);
316 serverpath_combo.set_active_text (server_strings.front());
318 if (server_strings.size() > 1) {
319 label = manage (left_aligned_label (_("Server:")));
320 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
321 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
325 /* device settings */
327 device_packer.set_spacings (6);
331 label = manage (left_aligned_label (_("Input device:")));
332 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
333 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
335 label = manage (left_aligned_label (_("Output device:")));
336 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
337 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
340 label = manage (left_aligned_label (_("Hardware input latency:")));
341 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
342 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
343 label = manage (left_aligned_label (_("samples")));
344 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
346 label = manage (left_aligned_label (_("Hardware output latency:")));
347 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
348 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
349 label = manage (left_aligned_label (_("samples")));
350 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
353 basic_hbox.pack_start (basic_packer, false, false);
354 options_hbox.pack_start (options_packer, false, false);
356 device_packer.set_border_width (12);
357 options_packer.set_border_width (12);
358 basic_packer.set_border_width (12);
360 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
361 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
362 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
363 notebook.set_border_width (12);
365 set_border_width (12);
366 pack_start (notebook);
369 EngineControl::~EngineControl ()
375 EngineControl::build_command_line (vector<string>& cmd)
379 bool using_alsa = false;
380 bool using_coreaudio = false;
381 bool using_dummy = false;
382 bool using_ffado = false;
384 /* first, path to jackd */
386 cmd.push_back (serverpath_combo.get_active_text ());
388 /* now jackd arguments */
390 str = timeout_combo.get_active_text ();
392 if (str != _("Ignore")) {
397 msecs = (uint32_t) floor (secs * 1000.0);
400 cmd.push_back ("-t");
401 cmd.push_back (to_string (msecs, std::dec));
405 if (no_memory_lock_button.get_active()) {
406 cmd.push_back ("-m"); /* no munlock */
409 cmd.push_back ("-p"); /* port max */
410 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
412 if (realtime_button.get_active()) {
413 cmd.push_back ("-R");
415 cmd.push_back ("-r"); /* override jackd's default --realtime */
418 if (unlock_memory_button.get_active()) {
419 cmd.push_back ("-u");
422 if (verbose_output_button.get_active()) {
423 cmd.push_back ("-v");
426 /* now add fixed arguments (not user-selectable) */
428 cmd.push_back ("-T"); // temporary */
430 /* next the driver */
432 cmd.push_back ("-d");
434 driver = driver_combo.get_active_text ();
436 if (driver == X_("ALSA")) {
438 cmd.push_back ("alsa");
439 } else if (driver == X_("OSS")) {
440 cmd.push_back ("oss");
441 } else if (driver == X_("CoreAudio")) {
442 using_coreaudio = true;
443 cmd.push_back ("coreaudio");
444 } else if (driver == X_("NetJACK")) {
445 cmd.push_back ("netjack");
446 } else if (driver == X_("FreeBoB")) {
447 cmd.push_back ("freebob");
448 } else if (driver == X_("FFADO")) {
450 cmd.push_back ("firewire");
451 } else if ( driver == X_("Dummy")) {
453 cmd.push_back ("dummy");
456 /* driver arguments */
458 if (!using_coreaudio) {
459 str = audio_mode_combo.get_active_text();
461 if (str == _("Playback/Recording on 1 Device")) {
465 } else if (str == _("Playback/Recording on 2 Devices")) {
467 string input_device = get_device_name (driver, input_device_combo.get_active_text());
468 string output_device = get_device_name (driver, output_device_combo.get_active_text());
470 if (input_device.empty() || output_device.empty()) {
475 cmd.push_back ("-C");
476 cmd.push_back (input_device);
478 cmd.push_back ("-P");
479 cmd.push_back (output_device);
481 } else if (str == _("Playback only")) {
482 cmd.push_back ("-P");
483 } else if (str == _("Recording only")) {
484 cmd.push_back ("-C");
488 cmd.push_back ("-n");
489 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
493 cmd.push_back ("-r");
494 cmd.push_back (to_string (get_rate(), std::dec));
496 cmd.push_back ("-p");
497 cmd.push_back (period_size_combo.get_active_text());
499 if (using_alsa || using_ffado || using_coreaudio) {
501 double val = input_latency_adjustment.get_value();
504 cmd.push_back ("-I");
505 cmd.push_back (to_string ((uint32_t) val, std::dec));
508 val = output_latency_adjustment.get_value();
511 cmd.push_back ("-O");
512 cmd.push_back (to_string ((uint32_t) val, std::dec));
518 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
520 string device = get_device_name (driver, interface_combo.get_active_text());
521 if (device.empty()) {
526 cmd.push_back ("-d");
527 cmd.push_back (device);
530 if (hw_meter_button.get_active()) {
531 cmd.push_back ("-M");
534 if (hw_monitor_button.get_active()) {
535 cmd.push_back ("-H");
538 str = dither_mode_combo.get_active_text();
540 if (str == _("None")) {
541 } else if (str == _("Triangular")) {
542 cmd.push_back ("-z triangular");
543 } else if (str == _("Rectangular")) {
544 cmd.push_back ("-z rectangular");
545 } else if (str == _("Shaped")) {
546 cmd.push_back ("-z shaped");
549 if (force16bit_button.get_active()) {
550 cmd.push_back ("-S");
553 if (soft_mode_button.get_active()) {
554 cmd.push_back ("-s");
557 str = midi_driver_combo.get_active_text ();
559 if (str == _("seq")) {
560 cmd.push_back ("-X seq");
561 } else if (str == _("raw")) {
562 cmd.push_back ("-X raw");
564 } else if (using_coreaudio) {
567 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
569 string device = get_device_name (driver, interface_combo.get_active_text());
570 if (device.empty()) {
575 cmd.push_back ("-d");
576 cmd.push_back (device);
583 EngineControl::engine_running ()
585 EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
586 boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
588 /* revert all environment settings back to whatever they were when ardour started
592 current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
593 global_epa->restore ();
596 jack_status_t status;
597 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
600 jack_client_close (c);
607 EngineControl::setup_engine ()
610 std::string cwd = "/tmp";
612 build_command_line (args);
615 return 1; // try again
618 std::string jackdrc_path = Glib::get_home_dir();
619 jackdrc_path += "/.jackdrc";
621 ofstream jackdrc (jackdrc_path.c_str());
623 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
626 cerr << "JACK COMMAND: ";
627 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
629 jackdrc << (*i) << ' ';
641 EngineControl::enumerate_devices (const string& driver)
643 /* note: case matters for the map keys */
645 if (driver == "CoreAudio") {
647 devices[driver] = enumerate_coreaudio_devices ();
651 } else if (driver == "ALSA") {
652 devices[driver] = enumerate_alsa_devices ();
653 } else if (driver == "FreeBOB") {
654 devices[driver] = enumerate_freebob_devices ();
655 } else if (driver == "FFADO") {
656 devices[driver] = enumerate_ffado_devices ();
657 } else if (driver == "OSS") {
658 devices[driver] = enumerate_oss_devices ();
659 } else if (driver == "Dummy") {
660 devices[driver] = enumerate_dummy_devices ();
661 } else if (driver == "NetJACK") {
662 devices[driver] = enumerate_netjack_devices ();
671 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
673 UInt32 size = sizeof(CFStringRef);
675 OSStatus res = AudioDeviceGetProperty(id, 0, false,
676 kAudioDevicePropertyDeviceUID, &size, &UI);
678 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
684 EngineControl::enumerate_coreaudio_devices ()
688 // Find out how many Core Audio devices are there, if any...
689 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
692 UInt32 outSize = sizeof(isWritable);
694 backend_devs.clear ();
696 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
697 &outSize, &isWritable);
699 // Calculate the number of device available...
700 int numCoreDevices = outSize / sizeof(AudioDeviceID);
701 // Make space for the devices we are about to get...
702 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
703 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
704 &outSize, (void *) coreDeviceIDs);
706 // Look for the CoreAudio device name...
707 char coreDeviceName[256];
710 for (int i = 0; i < numCoreDevices; i++) {
712 nameSize = sizeof (coreDeviceName);
714 /* enforce duplex devices only */
716 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
717 0, true, kAudioDevicePropertyStreams,
718 &outSize, &isWritable);
720 if (err != noErr || outSize == 0) {
724 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
725 0, false, kAudioDevicePropertyStreams,
726 &outSize, &isWritable);
728 if (err != noErr || outSize == 0) {
732 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
733 0, true, kAudioDevicePropertyDeviceName,
734 &outSize, &isWritable);
736 err = AudioDeviceGetProperty(coreDeviceIDs[i],
737 0, true, kAudioDevicePropertyDeviceName,
738 &nameSize, (void *) coreDeviceName);
740 char drivername[128];
742 // this returns the unique id for the device
743 // that must be used on the commandline for jack
745 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
746 devs.push_back (coreDeviceName);
747 backend_devs.push_back (drivername);
753 delete [] coreDeviceIDs;
757 if (devs.size() == 0) {
758 MessageDialog msg (_("\
759 You do not have any audio devices capable of\n\
760 simultaneous playback and recording.\n\n\
761 Please use Applications -> Utilities -> Audio MIDI Setup\n\
762 to create an \"aggregrate\" device, or install a suitable\n\
763 audio interface.\n\n\
764 Please send email to Apple and ask them why new Macs\n\
765 have no duplex audio device.\n\n\
766 Alternatively, if you really want just playback\n\
767 or recording but not both, start JACK before running\n\
768 Ardour and choose the relevant device then."
770 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
771 msg.set_title (_("No suitable audio devices"));
772 msg.set_position (Gtk::WIN_POS_MOUSE);
782 EngineControl::enumerate_alsa_devices ()
787 snd_ctl_card_info_t *info;
788 snd_pcm_info_t *pcminfo;
789 snd_ctl_card_info_alloca(&info);
790 snd_pcm_info_alloca(&pcminfo);
795 backend_devs.clear ();
797 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
800 devname += to_string (cardnum, std::dec);
802 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
804 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
806 snd_pcm_info_set_device (pcminfo, device);
807 snd_pcm_info_set_subdevice (pcminfo, 0);
808 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
810 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
811 devs.push_back (snd_pcm_info_get_name (pcminfo));
813 devname += to_string (device, std::dec);
814 backend_devs.push_back (devname);
818 snd_ctl_close(handle);
826 EngineControl::enumerate_ffado_devices ()
829 backend_devs.clear ();
834 EngineControl::enumerate_freebob_devices ()
841 EngineControl::enumerate_oss_devices ()
847 EngineControl::enumerate_dummy_devices ()
853 EngineControl::enumerate_netjack_devices ()
861 EngineControl::driver_changed ()
863 string driver = driver_combo.get_active_text();
864 string::size_type maxlen = 0;
867 enumerate_devices (driver);
869 vector<string>& strings = devices[driver];
871 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
875 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
876 if ((*i).length() > maxlen) {
877 maxlen = (*i).length();
881 set_popdown_strings (interface_combo, strings);
882 set_popdown_strings (input_device_combo, strings);
883 set_popdown_strings (output_device_combo, strings);
885 if (!strings.empty()) {
886 interface_combo.set_active_text (strings.front());
887 input_device_combo.set_active_text (strings.front());
888 output_device_combo.set_active_text (strings.front());
891 if (driver == "ALSA") {
892 soft_mode_button.set_sensitive (true);
893 force16bit_button.set_sensitive (true);
894 hw_monitor_button.set_sensitive (true);
895 hw_meter_button.set_sensitive (true);
896 monitor_button.set_sensitive (true);
898 soft_mode_button.set_sensitive (false);
899 force16bit_button.set_sensitive (false);
900 hw_monitor_button.set_sensitive (false);
901 hw_meter_button.set_sensitive (false);
902 monitor_button.set_sensitive (false);
907 EngineControl::get_rate ()
909 double r = atof (sample_rate_combo.get_active_text ());
910 /* the string may have been translated with an abbreviation for
911 * thousands, so use a crude heuristic to fix this.
920 EngineControl::redisplay_latency ()
922 uint32_t rate = get_rate();
926 float periods = periods_adjustment.get_value();
928 float period_size = atof (period_size_combo.get_active_text());
931 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
933 latency_label.set_text (buf);
934 latency_label.set_alignment (0, 0.5);
938 EngineControl::audio_mode_changed ()
940 std::string str = audio_mode_combo.get_active_text();
942 if (str == _("Playback/Recording on 1 Device")) {
943 input_device_combo.set_sensitive (false);
944 output_device_combo.set_sensitive (false);
945 } else if (str == _("Playback/Recording on 2 Devices")) {
946 input_device_combo.set_sensitive (true);
947 output_device_combo.set_sensitive (true);
948 } else if (str == _("Playback only")) {
949 output_device_combo.set_sensitive (true);
950 input_device_combo.set_sensitive (false);
951 } else if (str == _("Recording only")) {
952 input_device_combo.set_sensitive (true);
953 output_device_combo.set_sensitive (false);
957 static bool jack_server_filter(const string& str, void */*arg*/)
959 return str == "jackd" || str == "jackdmp";
963 EngineControl::find_jack_servers (vector<string>& strings)
966 /* this magic lets us finds the path to the OSX bundle, and then
967 we infer JACK's location from there
970 char execpath[MAXPATHLEN+1];
971 uint32_t pathsz = sizeof (execpath);
973 _NSGetExecutablePath (execpath, &pathsz);
975 string path (Glib::path_get_dirname (execpath));
978 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
979 strings.push_back (path);
982 if (getenv ("ARDOUR_WITH_JACK")) {
983 /* no other options - only use the JACK we supply */
984 if (strings.empty()) {
985 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
995 vector<string *> *jack_servers;
996 std::map<string,int> un;
998 bool need_minimal_path = false;
1000 p = getenv ("PATH");
1005 need_minimal_path = true;
1009 // many mac users don't have PATH set up to include
1010 // likely installed locations of JACK
1011 need_minimal_path = true;
1014 if (need_minimal_path) {
1016 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1018 path += ":/usr/local/bin:/opt/local/bin";
1023 // push it back into the environment so that auto-started JACK can find it.
1024 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1025 setenv ("PATH", path.c_str(), 1);
1028 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1029 if (!jack_servers) {
1033 vector<string *>::iterator iter;
1035 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1039 strings.push_back(p);
1046 EngineControl::get_device_name (const string& driver, const string& human_readable)
1048 vector<string>::iterator n;
1049 vector<string>::iterator i;
1051 if (human_readable.empty()) {
1052 /* this can happen if the user's .ardourrc file has a device name from
1053 another computer system in it
1055 MessageDialog msg (_("You need to choose an audio device first."));
1056 msg.set_position (WIN_POS_MOUSE);
1061 if (backend_devs.empty()) {
1062 return human_readable;
1065 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1066 if (human_readable == (*i)) {
1071 if (i == devices[driver].end()) {
1072 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1079 EngineControl::get_state ()
1081 XMLNode* root = new XMLNode ("AudioSetup");
1085 child = new XMLNode ("periods");
1086 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1087 root->add_child_nocopy (*child);
1089 child = new XMLNode ("ports");
1090 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1091 root->add_child_nocopy (*child);
1093 child = new XMLNode ("inlatency");
1094 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1095 root->add_child_nocopy (*child);
1097 child = new XMLNode ("outlatency");
1098 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1099 root->add_child_nocopy (*child);
1101 child = new XMLNode ("realtime");
1102 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1103 root->add_child_nocopy (*child);
1105 child = new XMLNode ("nomemorylock");
1106 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1107 root->add_child_nocopy (*child);
1109 child = new XMLNode ("unlockmemory");
1110 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1111 root->add_child_nocopy (*child);
1113 child = new XMLNode ("softmode");
1114 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1115 root->add_child_nocopy (*child);
1117 child = new XMLNode ("force16bit");
1118 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1119 root->add_child_nocopy (*child);
1121 child = new XMLNode ("hwmonitor");
1122 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1123 root->add_child_nocopy (*child);
1125 child = new XMLNode ("hwmeter");
1126 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1127 root->add_child_nocopy (*child);
1129 child = new XMLNode ("verbose");
1130 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1131 root->add_child_nocopy (*child);
1133 child = new XMLNode ("samplerate");
1134 child->add_property ("val", sample_rate_combo.get_active_text());
1135 root->add_child_nocopy (*child);
1137 child = new XMLNode ("periodsize");
1138 child->add_property ("val", period_size_combo.get_active_text());
1139 root->add_child_nocopy (*child);
1141 child = new XMLNode ("serverpath");
1142 child->add_property ("val", serverpath_combo.get_active_text());
1143 root->add_child_nocopy (*child);
1145 child = new XMLNode ("driver");
1146 child->add_property ("val", driver_combo.get_active_text());
1147 root->add_child_nocopy (*child);
1149 child = new XMLNode ("interface");
1150 child->add_property ("val", interface_combo.get_active_text());
1151 root->add_child_nocopy (*child);
1153 child = new XMLNode ("timeout");
1154 child->add_property ("val", timeout_combo.get_active_text());
1155 root->add_child_nocopy (*child);
1157 child = new XMLNode ("dither");
1158 child->add_property ("val", dither_mode_combo.get_active_text());
1159 root->add_child_nocopy (*child);
1161 child = new XMLNode ("audiomode");
1162 child->add_property ("val", audio_mode_combo.get_active_text());
1163 root->add_child_nocopy (*child);
1165 child = new XMLNode ("inputdevice");
1166 child->add_property ("val", input_device_combo.get_active_text());
1167 root->add_child_nocopy (*child);
1169 child = new XMLNode ("outputdevice");
1170 child->add_property ("val", output_device_combo.get_active_text());
1171 root->add_child_nocopy (*child);
1173 child = new XMLNode ("mididriver");
1174 child->add_property ("val", midi_driver_combo.get_active_text());
1175 root->add_child_nocopy (*child);
1181 EngineControl::set_state (const XMLNode& root)
1184 XMLNodeConstIterator citer;
1186 XMLProperty* prop = NULL;
1187 bool using_dummy = false;
1188 bool using_ffado = false;
1193 if ( (child = root.child ("driver"))){
1194 prop = child->property("val");
1196 if (prop && (prop->value() == "Dummy") ) {
1199 if (prop && (prop->value() == "FFADO") ) {
1205 clist = root.children();
1207 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1211 prop = child->property ("val");
1213 if (!prop || prop->value().empty()) {
1215 if (((using_dummy || using_ffado)
1216 && ( child->name() == "interface"
1217 || child->name() == "inputdevice"
1218 || child->name() == "outputdevice"))
1219 || child->name() == "timeout")
1224 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1228 strval = prop->value();
1230 /* adjustments/spinners */
1232 if (child->name() == "periods") {
1233 val = atoi (strval);
1234 periods_adjustment.set_value(val);
1235 } else if (child->name() == "ports") {
1236 val = atoi (strval);
1237 ports_adjustment.set_value(val);
1238 } else if (child->name() == "inlatency") {
1239 val = atoi (strval);
1240 input_latency.set_value(val);
1241 } else if (child->name() == "outlatency") {
1242 val = atoi (strval);
1243 output_latency.set_value(val);
1248 else if (child->name() == "realtime") {
1249 val = atoi (strval);
1250 realtime_button.set_active(val);
1251 } else if (child->name() == "nomemorylock") {
1252 val = atoi (strval);
1253 no_memory_lock_button.set_active(val);
1254 } else if (child->name() == "unlockmemory") {
1255 val = atoi (strval);
1256 unlock_memory_button.set_active(val);
1257 } else if (child->name() == "softmode") {
1258 val = atoi (strval);
1259 soft_mode_button.set_active(val);
1260 } else if (child->name() == "force16bit") {
1261 val = atoi (strval);
1262 force16bit_button.set_active(val);
1263 } else if (child->name() == "hwmonitor") {
1264 val = atoi (strval);
1265 hw_monitor_button.set_active(val);
1266 } else if (child->name() == "hwmeter") {
1267 val = atoi (strval);
1268 hw_meter_button.set_active(val);
1269 } else if (child->name() == "verbose") {
1270 val = atoi (strval);
1271 verbose_output_button.set_active(val);
1276 else if (child->name() == "samplerate") {
1277 sample_rate_combo.set_active_text(strval);
1278 } else if (child->name() == "periodsize") {
1279 period_size_combo.set_active_text(strval);
1280 } else if (child->name() == "serverpath") {
1282 /* only attempt to set this if we have bothered to look
1283 up server names already. otherwise this is all
1284 redundant (actually, all of this dialog/widget
1285 is redundant in that case ...)
1288 if (!server_strings.empty()) {
1289 /* do not allow us to use a server path that doesn't
1290 exist on this system. this handles cases where
1291 the user has an RC file listing a serverpath
1292 from some other machine.
1294 vector<string>::iterator x;
1295 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1300 if (x != server_strings.end()) {
1301 serverpath_combo.set_active_text (strval);
1303 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1309 } else if (child->name() == "driver") {
1310 driver_combo.set_active_text(strval);
1311 } else if (child->name() == "interface") {
1312 interface_combo.set_active_text(strval);
1313 } else if (child->name() == "timeout") {
1314 timeout_combo.set_active_text(strval);
1315 } else if (child->name() == "dither") {
1316 dither_mode_combo.set_active_text(strval);
1317 } else if (child->name() == "audiomode") {
1318 audio_mode_combo.set_active_text(strval);
1319 } else if (child->name() == "inputdevice") {
1320 input_device_combo.set_active_text(strval);
1321 } else if (child->name() == "outputdevice") {
1322 output_device_combo.set_active_text(strval);
1323 } else if (child->name() == "mididriver") {
1324 midi_driver_combo.set_active_text(strval);