2 Copyright (C) 2010 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <boost/scoped_ptr.hpp>
28 #include <gtkmm/messagedialog.h>
31 #include "pbd/xml++.h"
34 #include <CoreAudio/CoreAudio.h>
35 #include <CoreFoundation/CFString.h>
36 #include <sys/param.h>
37 #include <mach-o/dyld.h>
39 #include <alsa/asoundlib.h>
42 #include "ardour/profile.h"
43 #include <jack/jack.h>
45 #include <gtkmm/stock.h>
46 #include <gtkmm2ext/utils.h>
48 #include "pbd/convert.h"
49 #include "pbd/error.h"
50 #include "pbd/pathscanner.h"
56 #include "engine_dialog.h"
61 using namespace Gtkmm2ext
;
65 EngineControl::EngineControl ()
66 : periods_adjustment (2, 2, 16, 1, 2),
67 periods_spinner (periods_adjustment
),
68 priority_adjustment (60, 10, 90, 1, 10),
69 priority_spinner (priority_adjustment
),
70 ports_adjustment (128, 8, 1024, 1, 16),
71 ports_spinner (ports_adjustment
),
72 input_latency_adjustment (0, 0, 99999, 1),
73 input_latency (input_latency_adjustment
),
74 output_latency_adjustment (0, 0, 99999, 1),
75 output_latency (output_latency_adjustment
),
76 realtime_button (_("Realtime")),
77 no_memory_lock_button (_("Do not lock memory")),
78 unlock_memory_button (_("Unlock memory")),
79 soft_mode_button (_("No zombies")),
80 monitor_button (_("Provide monitor ports")),
81 force16bit_button (_("Force 16 bit")),
82 hw_monitor_button (_("H/W monitoring")),
83 hw_meter_button (_("H/W metering")),
84 verbose_output_button (_("Verbose output")),
85 start_button (_("Start")),
86 stop_button (_("Stop")),
89 options_packer (4, 2),
93 options_packer (14, 2),
97 using namespace Notebook_Helpers
;
99 vector
<string
> strings
;
104 strings
.push_back (_("8000Hz"));
105 strings
.push_back (_("22050Hz"));
106 strings
.push_back (_("44100Hz"));
107 strings
.push_back (_("48000Hz"));
108 strings
.push_back (_("88200Hz"));
109 strings
.push_back (_("96000Hz"));
110 strings
.push_back (_("192000Hz"));
111 set_popdown_strings (sample_rate_combo
, strings
);
112 sample_rate_combo
.set_active_text ("48000Hz");
115 strings
.push_back ("32");
116 strings
.push_back ("64");
117 strings
.push_back ("128");
118 strings
.push_back ("256");
119 strings
.push_back ("512");
120 strings
.push_back ("1024");
121 strings
.push_back ("2048");
122 strings
.push_back ("4096");
123 strings
.push_back ("8192");
124 set_popdown_strings (period_size_combo
, strings
);
125 period_size_combo
.set_active_text ("1024");
128 strings
.push_back (_("None"));
129 strings
.push_back (_("Triangular"));
130 strings
.push_back (_("Rectangular"));
131 strings
.push_back (_("Shaped"));
132 set_popdown_strings (dither_mode_combo
, strings
);
133 dither_mode_combo
.set_active_text (_("None"));
135 /* basic parameters */
137 basic_packer
.set_spacings (6);
141 strings
.push_back (X_("CoreAudio"));
143 strings
.push_back (X_("ALSA"));
144 strings
.push_back (X_("OSS"));
145 strings
.push_back (X_("FreeBoB"));
146 strings
.push_back (X_("FFADO"));
148 strings
.push_back (X_("NetJACK"));
149 strings
.push_back (X_("Dummy"));
150 set_popdown_strings (driver_combo
, strings
);
151 driver_combo
.set_active_text (strings
.front());
153 driver_combo
.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed
));
157 strings
.push_back (_("Playback/recording on 1 device"));
158 strings
.push_back (_("Playback/recording on 2 devices"));
159 strings
.push_back (_("Playback only"));
160 strings
.push_back (_("Recording only"));
161 set_popdown_strings (audio_mode_combo
, strings
);
162 audio_mode_combo
.set_active_text (strings
.front());
164 audio_mode_combo
.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed
));
165 audio_mode_changed ();
168 strings
.push_back (_("None"));
169 strings
.push_back (_("seq"));
170 strings
.push_back (_("raw"));
171 set_popdown_strings (midi_driver_combo
, strings
);
172 midi_driver_combo
.set_active_text (strings
.front ());
176 label
= manage (new Label (_("Driver:")));
177 label
->set_alignment (0, 0.5);
178 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
179 basic_packer
.attach (driver_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
182 label
= manage (new Label (_("Interface:")));
183 label
->set_alignment (0, 0.5);
184 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
185 basic_packer
.attach (interface_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
188 label
= manage (new Label (_("Sample rate:")));
189 label
->set_alignment (0, 0.5);
190 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
191 basic_packer
.attach (sample_rate_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
194 label
= manage (new Label (_("Buffer size:")));
195 label
->set_alignment (0, 0.5);
196 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
197 basic_packer
.attach (period_size_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
201 label
= manage (new Label (_("Number of buffers:")));
202 label
->set_alignment (0, 0.5);
203 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
204 basic_packer
.attach (periods_spinner
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
205 periods_spinner
.set_value (2);
209 label
= manage (new Label (_("Approximate latency:")));
210 label
->set_alignment (0, 0.5);
211 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
212 basic_packer
.attach (latency_label
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
215 sample_rate_combo
.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency
));
216 periods_adjustment
.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency
));
217 period_size_combo
.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency
));
220 /* no audio mode with CoreAudio, its duplex or nuthin' */
223 label
= manage (new Label (_("Audio mode:")));
224 label
->set_alignment (0, 0.5);
225 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
226 basic_packer
.attach (audio_mode_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
230 interface_combo
.set_size_request (250, -1);
231 input_device_combo
.set_size_request (250, -1);
232 output_device_combo
.set_size_request (250, -1);
236 if (engine_running()) {
237 start_button.set_sensitive (false);
239 stop_button.set_sensitive (false);
242 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
243 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
246 button_box
.pack_start (start_button
, false, false);
247 button_box
.pack_start (stop_button
, false, false);
249 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
253 options_packer
.set_spacings (6);
256 options_packer
.attach (realtime_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
259 realtime_button
.set_active (true);
260 realtime_button
.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed
));
263 #if PROVIDE_TOO_MANY_OPTIONS
266 label
= manage (new Label (_("Realtime Priority")));
267 label
->set_alignment (1.0, 0.5);
268 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
269 options_packer
.attach (priority_spinner
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
271 priority_spinner
.set_value (60);
273 options_packer
.attach (no_memory_lock_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
275 options_packer
.attach (unlock_memory_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
277 options_packer
.attach (soft_mode_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
279 options_packer
.attach (monitor_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
281 options_packer
.attach (force16bit_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
283 options_packer
.attach (hw_monitor_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
285 options_packer
.attach (hw_meter_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
287 options_packer
.attach (verbose_output_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
290 options_packer
.attach (verbose_output_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
295 strings
.push_back (_("Ignore"));
296 strings
.push_back ("500 msec");
297 strings
.push_back ("1 sec");
298 strings
.push_back ("2 sec");
299 strings
.push_back ("10 sec");
300 set_popdown_strings (timeout_combo
, strings
);
301 timeout_combo
.set_active_text (strings
.front ());
303 label
= manage (new Label (_("Client timeout")));
304 label
->set_alignment (1.0, 0.5);
305 options_packer
.attach (timeout_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, AttachOptions(0));
306 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
309 #endif /* PROVIDE_TOO_MANY_OPTIONS */
310 label
= manage (new Label (_("Number of ports:")));
311 label
->set_alignment (0, 0.5);
312 options_packer
.attach (ports_spinner
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, AttachOptions(0));
313 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
316 label
= manage (new Label (_("MIDI driver:")));
317 label
->set_alignment (0, 0.5);
318 options_packer
.attach (midi_driver_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, AttachOptions(0));
319 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
323 label
= manage (new Label (_("Dither:")));
324 label
->set_alignment (0, 0.5);
325 options_packer
.attach (dither_mode_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, AttachOptions(0));
326 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
330 find_jack_servers (server_strings
);
332 if (server_strings
.empty()) {
333 fatal
<< _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg
;
337 set_popdown_strings (serverpath_combo
, server_strings
);
338 serverpath_combo
.set_active_text (server_strings
.front());
340 if (server_strings
.size() > 1) {
341 label
= manage (new Label (_("Server:")));
342 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
343 label
->set_alignment (0.0, 0.5);
344 options_packer
.attach (serverpath_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
348 /* device settings */
350 device_packer
.set_spacings (6);
354 label
= manage (new Label (_("Input device:")));
355 label
->set_alignment (0, 0.5);
356 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
357 device_packer
.attach (input_device_combo
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
359 label
= manage (new Label (_("Output device:")));
360 label
->set_alignment (0, 0.5);
361 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
362 device_packer
.attach (output_device_combo
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
365 label
= manage (new Label (_("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);
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);
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);
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);
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 ()
414 EngineControl::build_command_line (vector
<string
>& cmd
)
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")) {
436 msecs
= (uint32_t) floor (secs
* 1000.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
));
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")) {
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")) {
491 cmd
.push_back ("firewire");
492 } else if ( driver
== X_("Dummy")) {
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")) {
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()) {
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");
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();
545 cmd
.push_back ("-I");
546 cmd
.push_back (to_string ((uint32_t) val
, std::dec
));
549 val
= output_latency_adjustment
.get_value();
552 cmd
.push_back ("-O");
553 cmd
.push_back (to_string ((uint32_t) val
, std::dec
));
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()) {
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
) {
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()) {
616 cmd
.push_back ("-d");
617 cmd
.push_back (device
);
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
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
);
641 jack_client_close (c
);
648 EngineControl::setup_engine ()
651 std::string cwd
= "/tmp";
653 build_command_line (args
);
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());
664 error
<< string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path
) << endmsg
;
667 cerr
<< "JACK COMMAND: ";
668 for (vector
<string
>::iterator i
= args
.begin(); i
!= args
.end(); ++i
) {
670 jackdrc
<< (*i
) << ' ';
682 EngineControl::realtime_changed ()
685 priority_spinner
.set_sensitive (realtime_button
.get_active());
690 EngineControl::enumerate_devices (const string
& driver
)
692 /* note: case matters for the map keys */
694 if (driver
== "CoreAudio") {
696 devices
[driver
] = enumerate_coreaudio_devices ();
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 ();
720 getDeviceUIDFromID( AudioDeviceID id
, char *name
, size_t nsize
)
722 UInt32 size
= sizeof(CFStringRef
);
724 OSStatus res
= AudioDeviceGetProperty(id
, 0, false,
725 kAudioDevicePropertyDeviceUID
, &size
, &UI
);
727 CFStringGetCString(UI
,name
,nsize
,CFStringGetSystemEncoding());
733 EngineControl::enumerate_coreaudio_devices ()
737 // Find out how many Core Audio devices are there, if any...
738 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
741 size_t outSize
= sizeof(isWritable
);
743 backend_devs
.clear ();
745 err
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices
,
746 &outSize
, &isWritable
);
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
);
755 // Look for the CoreAudio device name...
756 char coreDeviceName
[256];
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) {
773 err
= AudioDeviceGetPropertyInfo(coreDeviceIDs
[i
],
774 0, false, kAudioDevicePropertyStreams
,
775 &outSize
, &isWritable
);
777 if (err
!= noErr
|| outSize
== 0) {
781 err
= AudioDeviceGetPropertyInfo(coreDeviceIDs
[i
],
782 0, true, kAudioDevicePropertyDeviceName
,
783 &outSize
, &isWritable
);
785 err
= AudioDeviceGetProperty(coreDeviceIDs
[i
],
786 0, true, kAudioDevicePropertyDeviceName
,
787 &nameSize
, (void *) coreDeviceName
);
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
);
831 EngineControl::enumerate_alsa_devices ()
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
);
844 backend_devs
.clear ();
846 while (snd_card_next (&cardnum
) >= 0 && cardnum
>= 0) {
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
));
862 devname
+= to_string (device
, std::dec
);
863 backend_devs
.push_back (devname
);
867 snd_ctl_close(handle
);
875 EngineControl::enumerate_ffado_devices ()
878 backend_devs
.clear ();
883 EngineControl::enumerate_freebob_devices ()
890 EngineControl::enumerate_oss_devices ()
896 EngineControl::enumerate_dummy_devices ()
902 EngineControl::enumerate_netjack_devices ()
910 EngineControl::driver_changed ()
912 string driver
= driver_combo
.get_active_text();
913 string::size_type maxlen
= 0;
917 enumerate_devices (driver
);
919 vector
<string
>& strings
= devices
[driver
];
921 if (strings
.empty() && driver
!= "FreeBoB" && driver
!= "FFADO" && driver
!= "Dummy") {
922 error
<< string_compose (_("No devices found for driver \"%1\""), driver
) << endmsg
;
926 for (vector
<string
>::iterator i
= strings
.begin(); i
!= strings
.end(); ++i
, ++n
) {
927 if ((*i
).length() > maxlen
) {
928 maxlen
= (*i
).length();
933 set_popdown_strings (interface_combo
, strings
);
934 set_popdown_strings (input_device_combo
, strings
);
935 set_popdown_strings (output_device_combo
, strings
);
937 if (!strings
.empty()) {
938 interface_combo
.set_active_text (strings
.front());
939 input_device_combo
.set_active_text (strings
.front());
940 output_device_combo
.set_active_text (strings
.front());
943 if (driver
== "ALSA") {
944 soft_mode_button
.set_sensitive (true);
945 force16bit_button
.set_sensitive (true);
946 hw_monitor_button
.set_sensitive (true);
947 hw_meter_button
.set_sensitive (true);
948 monitor_button
.set_sensitive (true);
950 soft_mode_button
.set_sensitive (false);
951 force16bit_button
.set_sensitive (false);
952 hw_monitor_button
.set_sensitive (false);
953 hw_meter_button
.set_sensitive (false);
954 monitor_button
.set_sensitive (false);
959 EngineControl::get_rate ()
961 return atoi (sample_rate_combo
.get_active_text ());
965 EngineControl::redisplay_latency ()
967 uint32_t rate
= get_rate();
971 float periods
= periods_adjustment
.get_value();
973 float period_size
= atof (period_size_combo
.get_active_text());
976 snprintf (buf
, sizeof(buf
), "%.1fmsec", (periods
* period_size
) / (rate
/1000.0));
978 latency_label
.set_text (buf
);
979 latency_label
.set_alignment (0, 0.5);
983 EngineControl::audio_mode_changed ()
985 std::string str
= audio_mode_combo
.get_active_text();
987 if (str
== _("Playback/Recording on 1 Device")) {
988 input_device_combo
.set_sensitive (false);
989 output_device_combo
.set_sensitive (false);
990 } else if (str
== _("Playback/Recording on 2 Devices")) {
991 input_device_combo
.set_sensitive (true);
992 output_device_combo
.set_sensitive (true);
993 } else if (str
== _("Playback only")) {
994 output_device_combo
.set_sensitive (true);
995 } else if (str
== _("Recording only")) {
996 input_device_combo
.set_sensitive (true);
1000 static bool jack_server_filter(const string
& str
, void */
*arg*/
)
1002 return str
== "jackd" || str
== "jackdmp";
1006 EngineControl::find_jack_servers (vector
<string
>& strings
)
1009 /* this magic lets us finds the path to the OSX bundle, and then
1010 we infer JACK's location from there
1013 char execpath
[MAXPATHLEN
+1];
1014 uint32_t pathsz
= sizeof (execpath
);
1016 _NSGetExecutablePath (execpath
, &pathsz
);
1018 string
path (Glib::path_get_dirname (execpath
));
1021 if (Glib::file_test (path
, FILE_TEST_EXISTS
)) {
1022 strings
.push_back (path
);
1025 if (getenv ("ARDOUR_WITH_JACK")) {
1026 /* no other options - only use the JACK we supply */
1027 if (strings
.empty()) {
1028 fatal
<< string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME
) << endmsg
;
1037 PathScanner scanner
;
1038 vector
<string
*> *jack_servers
;
1039 std::map
<string
,int> un
;
1041 bool need_minimal_path
= false;
1043 p
= getenv ("PATH");
1048 need_minimal_path
= true;
1052 // many mac users don't have PATH set up to include
1053 // likely installed locations of JACK
1054 need_minimal_path
= true;
1057 if (need_minimal_path
) {
1059 path
= "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1061 path
+= ":/usr/local/bin:/opt/local/bin";
1066 // push it back into the environment so that auto-started JACK can find it.
1067 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1068 setenv ("PATH", path
.c_str(), 1);
1071 jack_servers
= scanner (path
, jack_server_filter
, 0, false, true);
1073 vector
<string
*>::iterator iter
;
1075 for (iter
= jack_servers
->begin(); iter
!= jack_servers
->end(); iter
++) {
1079 strings
.push_back(p
);
1086 EngineControl::get_device_name (const string
& driver
, const string
& human_readable
)
1088 vector
<string
>::iterator n
;
1089 vector
<string
>::iterator i
;
1091 if (human_readable
.empty()) {
1092 /* this can happen if the user's .ardourrc file has a device name from
1093 another computer system in it
1095 MessageDialog
msg (_("You need to choose an audio device first."));
1100 if (backend_devs
.empty()) {
1101 return human_readable
;
1104 for (i
= devices
[driver
].begin(), n
= backend_devs
.begin(); i
!= devices
[driver
].end(); ++i
, ++n
) {
1105 if (human_readable
== (*i
)) {
1110 if (i
== devices
[driver
].end()) {
1111 warning
<< string_compose (_("Audio device \"%1\" not known on this computer."), human_readable
) << endmsg
;
1118 EngineControl::get_state ()
1120 XMLNode
* root
= new XMLNode ("AudioSetup");
1124 child
= new XMLNode ("periods");
1125 child
->add_property ("val", to_string (periods_adjustment
.get_value(), std::dec
));
1126 root
->add_child_nocopy (*child
);
1128 child
= new XMLNode ("priority");
1129 child
->add_property ("val", to_string (priority_adjustment
.get_value(), std::dec
));
1130 root
->add_child_nocopy (*child
);
1132 child
= new XMLNode ("ports");
1133 child
->add_property ("val", to_string (ports_adjustment
.get_value(), std::dec
));
1134 root
->add_child_nocopy (*child
);
1136 child
= new XMLNode ("inchannels");
1137 child
->add_property ("val", to_string (input_channels
.get_value(), std::dec
));
1138 root
->add_child_nocopy (*child
);
1140 child
= new XMLNode ("outchannels");
1141 child
->add_property ("val", to_string (output_channels
.get_value(), std::dec
));
1142 root
->add_child_nocopy (*child
);
1144 child
= new XMLNode ("inlatency");
1145 child
->add_property ("val", to_string (input_latency
.get_value(), std::dec
));
1146 root
->add_child_nocopy (*child
);
1148 child
= new XMLNode ("outlatency");
1149 child
->add_property ("val", to_string (output_latency
.get_value(), std::dec
));
1150 root
->add_child_nocopy (*child
);
1152 child
= new XMLNode ("realtime");
1153 child
->add_property ("val", to_string (realtime_button
.get_active(), std::dec
));
1154 root
->add_child_nocopy (*child
);
1156 child
= new XMLNode ("nomemorylock");
1157 child
->add_property ("val", to_string (no_memory_lock_button
.get_active(), std::dec
));
1158 root
->add_child_nocopy (*child
);
1160 child
= new XMLNode ("unlockmemory");
1161 child
->add_property ("val", to_string (unlock_memory_button
.get_active(), std::dec
));
1162 root
->add_child_nocopy (*child
);
1164 child
= new XMLNode ("softmode");
1165 child
->add_property ("val", to_string (soft_mode_button
.get_active(), std::dec
));
1166 root
->add_child_nocopy (*child
);
1168 child
= new XMLNode ("force16bit");
1169 child
->add_property ("val", to_string (force16bit_button
.get_active(), std::dec
));
1170 root
->add_child_nocopy (*child
);
1172 child
= new XMLNode ("hwmonitor");
1173 child
->add_property ("val", to_string (hw_monitor_button
.get_active(), std::dec
));
1174 root
->add_child_nocopy (*child
);
1176 child
= new XMLNode ("hwmeter");
1177 child
->add_property ("val", to_string (hw_meter_button
.get_active(), std::dec
));
1178 root
->add_child_nocopy (*child
);
1180 child
= new XMLNode ("verbose");
1181 child
->add_property ("val", to_string (verbose_output_button
.get_active(), std::dec
));
1182 root
->add_child_nocopy (*child
);
1184 child
= new XMLNode ("samplerate");
1185 child
->add_property ("val", sample_rate_combo
.get_active_text());
1186 root
->add_child_nocopy (*child
);
1188 child
= new XMLNode ("periodsize");
1189 child
->add_property ("val", period_size_combo
.get_active_text());
1190 root
->add_child_nocopy (*child
);
1192 child
= new XMLNode ("serverpath");
1193 child
->add_property ("val", serverpath_combo
.get_active_text());
1194 root
->add_child_nocopy (*child
);
1196 child
= new XMLNode ("driver");
1197 child
->add_property ("val", driver_combo
.get_active_text());
1198 root
->add_child_nocopy (*child
);
1200 child
= new XMLNode ("interface");
1201 child
->add_property ("val", interface_combo
.get_active_text());
1202 root
->add_child_nocopy (*child
);
1204 child
= new XMLNode ("timeout");
1205 child
->add_property ("val", timeout_combo
.get_active_text());
1206 root
->add_child_nocopy (*child
);
1208 child
= new XMLNode ("dither");
1209 child
->add_property ("val", dither_mode_combo
.get_active_text());
1210 root
->add_child_nocopy (*child
);
1212 child
= new XMLNode ("audiomode");
1213 child
->add_property ("val", audio_mode_combo
.get_active_text());
1214 root
->add_child_nocopy (*child
);
1216 child
= new XMLNode ("inputdevice");
1217 child
->add_property ("val", input_device_combo
.get_active_text());
1218 root
->add_child_nocopy (*child
);
1220 child
= new XMLNode ("outputdevice");
1221 child
->add_property ("val", output_device_combo
.get_active_text());
1222 root
->add_child_nocopy (*child
);
1224 child
= new XMLNode ("mididriver");
1225 child
->add_property ("val", midi_driver_combo
.get_active_text());
1226 root
->add_child_nocopy (*child
);
1232 EngineControl::set_state (const XMLNode
& root
)
1235 XMLNodeConstIterator citer
;
1237 XMLProperty
* prop
= NULL
;
1238 bool using_dummy
= false;
1239 bool using_ffado
= false;
1244 if ( (child
= root
.child ("driver"))){
1245 prop
= child
->property("val");
1247 if (prop
&& (prop
->value() == "Dummy") ) {
1250 if (prop
&& (prop
->value() == "FFADO") ) {
1256 clist
= root
.children();
1258 for (citer
= clist
.begin(); citer
!= clist
.end(); ++citer
) {
1262 prop
= child
->property ("val");
1264 if (!prop
|| prop
->value().empty()) {
1266 if (((using_dummy
|| using_ffado
)
1267 && ( child
->name() == "interface"
1268 || child
->name() == "inputdevice"
1269 || child
->name() == "outputdevice"))
1270 || child
->name() == "timeout")
1275 error
<< string_compose (_("AudioSetup value for %1 is missing data"), child
->name()) << endmsg
;
1279 strval
= prop
->value();
1281 /* adjustments/spinners */
1283 if (child
->name() == "periods") {
1284 val
= atoi (strval
);
1285 periods_adjustment
.set_value(val
);
1286 } else if (child
->name() == "priority") {
1287 val
= atoi (strval
);
1288 priority_adjustment
.set_value(val
);
1289 } else if (child
->name() == "ports") {
1290 val
= atoi (strval
);
1291 ports_adjustment
.set_value(val
);
1292 } else if (child
->name() == "inchannels") {
1293 val
= atoi (strval
);
1294 input_channels
.set_value(val
);
1295 } else if (child
->name() == "outchannels") {
1296 val
= atoi (strval
);
1297 output_channels
.set_value(val
);
1298 } else if (child
->name() == "inlatency") {
1299 val
= atoi (strval
);
1300 input_latency
.set_value(val
);
1301 } else if (child
->name() == "outlatency") {
1302 val
= atoi (strval
);
1303 output_latency
.set_value(val
);
1308 else if (child
->name() == "realtime") {
1309 val
= atoi (strval
);
1310 realtime_button
.set_active(val
);
1311 } else if (child
->name() == "nomemorylock") {
1312 val
= atoi (strval
);
1313 no_memory_lock_button
.set_active(val
);
1314 } else if (child
->name() == "unlockmemory") {
1315 val
= atoi (strval
);
1316 unlock_memory_button
.set_active(val
);
1317 } else if (child
->name() == "softmode") {
1318 val
= atoi (strval
);
1319 soft_mode_button
.set_active(val
);
1320 } else if (child
->name() == "force16bit") {
1321 val
= atoi (strval
);
1322 force16bit_button
.set_active(val
);
1323 } else if (child
->name() == "hwmonitor") {
1324 val
= atoi (strval
);
1325 hw_monitor_button
.set_active(val
);
1326 } else if (child
->name() == "hwmeter") {
1327 val
= atoi (strval
);
1328 hw_meter_button
.set_active(val
);
1329 } else if (child
->name() == "verbose") {
1330 val
= atoi (strval
);
1331 verbose_output_button
.set_active(val
);
1336 else if (child
->name() == "samplerate") {
1337 sample_rate_combo
.set_active_text(strval
);
1338 } else if (child
->name() == "periodsize") {
1339 period_size_combo
.set_active_text(strval
);
1340 } else if (child
->name() == "serverpath") {
1342 /* only attempt to set this if we have bothered to look
1343 up server names already. otherwise this is all
1344 redundant (actually, all of this dialog/widget
1345 is redundant in that case ...)
1348 if (!server_strings
.empty()) {
1349 /* do not allow us to use a server path that doesn't
1350 exist on this system. this handles cases where
1351 the user has an RC file listing a serverpath
1352 from some other machine.
1354 vector
<string
>::iterator x
;
1355 for (x
= server_strings
.begin(); x
!= server_strings
.end(); ++x
) {
1360 if (x
!= server_strings
.end()) {
1361 serverpath_combo
.set_active_text (strval
);
1363 warning
<< string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1369 } else if (child
->name() == "driver") {
1370 driver_combo
.set_active_text(strval
);
1371 } else if (child
->name() == "interface") {
1372 interface_combo
.set_active_text(strval
);
1373 } else if (child
->name() == "timeout") {
1374 timeout_combo
.set_active_text(strval
);
1375 } else if (child
->name() == "dither") {
1376 dither_mode_combo
.set_active_text(strval
);
1377 } else if (child
->name() == "audiomode") {
1378 audio_mode_combo
.set_active_text(strval
);
1379 } else if (child
->name() == "inputdevice") {
1380 input_device_combo
.set_active_text(strval
);
1381 } else if (child
->name() == "outputdevice") {
1382 output_device_combo
.set_active_text(strval
);
1383 } else if (child
->name() == "mididriver") {
1384 midi_driver_combo
.set_active_text(strval
);