7 #include <gtkmm/messagedialog.h>
11 #include <CoreAudio/CoreAudio.h>
12 #include <CoreFoundation/CFString.h>
13 #include <sys/param.h>
14 #include <mach-o/dyld.h>
16 #include <alsa/asoundlib.h>
19 #include <ardour/profile.h>
20 #include <jack/jack.h>
22 #include <gtkmm/stock.h>
23 #include <gtkmm2ext/utils.h>
25 #include <pbd/convert.h>
26 #include <pbd/error.h>
27 #include <pbd/pathscanner.h>
33 #include "engine_dialog.h"
38 using namespace Gtkmm2ext
;
42 EngineControl::EngineControl ()
43 : periods_adjustment (2, 2, 16, 1, 2),
44 periods_spinner (periods_adjustment
),
45 priority_adjustment (60, 10, 90, 1, 10),
46 priority_spinner (priority_adjustment
),
47 ports_adjustment (128, 8, 1024, 1, 16),
48 ports_spinner (ports_adjustment
),
49 realtime_button (_("Realtime")),
50 no_memory_lock_button (_("Do not lock memory")),
51 unlock_memory_button (_("Unlock memory")),
52 soft_mode_button (_("No zombies")),
53 monitor_button (_("Provide monitor ports")),
54 force16bit_button (_("Force 16 bit")),
55 hw_monitor_button (_("H/W monitoring")),
56 hw_meter_button (_("H/W metering")),
57 verbose_output_button (_("Verbose output")),
58 start_button (_("Start")),
59 stop_button (_("Stop")),
62 options_packer (4, 2),
66 options_packer (14, 2),
70 using namespace Notebook_Helpers
;
72 vector
<string
> strings
;
76 _interface_chosen
= false;
78 strings
.push_back (_("8000Hz"));
79 strings
.push_back (_("22050Hz"));
80 strings
.push_back (_("44100Hz"));
81 strings
.push_back (_("48000Hz"));
82 strings
.push_back (_("88200Hz"));
83 strings
.push_back (_("96000Hz"));
84 strings
.push_back (_("192000Hz"));
85 set_popdown_strings (sample_rate_combo
, strings
);
86 sample_rate_combo
.set_active_text ("48000Hz");
89 strings
.push_back ("32");
90 strings
.push_back ("64");
91 strings
.push_back ("128");
92 strings
.push_back ("256");
93 strings
.push_back ("512");
94 strings
.push_back ("1024");
95 strings
.push_back ("2048");
96 strings
.push_back ("4096");
97 strings
.push_back ("8192");
98 set_popdown_strings (period_size_combo
, strings
);
99 period_size_combo
.set_active_text ("1024");
102 strings
.push_back (_("None"));
103 strings
.push_back (_("Triangular"));
104 strings
.push_back (_("Rectangular"));
105 strings
.push_back (_("Shaped"));
106 set_popdown_strings (dither_mode_combo
, strings
);
107 dither_mode_combo
.set_active_text (_("None"));
109 /* basic parameters */
111 basic_packer
.set_spacings (6);
115 strings
.push_back (X_("CoreAudio"));
117 strings
.push_back (X_("ALSA"));
118 strings
.push_back (X_("OSS"));
119 strings
.push_back (X_("FFADO"));
121 strings
.push_back (X_("NetJACK"));
122 strings
.push_back (X_("Dummy"));
123 set_popdown_strings (driver_combo
, strings
);
124 driver_combo
.set_active_text (strings
.front());
126 driver_combo
.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed
));
130 strings
.push_back (_("Playback/Recording on 1 Device"));
131 strings
.push_back (_("Playback/Recording on 2 Devices"));
132 strings
.push_back (_("Playback only"));
133 strings
.push_back (_("Recording only"));
134 set_popdown_strings (audio_mode_combo
, strings
);
135 audio_mode_combo
.set_active_text (strings
.front());
137 audio_mode_combo
.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed
));
138 audio_mode_changed ();
142 label
= manage (new Label (_("Driver")));
143 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
144 basic_packer
.attach (driver_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
147 label
= manage (new Label (_("Interface")));
148 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
149 basic_packer
.attach (interface_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
152 label
= manage (new Label (_("Sample Rate")));
153 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
154 basic_packer
.attach (sample_rate_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
157 label
= manage (new Label (_("Buffer size")));
158 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
159 basic_packer
.attach (period_size_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
163 label
= manage (new Label (_("Number of buffers")));
164 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
165 basic_packer
.attach (periods_spinner
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
166 periods_spinner
.set_value (2);
170 label
= manage (new Label (_("Approximate latency")));
171 label
->set_alignment (0.0, 0.5);
172 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
173 basic_packer
.attach (latency_label
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
176 sample_rate_combo
.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency
));
177 periods_adjustment
.signal_value_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency
));
178 period_size_combo
.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency
));
181 /* no audio mode with CoreAudio, its duplex or nuthin' */
184 label
= manage (new Label (_("Audio Mode")));
185 basic_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
186 basic_packer
.attach (audio_mode_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
190 interface_combo
.set_size_request (125, -1);
191 input_device_combo
.set_size_request (125, -1);
192 output_device_combo
.set_size_request (125, -1);
196 if (engine_running()) {
197 start_button.set_sensitive (false);
199 stop_button.set_sensitive (false);
202 start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
203 stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
206 button_box
.pack_start (start_button
, false, false);
207 button_box
.pack_start (stop_button
, false, false);
209 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
213 options_packer
.set_spacings (6);
216 options_packer
.attach (realtime_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
219 realtime_button
.set_active (true); /* RT is active by default */
220 realtime_button
.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed
));
224 label
= manage (new Label (_("Realtime Priority")));
225 label
->set_alignment (1.0, 0.5);
226 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
227 options_packer
.attach (priority_spinner
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
229 priority_spinner
.set_value (60);
231 options_packer
.attach (no_memory_lock_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
233 options_packer
.attach (unlock_memory_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
235 options_packer
.attach (soft_mode_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
237 options_packer
.attach (monitor_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
239 options_packer
.attach (force16bit_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
241 options_packer
.attach (hw_monitor_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
243 options_packer
.attach (hw_meter_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
245 options_packer
.attach (verbose_output_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
248 options_packer
.attach (verbose_output_button
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
253 strings
.push_back (_("Ignore"));
254 strings
.push_back ("500 msec");
255 strings
.push_back ("1 sec");
256 strings
.push_back ("2 sec");
257 strings
.push_back ("10 sec");
258 set_popdown_strings (timeout_combo
, strings
);
259 timeout_combo
.set_active_text (strings
.front ());
261 label
= manage (new Label (_("Client timeout")));
262 label
->set_alignment (1.0, 0.5);
263 options_packer
.attach (timeout_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, AttachOptions(0));
264 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
267 label
= manage (new Label (_("Number of ports")));
268 label
->set_alignment (1.0, 0.5);
269 options_packer
.attach (ports_spinner
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, AttachOptions(0));
270 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
274 label
= manage (new Label (_("Dither")));
275 label
->set_alignment (1.0, 0.5);
276 options_packer
.attach (dither_mode_combo
, 1, 2, row
, row
+ 1, FILL
|EXPAND
, AttachOptions(0));
277 options_packer
.attach (*label
, 0, 1, row
, row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
281 /* defer server stuff till later */
284 /* device settings */
286 device_packer
.set_spacings (6);
290 label
= manage (new Label (_("Input device")));
291 label
->set_alignment (1.0, 0.5);
292 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
293 device_packer
.attach (input_device_combo
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
295 label
= manage (new Label (_("Output device")));
296 label
->set_alignment (1.0, 0.5);
297 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
298 device_packer
.attach (output_device_combo
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
301 label
= manage (new Label (_("Input channels")));
302 label
->set_alignment (1.0, 0.5);
303 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
304 device_packer
.attach (input_channels
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
306 label
= manage (new Label (_("Output channels")));
307 label
->set_alignment (1.0, 0.5);
308 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
309 device_packer
.attach (output_channels
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
311 label
= manage (new Label (_("Hardware input latency (samples)")));
312 label
->set_alignment (1.0, 0.5);
313 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
314 device_packer
.attach (input_latency
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
316 label
= manage (new Label (_("Hardware output latency (samples)")));
317 label
->set_alignment (1.0, 0.5);
318 device_packer
.attach (*label
, 0, 1, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
319 device_packer
.attach (output_latency
, 1, 2, row
, row
+1, FILL
|EXPAND
, (AttachOptions
) 0);
322 basic_hbox
.pack_start (basic_packer
, false, false);
323 options_hbox
.pack_start (options_packer
, false, false);
325 device_packer
.set_border_width (12);
326 options_packer
.set_border_width (12);
327 basic_packer
.set_border_width (12);
329 notebook
.pages().push_back (TabElem (basic_hbox
, _("Device")));
330 notebook
.pages().push_back (TabElem (options_hbox
, _("Options")));
331 notebook
.pages().push_back (TabElem (device_packer
, _("Advanced")));
332 notebook
.set_border_width (12);
334 set_border_width (12);
335 pack_start (notebook
);
338 EngineControl::~EngineControl ()
344 EngineControl::discover_servers ()
346 find_jack_servers (server_strings
);
348 if (server_strings
.empty()) {
349 fatal
<< _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg
;
353 set_popdown_strings (serverpath_combo
, server_strings
);
354 serverpath_combo
.set_active_text (server_strings
.front());
356 if (server_strings
.size() > 1) {
357 Gtk::Label
* label
= manage (new Label (_("Server:")));
358 options_packer
.attach (*label
, 0, 1, server_row
, server_row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
359 label
->set_alignment (0.0, 0.5);
360 options_packer
.attach (serverpath_combo
, 1, 2, server_row
, server_row
+ 1, FILL
|EXPAND
, (AttachOptions
) 0);
365 EngineControl::build_command_line (vector
<string
>& cmd
)
369 bool using_oss
= false;
370 bool using_alsa
= false;
371 bool using_coreaudio
= false;
372 bool using_netjack
= false;
373 bool using_ffado
= false;
374 bool using_dummy
= false;
376 /* first, path to jackd */
378 cmd
.push_back (serverpath_combo
.get_active_text ());
380 /* now jackd arguments */
382 str
= timeout_combo
.get_active_text ();
383 if (str
!= _("Ignore")) {
387 msecs
= (uint32_t) floor (secs
* 1000.0);
388 cmd
.push_back ("-t");
389 cmd
.push_back (to_string (msecs
, std::dec
));
392 if (no_memory_lock_button
.get_active()) {
393 cmd
.push_back ("-m"); /* no munlock */
396 cmd
.push_back ("-p"); /* port max */
397 cmd
.push_back (to_string ((uint32_t) floor (ports_spinner
.get_value()), std::dec
));
399 if (realtime_button
.get_active()) {
400 cmd
.push_back ("-R");
401 cmd
.push_back ("-P");
402 cmd
.push_back (to_string ((uint32_t) floor (priority_spinner
.get_value()), std::dec
));
405 if (unlock_memory_button
.get_active()) {
406 cmd
.push_back ("-u");
409 if (verbose_output_button
.get_active()) {
410 cmd
.push_back ("-v");
413 /* now add fixed arguments (not user-selectable) */
415 cmd
.push_back ("-T"); // temporary */
417 /* next the driver */
419 cmd
.push_back ("-d");
421 driver
= driver_combo
.get_active_text ();
422 if (driver
== X_("ALSA")) {
424 cmd
.push_back ("alsa");
425 } else if (driver
== X_("OSS")) {
427 cmd
.push_back ("oss");
428 } else if (driver
== X_("CoreAudio")) {
429 using_coreaudio
= true;
430 cmd
.push_back ("coreaudio");
431 } else if (driver
== X_("NetJACK")) {
432 using_netjack
= true;
433 cmd
.push_back ("netjack");
434 } else if (driver
== X_("FFADO")) {
437 /* do this until FFADO becomes the standard */
439 char* hack
= getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
442 cmd
.push_back (hack
);
444 cmd
.push_back ("freebob");
447 } else if ( driver
== X_("Dummy")) {
449 cmd
.push_back ("dummy");
452 /* driver arguments */
454 if (!using_coreaudio
) {
455 str
= audio_mode_combo
.get_active_text();
457 if (str
== _("Playback/Recording on 1 Device")) {
461 } else if (str
== _("Playback/Recording on 2 Devices")) {
463 string input_device
= get_device_name (driver
, input_device_combo
.get_active_text());
464 string output_device
= get_device_name (driver
, output_device_combo
.get_active_text());
466 if (input_device
.empty() || output_device
.empty()) {
471 cmd
.push_back ("-C");
472 cmd
.push_back (input_device
);
473 cmd
.push_back ("-P");
474 cmd
.push_back (output_device
);
476 } else if (str
== _("Playback only")) {
477 cmd
.push_back ("-P");
478 } else if (str
== _("Recording only")) {
479 cmd
.push_back ("-C");
482 if (! using_dummy
) {
483 cmd
.push_back ("-n");
484 cmd
.push_back (to_string ((uint32_t) floor (periods_spinner
.get_value()), std::dec
));
488 cmd
.push_back ("-r");
489 cmd
.push_back (to_string (get_rate(), std::dec
));
491 cmd
.push_back ("-p");
492 cmd
.push_back (period_size_combo
.get_active_text());
496 if (audio_mode_combo
.get_active_text() != _("Playback/Recording on 2 Devices")) {
498 string device
= get_device_name (driver
, interface_combo
.get_active_text());
499 if (device
.empty()) {
504 cmd
.push_back ("-d");
505 cmd
.push_back (device
);
506 _interface_chosen
= true;
509 if (hw_meter_button
.get_active()) {
510 cmd
.push_back ("-M");
513 if (hw_monitor_button
.get_active()) {
514 cmd
.push_back ("-H");
517 str
= dither_mode_combo
.get_active_text();
519 if (str
== _("None")) {
520 } else if (str
== _("Triangular")) {
521 cmd
.push_back ("-z triangular");
522 } else if (str
== _("Rectangular")) {
523 cmd
.push_back ("-z rectangular");
524 } else if (str
== _("Shaped")) {
525 cmd
.push_back ("-z shaped");
528 if (force16bit_button
.get_active()) {
529 cmd
.push_back ("-S");
532 if (soft_mode_button
.get_active()) {
533 cmd
.push_back ("-s");
536 } else if (using_coreaudio
) {
539 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
541 string device
= get_device_name (driver
, interface_combo
.get_active_text());
542 if (device
.empty()) {
547 cmd
.push_back ("-d");
548 cmd
.push_back (device
);
551 } else if (using_oss
) {
553 } else if (using_netjack
) {
559 EngineControl::engine_running ()
561 jack_status_t status
;
562 jack_client_t
* c
= jack_client_open ("ardourprobe", JackNoStartServer
, &status
);
565 jack_client_close (c
);
572 EngineControl::setup_engine ()
575 std::string cwd
= "/tmp";
577 build_command_line (args
);
580 return 1; // try again
583 Glib::ustring jackdrc_path
= Glib::get_home_dir();
584 jackdrc_path
+= "/.jackdrc";
586 ofstream
jackdrc (jackdrc_path
.c_str());
588 error
<< string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path
) << endmsg
;
591 cerr
<< "JACK COMMAND: ";
592 for (vector
<string
>::iterator i
= args
.begin(); i
!= args
.end(); ++i
) {
594 jackdrc
<< (*i
) << ' ';
606 EngineControl::realtime_changed ()
609 priority_spinner
.set_sensitive (realtime_button
.get_active());
614 EngineControl::enumerate_devices (const string
& driver
)
616 /* note: case matters for the map keys */
618 if (driver
== "CoreAudio") {
620 devices
[driver
] = enumerate_coreaudio_devices ();
624 } else if (driver
== "ALSA") {
625 devices
[driver
] = enumerate_alsa_devices ();
626 } else if (driver
== "FFADO") {
627 devices
[driver
] = enumerate_ffado_devices ();
628 } else if (driver
== "OSS") {
629 devices
[driver
] = enumerate_oss_devices ();
630 } else if (driver
== "Dummy") {
631 devices
[driver
] = enumerate_dummy_devices ();
632 } else if (driver
== "NetJACK") {
633 devices
[driver
] = enumerate_netjack_devices ();
642 getDeviceUIDFromID( AudioDeviceID id
, char *name
, size_t nsize
)
644 UInt32 size
= sizeof(CFStringRef
);
646 OSStatus res
= AudioDeviceGetProperty(id
, 0, false,
647 kAudioDevicePropertyDeviceUID
, &size
, &UI
);
649 CFStringGetCString(UI
,name
,nsize
,CFStringGetSystemEncoding());
655 EngineControl::enumerate_coreaudio_devices ()
659 // Find out how many Core Audio devices are there, if any...
660 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
663 size_t outSize
= sizeof(isWritable
);
665 backend_devs
.clear ();
667 err
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices
,
668 &outSize
, &isWritable
);
670 // Calculate the number of device available...
671 int numCoreDevices
= outSize
/ sizeof(AudioDeviceID
);
672 // Make space for the devices we are about to get...
673 AudioDeviceID
*coreDeviceIDs
= new AudioDeviceID
[numCoreDevices
];
674 err
= AudioHardwareGetProperty(kAudioHardwarePropertyDevices
,
675 &outSize
, (void *) coreDeviceIDs
);
677 // Look for the CoreAudio device name...
678 char coreDeviceName
[256];
681 for (int i
= 0; i
< numCoreDevices
; i
++) {
683 nameSize
= sizeof (coreDeviceName
);
685 /* enforce duplex devices only */
687 err
= AudioDeviceGetPropertyInfo(coreDeviceIDs
[i
],
688 0, true, kAudioDevicePropertyStreams
,
689 &outSize
, &isWritable
);
691 if (err
!= noErr
|| outSize
== 0) {
695 err
= AudioDeviceGetPropertyInfo(coreDeviceIDs
[i
],
696 0, false, kAudioDevicePropertyStreams
,
697 &outSize
, &isWritable
);
699 if (err
!= noErr
|| outSize
== 0) {
703 err
= AudioDeviceGetPropertyInfo(coreDeviceIDs
[i
],
704 0, true, kAudioDevicePropertyDeviceName
,
705 &outSize
, &isWritable
);
707 err
= AudioDeviceGetProperty(coreDeviceIDs
[i
],
708 0, true, kAudioDevicePropertyDeviceName
,
709 &nameSize
, (void *) coreDeviceName
);
711 char drivername
[128];
713 // this returns the unique id for the device
714 // that must be used on the commandline for jack
716 if (getDeviceUIDFromID(coreDeviceIDs
[i
], drivername
, sizeof (drivername
)) == noErr
) {
717 devs
.push_back (coreDeviceName
);
718 backend_devs
.push_back (drivername
);
724 delete [] coreDeviceIDs
;
728 if (devs
.size() == 0) {
729 MessageDialog
msg (_("\
730 You do not have any audio devices capable of\n\
731 simultaneous playback and recording.\n\n\
732 Please use Applications -> Utilities -> Audio MIDI Setup\n\
733 to create an \"aggregrate\" device, or install a suitable\n\
734 audio interface.\n\n\
735 Please send email to Apple and ask them why new Macs\n\
736 have no duplex audio device.\n\n\
737 Alternatively, if you really want just playback\n\
738 or recording but not both, start JACK before running\n\
739 Ardour and choose the relevant device then."
741 true, Gtk::MESSAGE_ERROR
, Gtk::BUTTONS_OK
);
742 msg
.set_title (_("No suitable audio devices"));
743 msg
.set_position (Gtk::WIN_POS_MOUSE
);
753 EngineControl::enumerate_alsa_devices ()
758 snd_ctl_card_info_t
*info
;
759 snd_pcm_info_t
*pcminfo
;
760 snd_ctl_card_info_alloca(&info
);
761 snd_pcm_info_alloca(&pcminfo
);
766 backend_devs
.clear ();
768 while (snd_card_next (&cardnum
) >= 0 && cardnum
>= 0) {
771 devname
+= to_string (cardnum
, std::dec
);
773 if (snd_ctl_open (&handle
, devname
.c_str(), 0) >= 0 && snd_ctl_card_info (handle
, info
) >= 0) {
775 while (snd_ctl_pcm_next_device (handle
, &device
) >= 0 && device
>= 0) {
777 bool have_playback
= false;
778 bool have_capture
= false;
780 /* find duplex devices only */
782 snd_pcm_info_set_device (pcminfo
, device
);
783 snd_pcm_info_set_subdevice (pcminfo
, 0);
784 snd_pcm_info_set_stream (pcminfo
, SND_PCM_STREAM_CAPTURE
);
786 if (snd_ctl_pcm_info (handle
, pcminfo
) >= 0) {
790 snd_pcm_info_set_device (pcminfo
, device
);
791 snd_pcm_info_set_subdevice (pcminfo
, 0);
792 snd_pcm_info_set_stream (pcminfo
, SND_PCM_STREAM_PLAYBACK
);
794 if (snd_ctl_pcm_info (handle
, pcminfo
) >= 0) {
795 have_playback
= true;
798 if (have_capture
&& have_playback
) {
799 devs
.push_back (snd_pcm_info_get_name (pcminfo
));
801 devname
+= to_string (device
, std::dec
);
802 backend_devs
.push_back (devname
);
806 snd_ctl_close(handle
);
814 EngineControl::enumerate_ffado_devices ()
817 backend_devs
.clear ();
822 EngineControl::enumerate_oss_devices ()
828 EngineControl::enumerate_dummy_devices ()
834 EngineControl::enumerate_netjack_devices ()
842 EngineControl::driver_changed ()
844 string driver
= driver_combo
.get_active_text();
845 string::size_type maxlen
= 0;
849 enumerate_devices (driver
);
851 vector
<string
>& strings
= devices
[driver
];
853 if (strings
.empty() && driver
!= "FFADO" && driver
!= "Dummy") {
854 error
<< string_compose (_("No devices found for driver \"%1\""), driver
) << endmsg
;
858 for (vector
<string
>::iterator i
= strings
.begin(); i
!= strings
.end(); ++i
, ++n
) {
859 if ((*i
).length() > maxlen
) {
860 maxlen
= (*i
).length();
865 set_popdown_strings (interface_combo
, strings
);
866 set_popdown_strings (input_device_combo
, strings
);
867 set_popdown_strings (output_device_combo
, strings
);
869 if (!strings
.empty()) {
870 interface_combo
.set_active_text (strings
.front());
871 input_device_combo
.set_active_text (strings
.front());
872 output_device_combo
.set_active_text (strings
.front());
875 if (driver
== "ALSA") {
876 soft_mode_button
.set_sensitive (true);
877 force16bit_button
.set_sensitive (true);
878 hw_monitor_button
.set_sensitive (true);
879 hw_meter_button
.set_sensitive (true);
880 monitor_button
.set_sensitive (true);
882 soft_mode_button
.set_sensitive (false);
883 force16bit_button
.set_sensitive (false);
884 hw_monitor_button
.set_sensitive (false);
885 hw_meter_button
.set_sensitive (false);
886 monitor_button
.set_sensitive (false);
891 EngineControl::get_rate ()
893 return atoi (sample_rate_combo
.get_active_text ());
897 EngineControl::redisplay_latency ()
899 uint32_t rate
= get_rate();
903 float periods
= periods_adjustment
.get_value();
905 float period_size
= atof (period_size_combo
.get_active_text());
908 snprintf (buf
, sizeof(buf
), "%.1fmsec", (periods
* period_size
) / (rate
/1000.0));
910 latency_label
.set_text (buf
);
914 EngineControl::audio_mode_changed ()
916 Glib::ustring str
= audio_mode_combo
.get_active_text();
918 if (str
== _("Playback/Recording on 1 Device")) {
919 input_device_combo
.set_sensitive (false);
920 output_device_combo
.set_sensitive (false);
921 } else if (str
== _("Playback/Recording on 2 Devices")) {
922 input_device_combo
.set_sensitive (true);
923 output_device_combo
.set_sensitive (true);
924 } else if (str
== _("Playback only")) {
925 output_device_combo
.set_sensitive (true);
926 } else if (str
== _("Recording only")) {
927 input_device_combo
.set_sensitive (true);
931 static bool jack_server_filter(const string
& str
, void *arg
)
933 return str
== "jackd" || str
== "jackdmp";
937 EngineControl::find_jack_servers (vector
<string
>& strings
)
940 /* this magic lets us finds the path to the OSX bundle, and then
941 we infer JACK's location from there
944 char execpath
[MAXPATHLEN
+1];
945 uint32_t pathsz
= sizeof (execpath
);
947 _NSGetExecutablePath (execpath
, &pathsz
);
949 string
path (Glib::path_get_dirname (execpath
));
952 if (Glib::file_test (path
, FILE_TEST_EXISTS
)) {
953 strings
.push_back (path
);
956 if (getenv ("ARDOUR_WITH_JACK")) {
957 /* no other options - only use the JACK we supply */
958 if (strings
.empty()) {
959 fatal
<< _("JACK appears to be missing from the Ardour bundle") << endmsg
;
969 vector
<string
*> *jack_servers
;
970 std::map
<string
,int> un
;
972 bool need_minimal_path
= false;
979 need_minimal_path
= true;
983 // many mac users don't have PATH set up to include
984 // likely installed locations of JACK
985 need_minimal_path
= true;
988 if (need_minimal_path
) {
990 path
= "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
992 path
+= ":/usr/local/bin:/opt/local/bin";
997 // push it back into the environment so that auto-started JACK can find it.
998 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
999 setenv ("PATH", path
.c_str(), 1);
1002 jack_servers
= scanner (path
, jack_server_filter
, 0, false, true);
1004 vector
<string
*>::iterator iter
;
1006 for (iter
= jack_servers
->begin(); iter
!= jack_servers
->end(); iter
++) {
1010 strings
.push_back(p
);
1016 EngineControl::get_device_name (const string
& driver
, const string
& human_readable
)
1018 vector
<string
>::iterator n
;
1019 vector
<string
>::iterator i
;
1021 if (human_readable
.empty()) {
1022 /* this can happen if the user's .ardourrc file has a device name from
1023 another computer system in it
1025 MessageDialog
msg (_("You need to choose an audio device first."));
1030 if (backend_devs
.empty()) {
1031 return human_readable
;
1034 for (i
= devices
[driver
].begin(), n
= backend_devs
.begin(); i
!= devices
[driver
].end(); ++i
, ++n
) {
1035 if (human_readable
== (*i
)) {
1040 if (i
== devices
[driver
].end()) {
1041 warning
<< string_compose (_("Audio device \"%1\" not known on this computer."), human_readable
) << endmsg
;
1048 EngineControl::get_state ()
1050 XMLNode
* root
= new XMLNode ("AudioSetup");
1054 child
= new XMLNode ("periods");
1055 child
->add_property ("val", to_string (periods_adjustment
.get_value(), std::dec
));
1056 root
->add_child_nocopy (*child
);
1058 child
= new XMLNode ("priority");
1059 child
->add_property ("val", to_string (priority_adjustment
.get_value(), std::dec
));
1060 root
->add_child_nocopy (*child
);
1062 child
= new XMLNode ("ports");
1063 child
->add_property ("val", to_string (ports_adjustment
.get_value(), std::dec
));
1064 root
->add_child_nocopy (*child
);
1066 child
= new XMLNode ("inchannels");
1067 child
->add_property ("val", to_string (input_channels
.get_value(), std::dec
));
1068 root
->add_child_nocopy (*child
);
1070 child
= new XMLNode ("outchannels");
1071 child
->add_property ("val", to_string (output_channels
.get_value(), std::dec
));
1072 root
->add_child_nocopy (*child
);
1074 child
= new XMLNode ("inlatency");
1075 child
->add_property ("val", to_string (input_latency
.get_value(), std::dec
));
1076 root
->add_child_nocopy (*child
);
1078 child
= new XMLNode ("outlatency");
1079 child
->add_property ("val", to_string (output_latency
.get_value(), std::dec
));
1080 root
->add_child_nocopy (*child
);
1082 child
= new XMLNode ("realtime");
1083 child
->add_property ("val", to_string (realtime_button
.get_active(), std::dec
));
1084 root
->add_child_nocopy (*child
);
1086 child
= new XMLNode ("nomemorylock");
1087 child
->add_property ("val", to_string (no_memory_lock_button
.get_active(), std::dec
));
1088 root
->add_child_nocopy (*child
);
1090 child
= new XMLNode ("unlockmemory");
1091 child
->add_property ("val", to_string (unlock_memory_button
.get_active(), std::dec
));
1092 root
->add_child_nocopy (*child
);
1094 child
= new XMLNode ("softmode");
1095 child
->add_property ("val", to_string (soft_mode_button
.get_active(), std::dec
));
1096 root
->add_child_nocopy (*child
);
1098 child
= new XMLNode ("force16bit");
1099 child
->add_property ("val", to_string (force16bit_button
.get_active(), std::dec
));
1100 root
->add_child_nocopy (*child
);
1102 child
= new XMLNode ("hwmonitor");
1103 child
->add_property ("val", to_string (hw_monitor_button
.get_active(), std::dec
));
1104 root
->add_child_nocopy (*child
);
1106 child
= new XMLNode ("hwmeter");
1107 child
->add_property ("val", to_string (hw_meter_button
.get_active(), std::dec
));
1108 root
->add_child_nocopy (*child
);
1110 child
= new XMLNode ("verbose");
1111 child
->add_property ("val", to_string (verbose_output_button
.get_active(), std::dec
));
1112 root
->add_child_nocopy (*child
);
1114 child
= new XMLNode ("samplerate");
1115 child
->add_property ("val", sample_rate_combo
.get_active_text());
1116 root
->add_child_nocopy (*child
);
1118 child
= new XMLNode ("periodsize");
1119 child
->add_property ("val", period_size_combo
.get_active_text());
1120 root
->add_child_nocopy (*child
);
1122 child
= new XMLNode ("serverpath");
1123 child
->add_property ("val", serverpath_combo
.get_active_text());
1124 root
->add_child_nocopy (*child
);
1126 child
= new XMLNode ("driver");
1127 child
->add_property ("val", driver_combo
.get_active_text());
1128 root
->add_child_nocopy (*child
);
1130 child
= new XMLNode ("interface");
1131 child
->add_property ("val", interface_combo
.get_active_text());
1132 root
->add_child_nocopy (*child
);
1134 child
= new XMLNode ("timeout");
1135 child
->add_property ("val", timeout_combo
.get_active_text());
1136 root
->add_child_nocopy (*child
);
1138 child
= new XMLNode ("dither");
1139 child
->add_property ("val", dither_mode_combo
.get_active_text());
1140 root
->add_child_nocopy (*child
);
1142 child
= new XMLNode ("audiomode");
1143 child
->add_property ("val", audio_mode_combo
.get_active_text());
1144 root
->add_child_nocopy (*child
);
1146 child
= new XMLNode ("inputdevice");
1147 child
->add_property ("val", input_device_combo
.get_active_text());
1148 root
->add_child_nocopy (*child
);
1150 child
= new XMLNode ("outputdevice");
1151 child
->add_property ("val", output_device_combo
.get_active_text());
1152 root
->add_child_nocopy (*child
);
1158 EngineControl::set_state (const XMLNode
& root
)
1161 XMLNodeConstIterator citer
;
1163 XMLProperty
* prop
= NULL
;
1164 bool using_dummy
= false;
1169 if ( (child
= root
.child ("driver"))){
1170 prop
= child
->property("val");
1171 if (prop
&& (prop
->value() == "Dummy") ) {
1176 clist
= root
.children();
1178 for (citer
= clist
.begin(); citer
!= clist
.end(); ++citer
) {
1182 prop
= child
->property ("val");
1184 if (!prop
|| prop
->value().empty()) {
1186 if (using_dummy
&& ( child
->name() == "interface" || child
->name() == "inputdevice" || child
->name() == "outputdevice" )) {
1190 error
<< string_compose (_("AudioSetup value for %1 is missing data"), child
->name()) << endmsg
;
1194 strval
= prop
->value();
1196 /* adjustments/spinners */
1198 if (child
->name() == "periods") {
1199 val
= atoi (strval
);
1200 periods_adjustment
.set_value(val
);
1201 } else if (child
->name() == "priority") {
1202 val
= atoi (strval
);
1203 priority_adjustment
.set_value(val
);
1204 } else if (child
->name() == "ports") {
1205 val
= atoi (strval
);
1206 ports_adjustment
.set_value(val
);
1207 } else if (child
->name() == "inchannels") {
1208 val
= atoi (strval
);
1209 input_channels
.set_value(val
);
1210 } else if (child
->name() == "outchannels") {
1211 val
= atoi (strval
);
1212 output_channels
.set_value(val
);
1213 } else if (child
->name() == "inlatency") {
1214 val
= atoi (strval
);
1215 input_latency
.set_value(val
);
1216 } else if (child
->name() == "outlatency") {
1217 val
= atoi (strval
);
1218 output_latency
.set_value(val
);
1223 else if (child
->name() == "realtime") {
1224 val
= atoi (strval
);
1225 realtime_button
.set_active(val
);
1226 } else if (child
->name() == "nomemorylock") {
1227 val
= atoi (strval
);
1228 no_memory_lock_button
.set_active(val
);
1229 } else if (child
->name() == "unlockmemory") {
1230 val
= atoi (strval
);
1231 unlock_memory_button
.set_active(val
);
1232 } else if (child
->name() == "softmode") {
1233 val
= atoi (strval
);
1234 soft_mode_button
.set_active(val
);
1235 } else if (child
->name() == "force16bit") {
1236 val
= atoi (strval
);
1237 force16bit_button
.set_active(val
);
1238 } else if (child
->name() == "hwmonitor") {
1239 val
= atoi (strval
);
1240 hw_monitor_button
.set_active(val
);
1241 } else if (child
->name() == "hwmeter") {
1242 val
= atoi (strval
);
1243 hw_meter_button
.set_active(val
);
1244 } else if (child
->name() == "verbose") {
1245 val
= atoi (strval
);
1246 verbose_output_button
.set_active(val
);
1251 else if (child
->name() == "samplerate") {
1252 sample_rate_combo
.set_active_text(strval
);
1253 } else if (child
->name() == "periodsize") {
1254 period_size_combo
.set_active_text(strval
);
1255 } else if (child
->name() == "serverpath") {
1257 /* only attempt to set this if we have bothered to look
1258 up server names already. otherwise this is all
1259 redundant (actually, all of this dialog/widget
1260 is redundant in that case ...)
1264 if (!server_strings
.empty()) {
1265 /* do not allow us to use a server path that doesn't
1266 exist on this system. this handles cases where
1267 the user has an RC file listing a serverpath
1268 from some other machine.
1270 vector
<string
>::iterator x
;
1271 for (x
= server_strings
.begin(); x
!= server_strings
.end(); ++x
) {
1276 if (x
!= server_strings
.end()) {
1277 serverpath_combo
.set_active_text (strval
);
1279 warning
<< string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1285 } else if (child
->name() == "driver") {
1286 driver_combo
.set_active_text(strval
);
1287 } else if (child
->name() == "interface") {
1288 interface_combo
.set_active_text(strval
);
1289 if (!strval
.empty()) {
1290 _interface_chosen
= true;
1292 } else if (child
->name() == "timeout") {
1293 timeout_combo
.set_active_text(strval
);
1294 } else if (child
->name() == "dither") {
1295 dither_mode_combo
.set_active_text(strval
);
1296 } else if (child
->name() == "audiomode") {
1297 audio_mode_combo
.set_active_text(strval
);
1298 } else if (child
->name() == "inputdevice") {
1299 input_device_combo
.set_active_text(strval
);
1300 } else if (child
->name() == "outputdevice") {
1301 output_device_combo
.set_active_text(strval
);