7 #include <gtkmm/messagedialog.h>
11 #include <CoreAudio/CoreAudio.h>
12 #include <CoreFoundation/CFString.h>
13 #include <sys/param.h>
14 #include <mach-o/dyld.h>
16 #include <alsa/asoundlib.h>
19 #include <ardour/profile.h>
20 #include <jack/jack.h>
22 #include <gtkmm/stock.h>
23 #include <gtkmm2ext/utils.h>
25 #include <pbd/convert.h>
26 #include <pbd/error.h>
27 #include <pbd/pathscanner.h>
33 #include "engine_dialog.h"
38 using namespace Gtkmm2ext;
42 EngineControl::EngineControl ()
43 : periods_adjustment (2, 2, 16, 1, 2),
44 periods_spinner (periods_adjustment),
45 priority_adjustment (60, 10, 90, 1, 10),
46 priority_spinner (priority_adjustment),
47 ports_adjustment (128, 8, 1024, 1, 16),
48 ports_spinner (ports_adjustment),
49 realtime_button (_("Realtime")),
50 no_memory_lock_button (_("Do not lock memory")),
51 unlock_memory_button (_("Unlock memory")),
52 soft_mode_button (_("No zombies")),
53 monitor_button (_("Provide monitor ports")),
54 force16bit_button (_("Force 16 bit")),
55 hw_monitor_button (_("H/W monitoring")),
56 hw_meter_button (_("H/W metering")),
57 verbose_output_button (_("Verbose output")),
58 start_button (_("Start")),
59 stop_button (_("Stop")),
62 options_packer (4, 2),
66 options_packer (14, 2),
70 using namespace Notebook_Helpers;
72 vector<string> strings;
76 _interface_chosen = false;
78 strings.push_back (_("8000Hz"));
79 strings.push_back (_("22050Hz"));
80 strings.push_back (_("44100Hz"));
81 strings.push_back (_("48000Hz"));
82 strings.push_back (_("88200Hz"));
83 strings.push_back (_("96000Hz"));
84 strings.push_back (_("192000Hz"));
85 set_popdown_strings (sample_rate_combo, strings);
86 sample_rate_combo.set_active_text ("48000Hz");
89 strings.push_back ("32");
90 strings.push_back ("64");
91 strings.push_back ("128");
92 strings.push_back ("256");
93 strings.push_back ("512");
94 strings.push_back ("1024");
95 strings.push_back ("2048");
96 strings.push_back ("4096");
97 strings.push_back ("8192");
98 set_popdown_strings (period_size_combo, strings);
99 period_size_combo.set_active_text ("1024");
102 strings.push_back (_("None"));
103 strings.push_back (_("Triangular"));
104 strings.push_back (_("Rectangular"));
105 strings.push_back (_("Shaped"));
106 set_popdown_strings (dither_mode_combo, strings);
107 dither_mode_combo.set_active_text (_("None"));
109 /* basic parameters */
111 basic_packer.set_spacings (6);
115 strings.push_back (X_("CoreAudio"));
117 strings.push_back (X_("ALSA"));
118 strings.push_back (X_("OSS"));
119 strings.push_back (X_("FFADO"));
121 strings.push_back (X_("NetJACK"));
122 strings.push_back (X_("Dummy"));
123 set_popdown_strings (driver_combo, strings);
124 driver_combo.set_active_text (strings.front());
126 driver_combo.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed));
130 strings.push_back (_("Playback/Recording on 1 Device"));
131 strings.push_back (_("Playback/Recording on 2 Devices"));
132 strings.push_back (_("Playback only"));
133 strings.push_back (_("Recording only"));
134 set_popdown_strings (audio_mode_combo, strings);
135 audio_mode_combo.set_active_text (strings.front());
137 audio_mode_combo.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed));
138 audio_mode_changed ();
142 label = manage (new Label (_("Driver")));
143 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
144 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
147 label = manage (new Label (_("Interface")));
148 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
149 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
152 label = manage (new Label (_("Sample Rate")));
153 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
154 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
157 label = manage (new Label (_("Buffer size")));
158 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
159 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
163 label = manage (new Label (_("Number of buffers")));
164 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
165 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
166 periods_spinner.set_value (2);
170 label = manage (new Label (_("Approximate latency")));
171 label->set_alignment (0.0, 0.5);
172 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
173 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
176 sample_rate_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
177 periods_adjustment.signal_value_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
178 period_size_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
181 /* no audio mode with CoreAudio, its duplex or nuthin' */
184 label = manage (new Label (_("Audio Mode")));
185 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
186 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
190 interface_combo.set_size_request (125, -1);
191 input_device_combo.set_size_request (125, -1);
192 output_device_combo.set_size_request (125, -1);
196 if (engine_running()) {
197 start_button.set_sensitive (false);
199 stop_button.set_sensitive (false);
202 start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
203 stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
206 button_box.pack_start (start_button, false, false);
207 button_box.pack_start (stop_button, false, false);
209 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
213 options_packer.set_spacings (6);
216 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
219 realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed));
223 label = manage (new Label (_("Realtime Priority")));
224 label->set_alignment (1.0, 0.5);
225 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
226 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
228 priority_spinner.set_value (60);
230 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
232 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
234 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
236 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
238 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
240 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
242 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
244 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
247 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
252 strings.push_back (_("Ignore"));
253 strings.push_back ("500 msec");
254 strings.push_back ("1 sec");
255 strings.push_back ("2 sec");
256 strings.push_back ("10 sec");
257 set_popdown_strings (timeout_combo, strings);
258 timeout_combo.set_active_text (strings.front ());
260 label = manage (new Label (_("Client timeout")));
261 label->set_alignment (1.0, 0.5);
262 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
263 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266 label = manage (new Label (_("Number of ports")));
267 label->set_alignment (1.0, 0.5);
268 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
269 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
273 label = manage (new Label (_("Dither")));
274 label->set_alignment (1.0, 0.5);
275 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
276 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
280 /* defer server stuff till later */
283 /* device settings */
285 device_packer.set_spacings (6);
289 label = manage (new Label (_("Input device")));
290 label->set_alignment (1.0, 0.5);
291 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
292 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
294 label = manage (new Label (_("Output device")));
295 label->set_alignment (1.0, 0.5);
296 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
297 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
300 label = manage (new Label (_("Input channels")));
301 label->set_alignment (1.0, 0.5);
302 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
303 device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
305 label = manage (new Label (_("Output channels")));
306 label->set_alignment (1.0, 0.5);
307 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
308 device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
310 label = manage (new Label (_("Hardware input latency (samples)")));
311 label->set_alignment (1.0, 0.5);
312 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
313 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
315 label = manage (new Label (_("Hardware output latency (samples)")));
316 label->set_alignment (1.0, 0.5);
317 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
318 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
321 basic_hbox.pack_start (basic_packer, false, false);
322 options_hbox.pack_start (options_packer, false, false);
324 device_packer.set_border_width (12);
325 options_packer.set_border_width (12);
326 basic_packer.set_border_width (12);
328 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
329 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
330 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
331 notebook.set_border_width (12);
333 set_border_width (12);
334 pack_start (notebook);
337 EngineControl::~EngineControl ()
343 EngineControl::discover_servers ()
345 find_jack_servers (server_strings);
347 if (server_strings.empty()) {
348 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
352 set_popdown_strings (serverpath_combo, server_strings);
353 serverpath_combo.set_active_text (server_strings.front());
355 if (server_strings.size() > 1) {
356 Gtk::Label* label = manage (new Label (_("Server:")));
357 options_packer.attach (*label, 0, 1, server_row, server_row + 1, FILL|EXPAND, (AttachOptions) 0);
358 label->set_alignment (0.0, 0.5);
359 options_packer.attach (serverpath_combo, 1, 2, server_row, server_row + 1, FILL|EXPAND, (AttachOptions) 0);
364 EngineControl::build_command_line (vector<string>& cmd)
368 bool using_oss = false;
369 bool using_alsa = false;
370 bool using_coreaudio = false;
371 bool using_netjack = false;
372 bool using_ffado = false;
373 bool using_dummy = false;
375 /* first, path to jackd */
377 cmd.push_back (serverpath_combo.get_active_text ());
379 /* now jackd arguments */
381 str = timeout_combo.get_active_text ();
382 if (str != _("Ignore")) {
386 msecs = (uint32_t) floor (secs * 1000.0);
387 cmd.push_back ("-t");
388 cmd.push_back (to_string (msecs, std::dec));
391 if (no_memory_lock_button.get_active()) {
392 cmd.push_back ("-m"); /* no munlock */
395 cmd.push_back ("-p"); /* port max */
396 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
398 if (realtime_button.get_active()) {
399 cmd.push_back ("-R");
400 cmd.push_back ("-P");
401 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
404 if (unlock_memory_button.get_active()) {
405 cmd.push_back ("-u");
408 if (verbose_output_button.get_active()) {
409 cmd.push_back ("-v");
412 /* now add fixed arguments (not user-selectable) */
414 cmd.push_back ("-T"); // temporary */
416 /* next the driver */
418 cmd.push_back ("-d");
420 driver = driver_combo.get_active_text ();
421 if (driver == X_("ALSA")) {
423 cmd.push_back ("alsa");
424 } else if (driver == X_("OSS")) {
426 cmd.push_back ("oss");
427 } else if (driver == X_("CoreAudio")) {
428 using_coreaudio = true;
429 cmd.push_back ("coreaudio");
430 } else if (driver == X_("NetJACK")) {
431 using_netjack = true;
432 cmd.push_back ("netjack");
433 } else if (driver == X_("FFADO")) {
436 /* do this until FFADO becomes the standard */
438 char* hack = getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
441 cmd.push_back (hack);
443 cmd.push_back ("freebob");
446 } else if ( driver == X_("Dummy")) {
448 cmd.push_back ("dummy");
451 /* driver arguments */
453 if (!using_coreaudio) {
454 str = audio_mode_combo.get_active_text();
456 if (str == _("Playback/Recording on 1 Device")) {
460 } else if (str == _("Playback/Recording on 2 Devices")) {
462 string input_device = get_device_name (driver, input_device_combo.get_active_text());
463 string output_device = get_device_name (driver, output_device_combo.get_active_text());
465 if (input_device.empty() || output_device.empty()) {
470 cmd.push_back ("-C");
471 cmd.push_back (input_device);
472 cmd.push_back ("-P");
473 cmd.push_back (output_device);
475 } else if (str == _("Playback only")) {
476 cmd.push_back ("-P");
477 } else if (str == _("Recording only")) {
478 cmd.push_back ("-C");
481 if (! using_dummy ) {
482 cmd.push_back ("-n");
483 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
487 cmd.push_back ("-r");
488 cmd.push_back (to_string (get_rate(), std::dec));
490 cmd.push_back ("-p");
491 cmd.push_back (period_size_combo.get_active_text());
495 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
497 string device = get_device_name (driver, interface_combo.get_active_text());
498 if (device.empty()) {
503 cmd.push_back ("-d");
504 cmd.push_back (device);
505 _interface_chosen = true;
508 if (hw_meter_button.get_active()) {
509 cmd.push_back ("-M");
512 if (hw_monitor_button.get_active()) {
513 cmd.push_back ("-H");
516 str = dither_mode_combo.get_active_text();
518 if (str == _("None")) {
519 } else if (str == _("Triangular")) {
520 cmd.push_back ("-z triangular");
521 } else if (str == _("Rectangular")) {
522 cmd.push_back ("-z rectangular");
523 } else if (str == _("Shaped")) {
524 cmd.push_back ("-z shaped");
527 if (force16bit_button.get_active()) {
528 cmd.push_back ("-S");
531 if (soft_mode_button.get_active()) {
532 cmd.push_back ("-s");
535 } else if (using_coreaudio) {
538 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
540 string device = get_device_name (driver, interface_combo.get_active_text());
541 if (device.empty()) {
546 cmd.push_back ("-d");
547 cmd.push_back (device);
550 } else if (using_oss) {
552 } else if (using_netjack) {
558 EngineControl::engine_running ()
560 jack_status_t status;
561 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
564 jack_client_close (c);
571 EngineControl::setup_engine ()
574 std::string cwd = "/tmp";
576 build_command_line (args);
579 return 1; // try again
582 Glib::ustring jackdrc_path = Glib::get_home_dir();
583 jackdrc_path += "/.jackdrc";
585 ofstream jackdrc (jackdrc_path.c_str());
587 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
590 cerr << "JACK COMMAND: ";
591 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
593 jackdrc << (*i) << ' ';
605 EngineControl::realtime_changed ()
608 priority_spinner.set_sensitive (realtime_button.get_active());
613 EngineControl::enumerate_devices (const string& driver)
615 /* note: case matters for the map keys */
617 if (driver == "CoreAudio") {
619 devices[driver] = enumerate_coreaudio_devices ();
623 } else if (driver == "ALSA") {
624 devices[driver] = enumerate_alsa_devices ();
625 } else if (driver == "FFADO") {
626 devices[driver] = enumerate_ffado_devices ();
627 } else if (driver == "OSS") {
628 devices[driver] = enumerate_oss_devices ();
629 } else if (driver == "Dummy") {
630 devices[driver] = enumerate_dummy_devices ();
631 } else if (driver == "NetJACK") {
632 devices[driver] = enumerate_netjack_devices ();
641 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
643 UInt32 size = sizeof(CFStringRef);
645 OSStatus res = AudioDeviceGetProperty(id, 0, false,
646 kAudioDevicePropertyDeviceUID, &size, &UI);
648 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
654 EngineControl::enumerate_coreaudio_devices ()
658 // Find out how many Core Audio devices are there, if any...
659 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
662 size_t outSize = sizeof(isWritable);
664 backend_devs.clear ();
666 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
667 &outSize, &isWritable);
669 // Calculate the number of device available...
670 int numCoreDevices = outSize / sizeof(AudioDeviceID);
671 // Make space for the devices we are about to get...
672 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
673 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
674 &outSize, (void *) coreDeviceIDs);
676 // Look for the CoreAudio device name...
677 char coreDeviceName[256];
680 for (int i = 0; i < numCoreDevices; i++) {
682 nameSize = sizeof (coreDeviceName);
684 /* enforce duplex devices only */
686 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
687 0, true, kAudioDevicePropertyStreams,
688 &outSize, &isWritable);
690 if (err != noErr || outSize == 0) {
694 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
695 0, false, kAudioDevicePropertyStreams,
696 &outSize, &isWritable);
698 if (err != noErr || outSize == 0) {
702 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
703 0, true, kAudioDevicePropertyDeviceName,
704 &outSize, &isWritable);
706 err = AudioDeviceGetProperty(coreDeviceIDs[i],
707 0, true, kAudioDevicePropertyDeviceName,
708 &nameSize, (void *) coreDeviceName);
710 char drivername[128];
712 // this returns the unique id for the device
713 // that must be used on the commandline for jack
715 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
716 devs.push_back (coreDeviceName);
717 backend_devs.push_back (drivername);
723 delete [] coreDeviceIDs;
727 if (devs.size() == 0) {
728 MessageDialog msg (_("\
729 You do not have any audio devices capable of\n\
730 simultaneous playback and recording.\n\n\
731 Please use Applications -> Utilities -> Audio MIDI Setup\n\
732 to create an \"aggregrate\" device, or install a suitable\n\
733 audio interface.\n\n\
734 Please send email to Apple and ask them why new Macs\n\
735 have no duplex audio device.\n\n\
736 Alternatively, if you really want just playback\n\
737 or recording but not both, start JACK before running\n\
738 Ardour and choose the relevant device then."
740 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
741 msg.set_title (_("No suitable audio devices"));
742 msg.set_position (Gtk::WIN_POS_MOUSE);
752 EngineControl::enumerate_alsa_devices ()
757 snd_ctl_card_info_t *info;
758 snd_pcm_info_t *pcminfo;
759 snd_ctl_card_info_alloca(&info);
760 snd_pcm_info_alloca(&pcminfo);
765 backend_devs.clear ();
767 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
770 devname += to_string (cardnum, std::dec);
772 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
774 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
776 bool have_playback = false;
777 bool have_capture = false;
779 /* find duplex devices only */
781 snd_pcm_info_set_device (pcminfo, device);
782 snd_pcm_info_set_subdevice (pcminfo, 0);
783 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE);
785 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
789 snd_pcm_info_set_device (pcminfo, device);
790 snd_pcm_info_set_subdevice (pcminfo, 0);
791 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
793 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
794 have_playback = true;
797 if (have_capture && have_playback) {
798 devs.push_back (snd_pcm_info_get_name (pcminfo));
800 devname += to_string (device, std::dec);
801 backend_devs.push_back (devname);
805 snd_ctl_close(handle);
813 EngineControl::enumerate_ffado_devices ()
816 backend_devs.clear ();
821 EngineControl::enumerate_oss_devices ()
827 EngineControl::enumerate_dummy_devices ()
833 EngineControl::enumerate_netjack_devices ()
841 EngineControl::driver_changed ()
843 string driver = driver_combo.get_active_text();
844 string::size_type maxlen = 0;
848 enumerate_devices (driver);
850 vector<string>& strings = devices[driver];
852 if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
853 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
857 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
858 if ((*i).length() > maxlen) {
859 maxlen = (*i).length();
864 set_popdown_strings (interface_combo, strings);
865 set_popdown_strings (input_device_combo, strings);
866 set_popdown_strings (output_device_combo, strings);
868 if (!strings.empty()) {
869 interface_combo.set_active_text (strings.front());
870 input_device_combo.set_active_text (strings.front());
871 output_device_combo.set_active_text (strings.front());
874 if (driver == "ALSA") {
875 soft_mode_button.set_sensitive (true);
876 force16bit_button.set_sensitive (true);
877 hw_monitor_button.set_sensitive (true);
878 hw_meter_button.set_sensitive (true);
879 monitor_button.set_sensitive (true);
881 soft_mode_button.set_sensitive (false);
882 force16bit_button.set_sensitive (false);
883 hw_monitor_button.set_sensitive (false);
884 hw_meter_button.set_sensitive (false);
885 monitor_button.set_sensitive (false);
890 EngineControl::get_rate ()
892 return atoi (sample_rate_combo.get_active_text ());
896 EngineControl::redisplay_latency ()
898 uint32_t rate = get_rate();
902 float periods = periods_adjustment.get_value();
904 float period_size = atof (period_size_combo.get_active_text());
907 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
909 latency_label.set_text (buf);
913 EngineControl::audio_mode_changed ()
915 Glib::ustring str = audio_mode_combo.get_active_text();
917 if (str == _("Playback/Recording on 1 Device")) {
918 input_device_combo.set_sensitive (false);
919 output_device_combo.set_sensitive (false);
920 } else if (str == _("Playback/Recording on 2 Devices")) {
921 input_device_combo.set_sensitive (true);
922 output_device_combo.set_sensitive (true);
923 } else if (str == _("Playback only")) {
924 output_device_combo.set_sensitive (true);
925 } else if (str == _("Recording only")) {
926 input_device_combo.set_sensitive (true);
930 static bool jack_server_filter(const string& str, void *arg)
932 return str == "jackd" || str == "jackdmp";
936 EngineControl::find_jack_servers (vector<string>& strings)
939 /* this magic lets us finds the path to the OSX bundle, and then
940 we infer JACK's location from there
943 char execpath[MAXPATHLEN+1];
944 uint32_t pathsz = sizeof (execpath);
946 _NSGetExecutablePath (execpath, &pathsz);
948 string path (Glib::path_get_dirname (execpath));
951 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
952 strings.push_back (path);
955 if (getenv ("ARDOUR_WITH_JACK")) {
956 /* no other options - only use the JACK we supply */
957 if (strings.empty()) {
958 fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
968 vector<string *> *jack_servers;
969 std::map<string,int> un;
971 bool need_minimal_path = false;
978 need_minimal_path = true;
982 // many mac users don't have PATH set up to include
983 // likely installed locations of JACK
984 need_minimal_path = true;
987 if (need_minimal_path) {
989 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
991 path += ":/usr/local/bin:/opt/local/bin";
996 // push it back into the environment so that auto-started JACK can find it.
997 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
998 setenv ("PATH", path.c_str(), 1);
1001 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1003 vector<string *>::iterator iter;
1005 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1009 strings.push_back(p);
1015 EngineControl::get_device_name (const string& driver, const string& human_readable)
1017 vector<string>::iterator n;
1018 vector<string>::iterator i;
1020 if (human_readable.empty()) {
1021 /* this can happen if the user's .ardourrc file has a device name from
1022 another computer system in it
1024 MessageDialog msg (_("You need to choose an audio device first."));
1029 if (backend_devs.empty()) {
1030 return human_readable;
1033 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1034 if (human_readable == (*i)) {
1039 if (i == devices[driver].end()) {
1040 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1047 EngineControl::get_state ()
1049 XMLNode* root = new XMLNode ("AudioSetup");
1053 child = new XMLNode ("periods");
1054 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1055 root->add_child_nocopy (*child);
1057 child = new XMLNode ("priority");
1058 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1059 root->add_child_nocopy (*child);
1061 child = new XMLNode ("ports");
1062 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1063 root->add_child_nocopy (*child);
1065 child = new XMLNode ("inchannels");
1066 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1067 root->add_child_nocopy (*child);
1069 child = new XMLNode ("outchannels");
1070 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1071 root->add_child_nocopy (*child);
1073 child = new XMLNode ("inlatency");
1074 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1075 root->add_child_nocopy (*child);
1077 child = new XMLNode ("outlatency");
1078 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1079 root->add_child_nocopy (*child);
1081 child = new XMLNode ("realtime");
1082 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1083 root->add_child_nocopy (*child);
1085 child = new XMLNode ("nomemorylock");
1086 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1087 root->add_child_nocopy (*child);
1089 child = new XMLNode ("unlockmemory");
1090 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1091 root->add_child_nocopy (*child);
1093 child = new XMLNode ("softmode");
1094 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1095 root->add_child_nocopy (*child);
1097 child = new XMLNode ("force16bit");
1098 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1099 root->add_child_nocopy (*child);
1101 child = new XMLNode ("hwmonitor");
1102 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1103 root->add_child_nocopy (*child);
1105 child = new XMLNode ("hwmeter");
1106 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1107 root->add_child_nocopy (*child);
1109 child = new XMLNode ("verbose");
1110 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1111 root->add_child_nocopy (*child);
1113 child = new XMLNode ("samplerate");
1114 child->add_property ("val", sample_rate_combo.get_active_text());
1115 root->add_child_nocopy (*child);
1117 child = new XMLNode ("periodsize");
1118 child->add_property ("val", period_size_combo.get_active_text());
1119 root->add_child_nocopy (*child);
1121 child = new XMLNode ("serverpath");
1122 child->add_property ("val", serverpath_combo.get_active_text());
1123 root->add_child_nocopy (*child);
1125 child = new XMLNode ("driver");
1126 child->add_property ("val", driver_combo.get_active_text());
1127 root->add_child_nocopy (*child);
1129 child = new XMLNode ("interface");
1130 child->add_property ("val", interface_combo.get_active_text());
1131 root->add_child_nocopy (*child);
1133 child = new XMLNode ("timeout");
1134 child->add_property ("val", timeout_combo.get_active_text());
1135 root->add_child_nocopy (*child);
1137 child = new XMLNode ("dither");
1138 child->add_property ("val", dither_mode_combo.get_active_text());
1139 root->add_child_nocopy (*child);
1141 child = new XMLNode ("audiomode");
1142 child->add_property ("val", audio_mode_combo.get_active_text());
1143 root->add_child_nocopy (*child);
1145 child = new XMLNode ("inputdevice");
1146 child->add_property ("val", input_device_combo.get_active_text());
1147 root->add_child_nocopy (*child);
1149 child = new XMLNode ("outputdevice");
1150 child->add_property ("val", output_device_combo.get_active_text());
1151 root->add_child_nocopy (*child);
1157 EngineControl::set_state (const XMLNode& root)
1160 XMLNodeConstIterator citer;
1162 XMLProperty* prop = NULL;
1163 bool using_dummy = false;
1168 if ( (child = root.child ("driver"))){
1169 prop = child->property("val");
1170 if (prop && (prop->value() == "Dummy") ) {
1175 clist = root.children();
1177 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1179 if (prop && (prop->value() == "FFADO" )) {
1185 prop = child->property ("val");
1187 if (!prop || prop->value().empty()) {
1189 if (using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" )) {
1193 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1197 strval = prop->value();
1199 /* adjustments/spinners */
1201 if (child->name() == "periods") {
1202 val = atoi (strval);
1203 periods_adjustment.set_value(val);
1204 } else if (child->name() == "priority") {
1205 val = atoi (strval);
1206 priority_adjustment.set_value(val);
1207 } else if (child->name() == "ports") {
1208 val = atoi (strval);
1209 ports_adjustment.set_value(val);
1210 } else if (child->name() == "inchannels") {
1211 val = atoi (strval);
1212 input_channels.set_value(val);
1213 } else if (child->name() == "outchannels") {
1214 val = atoi (strval);
1215 output_channels.set_value(val);
1216 } else if (child->name() == "inlatency") {
1217 val = atoi (strval);
1218 input_latency.set_value(val);
1219 } else if (child->name() == "outlatency") {
1220 val = atoi (strval);
1221 output_latency.set_value(val);
1226 else if (child->name() == "realtime") {
1227 val = atoi (strval);
1228 realtime_button.set_active(val);
1229 } else if (child->name() == "nomemorylock") {
1230 val = atoi (strval);
1231 no_memory_lock_button.set_active(val);
1232 } else if (child->name() == "unlockmemory") {
1233 val = atoi (strval);
1234 unlock_memory_button.set_active(val);
1235 } else if (child->name() == "softmode") {
1236 val = atoi (strval);
1237 soft_mode_button.set_active(val);
1238 } else if (child->name() == "force16bit") {
1239 val = atoi (strval);
1240 force16bit_button.set_active(val);
1241 } else if (child->name() == "hwmonitor") {
1242 val = atoi (strval);
1243 hw_monitor_button.set_active(val);
1244 } else if (child->name() == "hwmeter") {
1245 val = atoi (strval);
1246 hw_meter_button.set_active(val);
1247 } else if (child->name() == "verbose") {
1248 val = atoi (strval);
1249 verbose_output_button.set_active(val);
1254 else if (child->name() == "samplerate") {
1255 sample_rate_combo.set_active_text(strval);
1256 } else if (child->name() == "periodsize") {
1257 period_size_combo.set_active_text(strval);
1258 } else if (child->name() == "serverpath") {
1260 /* only attempt to set this if we have bothered to look
1261 up server names already. otherwise this is all
1262 redundant (actually, all of this dialog/widget
1263 is redundant in that case ...)
1267 if (!server_strings.empty()) {
1268 /* do not allow us to use a server path that doesn't
1269 exist on this system. this handles cases where
1270 the user has an RC file listing a serverpath
1271 from some other machine.
1273 vector<string>::iterator x;
1274 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1279 if (x != server_strings.end()) {
1280 serverpath_combo.set_active_text (strval);
1282 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1288 } else if (child->name() == "driver") {
1289 driver_combo.set_active_text(strval);
1290 } else if (child->name() == "interface") {
1291 interface_combo.set_active_text(strval);
1292 if (!strval.empty()) {
1293 _interface_chosen = true;
1295 } else if (child->name() == "timeout") {
1296 timeout_combo.set_active_text(strval);
1297 } else if (child->name() == "dither") {
1298 dither_mode_combo.set_active_text(strval);
1299 } else if (child->name() == "audiomode") {
1300 audio_mode_combo.set_active_text(strval);
1301 } else if (child->name() == "inputdevice") {
1302 input_device_combo.set_active_text(strval);
1303 } else if (child->name() == "outputdevice") {
1304 output_device_combo.set_active_text(strval);