changes associated with save/restore of AutomationControl id's
[ardour2.git] / gtk2_ardour / engine_dialog.cc
bloba3c3edd1cc4815fad04270771516184b442e7fb9
1 /*
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.
20 #include <vector>
21 #include <cmath>
22 #include <fstream>
23 #include <map>
25 #include <glibmm.h>
26 #include <gtkmm/messagedialog.h>
27 #include "pbd/xml++.h"
29 #ifdef __APPLE__
30 #include <CoreAudio/CoreAudio.h>
31 #include <CoreFoundation/CFString.h>
32 #include <sys/param.h>
33 #include <mach-o/dyld.h>
34 #else
35 #include <alsa/asoundlib.h>
36 #endif
38 #include "ardour/profile.h"
39 #include <jack/jack.h>
41 #include <gtkmm/stock.h>
42 #include <gtkmm2ext/utils.h>
44 #include "pbd/convert.h"
45 #include "pbd/error.h"
46 #include "pbd/pathscanner.h"
48 #ifdef __APPLE
49 #include <CFBundle.h>
50 #endif
52 #include "engine_dialog.h"
53 #include "i18n.h"
55 using namespace std;
56 using namespace Gtk;
57 using namespace Gtkmm2ext;
58 using namespace PBD;
59 using namespace Glib;
61 EngineControl::EngineControl ()
62 : periods_adjustment (2, 2, 16, 1, 2),
63 periods_spinner (periods_adjustment),
64 priority_adjustment (60, 10, 90, 1, 10),
65 priority_spinner (priority_adjustment),
66 ports_adjustment (128, 8, 1024, 1, 16),
67 ports_spinner (ports_adjustment),
68 input_latency_adjustment (0, 0, 99999, 1),
69 input_latency (input_latency_adjustment),
70 output_latency_adjustment (0, 0, 99999, 1),
71 output_latency (output_latency_adjustment),
72 realtime_button (_("Realtime")),
73 no_memory_lock_button (_("Do not lock memory")),
74 unlock_memory_button (_("Unlock memory")),
75 soft_mode_button (_("No zombies")),
76 monitor_button (_("Provide monitor ports")),
77 force16bit_button (_("Force 16 bit")),
78 hw_monitor_button (_("H/W monitoring")),
79 hw_meter_button (_("H/W metering")),
80 verbose_output_button (_("Verbose output")),
81 start_button (_("Start")),
82 stop_button (_("Stop")),
83 #ifdef __APPLE__
84 basic_packer (5, 2),
85 options_packer (4, 2),
86 device_packer (4, 2)
87 #else
88 basic_packer (8, 2),
89 options_packer (14, 2),
90 device_packer (6, 2)
91 #endif
93 using namespace Notebook_Helpers;
94 Label* label;
95 vector<string> strings;
96 int row = 0;
98 _used = false;
100 strings.push_back (_("8000Hz"));
101 strings.push_back (_("22050Hz"));
102 strings.push_back (_("44100Hz"));
103 strings.push_back (_("48000Hz"));
104 strings.push_back (_("88200Hz"));
105 strings.push_back (_("96000Hz"));
106 strings.push_back (_("192000Hz"));
107 set_popdown_strings (sample_rate_combo, strings);
108 sample_rate_combo.set_active_text ("48000Hz");
110 strings.clear ();
111 strings.push_back ("32");
112 strings.push_back ("64");
113 strings.push_back ("128");
114 strings.push_back ("256");
115 strings.push_back ("512");
116 strings.push_back ("1024");
117 strings.push_back ("2048");
118 strings.push_back ("4096");
119 strings.push_back ("8192");
120 set_popdown_strings (period_size_combo, strings);
121 period_size_combo.set_active_text ("1024");
123 strings.clear ();
124 strings.push_back (_("None"));
125 strings.push_back (_("Triangular"));
126 strings.push_back (_("Rectangular"));
127 strings.push_back (_("Shaped"));
128 set_popdown_strings (dither_mode_combo, strings);
129 dither_mode_combo.set_active_text (_("None"));
131 /* basic parameters */
133 basic_packer.set_spacings (6);
135 strings.clear ();
136 #ifdef __APPLE__
137 strings.push_back (X_("CoreAudio"));
138 #else
139 strings.push_back (X_("ALSA"));
140 strings.push_back (X_("OSS"));
141 strings.push_back (X_("FreeBoB"));
142 strings.push_back (X_("FFADO"));
143 #endif
144 strings.push_back (X_("NetJACK"));
145 strings.push_back (X_("Dummy"));
146 set_popdown_strings (driver_combo, strings);
147 driver_combo.set_active_text (strings.front());
149 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
150 driver_changed ();
152 strings.clear ();
153 strings.push_back (_("Playback/recording on 1 device"));
154 strings.push_back (_("Playback/recording on 2 devices"));
155 strings.push_back (_("Playback only"));
156 strings.push_back (_("Recording only"));
157 set_popdown_strings (audio_mode_combo, strings);
158 audio_mode_combo.set_active_text (strings.front());
160 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
161 audio_mode_changed ();
163 strings.clear ();
164 strings.push_back (_("None"));
165 strings.push_back (_("seq"));
166 strings.push_back (_("raw"));
167 set_popdown_strings (midi_driver_combo, strings);
168 midi_driver_combo.set_active_text (strings.front ());
170 row = 0;
172 label = manage (new Label (_("Driver:")));
173 label->set_alignment (0, 0.5);
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);
176 row++;
178 label = manage (new Label (_("Interface:")));
179 label->set_alignment (0, 0.5);
180 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
181 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
182 row++;
184 label = manage (new Label (_("Sample rate:")));
185 label->set_alignment (0, 0.5);
186 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
187 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
188 row++;
190 label = manage (new Label (_("Buffer size:")));
191 label->set_alignment (0, 0.5);
192 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
193 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
194 row++;
196 #ifndef __APPLE__
197 label = manage (new Label (_("Number of buffers:")));
198 label->set_alignment (0, 0.5);
199 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
200 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
201 periods_spinner.set_value (2);
202 row++;
203 #endif
205 label = manage (new Label (_("Approximate latency:")));
206 label->set_alignment (0, 0.5);
207 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
208 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
209 row++;
211 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
212 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
213 period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
214 redisplay_latency();
215 row++;
216 /* no audio mode with CoreAudio, its duplex or nuthin' */
218 #ifndef __APPLE__
219 label = manage (new Label (_("Audio mode:")));
220 label->set_alignment (0, 0.5);
221 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
222 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
223 row++;
224 #endif
226 interface_combo.set_size_request (250, -1);
227 input_device_combo.set_size_request (250, -1);
228 output_device_combo.set_size_request (250, -1);
232 if (engine_running()) {
233 start_button.set_sensitive (false);
234 } else {
235 stop_button.set_sensitive (false);
238 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
239 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
242 button_box.pack_start (start_button, false, false);
243 button_box.pack_start (stop_button, false, false);
245 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
247 /* options */
249 options_packer.set_spacings (6);
250 row = 0;
252 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
253 ++row;
255 realtime_button.set_active (true);
256 realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
257 realtime_changed ();
259 #if PROVIDE_TOO_MANY_OPTIONS
261 #ifndef __APPLE__
262 label = manage (new Label (_("Realtime Priority")));
263 label->set_alignment (1.0, 0.5);
264 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
265 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266 ++row;
267 priority_spinner.set_value (60);
269 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
270 ++row;
271 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
272 ++row;
273 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
274 ++row;
275 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
276 ++row;
277 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
278 ++row;
279 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
280 ++row;
281 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
282 ++row;
283 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
284 ++row;
285 #else
286 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
287 ++row;
288 #endif
290 strings.clear ();
291 strings.push_back (_("Ignore"));
292 strings.push_back ("500 msec");
293 strings.push_back ("1 sec");
294 strings.push_back ("2 sec");
295 strings.push_back ("10 sec");
296 set_popdown_strings (timeout_combo, strings);
297 timeout_combo.set_active_text (strings.front ());
299 label = manage (new Label (_("Client timeout")));
300 label->set_alignment (1.0, 0.5);
301 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
302 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
303 ++row;
305 #endif /* PROVIDE_TOO_MANY_OPTIONS */
306 label = manage (new Label (_("Number of ports:")));
307 label->set_alignment (0, 0.5);
308 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
309 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
310 ++row;
312 label = manage (new Label (_("MIDI driver:")));
313 label->set_alignment (0, 0.5);
314 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
315 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
316 ++row;
318 #ifndef __APPLE__
319 label = manage (new Label (_("Dither:")));
320 label->set_alignment (0, 0.5);
321 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
322 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
323 ++row;
324 #endif
326 find_jack_servers (server_strings);
328 if (server_strings.empty()) {
329 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
330 /*NOTREACHED*/
333 set_popdown_strings (serverpath_combo, server_strings);
334 serverpath_combo.set_active_text (server_strings.front());
336 if (server_strings.size() > 1) {
337 label = manage (new Label (_("Server:")));
338 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
339 label->set_alignment (0.0, 0.5);
340 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
341 ++row;
344 /* device settings */
346 device_packer.set_spacings (6);
347 row = 0;
349 #ifndef __APPLE__
350 label = manage (new Label (_("Input device:")));
351 label->set_alignment (0, 0.5);
352 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
353 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
354 ++row;
355 label = manage (new Label (_("Output device:")));
356 label->set_alignment (0, 0.5);
357 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
358 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
359 ++row;
360 #endif
361 label = manage (new Label (_("Input channels:")));
362 label->set_alignment (0, 0.5);
363 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
364 device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
365 ++row;
366 label = manage (new Label (_("Output channels:")));
367 label->set_alignment (0, 0.5);
368 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
369 device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
370 ++row;
371 label = manage (new Label (_("Hardware input latency:")));
372 label->set_alignment (0, 0.5);
373 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
374 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
375 label = manage (new Label (_("samples")));
376 label->set_alignment (0, 0.5);
377 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
378 ++row;
379 label = manage (new Label (_("Hardware output latency:")));
380 label->set_alignment (0, 0.5);
381 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
382 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
383 label = manage (new Label (_("samples")));
384 label->set_alignment (0, 0.5);
385 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
386 ++row;
388 basic_hbox.pack_start (basic_packer, false, false);
389 options_hbox.pack_start (options_packer, false, false);
391 device_packer.set_border_width (12);
392 options_packer.set_border_width (12);
393 basic_packer.set_border_width (12);
395 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
396 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
397 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
398 notebook.set_border_width (12);
400 set_border_width (12);
401 pack_start (notebook);
404 EngineControl::~EngineControl ()
409 void
410 EngineControl::build_command_line (vector<string>& cmd)
412 string str;
413 string driver;
414 bool using_alsa = false;
415 bool using_coreaudio = false;
416 bool using_dummy = false;
417 bool using_ffado = false;
419 /* first, path to jackd */
421 cmd.push_back (serverpath_combo.get_active_text ());
423 /* now jackd arguments */
425 str = timeout_combo.get_active_text ();
427 if (str != _("Ignore")) {
429 double secs = 0;
430 uint32_t msecs;
431 secs = atof (str);
432 msecs = (uint32_t) floor (secs * 1000.0);
434 if (msecs > 0) {
435 cmd.push_back ("-t");
436 cmd.push_back (to_string (msecs, std::dec));
440 if (no_memory_lock_button.get_active()) {
441 cmd.push_back ("-m"); /* no munlock */
444 cmd.push_back ("-p"); /* port max */
445 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
447 if (realtime_button.get_active()) {
448 cmd.push_back ("-R");
449 cmd.push_back ("-P");
450 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
451 } else {
452 cmd.push_back ("-r"); /* override jackd's default --realtime */
455 if (unlock_memory_button.get_active()) {
456 cmd.push_back ("-u");
459 if (verbose_output_button.get_active()) {
460 cmd.push_back ("-v");
463 /* now add fixed arguments (not user-selectable) */
465 cmd.push_back ("-T"); // temporary */
467 /* next the driver */
469 cmd.push_back ("-d");
471 driver = driver_combo.get_active_text ();
473 if (driver == X_("ALSA")) {
474 using_alsa = true;
475 cmd.push_back ("alsa");
476 } else if (driver == X_("OSS")) {
477 cmd.push_back ("oss");
478 } else if (driver == X_("CoreAudio")) {
479 using_coreaudio = true;
480 cmd.push_back ("coreaudio");
481 } else if (driver == X_("NetJACK")) {
482 cmd.push_back ("netjack");
483 } else if (driver == X_("FreeBoB")) {
484 cmd.push_back ("freebob");
485 } else if (driver == X_("FFADO")) {
486 using_ffado = true;
487 cmd.push_back ("firewire");
488 } else if ( driver == X_("Dummy")) {
489 using_dummy = true;
490 cmd.push_back ("dummy");
493 /* driver arguments */
495 if (!using_coreaudio) {
496 str = audio_mode_combo.get_active_text();
498 if (str == _("Playback/Recording on 1 Device")) {
500 /* relax */
502 } else if (str == _("Playback/Recording on 2 Devices")) {
504 string input_device = get_device_name (driver, input_device_combo.get_active_text());
505 string output_device = get_device_name (driver, output_device_combo.get_active_text());
507 if (input_device.empty() || output_device.empty()) {
508 cmd.clear ();
509 return;
512 cmd.push_back ("-C");
513 cmd.push_back (input_device);
515 cmd.push_back ("-P");
516 cmd.push_back (output_device);
518 } else if (str == _("Playback only")) {
519 cmd.push_back ("-P");
520 } else if (str == _("Recording only")) {
521 cmd.push_back ("-C");
524 if (!using_dummy) {
525 cmd.push_back ("-n");
526 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
530 cmd.push_back ("-r");
531 cmd.push_back (to_string (get_rate(), std::dec));
533 cmd.push_back ("-p");
534 cmd.push_back (period_size_combo.get_active_text());
536 if (using_alsa || using_ffado || using_coreaudio) {
538 double val = input_latency_adjustment.get_value();
540 if (val) {
541 cmd.push_back ("-I");
542 cmd.push_back (to_string ((uint32_t) val, std::dec));
545 val = output_latency_adjustment.get_value();
547 if (val) {
548 cmd.push_back ("-O");
549 cmd.push_back (to_string ((uint32_t) val, std::dec));
553 if (using_alsa) {
555 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
557 string device = get_device_name (driver, interface_combo.get_active_text());
558 if (device.empty()) {
559 cmd.clear ();
560 return;
563 cmd.push_back ("-d");
564 cmd.push_back (device);
567 if (hw_meter_button.get_active()) {
568 cmd.push_back ("-M");
571 if (hw_monitor_button.get_active()) {
572 cmd.push_back ("-H");
575 str = dither_mode_combo.get_active_text();
577 if (str == _("None")) {
578 } else if (str == _("Triangular")) {
579 cmd.push_back ("-z triangular");
580 } else if (str == _("Rectangular")) {
581 cmd.push_back ("-z rectangular");
582 } else if (str == _("Shaped")) {
583 cmd.push_back ("-z shaped");
586 if (force16bit_button.get_active()) {
587 cmd.push_back ("-S");
590 if (soft_mode_button.get_active()) {
591 cmd.push_back ("-s");
594 str = midi_driver_combo.get_active_text ();
596 if (str == _("seq")) {
597 cmd.push_back ("-X seq");
598 } else if (str == _("raw")) {
599 cmd.push_back ("-X raw");
601 } else if (using_coreaudio) {
603 #ifdef __APPLE__
604 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
606 string device = get_device_name (driver, interface_combo.get_active_text());
607 if (device.empty()) {
608 cmd.clear ();
609 return;
612 cmd.push_back ("-d");
613 cmd.push_back (device);
614 #endif
619 bool
620 EngineControl::engine_running ()
622 jack_status_t status;
623 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
625 if (status == 0) {
626 jack_client_close (c);
627 return true;
629 return false;
633 EngineControl::setup_engine ()
635 vector<string> args;
636 std::string cwd = "/tmp";
638 build_command_line (args);
640 if (args.empty()) {
641 return 1; // try again
644 std::string jackdrc_path = Glib::get_home_dir();
645 jackdrc_path += "/.jackdrc";
647 ofstream jackdrc (jackdrc_path.c_str());
648 if (!jackdrc) {
649 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
650 return -1;
652 cerr << "JACK COMMAND: ";
653 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
654 cerr << (*i) << ' ';
655 jackdrc << (*i) << ' ';
657 cerr << endl;
658 jackdrc << endl;
659 jackdrc.close ();
661 _used = true;
663 return 0;
666 void
667 EngineControl::realtime_changed ()
669 #ifndef __APPLE__
670 priority_spinner.set_sensitive (realtime_button.get_active());
671 #endif
674 void
675 EngineControl::enumerate_devices (const string& driver)
677 /* note: case matters for the map keys */
679 if (driver == "CoreAudio") {
680 #ifdef __APPLE__
681 devices[driver] = enumerate_coreaudio_devices ();
682 #endif
684 #ifndef __APPLE__
685 } else if (driver == "ALSA") {
686 devices[driver] = enumerate_alsa_devices ();
687 } else if (driver == "FreeBOB") {
688 devices[driver] = enumerate_freebob_devices ();
689 } else if (driver == "FFADO") {
690 devices[driver] = enumerate_ffado_devices ();
691 } else if (driver == "OSS") {
692 devices[driver] = enumerate_oss_devices ();
693 } else if (driver == "Dummy") {
694 devices[driver] = enumerate_dummy_devices ();
695 } else if (driver == "NetJACK") {
696 devices[driver] = enumerate_netjack_devices ();
698 #else
700 #endif
703 #ifdef __APPLE__
704 static OSStatus
705 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
707 UInt32 size = sizeof(CFStringRef);
708 CFStringRef UI;
709 OSStatus res = AudioDeviceGetProperty(id, 0, false,
710 kAudioDevicePropertyDeviceUID, &size, &UI);
711 if (res == noErr)
712 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
713 CFRelease(UI);
714 return res;
717 vector<string>
718 EngineControl::enumerate_coreaudio_devices ()
720 vector<string> devs;
722 // Find out how many Core Audio devices are there, if any...
723 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
724 OSStatus err;
725 Boolean isWritable;
726 size_t outSize = sizeof(isWritable);
728 backend_devs.clear ();
730 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
731 &outSize, &isWritable);
732 if (err == noErr) {
733 // Calculate the number of device available...
734 int numCoreDevices = outSize / sizeof(AudioDeviceID);
735 // Make space for the devices we are about to get...
736 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
737 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
738 &outSize, (void *) coreDeviceIDs);
739 if (err == noErr) {
740 // Look for the CoreAudio device name...
741 char coreDeviceName[256];
742 size_t nameSize;
744 for (int i = 0; i < numCoreDevices; i++) {
746 nameSize = sizeof (coreDeviceName);
748 /* enforce duplex devices only */
750 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
751 0, true, kAudioDevicePropertyStreams,
752 &outSize, &isWritable);
754 if (err != noErr || outSize == 0) {
755 continue;
758 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
759 0, false, kAudioDevicePropertyStreams,
760 &outSize, &isWritable);
762 if (err != noErr || outSize == 0) {
763 continue;
766 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
767 0, true, kAudioDevicePropertyDeviceName,
768 &outSize, &isWritable);
769 if (err == noErr) {
770 err = AudioDeviceGetProperty(coreDeviceIDs[i],
771 0, true, kAudioDevicePropertyDeviceName,
772 &nameSize, (void *) coreDeviceName);
773 if (err == noErr) {
774 char drivername[128];
776 // this returns the unique id for the device
777 // that must be used on the commandline for jack
779 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
780 devs.push_back (coreDeviceName);
781 backend_devs.push_back (drivername);
787 delete [] coreDeviceIDs;
791 if (devs.size() == 0) {
792 MessageDialog msg (_("\
793 You do not have any audio devices capable of\n\
794 simultaneous playback and recording.\n\n\
795 Please use Applications -> Utilities -> Audio MIDI Setup\n\
796 to create an \"aggregrate\" device, or install a suitable\n\
797 audio interface.\n\n\
798 Please send email to Apple and ask them why new Macs\n\
799 have no duplex audio device.\n\n\
800 Alternatively, if you really want just playback\n\
801 or recording but not both, start JACK before running\n\
802 Ardour and choose the relevant device then."
804 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
805 msg.set_title (_("No suitable audio devices"));
806 msg.set_position (Gtk::WIN_POS_MOUSE);
807 msg.run ();
808 exit (1);
812 return devs;
814 #else
815 vector<string>
816 EngineControl::enumerate_alsa_devices ()
818 vector<string> devs;
820 snd_ctl_t *handle;
821 snd_ctl_card_info_t *info;
822 snd_pcm_info_t *pcminfo;
823 snd_ctl_card_info_alloca(&info);
824 snd_pcm_info_alloca(&pcminfo);
825 string devname;
826 int cardnum = -1;
827 int device = -1;
829 backend_devs.clear ();
831 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
833 devname = "hw:";
834 devname += to_string (cardnum, std::dec);
836 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
838 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
840 snd_pcm_info_set_device (pcminfo, device);
841 snd_pcm_info_set_subdevice (pcminfo, 0);
842 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
844 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
845 devs.push_back (snd_pcm_info_get_name (pcminfo));
846 devname += ',';
847 devname += to_string (device, std::dec);
848 backend_devs.push_back (devname);
852 snd_ctl_close(handle);
856 return devs;
859 vector<string>
860 EngineControl::enumerate_ffado_devices ()
862 vector<string> devs;
863 backend_devs.clear ();
864 return devs;
867 vector<string>
868 EngineControl::enumerate_freebob_devices ()
870 vector<string> devs;
871 return devs;
874 vector<string>
875 EngineControl::enumerate_oss_devices ()
877 vector<string> devs;
878 return devs;
880 vector<string>
881 EngineControl::enumerate_dummy_devices ()
883 vector<string> devs;
884 return devs;
886 vector<string>
887 EngineControl::enumerate_netjack_devices ()
889 vector<string> devs;
890 return devs;
892 #endif
894 void
895 EngineControl::driver_changed ()
897 string driver = driver_combo.get_active_text();
898 string::size_type maxlen = 0;
899 int maxindex = -1;
900 int n = 0;
902 enumerate_devices (driver);
904 vector<string>& strings = devices[driver];
906 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
907 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
908 return;
911 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
912 if ((*i).length() > maxlen) {
913 maxlen = (*i).length();
914 maxindex = n;
918 set_popdown_strings (interface_combo, strings);
919 set_popdown_strings (input_device_combo, strings);
920 set_popdown_strings (output_device_combo, strings);
922 if (!strings.empty()) {
923 interface_combo.set_active_text (strings.front());
924 input_device_combo.set_active_text (strings.front());
925 output_device_combo.set_active_text (strings.front());
928 if (driver == "ALSA") {
929 soft_mode_button.set_sensitive (true);
930 force16bit_button.set_sensitive (true);
931 hw_monitor_button.set_sensitive (true);
932 hw_meter_button.set_sensitive (true);
933 monitor_button.set_sensitive (true);
934 } else {
935 soft_mode_button.set_sensitive (false);
936 force16bit_button.set_sensitive (false);
937 hw_monitor_button.set_sensitive (false);
938 hw_meter_button.set_sensitive (false);
939 monitor_button.set_sensitive (false);
943 uint32_t
944 EngineControl::get_rate ()
946 return atoi (sample_rate_combo.get_active_text ());
949 void
950 EngineControl::redisplay_latency ()
952 uint32_t rate = get_rate();
953 #ifdef __APPLE_
954 float periods = 2;
955 #else
956 float periods = periods_adjustment.get_value();
957 #endif
958 float period_size = atof (period_size_combo.get_active_text());
960 char buf[32];
961 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
963 latency_label.set_text (buf);
964 latency_label.set_alignment (0, 0.5);
967 void
968 EngineControl::audio_mode_changed ()
970 std::string str = audio_mode_combo.get_active_text();
972 if (str == _("Playback/Recording on 1 Device")) {
973 input_device_combo.set_sensitive (false);
974 output_device_combo.set_sensitive (false);
975 } else if (str == _("Playback/Recording on 2 Devices")) {
976 input_device_combo.set_sensitive (true);
977 output_device_combo.set_sensitive (true);
978 } else if (str == _("Playback only")) {
979 output_device_combo.set_sensitive (true);
980 } else if (str == _("Recording only")) {
981 input_device_combo.set_sensitive (true);
985 static bool jack_server_filter(const string& str, void */*arg*/)
987 return str == "jackd" || str == "jackdmp";
990 void
991 EngineControl::find_jack_servers (vector<string>& strings)
993 #ifdef __APPLE__
994 /* this magic lets us finds the path to the OSX bundle, and then
995 we infer JACK's location from there
998 char execpath[MAXPATHLEN+1];
999 uint32_t pathsz = sizeof (execpath);
1001 _NSGetExecutablePath (execpath, &pathsz);
1003 string path (Glib::path_get_dirname (execpath));
1004 path += "/jackd";
1006 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
1007 strings.push_back (path);
1010 if (getenv ("ARDOUR_WITH_JACK")) {
1011 /* no other options - only use the JACK we supply */
1012 if (strings.empty()) {
1013 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1014 /*NOTREACHED*/
1016 return;
1018 #else
1019 string path;
1020 #endif
1022 PathScanner scanner;
1023 vector<string *> *jack_servers;
1024 std::map<string,int> un;
1025 char *p;
1026 bool need_minimal_path = false;
1028 p = getenv ("PATH");
1030 if (p && *p) {
1031 path = p;
1032 } else {
1033 need_minimal_path = true;
1036 #ifdef __APPLE__
1037 // many mac users don't have PATH set up to include
1038 // likely installed locations of JACK
1039 need_minimal_path = true;
1040 #endif
1042 if (need_minimal_path) {
1043 if (path.empty()) {
1044 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1045 } else {
1046 path += ":/usr/local/bin:/opt/local/bin";
1050 #ifdef __APPLE__
1051 // push it back into the environment so that auto-started JACK can find it.
1052 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1053 setenv ("PATH", path.c_str(), 1);
1054 #endif
1056 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1058 vector<string *>::iterator iter;
1060 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1061 string p = **iter;
1063 if (un[p]++ == 0) {
1064 strings.push_back(p);
1070 string
1071 EngineControl::get_device_name (const string& driver, const string& human_readable)
1073 vector<string>::iterator n;
1074 vector<string>::iterator i;
1076 if (human_readable.empty()) {
1077 /* this can happen if the user's .ardourrc file has a device name from
1078 another computer system in it
1080 MessageDialog msg (_("You need to choose an audio device first."));
1081 msg.run ();
1082 return string();
1085 if (backend_devs.empty()) {
1086 return human_readable;
1089 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1090 if (human_readable == (*i)) {
1091 return (*n);
1095 if (i == devices[driver].end()) {
1096 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1099 return string();
1102 XMLNode&
1103 EngineControl::get_state ()
1105 XMLNode* root = new XMLNode ("AudioSetup");
1106 XMLNode* child;
1107 std::string path;
1109 child = new XMLNode ("periods");
1110 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1111 root->add_child_nocopy (*child);
1113 child = new XMLNode ("priority");
1114 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1115 root->add_child_nocopy (*child);
1117 child = new XMLNode ("ports");
1118 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1119 root->add_child_nocopy (*child);
1121 child = new XMLNode ("inchannels");
1122 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1123 root->add_child_nocopy (*child);
1125 child = new XMLNode ("outchannels");
1126 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1127 root->add_child_nocopy (*child);
1129 child = new XMLNode ("inlatency");
1130 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1131 root->add_child_nocopy (*child);
1133 child = new XMLNode ("outlatency");
1134 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1135 root->add_child_nocopy (*child);
1137 child = new XMLNode ("realtime");
1138 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1139 root->add_child_nocopy (*child);
1141 child = new XMLNode ("nomemorylock");
1142 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1143 root->add_child_nocopy (*child);
1145 child = new XMLNode ("unlockmemory");
1146 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1147 root->add_child_nocopy (*child);
1149 child = new XMLNode ("softmode");
1150 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1151 root->add_child_nocopy (*child);
1153 child = new XMLNode ("force16bit");
1154 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1155 root->add_child_nocopy (*child);
1157 child = new XMLNode ("hwmonitor");
1158 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1159 root->add_child_nocopy (*child);
1161 child = new XMLNode ("hwmeter");
1162 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1163 root->add_child_nocopy (*child);
1165 child = new XMLNode ("verbose");
1166 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1167 root->add_child_nocopy (*child);
1169 child = new XMLNode ("samplerate");
1170 child->add_property ("val", sample_rate_combo.get_active_text());
1171 root->add_child_nocopy (*child);
1173 child = new XMLNode ("periodsize");
1174 child->add_property ("val", period_size_combo.get_active_text());
1175 root->add_child_nocopy (*child);
1177 child = new XMLNode ("serverpath");
1178 child->add_property ("val", serverpath_combo.get_active_text());
1179 root->add_child_nocopy (*child);
1181 child = new XMLNode ("driver");
1182 child->add_property ("val", driver_combo.get_active_text());
1183 root->add_child_nocopy (*child);
1185 child = new XMLNode ("interface");
1186 child->add_property ("val", interface_combo.get_active_text());
1187 root->add_child_nocopy (*child);
1189 child = new XMLNode ("timeout");
1190 child->add_property ("val", timeout_combo.get_active_text());
1191 root->add_child_nocopy (*child);
1193 child = new XMLNode ("dither");
1194 child->add_property ("val", dither_mode_combo.get_active_text());
1195 root->add_child_nocopy (*child);
1197 child = new XMLNode ("audiomode");
1198 child->add_property ("val", audio_mode_combo.get_active_text());
1199 root->add_child_nocopy (*child);
1201 child = new XMLNode ("inputdevice");
1202 child->add_property ("val", input_device_combo.get_active_text());
1203 root->add_child_nocopy (*child);
1205 child = new XMLNode ("outputdevice");
1206 child->add_property ("val", output_device_combo.get_active_text());
1207 root->add_child_nocopy (*child);
1209 child = new XMLNode ("mididriver");
1210 child->add_property ("val", midi_driver_combo.get_active_text());
1211 root->add_child_nocopy (*child);
1213 return *root;
1216 void
1217 EngineControl::set_state (const XMLNode& root)
1219 XMLNodeList clist;
1220 XMLNodeConstIterator citer;
1221 XMLNode* child;
1222 XMLProperty* prop = NULL;
1223 bool using_dummy = false;
1224 bool using_ffado = false;
1226 int val;
1227 string strval;
1229 if ( (child = root.child ("driver"))){
1230 prop = child->property("val");
1232 if (prop && (prop->value() == "Dummy") ) {
1233 using_dummy = true;
1235 if (prop && (prop->value() == "FFADO") ) {
1236 using_ffado = true;
1241 clist = root.children();
1243 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1245 child = *citer;
1247 prop = child->property ("val");
1249 if (!prop || prop->value().empty()) {
1251 if (((using_dummy || using_ffado)
1252 && ( child->name() == "interface"
1253 || child->name() == "inputdevice"
1254 || child->name() == "outputdevice"))
1255 || child->name() == "timeout")
1257 continue;
1260 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1261 continue;
1264 strval = prop->value();
1266 /* adjustments/spinners */
1268 if (child->name() == "periods") {
1269 val = atoi (strval);
1270 periods_adjustment.set_value(val);
1271 } else if (child->name() == "priority") {
1272 val = atoi (strval);
1273 priority_adjustment.set_value(val);
1274 } else if (child->name() == "ports") {
1275 val = atoi (strval);
1276 ports_adjustment.set_value(val);
1277 } else if (child->name() == "inchannels") {
1278 val = atoi (strval);
1279 input_channels.set_value(val);
1280 } else if (child->name() == "outchannels") {
1281 val = atoi (strval);
1282 output_channels.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);
1291 /* buttons */
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);
1319 /* combos */
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) {
1341 if (*x == strval) {
1342 break;
1345 if (x != server_strings.end()) {
1346 serverpath_combo.set_active_text (strval);
1347 } else {
1348 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1349 strval)
1350 << endmsg;
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);