7 #include <gtkmm/messagedialog.h>
11 #include <CoreAudio/CoreAudio.h>
12 #include <CoreFoundation/CFString.h>
13 #include <sys/param.h>
14 #include <mach-o/dyld.h>
16 #include <alsa/asoundlib.h>
19 #include "ardour/profile.h"
20 #include <jack/jack.h>
22 #include <gtkmm/stock.h>
23 #include <gtkmm2ext/utils.h>
25 #include "pbd/convert.h"
26 #include "pbd/error.h"
27 #include "pbd/pathscanner.h"
33 #include "engine_dialog.h"
38 using namespace Gtkmm2ext
;
42 EngineControl::EngineControl ()
43 : periods_adjustment (2, 2, 16, 1, 2),
44 periods_spinner (periods_adjustment
),
45 priority_adjustment (60, 10, 90, 1, 10),
46 priority_spinner (priority_adjustment
),
47 ports_adjustment (128, 8, 1024, 1, 16),
48 ports_spinner (ports_adjustment
),
49 realtime_button (_("Realtime")),
50 no_memory_lock_button (_("Do not lock memory")),
51 unlock_memory_button (_("Unlock memory")),
52 soft_mode_button (_("No zombies")),
53 monitor_button (_("Provide monitor ports")),
54 force16bit_button (_("Force 16 bit")),
55 hw_monitor_button (_("H/W monitoring")),
56 hw_meter_button (_("H/W metering")),
57 verbose_output_button (_("Verbose output")),
58 start_button (_("Start")),
59 stop_button (_("Stop")),
62 options_packer (4, 2),
66 options_packer (14, 2),
70 using namespace Notebook_Helpers
;
72 vector
<string
> strings
;
77 strings
.push_back (_("8000Hz"));
78 strings
.push_back (_("22050Hz"));
79 strings
.push_back (_("44100Hz"));
80 strings
.push_back (_("48000Hz"));
81 strings
.push_back (_("88200Hz"));
82 strings
.push_back (_("96000Hz"));
83 strings
.push_back (_("192000Hz"));
84 set_popdown_strings (sample_rate_combo
, strings
);
85 sample_rate_combo
.set_active_text ("48000Hz");
88 strings
.push_back ("32");
89 strings
.push_back ("64");
90 strings
.push_back ("128");
91 strings
.push_back ("256");
92 strings
.push_back ("512");
93 strings
.push_back ("1024");
94 strings
.push_back ("2048");
95 strings
.push_back ("4096");
96 strings
.push_back ("8192");
97 set_popdown_strings (period_size_combo
, strings
);
98 period_size_combo
.set_active_text ("1024");
101 strings
.push_back (_("None"));
102 strings
.push_back (_("Triangular"));
103 strings
.push_back (_("Rectangular"));
104 strings
.push_back (_("Shaped"));
105 set_popdown_strings (dither_mode_combo
, strings
);
106 dither_mode_combo
.set_active_text (_("None"));
108 /* basic parameters */
110 basic_packer
.set_spacings (6);
114 strings
.push_back (X_("CoreAudio"));
116 strings
.push_back (X_("ALSA"));
117 strings
.push_back (X_("OSS"));
118 strings
.push_back (X_("FFADO"));
120 strings
.push_back (X_("NetJACK"));
121 strings
.push_back (X_("Dummy"));
122 set_popdown_strings (driver_combo
, strings
);
123 driver_combo
.set_active_text (strings
.front());
125 driver_combo
.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed
));
129 strings
.push_back (_("Playback/Recording on 1 Device"));
130 strings
.push_back (_("Playback/Recording on 2 Devices"));
131 strings
.push_back (_("Playback only"));
132 strings
.push_back (_("Recording only"));
133 set_popdown_strings (audio_mode_combo
, strings
);
134 audio_mode_combo
.set_active_text (strings
.front());
136 audio_mode_combo
.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed
));
137 audio_mode_changed ();
141 label
= manage (new Label (_("Driver")));
142 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
143 basic_packer
.attach (driver_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
146 label
= manage (new Label (_("Interface")));
147 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
148 basic_packer
.attach (interface_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
151 label
= manage (new Label (_("Sample Rate")));
152 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
153 basic_packer
.attach (sample_rate_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
156 label
= manage (new Label (_("Buffer size")));
157 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
158 basic_packer
.attach (period_size_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
162 label
= manage (new Label (_("Number of buffers")));
163 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
164 basic_packer
.attach (periods_spinner
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
165 periods_spinner
.set_value (2);
169 label
= manage (new Label (_("Approximate latency")));
170 label
->set_alignment (0.0, 0.5);
171 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
172 basic_packer
.attach (latency_label
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
175 sample_rate_combo
.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency
));
176 periods_adjustment
.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency
));
177 period_size_combo
.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency
));
180 /* no audio mode with CoreAudio, its duplex or nuthin' */
183 label
= manage (new Label (_("Audio Mode")));
184 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
185 basic_packer
.attach (audio_mode_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
189 interface_combo
.set_size_request (125, -1);
190 input_device_combo
.set_size_request (125, -1);
191 output_device_combo
.set_size_request (125, -1);
195 if (engine_running()) {
196 start_button.set_sensitive (false);
198 stop_button.set_sensitive (false);
201 start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
202 stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
205 button_box
.pack_start (start_button
, false, false);
206 button_box
.pack_start (stop_button
, false, false);
208 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
212 options_packer
.set_spacings (6);
215 options_packer
.attach (realtime_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
218 realtime_button
.set_active (true);
219 realtime_button
.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed
));
222 #if PROVIDE_TOO_MANY_OPTIONS
225 label
= manage (new Label (_("Realtime Priority")));
226 label
->set_alignment (1.0, 0.5);
227 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
228 options_packer
.attach (priority_spinner
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
230 priority_spinner
.set_value (60);
232 options_packer
.attach (no_memory_lock_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
234 options_packer
.attach (unlock_memory_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
236 options_packer
.attach (soft_mode_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
238 options_packer
.attach (monitor_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
240 options_packer
.attach (force16bit_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
242 options_packer
.attach (hw_monitor_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
244 options_packer
.attach (hw_meter_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
246 options_packer
.attach (verbose_output_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
249 options_packer
.attach (verbose_output_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
254 strings
.push_back (_("Ignore"));
255 strings
.push_back ("500 msec");
256 strings
.push_back ("1 sec");
257 strings
.push_back ("2 sec");
258 strings
.push_back ("10 sec");
259 set_popdown_strings (timeout_combo
, strings
);
260 timeout_combo
.set_active_text (strings
.front ());
262 label
= manage (new Label (_("Client timeout")));
263 label
->set_alignment (1.0, 0.5);
264 options_packer
.attach (timeout_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, AttachOptions(0));
265 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
268 #endif /* PROVIDE_TOO_MANY_OPTIONS */
269 label
= manage (new Label (_("Number of ports")));
270 label
->set_alignment (1.0, 0.5);
271 options_packer
.attach (ports_spinner
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, AttachOptions(0));
272 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
276 label
= manage (new Label (_("Dither")));
277 label
->set_alignment (1.0, 0.5);
278 options_packer
.attach (dither_mode_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, AttachOptions(0));
279 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
283 find_jack_servers (server_strings
);
285 if (server_strings
.empty()) {
286 fatal
<< _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg
;
290 set_popdown_strings (serverpath_combo
, server_strings
);
291 serverpath_combo
.set_active_text (server_strings
.front());
293 if (server_strings
.size() > 1) {
294 label
= manage (new Label (_("Server:")));
295 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
296 label
->set_alignment (0.0, 0.5);
297 options_packer
.attach (serverpath_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
301 /* device settings */
303 device_packer
.set_spacings (6);
307 label
= manage (new Label (_("Input device")));
308 label
->set_alignment (1.0, 0.5);
309 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
310 device_packer
.attach (input_device_combo
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
312 label
= manage (new Label (_("Output device")));
313 label
->set_alignment (1.0, 0.5);
314 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
315 device_packer
.attach (output_device_combo
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
318 label
= manage (new Label (_("Input channels")));
319 label
->set_alignment (1.0, 0.5);
320 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
321 device_packer
.attach (input_channels
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
323 label
= manage (new Label (_("Output channels")));
324 label
->set_alignment (1.0, 0.5);
325 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
326 device_packer
.attach (output_channels
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
328 label
= manage (new Label (_("Hardware input latency (samples)")));
329 label
->set_alignment (1.0, 0.5);
330 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
331 device_packer
.attach (input_latency
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
333 label
= manage (new Label (_("Hardware output latency (samples)")));
334 label
->set_alignment (1.0, 0.5);
335 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
336 device_packer
.attach (output_latency
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
339 basic_hbox
.pack_start (basic_packer
, false, false);
340 options_hbox
.pack_start (options_packer
, false, false);
342 device_packer
.set_border_width (12);
343 options_packer
.set_border_width (12);
344 basic_packer
.set_border_width (12);
346 notebook
.pages().push_back (TabElem (basic_hbox
, _("Device")));
347 notebook
.pages().push_back (TabElem (options_hbox
, _("Options")));
348 notebook
.pages().push_back (TabElem (device_packer
, _("Advanced")));
349 notebook
.set_border_width (12);
351 set_border_width (12);
352 pack_start (notebook
);
355 EngineControl::~EngineControl ()
361 EngineControl::build_command_line (vector
<string
>& cmd
)
365 bool using_oss
= false;
366 bool using_alsa
= false;
367 bool using_coreaudio
= false;
368 bool using_netjack
= false;
369 bool using_ffado
= false;
370 bool using_dummy
= false;
372 /* first, path to jackd */
374 cmd
.push_back (serverpath_combo
.get_active_text ());
376 /* now jackd arguments */
378 str
= timeout_combo
.get_active_text ();
379 if (str
!= _("Ignore")) {
383 msecs
= (uint32_t) floor (secs
* 1000.0);
385 cmd
.push_back ("-t");
386 cmd
.push_back (to_string (msecs
, std::dec
));
390 if (no_memory_lock_button
.get_active()) {
391 cmd
.push_back ("-m"); /* no munlock */
394 cmd
.push_back ("-p"); /* port max */
395 cmd
.push_back (to_string ((uint32_t) floor (ports_spinner
.get_value()), std::dec
));
397 if (realtime_button
.get_active()) {
398 cmd
.push_back ("-R");
399 cmd
.push_back ("-P");
400 cmd
.push_back (to_string ((uint32_t) floor (priority_spinner
.get_value()), std::dec
));
403 if (unlock_memory_button
.get_active()) {
404 cmd
.push_back ("-u");
407 if (verbose_output_button
.get_active()) {
408 cmd
.push_back ("-v");
411 /* now add fixed arguments (not user-selectable) */
413 cmd
.push_back ("-T"); // temporary */
415 /* next the driver */
417 cmd
.push_back ("-d");
419 driver
= driver_combo
.get_active_text ();
420 if (driver
== X_("ALSA")) {
422 cmd
.push_back ("alsa");
423 } else if (driver
== X_("OSS")) {
425 cmd
.push_back ("oss");
426 } else if (driver
== X_("CoreAudio")) {
427 using_coreaudio
= true;
428 cmd
.push_back ("coreaudio");
429 } else if (driver
== X_("NetJACK")) {
430 using_netjack
= true;
431 cmd
.push_back ("netjack");
432 } else if (driver
== X_("FFADO")) {
435 /* do this until FFADO becomes the standard */
437 char* hack
= getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
440 cmd
.push_back (hack
);
442 cmd
.push_back ("freebob");
445 } else if ( driver
== X_("Dummy")) {
447 cmd
.push_back ("dummy");
450 /* driver arguments */
452 if (!using_coreaudio
) {
453 str
= audio_mode_combo
.get_active_text();
455 if (str
== _("Playback/Recording on 1 Device")) {
459 } else if (str
== _("Playback/Recording on 2 Devices")) {
461 string input_device
= get_device_name (driver
, input_device_combo
.get_active_text());
462 string output_device
= get_device_name (driver
, output_device_combo
.get_active_text());
464 if (input_device
.empty() || output_device
.empty()) {
469 cmd
.push_back ("-C");
470 cmd
.push_back (input_device
);
471 cmd
.push_back ("-P");
472 cmd
.push_back (output_device
);
474 } else if (str
== _("Playback only")) {
475 cmd
.push_back ("-P");
476 } else if (str
== _("Recording only")) {
477 cmd
.push_back ("-C");
480 if (! using_dummy
) {
481 cmd
.push_back ("-n");
482 cmd
.push_back (to_string ((uint32_t) floor (periods_spinner
.get_value()), std::dec
));
486 cmd
.push_back ("-r");
487 cmd
.push_back (to_string (get_rate(), std::dec
));
489 cmd
.push_back ("-p");
490 cmd
.push_back (period_size_combo
.get_active_text());
494 if (audio_mode_combo
.get_active_text() != _("Playback/Recording on 2 Devices")) {
496 string device
= get_device_name (driver
, interface_combo
.get_active_text());
497 if (device
.empty()) {
502 cmd
.push_back ("-d");
503 cmd
.push_back (device
);
506 if (hw_meter_button
.get_active()) {
507 cmd
.push_back ("-M");
510 if (hw_monitor_button
.get_active()) {
511 cmd
.push_back ("-H");
514 str
= dither_mode_combo
.get_active_text();
516 if (str
== _("None")) {
517 } else if (str
== _("Triangular")) {
518 cmd
.push_back ("-z triangular");
519 } else if (str
== _("Rectangular")) {
520 cmd
.push_back ("-z rectangular");
521 } else if (str
== _("Shaped")) {
522 cmd
.push_back ("-z shaped");
525 if (force16bit_button
.get_active()) {
526 cmd
.push_back ("-S");
529 if (soft_mode_button
.get_active()) {
530 cmd
.push_back ("-s");
533 } else if (using_coreaudio
) {
536 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
538 string device
= get_device_name (driver
, interface_combo
.get_active_text());
539 if (device
.empty()) {
544 cmd
.push_back ("-d");
545 cmd
.push_back (device
);
548 } else if (using_oss
) {
550 } else if (using_netjack
) {
556 EngineControl::engine_running ()
558 jack_status_t status
;
559 jack_client_t
* c
= jack_client_open ("ardourprobe", JackNoStartServer
, &status
);
562 jack_client_close (c
);
569 EngineControl::setup_engine ()
572 std::string cwd
= "/tmp";
574 build_command_line (args
);
577 return 1; // try again
580 Glib::ustring jackdrc_path
= Glib::get_home_dir();
581 jackdrc_path
+= "/.jackdrc";
583 ofstream
jackdrc (jackdrc_path
.c_str());
585 error
<< string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path
) << endmsg
;
588 cerr
<< "JACK COMMAND: ";
589 for (vector
<string
>::iterator i
= args
.begin(); i
!= args
.end(); ++i
) {
591 jackdrc
<< (*i
) << ' ';
603 EngineControl::realtime_changed ()
606 priority_spinner
.set_sensitive (realtime_button
.get_active());
611 EngineControl::enumerate_devices (const string
& driver
)
613 /* note: case matters for the map keys */
615 if (driver
== "CoreAudio") {
617 devices
[driver
] = enumerate_coreaudio_devices ();
621 } else if (driver
== "ALSA") {
622 devices
[driver
] = enumerate_alsa_devices ();
623 } else if (driver
== "FFADO") {
624 devices
[driver
] = enumerate_ffado_devices ();
625 } else if (driver
== "OSS") {
626 devices
[driver
] = enumerate_oss_devices ();
627 } else if (driver
== "Dummy") {
628 devices
[driver
] = enumerate_dummy_devices ();
629 } else if (driver
== "NetJACK") {
630 devices
[driver
] = enumerate_netjack_devices ();
639 getDeviceUIDFromID( AudioDeviceID id
, char *name
, size_t nsize
)
641 UInt32 size
= sizeof(CFStringRef
);
643 OSStatus res
= AudioDeviceGetProperty(id
, 0, false,
644 kAudioDevicePropertyDeviceUID
, &size
, &UI
);
646 CFStringGetCString(UI
,name
,nsize
,CFStringGetSystemEncoding());
652 EngineControl::enumerate_coreaudio_devices ()
656 // Find out how many Core Audio devices are there, if any...
657 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
660 size_t outSize
= sizeof(isWritable
);
662 backend_devs
.clear ();
664 err
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices
,
665 &outSize
, &isWritable
);
667 // Calculate the number of device available...
668 int numCoreDevices
= outSize
/ sizeof(AudioDeviceID
);
669 // Make space for the devices we are about to get...
670 AudioDeviceID
*coreDeviceIDs
= new AudioDeviceID
[numCoreDevices
];
671 err
= AudioHardwareGetProperty(kAudioHardwarePropertyDevices
,
672 &outSize
, (void *) coreDeviceIDs
);
674 // Look for the CoreAudio device name...
675 char coreDeviceName
[256];
678 for (int i
= 0; i
< numCoreDevices
; i
++) {
680 nameSize
= sizeof (coreDeviceName
);
682 /* enforce duplex devices only */
684 err
= AudioDeviceGetPropertyInfo(coreDeviceIDs
[i
],
685 0, true, kAudioDevicePropertyStreams
,
686 &outSize
, &isWritable
);
688 if (err
!= noErr
|| outSize
== 0) {
692 err
= AudioDeviceGetPropertyInfo(coreDeviceIDs
[i
],
693 0, false, kAudioDevicePropertyStreams
,
694 &outSize
, &isWritable
);
696 if (err
!= noErr
|| outSize
== 0) {
700 err
= AudioDeviceGetPropertyInfo(coreDeviceIDs
[i
],
701 0, true, kAudioDevicePropertyDeviceName
,
702 &outSize
, &isWritable
);
704 err
= AudioDeviceGetProperty(coreDeviceIDs
[i
],
705 0, true, kAudioDevicePropertyDeviceName
,
706 &nameSize
, (void *) coreDeviceName
);
708 char drivername
[128];
710 // this returns the unique id for the device
711 // that must be used on the commandline for jack
713 if (getDeviceUIDFromID(coreDeviceIDs
[i
], drivername
, sizeof (drivername
)) == noErr
) {
714 devs
.push_back (coreDeviceName
);
715 backend_devs
.push_back (drivername
);
721 delete [] coreDeviceIDs
;
725 if (devs
.size() == 0) {
726 MessageDialog
msg (_("\
727 You do not have any audio devices capable of\n\
728 simultaneous playback and recording.\n\n\
729 Please use Applications -> Utilities -> Audio MIDI Setup\n\
730 to create an \"aggregrate\" device, or install a suitable\n\
731 audio interface.\n\n\
732 Please send email to Apple and ask them why new Macs\n\
733 have no duplex audio device.\n\n\
734 Alternatively, if you really want just playback\n\
735 or recording but not both, start JACK before running\n\
736 Ardour and choose the relevant device then."
738 true, Gtk::MESSAGE_ERROR
, Gtk::BUTTONS_OK
);
739 msg
.set_title (_("No suitable audio devices"));
740 msg
.set_position (Gtk::WIN_POS_MOUSE
);
750 EngineControl::enumerate_alsa_devices ()
755 snd_ctl_card_info_t
*info
;
756 snd_pcm_info_t
*pcminfo
;
757 snd_ctl_card_info_alloca(&info
);
758 snd_pcm_info_alloca(&pcminfo
);
763 backend_devs
.clear ();
765 while (snd_card_next (&cardnum
) >= 0 && cardnum
>= 0) {
768 devname
+= to_string (cardnum
, std::dec
);
770 if (snd_ctl_open (&handle
, devname
.c_str(), 0) >= 0 && snd_ctl_card_info (handle
, info
) >= 0) {
772 while (snd_ctl_pcm_next_device (handle
, &device
) >= 0 && device
>= 0) {
774 bool have_playback
= false;
775 bool have_capture
= false;
777 /* find duplex devices only */
779 snd_pcm_info_set_device (pcminfo
, device
);
780 snd_pcm_info_set_subdevice (pcminfo
, 0);
781 snd_pcm_info_set_stream (pcminfo
, SND_PCM_STREAM_CAPTURE
);
783 if (snd_ctl_pcm_info (handle
, pcminfo
) >= 0) {
787 snd_pcm_info_set_device (pcminfo
, device
);
788 snd_pcm_info_set_subdevice (pcminfo
, 0);
789 snd_pcm_info_set_stream (pcminfo
, SND_PCM_STREAM_PLAYBACK
);
791 if (snd_ctl_pcm_info (handle
, pcminfo
) >= 0) {
792 have_playback
= true;
795 if (have_capture
&& have_playback
) {
796 devs
.push_back (snd_pcm_info_get_name (pcminfo
));
798 devname
+= to_string (device
, std::dec
);
799 backend_devs
.push_back (devname
);
803 snd_ctl_close(handle
);
811 EngineControl::enumerate_ffado_devices ()
814 backend_devs
.clear ();
819 EngineControl::enumerate_freebob_devices ()
825 EngineControl::enumerate_oss_devices ()
831 EngineControl::enumerate_dummy_devices ()
837 EngineControl::enumerate_netjack_devices ()
845 EngineControl::driver_changed ()
847 string driver
= driver_combo
.get_active_text();
848 string::size_type maxlen
= 0;
852 enumerate_devices (driver
);
854 vector
<string
>& strings
= devices
[driver
];
856 if (strings
.empty() && driver
!= "FFADO" && driver
!= "Dummy") {
857 error
<< string_compose (_("No devices found for driver \"%1\""), driver
) << endmsg
;
861 for (vector
<string
>::iterator i
= strings
.begin(); i
!= strings
.end(); ++i
, ++n
) {
862 if ((*i
).length() > maxlen
) {
863 maxlen
= (*i
).length();
868 set_popdown_strings (interface_combo
, strings
);
869 set_popdown_strings (input_device_combo
, strings
);
870 set_popdown_strings (output_device_combo
, strings
);
872 if (!strings
.empty()) {
873 interface_combo
.set_active_text (strings
.front());
874 input_device_combo
.set_active_text (strings
.front());
875 output_device_combo
.set_active_text (strings
.front());
878 if (driver
== "ALSA") {
879 soft_mode_button
.set_sensitive (true);
880 force16bit_button
.set_sensitive (true);
881 hw_monitor_button
.set_sensitive (true);
882 hw_meter_button
.set_sensitive (true);
883 monitor_button
.set_sensitive (true);
885 soft_mode_button
.set_sensitive (false);
886 force16bit_button
.set_sensitive (false);
887 hw_monitor_button
.set_sensitive (false);
888 hw_meter_button
.set_sensitive (false);
889 monitor_button
.set_sensitive (false);
894 EngineControl::get_rate ()
896 return atoi (sample_rate_combo
.get_active_text ());
900 EngineControl::redisplay_latency ()
902 uint32_t rate
= get_rate();
906 float periods
= periods_adjustment
.get_value();
908 float period_size
= atof (period_size_combo
.get_active_text());
911 snprintf (buf
, sizeof(buf
), "%.1fmsec", (periods
* period_size
) / (rate
/1000.0));
913 latency_label
.set_text (buf
);
917 EngineControl::audio_mode_changed ()
919 Glib::ustring str
= audio_mode_combo
.get_active_text();
921 if (str
== _("Playback/Recording on 1 Device")) {
922 input_device_combo
.set_sensitive (false);
923 output_device_combo
.set_sensitive (false);
924 } else if (str
== _("Playback/Recording on 2 Devices")) {
925 input_device_combo
.set_sensitive (true);
926 output_device_combo
.set_sensitive (true);
927 } else if (str
== _("Playback only")) {
928 output_device_combo
.set_sensitive (true);
929 } else if (str
== _("Recording only")) {
930 input_device_combo
.set_sensitive (true);
934 static bool jack_server_filter(const string
& str
, void */
*arg*/
)
936 return str
== "jackd" || str
== "jackdmp";
940 EngineControl::find_jack_servers (vector
<string
>& strings
)
943 /* this magic lets us finds the path to the OSX bundle, and then
944 we infer JACK's location from there
947 char execpath
[MAXPATHLEN
+1];
948 uint32_t pathsz
= sizeof (execpath
);
950 _NSGetExecutablePath (execpath
, &pathsz
);
952 string
path (Glib::path_get_dirname (execpath
));
955 if (Glib::file_test (path
, FILE_TEST_EXISTS
)) {
956 strings
.push_back (path
);
959 if (getenv ("ARDOUR_WITH_JACK")) {
960 /* no other options - only use the JACK we supply */
961 if (strings
.empty()) {
962 fatal
<< string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME
) << endmsg
;
972 vector
<string
*> *jack_servers
;
973 std::map
<string
,int> un
;
975 bool need_minimal_path
= false;
982 need_minimal_path
= true;
986 // many mac users don't have PATH set up to include
987 // likely installed locations of JACK
988 need_minimal_path
= true;
991 if (need_minimal_path
) {
993 path
= "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
995 path
+= ":/usr/local/bin:/opt/local/bin";
1000 // push it back into the environment so that auto-started JACK can find it.
1001 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1002 setenv ("PATH", path
.c_str(), 1);
1005 jack_servers
= scanner (path
, jack_server_filter
, 0, false, true);
1007 vector
<string
*>::iterator iter
;
1009 for (iter
= jack_servers
->begin(); iter
!= jack_servers
->end(); iter
++) {
1013 strings
.push_back(p
);
1020 EngineControl::get_device_name (const string
& driver
, const string
& human_readable
)
1022 vector
<string
>::iterator n
;
1023 vector
<string
>::iterator i
;
1025 if (human_readable
.empty()) {
1026 /* this can happen if the user's .ardourrc file has a device name from
1027 another computer system in it
1029 MessageDialog
msg (_("You need to choose an audio device first."));
1034 if (backend_devs
.empty()) {
1035 return human_readable
;
1038 for (i
= devices
[driver
].begin(), n
= backend_devs
.begin(); i
!= devices
[driver
].end(); ++i
, ++n
) {
1039 if (human_readable
== (*i
)) {
1044 if (i
== devices
[driver
].end()) {
1045 warning
<< string_compose (_("Audio device \"%1\" not known on this computer."), human_readable
) << endmsg
;
1052 EngineControl::get_state ()
1054 XMLNode
* root
= new XMLNode ("AudioSetup");
1058 child
= new XMLNode ("periods");
1059 child
->add_property ("val", to_string (periods_adjustment
.get_value(), std::dec
));
1060 root
->add_child_nocopy (*child
);
1062 child
= new XMLNode ("priority");
1063 child
->add_property ("val", to_string (priority_adjustment
.get_value(), std::dec
));
1064 root
->add_child_nocopy (*child
);
1066 child
= new XMLNode ("ports");
1067 child
->add_property ("val", to_string (ports_adjustment
.get_value(), std::dec
));
1068 root
->add_child_nocopy (*child
);
1070 child
= new XMLNode ("inchannels");
1071 child
->add_property ("val", to_string (input_channels
.get_value(), std::dec
));
1072 root
->add_child_nocopy (*child
);
1074 child
= new XMLNode ("outchannels");
1075 child
->add_property ("val", to_string (output_channels
.get_value(), std::dec
));
1076 root
->add_child_nocopy (*child
);
1078 child
= new XMLNode ("inlatency");
1079 child
->add_property ("val", to_string (input_latency
.get_value(), std::dec
));
1080 root
->add_child_nocopy (*child
);
1082 child
= new XMLNode ("outlatency");
1083 child
->add_property ("val", to_string (output_latency
.get_value(), std::dec
));
1084 root
->add_child_nocopy (*child
);
1086 child
= new XMLNode ("realtime");
1087 child
->add_property ("val", to_string (realtime_button
.get_active(), std::dec
));
1088 root
->add_child_nocopy (*child
);
1090 child
= new XMLNode ("nomemorylock");
1091 child
->add_property ("val", to_string (no_memory_lock_button
.get_active(), std::dec
));
1092 root
->add_child_nocopy (*child
);
1094 child
= new XMLNode ("unlockmemory");
1095 child
->add_property ("val", to_string (unlock_memory_button
.get_active(), std::dec
));
1096 root
->add_child_nocopy (*child
);
1098 child
= new XMLNode ("softmode");
1099 child
->add_property ("val", to_string (soft_mode_button
.get_active(), std::dec
));
1100 root
->add_child_nocopy (*child
);
1102 child
= new XMLNode ("force16bit");
1103 child
->add_property ("val", to_string (force16bit_button
.get_active(), std::dec
));
1104 root
->add_child_nocopy (*child
);
1106 child
= new XMLNode ("hwmonitor");
1107 child
->add_property ("val", to_string (hw_monitor_button
.get_active(), std::dec
));
1108 root
->add_child_nocopy (*child
);
1110 child
= new XMLNode ("hwmeter");
1111 child
->add_property ("val", to_string (hw_meter_button
.get_active(), std::dec
));
1112 root
->add_child_nocopy (*child
);
1114 child
= new XMLNode ("verbose");
1115 child
->add_property ("val", to_string (verbose_output_button
.get_active(), std::dec
));
1116 root
->add_child_nocopy (*child
);
1118 child
= new XMLNode ("samplerate");
1119 child
->add_property ("val", sample_rate_combo
.get_active_text());
1120 root
->add_child_nocopy (*child
);
1122 child
= new XMLNode ("periodsize");
1123 child
->add_property ("val", period_size_combo
.get_active_text());
1124 root
->add_child_nocopy (*child
);
1126 child
= new XMLNode ("serverpath");
1127 child
->add_property ("val", serverpath_combo
.get_active_text());
1128 root
->add_child_nocopy (*child
);
1130 child
= new XMLNode ("driver");
1131 child
->add_property ("val", driver_combo
.get_active_text());
1132 root
->add_child_nocopy (*child
);
1134 child
= new XMLNode ("interface");
1135 child
->add_property ("val", interface_combo
.get_active_text());
1136 root
->add_child_nocopy (*child
);
1138 child
= new XMLNode ("timeout");
1139 child
->add_property ("val", timeout_combo
.get_active_text());
1140 root
->add_child_nocopy (*child
);
1142 child
= new XMLNode ("dither");
1143 child
->add_property ("val", dither_mode_combo
.get_active_text());
1144 root
->add_child_nocopy (*child
);
1146 child
= new XMLNode ("audiomode");
1147 child
->add_property ("val", audio_mode_combo
.get_active_text());
1148 root
->add_child_nocopy (*child
);
1150 child
= new XMLNode ("inputdevice");
1151 child
->add_property ("val", input_device_combo
.get_active_text());
1152 root
->add_child_nocopy (*child
);
1154 child
= new XMLNode ("outputdevice");
1155 child
->add_property ("val", output_device_combo
.get_active_text());
1156 root
->add_child_nocopy (*child
);
1162 EngineControl::set_state (const XMLNode
& root
)
1165 XMLNodeConstIterator citer
;
1167 XMLProperty
* prop
= NULL
;
1168 bool using_dummy
= false;
1173 if ( (child
= root
.child ("driver"))){
1174 prop
= child
->property("val");
1175 if (prop
&& (prop
->value() == "Dummy") ) {
1180 clist
= root
.children();
1182 for (citer
= clist
.begin(); citer
!= clist
.end(); ++citer
) {
1186 prop
= child
->property ("val");
1188 if (!prop
|| prop
->value().empty()) {
1190 if ((using_dummy
&& ( child
->name() == "interface" || child
->name() == "inputdevice" || child
->name() == "outputdevice" )) ||
1191 child
->name() == "timeout")
1193 error
<< string_compose (_("AudioSetup value for %1 is missing data"), child
->name()) << endmsg
;
1197 strval
= prop
->value();
1199 /* adjustments/spinners */
1201 if (child
->name() == "periods") {
1202 val
= atoi (strval
);
1203 periods_adjustment
.set_value(val
);
1204 } else if (child
->name() == "priority") {
1205 val
= atoi (strval
);
1206 priority_adjustment
.set_value(val
);
1207 } else if (child
->name() == "ports") {
1208 val
= atoi (strval
);
1209 ports_adjustment
.set_value(val
);
1210 } else if (child
->name() == "inchannels") {
1211 val
= atoi (strval
);
1212 input_channels
.set_value(val
);
1213 } else if (child
->name() == "outchannels") {
1214 val
= atoi (strval
);
1215 output_channels
.set_value(val
);
1216 } else if (child
->name() == "inlatency") {
1217 val
= atoi (strval
);
1218 input_latency
.set_value(val
);
1219 } else if (child
->name() == "outlatency") {
1220 val
= atoi (strval
);
1221 output_latency
.set_value(val
);
1226 else if (child
->name() == "realtime") {
1227 val
= atoi (strval
);
1228 realtime_button
.set_active(val
);
1229 } else if (child
->name() == "nomemorylock") {
1230 val
= atoi (strval
);
1231 no_memory_lock_button
.set_active(val
);
1232 } else if (child
->name() == "unlockmemory") {
1233 val
= atoi (strval
);
1234 unlock_memory_button
.set_active(val
);
1235 } else if (child
->name() == "softmode") {
1236 val
= atoi (strval
);
1237 soft_mode_button
.set_active(val
);
1238 } else if (child
->name() == "force16bit") {
1239 val
= atoi (strval
);
1240 force16bit_button
.set_active(val
);
1241 } else if (child
->name() == "hwmonitor") {
1242 val
= atoi (strval
);
1243 hw_monitor_button
.set_active(val
);
1244 } else if (child
->name() == "hwmeter") {
1245 val
= atoi (strval
);
1246 hw_meter_button
.set_active(val
);
1247 } else if (child
->name() == "verbose") {
1248 val
= atoi (strval
);
1249 verbose_output_button
.set_active(val
);
1254 else if (child
->name() == "samplerate") {
1255 sample_rate_combo
.set_active_text(strval
);
1256 } else if (child
->name() == "periodsize") {
1257 period_size_combo
.set_active_text(strval
);
1258 } else if (child
->name() == "serverpath") {
1260 /* only attempt to set this if we have bothered to look
1261 up server names already. otherwise this is all
1262 redundant (actually, all of this dialog/widget
1263 is redundant in that case ...)
1266 if (!server_strings
.empty()) {
1267 /* do not allow us to use a server path that doesn't
1268 exist on this system. this handles cases where
1269 the user has an RC file listing a serverpath
1270 from some other machine.
1272 vector
<string
>::iterator x
;
1273 for (x
= server_strings
.begin(); x
!= server_strings
.end(); ++x
) {
1278 if (x
!= server_strings
.end()) {
1279 serverpath_combo
.set_active_text (strval
);
1281 warning
<< string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1287 } else if (child
->name() == "driver") {
1288 driver_combo
.set_active_text(strval
);
1289 } else if (child
->name() == "interface") {
1290 interface_combo
.set_active_text(strval
);
1291 } else if (child
->name() == "timeout") {
1292 timeout_combo
.set_active_text(strval
);
1293 } else if (child
->name() == "dither") {
1294 dither_mode_combo
.set_active_text(strval
);
1295 } else if (child
->name() == "audiomode") {
1296 audio_mode_combo
.set_active_text(strval
);
1297 } else if (child
->name() == "inputdevice") {
1298 input_device_combo
.set_active_text(strval
);
1299 } else if (child
->name() == "outputdevice") {
1300 output_device_combo
.set_active_text(strval
);