Remove erroneous assert which I added earlier.
[ardour2.git] / gtk2_ardour / engine_dialog.cc
blobb207124ecbece7d95997af3483b50670b686d284
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 <boost/scoped_ptr.hpp>
27 #include <glibmm.h>
28 #include <gtkmm/messagedialog.h>
30 #include "pbd/epa.h"
31 #include "pbd/xml++.h"
33 #ifdef __APPLE__
34 #include <CoreAudio/CoreAudio.h>
35 #include <CoreFoundation/CFString.h>
36 #include <sys/param.h>
37 #include <mach-o/dyld.h>
38 #else
39 #include <alsa/asoundlib.h>
40 #endif
42 #include "ardour/profile.h"
43 #include <jack/jack.h>
45 #include <gtkmm/stock.h>
46 #include <gtkmm2ext/utils.h>
48 #include "pbd/convert.h"
49 #include "pbd/error.h"
50 #include "pbd/pathscanner.h"
52 #ifdef __APPLE
53 #include <CFBundle.h>
54 #endif
56 #include "engine_dialog.h"
57 #include "i18n.h"
59 using namespace std;
60 using namespace Gtk;
61 using namespace Gtkmm2ext;
62 using namespace PBD;
63 using namespace Glib;
65 EngineControl::EngineControl ()
66 : periods_adjustment (2, 2, 16, 1, 2),
67 periods_spinner (periods_adjustment),
68 priority_adjustment (60, 10, 90, 1, 10),
69 priority_spinner (priority_adjustment),
70 ports_adjustment (128, 8, 1024, 1, 16),
71 ports_spinner (ports_adjustment),
72 input_latency_adjustment (0, 0, 99999, 1),
73 input_latency (input_latency_adjustment),
74 output_latency_adjustment (0, 0, 99999, 1),
75 output_latency (output_latency_adjustment),
76 realtime_button (_("Realtime")),
77 no_memory_lock_button (_("Do not lock memory")),
78 unlock_memory_button (_("Unlock memory")),
79 soft_mode_button (_("No zombies")),
80 monitor_button (_("Provide monitor ports")),
81 force16bit_button (_("Force 16 bit")),
82 hw_monitor_button (_("H/W monitoring")),
83 hw_meter_button (_("H/W metering")),
84 verbose_output_button (_("Verbose output")),
85 start_button (_("Start")),
86 stop_button (_("Stop")),
87 #ifdef __APPLE__
88 basic_packer (5, 2),
89 options_packer (4, 2),
90 device_packer (4, 2)
91 #else
92 basic_packer (8, 2),
93 options_packer (14, 2),
94 device_packer (6, 2)
95 #endif
97 using namespace Notebook_Helpers;
98 Label* label;
99 vector<string> strings;
100 int row = 0;
102 _used = false;
104 strings.push_back (_("8000Hz"));
105 strings.push_back (_("22050Hz"));
106 strings.push_back (_("44100Hz"));
107 strings.push_back (_("48000Hz"));
108 strings.push_back (_("88200Hz"));
109 strings.push_back (_("96000Hz"));
110 strings.push_back (_("192000Hz"));
111 set_popdown_strings (sample_rate_combo, strings);
112 sample_rate_combo.set_active_text ("48000Hz");
114 strings.clear ();
115 strings.push_back ("32");
116 strings.push_back ("64");
117 strings.push_back ("128");
118 strings.push_back ("256");
119 strings.push_back ("512");
120 strings.push_back ("1024");
121 strings.push_back ("2048");
122 strings.push_back ("4096");
123 strings.push_back ("8192");
124 set_popdown_strings (period_size_combo, strings);
125 period_size_combo.set_active_text ("1024");
127 strings.clear ();
128 strings.push_back (_("None"));
129 strings.push_back (_("Triangular"));
130 strings.push_back (_("Rectangular"));
131 strings.push_back (_("Shaped"));
132 set_popdown_strings (dither_mode_combo, strings);
133 dither_mode_combo.set_active_text (_("None"));
135 /* basic parameters */
137 basic_packer.set_spacings (6);
139 strings.clear ();
140 #ifdef __APPLE__
141 strings.push_back (X_("CoreAudio"));
142 #else
143 strings.push_back (X_("ALSA"));
144 strings.push_back (X_("OSS"));
145 strings.push_back (X_("FreeBoB"));
146 strings.push_back (X_("FFADO"));
147 #endif
148 strings.push_back (X_("NetJACK"));
149 strings.push_back (X_("Dummy"));
150 set_popdown_strings (driver_combo, strings);
151 driver_combo.set_active_text (strings.front());
153 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
154 driver_changed ();
156 strings.clear ();
157 strings.push_back (_("Playback/recording on 1 device"));
158 strings.push_back (_("Playback/recording on 2 devices"));
159 strings.push_back (_("Playback only"));
160 strings.push_back (_("Recording only"));
161 set_popdown_strings (audio_mode_combo, strings);
162 audio_mode_combo.set_active_text (strings.front());
164 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
165 audio_mode_changed ();
167 strings.clear ();
168 strings.push_back (_("None"));
169 strings.push_back (_("seq"));
170 strings.push_back (_("raw"));
171 set_popdown_strings (midi_driver_combo, strings);
172 midi_driver_combo.set_active_text (strings.front ());
174 row = 0;
176 label = manage (new Label (_("Driver:")));
177 label->set_alignment (0, 0.5);
178 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
179 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
180 row++;
182 label = manage (new Label (_("Interface:")));
183 label->set_alignment (0, 0.5);
184 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
185 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
186 row++;
188 label = manage (new Label (_("Sample rate:")));
189 label->set_alignment (0, 0.5);
190 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
191 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
192 row++;
194 label = manage (new Label (_("Buffer size:")));
195 label->set_alignment (0, 0.5);
196 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
197 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
198 row++;
200 #ifndef __APPLE__
201 label = manage (new Label (_("Number of buffers:")));
202 label->set_alignment (0, 0.5);
203 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
204 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
205 periods_spinner.set_value (2);
206 row++;
207 #endif
209 label = manage (new Label (_("Approximate latency:")));
210 label->set_alignment (0, 0.5);
211 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
212 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
213 row++;
215 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
216 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
217 period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
218 redisplay_latency();
219 row++;
220 /* no audio mode with CoreAudio, its duplex or nuthin' */
222 #ifndef __APPLE__
223 label = manage (new Label (_("Audio mode:")));
224 label->set_alignment (0, 0.5);
225 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
226 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
227 row++;
228 #endif
230 interface_combo.set_size_request (250, -1);
231 input_device_combo.set_size_request (250, -1);
232 output_device_combo.set_size_request (250, -1);
236 if (engine_running()) {
237 start_button.set_sensitive (false);
238 } else {
239 stop_button.set_sensitive (false);
242 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
243 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
246 button_box.pack_start (start_button, false, false);
247 button_box.pack_start (stop_button, false, false);
249 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
251 /* options */
253 options_packer.set_spacings (6);
254 row = 0;
256 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
257 ++row;
259 realtime_button.set_active (true);
260 realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
261 realtime_changed ();
263 #if PROVIDE_TOO_MANY_OPTIONS
265 #ifndef __APPLE__
266 label = manage (new Label (_("Realtime Priority")));
267 label->set_alignment (1.0, 0.5);
268 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
269 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
270 ++row;
271 priority_spinner.set_value (60);
273 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
274 ++row;
275 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
276 ++row;
277 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
278 ++row;
279 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
280 ++row;
281 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
282 ++row;
283 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
284 ++row;
285 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
286 ++row;
287 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
288 ++row;
289 #else
290 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
291 ++row;
292 #endif
294 strings.clear ();
295 strings.push_back (_("Ignore"));
296 strings.push_back ("500 msec");
297 strings.push_back ("1 sec");
298 strings.push_back ("2 sec");
299 strings.push_back ("10 sec");
300 set_popdown_strings (timeout_combo, strings);
301 timeout_combo.set_active_text (strings.front ());
303 label = manage (new Label (_("Client timeout")));
304 label->set_alignment (1.0, 0.5);
305 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
306 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
307 ++row;
309 #endif /* PROVIDE_TOO_MANY_OPTIONS */
310 label = manage (new Label (_("Number of ports:")));
311 label->set_alignment (0, 0.5);
312 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
313 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
314 ++row;
316 label = manage (new Label (_("MIDI driver:")));
317 label->set_alignment (0, 0.5);
318 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
319 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
320 ++row;
322 #ifndef __APPLE__
323 label = manage (new Label (_("Dither:")));
324 label->set_alignment (0, 0.5);
325 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
326 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
327 ++row;
328 #endif
330 find_jack_servers (server_strings);
332 if (server_strings.empty()) {
333 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
334 /*NOTREACHED*/
337 set_popdown_strings (serverpath_combo, server_strings);
338 serverpath_combo.set_active_text (server_strings.front());
340 if (server_strings.size() > 1) {
341 label = manage (new Label (_("Server:")));
342 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
343 label->set_alignment (0.0, 0.5);
344 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
345 ++row;
348 /* device settings */
350 device_packer.set_spacings (6);
351 row = 0;
353 #ifndef __APPLE__
354 label = manage (new Label (_("Input device:")));
355 label->set_alignment (0, 0.5);
356 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
357 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
358 ++row;
359 label = manage (new Label (_("Output device:")));
360 label->set_alignment (0, 0.5);
361 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
362 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
363 ++row;
364 #endif
365 label = manage (new Label (_("Input channels:")));
366 label->set_alignment (0, 0.5);
367 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
368 device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
369 ++row;
370 label = manage (new Label (_("Output channels:")));
371 label->set_alignment (0, 0.5);
372 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
373 device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
374 ++row;
375 label = manage (new Label (_("Hardware input latency:")));
376 label->set_alignment (0, 0.5);
377 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
378 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
379 label = manage (new Label (_("samples")));
380 label->set_alignment (0, 0.5);
381 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
382 ++row;
383 label = manage (new Label (_("Hardware output latency:")));
384 label->set_alignment (0, 0.5);
385 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
386 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
387 label = manage (new Label (_("samples")));
388 label->set_alignment (0, 0.5);
389 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
390 ++row;
392 basic_hbox.pack_start (basic_packer, false, false);
393 options_hbox.pack_start (options_packer, false, false);
395 device_packer.set_border_width (12);
396 options_packer.set_border_width (12);
397 basic_packer.set_border_width (12);
399 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
400 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
401 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
402 notebook.set_border_width (12);
404 set_border_width (12);
405 pack_start (notebook);
408 EngineControl::~EngineControl ()
413 void
414 EngineControl::build_command_line (vector<string>& cmd)
416 string str;
417 string driver;
418 bool using_alsa = false;
419 bool using_coreaudio = false;
420 bool using_dummy = false;
421 bool using_ffado = false;
423 /* first, path to jackd */
425 cmd.push_back (serverpath_combo.get_active_text ());
427 /* now jackd arguments */
429 str = timeout_combo.get_active_text ();
431 if (str != _("Ignore")) {
433 double secs = 0;
434 uint32_t msecs;
435 secs = atof (str);
436 msecs = (uint32_t) floor (secs * 1000.0);
438 if (msecs > 0) {
439 cmd.push_back ("-t");
440 cmd.push_back (to_string (msecs, std::dec));
444 if (no_memory_lock_button.get_active()) {
445 cmd.push_back ("-m"); /* no munlock */
448 cmd.push_back ("-p"); /* port max */
449 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
451 if (realtime_button.get_active()) {
452 cmd.push_back ("-R");
453 cmd.push_back ("-P");
454 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
455 } else {
456 cmd.push_back ("-r"); /* override jackd's default --realtime */
459 if (unlock_memory_button.get_active()) {
460 cmd.push_back ("-u");
463 if (verbose_output_button.get_active()) {
464 cmd.push_back ("-v");
467 /* now add fixed arguments (not user-selectable) */
469 cmd.push_back ("-T"); // temporary */
471 /* next the driver */
473 cmd.push_back ("-d");
475 driver = driver_combo.get_active_text ();
477 if (driver == X_("ALSA")) {
478 using_alsa = true;
479 cmd.push_back ("alsa");
480 } else if (driver == X_("OSS")) {
481 cmd.push_back ("oss");
482 } else if (driver == X_("CoreAudio")) {
483 using_coreaudio = true;
484 cmd.push_back ("coreaudio");
485 } else if (driver == X_("NetJACK")) {
486 cmd.push_back ("netjack");
487 } else if (driver == X_("FreeBoB")) {
488 cmd.push_back ("freebob");
489 } else if (driver == X_("FFADO")) {
490 using_ffado = true;
491 cmd.push_back ("firewire");
492 } else if ( driver == X_("Dummy")) {
493 using_dummy = true;
494 cmd.push_back ("dummy");
497 /* driver arguments */
499 if (!using_coreaudio) {
500 str = audio_mode_combo.get_active_text();
502 if (str == _("Playback/Recording on 1 Device")) {
504 /* relax */
506 } else if (str == _("Playback/Recording on 2 Devices")) {
508 string input_device = get_device_name (driver, input_device_combo.get_active_text());
509 string output_device = get_device_name (driver, output_device_combo.get_active_text());
511 if (input_device.empty() || output_device.empty()) {
512 cmd.clear ();
513 return;
516 cmd.push_back ("-C");
517 cmd.push_back (input_device);
519 cmd.push_back ("-P");
520 cmd.push_back (output_device);
522 } else if (str == _("Playback only")) {
523 cmd.push_back ("-P");
524 } else if (str == _("Recording only")) {
525 cmd.push_back ("-C");
528 if (!using_dummy) {
529 cmd.push_back ("-n");
530 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
534 cmd.push_back ("-r");
535 cmd.push_back (to_string (get_rate(), std::dec));
537 cmd.push_back ("-p");
538 cmd.push_back (period_size_combo.get_active_text());
540 if (using_alsa || using_ffado || using_coreaudio) {
542 double val = input_latency_adjustment.get_value();
544 if (val) {
545 cmd.push_back ("-I");
546 cmd.push_back (to_string ((uint32_t) val, std::dec));
549 val = output_latency_adjustment.get_value();
551 if (val) {
552 cmd.push_back ("-O");
553 cmd.push_back (to_string ((uint32_t) val, std::dec));
557 if (using_alsa) {
559 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
561 string device = get_device_name (driver, interface_combo.get_active_text());
562 if (device.empty()) {
563 cmd.clear ();
564 return;
567 cmd.push_back ("-d");
568 cmd.push_back (device);
571 if (hw_meter_button.get_active()) {
572 cmd.push_back ("-M");
575 if (hw_monitor_button.get_active()) {
576 cmd.push_back ("-H");
579 str = dither_mode_combo.get_active_text();
581 if (str == _("None")) {
582 } else if (str == _("Triangular")) {
583 cmd.push_back ("-z triangular");
584 } else if (str == _("Rectangular")) {
585 cmd.push_back ("-z rectangular");
586 } else if (str == _("Shaped")) {
587 cmd.push_back ("-z shaped");
590 if (force16bit_button.get_active()) {
591 cmd.push_back ("-S");
594 if (soft_mode_button.get_active()) {
595 cmd.push_back ("-s");
598 str = midi_driver_combo.get_active_text ();
600 if (str == _("seq")) {
601 cmd.push_back ("-X seq");
602 } else if (str == _("raw")) {
603 cmd.push_back ("-X raw");
605 } else if (using_coreaudio) {
607 #ifdef __APPLE__
608 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
610 string device = get_device_name (driver, interface_combo.get_active_text());
611 if (device.empty()) {
612 cmd.clear ();
613 return;
616 cmd.push_back ("-d");
617 cmd.push_back (device);
618 #endif
623 bool
624 EngineControl::engine_running ()
626 EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
627 boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
629 /* revert all environment settings back to whatever they were when ardour started
632 if (global_epa) {
633 current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
634 global_epa->restore ();
637 jack_status_t status;
638 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
640 if (status == 0) {
641 jack_client_close (c);
642 return true;
644 return false;
648 EngineControl::setup_engine ()
650 vector<string> args;
651 std::string cwd = "/tmp";
653 build_command_line (args);
655 if (args.empty()) {
656 return 1; // try again
659 std::string jackdrc_path = Glib::get_home_dir();
660 jackdrc_path += "/.jackdrc";
662 ofstream jackdrc (jackdrc_path.c_str());
663 if (!jackdrc) {
664 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
665 return -1;
667 cerr << "JACK COMMAND: ";
668 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
669 cerr << (*i) << ' ';
670 jackdrc << (*i) << ' ';
672 cerr << endl;
673 jackdrc << endl;
674 jackdrc.close ();
676 _used = true;
678 return 0;
681 void
682 EngineControl::realtime_changed ()
684 #ifndef __APPLE__
685 priority_spinner.set_sensitive (realtime_button.get_active());
686 #endif
689 void
690 EngineControl::enumerate_devices (const string& driver)
692 /* note: case matters for the map keys */
694 if (driver == "CoreAudio") {
695 #ifdef __APPLE__
696 devices[driver] = enumerate_coreaudio_devices ();
697 #endif
699 #ifndef __APPLE__
700 } else if (driver == "ALSA") {
701 devices[driver] = enumerate_alsa_devices ();
702 } else if (driver == "FreeBOB") {
703 devices[driver] = enumerate_freebob_devices ();
704 } else if (driver == "FFADO") {
705 devices[driver] = enumerate_ffado_devices ();
706 } else if (driver == "OSS") {
707 devices[driver] = enumerate_oss_devices ();
708 } else if (driver == "Dummy") {
709 devices[driver] = enumerate_dummy_devices ();
710 } else if (driver == "NetJACK") {
711 devices[driver] = enumerate_netjack_devices ();
713 #else
715 #endif
718 #ifdef __APPLE__
719 static OSStatus
720 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
722 UInt32 size = sizeof(CFStringRef);
723 CFStringRef UI;
724 OSStatus res = AudioDeviceGetProperty(id, 0, false,
725 kAudioDevicePropertyDeviceUID, &size, &UI);
726 if (res == noErr)
727 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
728 CFRelease(UI);
729 return res;
732 vector<string>
733 EngineControl::enumerate_coreaudio_devices ()
735 vector<string> devs;
737 // Find out how many Core Audio devices are there, if any...
738 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
739 OSStatus err;
740 Boolean isWritable;
741 size_t outSize = sizeof(isWritable);
743 backend_devs.clear ();
745 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
746 &outSize, &isWritable);
747 if (err == noErr) {
748 // Calculate the number of device available...
749 int numCoreDevices = outSize / sizeof(AudioDeviceID);
750 // Make space for the devices we are about to get...
751 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
752 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
753 &outSize, (void *) coreDeviceIDs);
754 if (err == noErr) {
755 // Look for the CoreAudio device name...
756 char coreDeviceName[256];
757 size_t nameSize;
759 for (int i = 0; i < numCoreDevices; i++) {
761 nameSize = sizeof (coreDeviceName);
763 /* enforce duplex devices only */
765 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
766 0, true, kAudioDevicePropertyStreams,
767 &outSize, &isWritable);
769 if (err != noErr || outSize == 0) {
770 continue;
773 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
774 0, false, kAudioDevicePropertyStreams,
775 &outSize, &isWritable);
777 if (err != noErr || outSize == 0) {
778 continue;
781 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
782 0, true, kAudioDevicePropertyDeviceName,
783 &outSize, &isWritable);
784 if (err == noErr) {
785 err = AudioDeviceGetProperty(coreDeviceIDs[i],
786 0, true, kAudioDevicePropertyDeviceName,
787 &nameSize, (void *) coreDeviceName);
788 if (err == noErr) {
789 char drivername[128];
791 // this returns the unique id for the device
792 // that must be used on the commandline for jack
794 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
795 devs.push_back (coreDeviceName);
796 backend_devs.push_back (drivername);
802 delete [] coreDeviceIDs;
806 if (devs.size() == 0) {
807 MessageDialog msg (_("\
808 You do not have any audio devices capable of\n\
809 simultaneous playback and recording.\n\n\
810 Please use Applications -> Utilities -> Audio MIDI Setup\n\
811 to create an \"aggregrate\" device, or install a suitable\n\
812 audio interface.\n\n\
813 Please send email to Apple and ask them why new Macs\n\
814 have no duplex audio device.\n\n\
815 Alternatively, if you really want just playback\n\
816 or recording but not both, start JACK before running\n\
817 Ardour and choose the relevant device then."
819 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
820 msg.set_title (_("No suitable audio devices"));
821 msg.set_position (Gtk::WIN_POS_MOUSE);
822 msg.run ();
823 exit (1);
827 return devs;
829 #else
830 vector<string>
831 EngineControl::enumerate_alsa_devices ()
833 vector<string> devs;
835 snd_ctl_t *handle;
836 snd_ctl_card_info_t *info;
837 snd_pcm_info_t *pcminfo;
838 snd_ctl_card_info_alloca(&info);
839 snd_pcm_info_alloca(&pcminfo);
840 string devname;
841 int cardnum = -1;
842 int device = -1;
844 backend_devs.clear ();
846 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
848 devname = "hw:";
849 devname += to_string (cardnum, std::dec);
851 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
853 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
855 snd_pcm_info_set_device (pcminfo, device);
856 snd_pcm_info_set_subdevice (pcminfo, 0);
857 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
859 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
860 devs.push_back (snd_pcm_info_get_name (pcminfo));
861 devname += ',';
862 devname += to_string (device, std::dec);
863 backend_devs.push_back (devname);
867 snd_ctl_close(handle);
871 return devs;
874 vector<string>
875 EngineControl::enumerate_ffado_devices ()
877 vector<string> devs;
878 backend_devs.clear ();
879 return devs;
882 vector<string>
883 EngineControl::enumerate_freebob_devices ()
885 vector<string> devs;
886 return devs;
889 vector<string>
890 EngineControl::enumerate_oss_devices ()
892 vector<string> devs;
893 return devs;
895 vector<string>
896 EngineControl::enumerate_dummy_devices ()
898 vector<string> devs;
899 return devs;
901 vector<string>
902 EngineControl::enumerate_netjack_devices ()
904 vector<string> devs;
905 return devs;
907 #endif
909 void
910 EngineControl::driver_changed ()
912 string driver = driver_combo.get_active_text();
913 string::size_type maxlen = 0;
914 int n = 0;
916 enumerate_devices (driver);
918 vector<string>& strings = devices[driver];
920 if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
921 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
922 return;
925 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
926 if ((*i).length() > maxlen) {
927 maxlen = (*i).length();
931 set_popdown_strings (interface_combo, strings);
932 set_popdown_strings (input_device_combo, strings);
933 set_popdown_strings (output_device_combo, strings);
935 if (!strings.empty()) {
936 interface_combo.set_active_text (strings.front());
937 input_device_combo.set_active_text (strings.front());
938 output_device_combo.set_active_text (strings.front());
941 if (driver == "ALSA") {
942 soft_mode_button.set_sensitive (true);
943 force16bit_button.set_sensitive (true);
944 hw_monitor_button.set_sensitive (true);
945 hw_meter_button.set_sensitive (true);
946 monitor_button.set_sensitive (true);
947 } else {
948 soft_mode_button.set_sensitive (false);
949 force16bit_button.set_sensitive (false);
950 hw_monitor_button.set_sensitive (false);
951 hw_meter_button.set_sensitive (false);
952 monitor_button.set_sensitive (false);
956 uint32_t
957 EngineControl::get_rate ()
959 return atoi (sample_rate_combo.get_active_text ());
962 void
963 EngineControl::redisplay_latency ()
965 uint32_t rate = get_rate();
966 #ifdef __APPLE_
967 float periods = 2;
968 #else
969 float periods = periods_adjustment.get_value();
970 #endif
971 float period_size = atof (period_size_combo.get_active_text());
973 char buf[32];
974 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
976 latency_label.set_text (buf);
977 latency_label.set_alignment (0, 0.5);
980 void
981 EngineControl::audio_mode_changed ()
983 std::string str = audio_mode_combo.get_active_text();
985 if (str == _("Playback/Recording on 1 Device")) {
986 input_device_combo.set_sensitive (false);
987 output_device_combo.set_sensitive (false);
988 } else if (str == _("Playback/Recording on 2 Devices")) {
989 input_device_combo.set_sensitive (true);
990 output_device_combo.set_sensitive (true);
991 } else if (str == _("Playback only")) {
992 output_device_combo.set_sensitive (true);
993 } else if (str == _("Recording only")) {
994 input_device_combo.set_sensitive (true);
998 static bool jack_server_filter(const string& str, void */*arg*/)
1000 return str == "jackd" || str == "jackdmp";
1003 void
1004 EngineControl::find_jack_servers (vector<string>& strings)
1006 #ifdef __APPLE__
1007 /* this magic lets us finds the path to the OSX bundle, and then
1008 we infer JACK's location from there
1011 char execpath[MAXPATHLEN+1];
1012 uint32_t pathsz = sizeof (execpath);
1014 _NSGetExecutablePath (execpath, &pathsz);
1016 string path (Glib::path_get_dirname (execpath));
1017 path += "/jackd";
1019 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
1020 strings.push_back (path);
1023 if (getenv ("ARDOUR_WITH_JACK")) {
1024 /* no other options - only use the JACK we supply */
1025 if (strings.empty()) {
1026 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1027 /*NOTREACHED*/
1029 return;
1031 #else
1032 string path;
1033 #endif
1035 PathScanner scanner;
1036 vector<string *> *jack_servers;
1037 std::map<string,int> un;
1038 char *p;
1039 bool need_minimal_path = false;
1041 p = getenv ("PATH");
1043 if (p && *p) {
1044 path = p;
1045 } else {
1046 need_minimal_path = true;
1049 #ifdef __APPLE__
1050 // many mac users don't have PATH set up to include
1051 // likely installed locations of JACK
1052 need_minimal_path = true;
1053 #endif
1055 if (need_minimal_path) {
1056 if (path.empty()) {
1057 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1058 } else {
1059 path += ":/usr/local/bin:/opt/local/bin";
1063 #ifdef __APPLE__
1064 // push it back into the environment so that auto-started JACK can find it.
1065 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1066 setenv ("PATH", path.c_str(), 1);
1067 #endif
1069 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1071 vector<string *>::iterator iter;
1073 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1074 string p = **iter;
1076 if (un[p]++ == 0) {
1077 strings.push_back(p);
1083 string
1084 EngineControl::get_device_name (const string& driver, const string& human_readable)
1086 vector<string>::iterator n;
1087 vector<string>::iterator i;
1089 if (human_readable.empty()) {
1090 /* this can happen if the user's .ardourrc file has a device name from
1091 another computer system in it
1093 MessageDialog msg (_("You need to choose an audio device first."));
1094 msg.run ();
1095 return string();
1098 if (backend_devs.empty()) {
1099 return human_readable;
1102 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1103 if (human_readable == (*i)) {
1104 return (*n);
1108 if (i == devices[driver].end()) {
1109 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1112 return string();
1115 XMLNode&
1116 EngineControl::get_state ()
1118 XMLNode* root = new XMLNode ("AudioSetup");
1119 XMLNode* child;
1120 std::string path;
1122 child = new XMLNode ("periods");
1123 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1124 root->add_child_nocopy (*child);
1126 child = new XMLNode ("priority");
1127 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1128 root->add_child_nocopy (*child);
1130 child = new XMLNode ("ports");
1131 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1132 root->add_child_nocopy (*child);
1134 child = new XMLNode ("inchannels");
1135 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1136 root->add_child_nocopy (*child);
1138 child = new XMLNode ("outchannels");
1139 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1140 root->add_child_nocopy (*child);
1142 child = new XMLNode ("inlatency");
1143 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1144 root->add_child_nocopy (*child);
1146 child = new XMLNode ("outlatency");
1147 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1148 root->add_child_nocopy (*child);
1150 child = new XMLNode ("realtime");
1151 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1152 root->add_child_nocopy (*child);
1154 child = new XMLNode ("nomemorylock");
1155 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1156 root->add_child_nocopy (*child);
1158 child = new XMLNode ("unlockmemory");
1159 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1160 root->add_child_nocopy (*child);
1162 child = new XMLNode ("softmode");
1163 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1164 root->add_child_nocopy (*child);
1166 child = new XMLNode ("force16bit");
1167 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1168 root->add_child_nocopy (*child);
1170 child = new XMLNode ("hwmonitor");
1171 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1172 root->add_child_nocopy (*child);
1174 child = new XMLNode ("hwmeter");
1175 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1176 root->add_child_nocopy (*child);
1178 child = new XMLNode ("verbose");
1179 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1180 root->add_child_nocopy (*child);
1182 child = new XMLNode ("samplerate");
1183 child->add_property ("val", sample_rate_combo.get_active_text());
1184 root->add_child_nocopy (*child);
1186 child = new XMLNode ("periodsize");
1187 child->add_property ("val", period_size_combo.get_active_text());
1188 root->add_child_nocopy (*child);
1190 child = new XMLNode ("serverpath");
1191 child->add_property ("val", serverpath_combo.get_active_text());
1192 root->add_child_nocopy (*child);
1194 child = new XMLNode ("driver");
1195 child->add_property ("val", driver_combo.get_active_text());
1196 root->add_child_nocopy (*child);
1198 child = new XMLNode ("interface");
1199 child->add_property ("val", interface_combo.get_active_text());
1200 root->add_child_nocopy (*child);
1202 child = new XMLNode ("timeout");
1203 child->add_property ("val", timeout_combo.get_active_text());
1204 root->add_child_nocopy (*child);
1206 child = new XMLNode ("dither");
1207 child->add_property ("val", dither_mode_combo.get_active_text());
1208 root->add_child_nocopy (*child);
1210 child = new XMLNode ("audiomode");
1211 child->add_property ("val", audio_mode_combo.get_active_text());
1212 root->add_child_nocopy (*child);
1214 child = new XMLNode ("inputdevice");
1215 child->add_property ("val", input_device_combo.get_active_text());
1216 root->add_child_nocopy (*child);
1218 child = new XMLNode ("outputdevice");
1219 child->add_property ("val", output_device_combo.get_active_text());
1220 root->add_child_nocopy (*child);
1222 child = new XMLNode ("mididriver");
1223 child->add_property ("val", midi_driver_combo.get_active_text());
1224 root->add_child_nocopy (*child);
1226 return *root;
1229 void
1230 EngineControl::set_state (const XMLNode& root)
1232 XMLNodeList clist;
1233 XMLNodeConstIterator citer;
1234 XMLNode* child;
1235 XMLProperty* prop = NULL;
1236 bool using_dummy = false;
1237 bool using_ffado = false;
1239 int val;
1240 string strval;
1242 if ( (child = root.child ("driver"))){
1243 prop = child->property("val");
1245 if (prop && (prop->value() == "Dummy") ) {
1246 using_dummy = true;
1248 if (prop && (prop->value() == "FFADO") ) {
1249 using_ffado = true;
1254 clist = root.children();
1256 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1258 child = *citer;
1260 prop = child->property ("val");
1262 if (!prop || prop->value().empty()) {
1264 if (((using_dummy || using_ffado)
1265 && ( child->name() == "interface"
1266 || child->name() == "inputdevice"
1267 || child->name() == "outputdevice"))
1268 || child->name() == "timeout")
1270 continue;
1273 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1274 continue;
1277 strval = prop->value();
1279 /* adjustments/spinners */
1281 if (child->name() == "periods") {
1282 val = atoi (strval);
1283 periods_adjustment.set_value(val);
1284 } else if (child->name() == "priority") {
1285 val = atoi (strval);
1286 priority_adjustment.set_value(val);
1287 } else if (child->name() == "ports") {
1288 val = atoi (strval);
1289 ports_adjustment.set_value(val);
1290 } else if (child->name() == "inchannels") {
1291 val = atoi (strval);
1292 input_channels.set_value(val);
1293 } else if (child->name() == "outchannels") {
1294 val = atoi (strval);
1295 output_channels.set_value(val);
1296 } else if (child->name() == "inlatency") {
1297 val = atoi (strval);
1298 input_latency.set_value(val);
1299 } else if (child->name() == "outlatency") {
1300 val = atoi (strval);
1301 output_latency.set_value(val);
1304 /* buttons */
1306 else if (child->name() == "realtime") {
1307 val = atoi (strval);
1308 realtime_button.set_active(val);
1309 } else if (child->name() == "nomemorylock") {
1310 val = atoi (strval);
1311 no_memory_lock_button.set_active(val);
1312 } else if (child->name() == "unlockmemory") {
1313 val = atoi (strval);
1314 unlock_memory_button.set_active(val);
1315 } else if (child->name() == "softmode") {
1316 val = atoi (strval);
1317 soft_mode_button.set_active(val);
1318 } else if (child->name() == "force16bit") {
1319 val = atoi (strval);
1320 force16bit_button.set_active(val);
1321 } else if (child->name() == "hwmonitor") {
1322 val = atoi (strval);
1323 hw_monitor_button.set_active(val);
1324 } else if (child->name() == "hwmeter") {
1325 val = atoi (strval);
1326 hw_meter_button.set_active(val);
1327 } else if (child->name() == "verbose") {
1328 val = atoi (strval);
1329 verbose_output_button.set_active(val);
1332 /* combos */
1334 else if (child->name() == "samplerate") {
1335 sample_rate_combo.set_active_text(strval);
1336 } else if (child->name() == "periodsize") {
1337 period_size_combo.set_active_text(strval);
1338 } else if (child->name() == "serverpath") {
1340 /* only attempt to set this if we have bothered to look
1341 up server names already. otherwise this is all
1342 redundant (actually, all of this dialog/widget
1343 is redundant in that case ...)
1346 if (!server_strings.empty()) {
1347 /* do not allow us to use a server path that doesn't
1348 exist on this system. this handles cases where
1349 the user has an RC file listing a serverpath
1350 from some other machine.
1352 vector<string>::iterator x;
1353 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1354 if (*x == strval) {
1355 break;
1358 if (x != server_strings.end()) {
1359 serverpath_combo.set_active_text (strval);
1360 } else {
1361 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1362 strval)
1363 << endmsg;
1367 } else if (child->name() == "driver") {
1368 driver_combo.set_active_text(strval);
1369 } else if (child->name() == "interface") {
1370 interface_combo.set_active_text(strval);
1371 } else if (child->name() == "timeout") {
1372 timeout_combo.set_active_text(strval);
1373 } else if (child->name() == "dither") {
1374 dither_mode_combo.set_active_text(strval);
1375 } else if (child->name() == "audiomode") {
1376 audio_mode_combo.set_active_text(strval);
1377 } else if (child->name() == "inputdevice") {
1378 input_device_combo.set_active_text(strval);
1379 } else if (child->name() == "outputdevice") {
1380 output_device_combo.set_active_text(strval);
1381 } else if (child->name() == "mididriver") {
1382 midi_driver_combo.set_active_text(strval);