start work on the changes to EngineControl (dialog) to integrate with new backend design, and add "requires-driver" concept to AudioBackend to handle JACK specifically

This commit is contained in:
Paul Davis 2013-08-04 14:03:19 -04:00
parent 333a3c9d02
commit 7218bd91de
10 changed files with 250 additions and 882 deletions

View file

@ -17,6 +17,7 @@ export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:
export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
export ARDOUR_MCP_PATH=$TOP/mcp:.
export ARDOUR_EXPORT_FORMATS_PATH=$TOP/export:.
export ARDOUR_BACKEND_PATH=$TOP/build/libs/ardour
#
# even though we set the above variables, ardour requires that these

View file

@ -397,7 +397,6 @@ ARDOUR_UI::attach_to_engine ()
engine->SampleRateChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context());
engine->Halted.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
engine->BackendAvailable.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::post_engine, this));
ARDOUR::Port::set_connecting_blocked (ARDOUR_COMMAND_LINE::no_connect_ports);

View file

@ -66,33 +66,25 @@ using namespace PBD;
using namespace Glib;
EngineControl::EngineControl ()
: periods_adjustment (2, 2, 16, 1, 2),
periods_spinner (periods_adjustment),
ports_adjustment (128, 8, 1024, 1, 16),
ports_spinner (ports_adjustment),
input_latency_adjustment (0, 0, 99999, 1),
input_latency (input_latency_adjustment),
output_latency_adjustment (0, 0, 99999, 1),
output_latency (output_latency_adjustment),
realtime_button (_("Realtime")),
no_memory_lock_button (_("Do not lock memory")),
unlock_memory_button (_("Unlock memory")),
soft_mode_button (_("No zombies")),
monitor_button (_("Provide monitor ports")),
force16bit_button (_("Force 16 bit")),
hw_monitor_button (_("H/W monitoring")),
hw_meter_button (_("H/W metering")),
verbose_output_button (_("Verbose output")),
start_button (_("Start")),
stop_button (_("Stop")),
: input_latency_adjustment (0, 0, 99999, 1)
, input_latency (input_latency_adjustment)
, output_latency_adjustment (0, 0, 99999, 1)
, output_latency (output_latency_adjustment)
, input_channels_adjustment (2, 0, 256, 1)
, input_channels (input_channels_adjustment)
, output_channels_adjustment (2, 0, 256, 1)
, output_channels (output_channels_adjustment)
, ports_adjustment (128, 8, 1024, 1, 16)
, ports_spinner (ports_adjustment)
, realtime_button (_("Realtime"))
#ifdef __APPLE___
basic_packer (5, 2),
options_packer (4, 2),
device_packer (4, 2)
, basic_packer (6, 2)
, options_packer (4, 2)
, device_packer (4, 2)
#else
basic_packer (8, 2),
options_packer (14, 2),
device_packer (6, 2)
, basic_packer (9, 2)
, options_packer (14, 2)
, device_packer (6, 2)
#endif
{
using namespace Notebook_Helpers;
@ -102,59 +94,24 @@ EngineControl::EngineControl ()
_used = false;
strings.push_back (_("8000Hz"));
strings.push_back (_("22050Hz"));
strings.push_back (_("44100Hz"));
strings.push_back (_("48000Hz"));
strings.push_back (_("88200Hz"));
strings.push_back (_("96000Hz"));
strings.push_back (_("192000Hz"));
set_popdown_strings (sample_rate_combo, strings);
sample_rate_combo.set_active_text ("48000Hz");
strings.clear ();
strings.push_back ("32");
strings.push_back ("64");
strings.push_back ("128");
strings.push_back ("256");
strings.push_back ("512");
strings.push_back ("1024");
strings.push_back ("2048");
strings.push_back ("4096");
strings.push_back ("8192");
set_popdown_strings (period_size_combo, strings);
period_size_combo.set_active_text ("1024");
strings.clear ();
strings.push_back (_("None"));
strings.push_back (_("Triangular"));
strings.push_back (_("Rectangular"));
strings.push_back (_("Shaped"));
set_popdown_strings (dither_mode_combo, strings);
dither_mode_combo.set_active_text (_("None"));
/* basic parameters */
basic_packer.set_spacings (6);
strings.clear ();
#ifdef __APPLE__
strings.push_back (X_("CoreAudio"));
#else
#ifndef __FreeBSD__
strings.push_back (X_("ALSA"));
#endif
strings.push_back (X_("OSS"));
strings.push_back (X_("FreeBoB"));
strings.push_back (X_("FFADO"));
#endif
strings.push_back (X_("NetJACK"));
strings.push_back (X_("Dummy"));
set_popdown_strings (driver_combo, strings);
driver_combo.set_active_text (strings.front());
vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
strings.push_back ((*b)->name);
}
set_popdown_strings (backend_combo, strings);
backend_combo.set_active_text (strings.front());
backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
backend_changed ();
driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
driver_changed ();
strings.clear ();
strings.push_back (_("Playback/recording on 1 device"));
@ -176,6 +133,11 @@ EngineControl::EngineControl ()
row = 0;
label = manage (left_aligned_label (_("Audio Driver:")));
basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
basic_packer.attach (backend_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
row++;
label = manage (left_aligned_label (_("Driver:")));
basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
@ -193,25 +155,16 @@ EngineControl::EngineControl ()
label = manage (left_aligned_label (_("Buffer size:")));
basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
row++;
#if !defined(__APPLE__) && !defined(__FreeBSD__)
label = manage (left_aligned_label (_("Number of buffers:")));
basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
periods_spinner.set_value (2);
row++;
#endif
label = manage (left_aligned_label (_("Approximate latency:")));
basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
row++;
sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
redisplay_latency();
row++;
/* no audio mode with CoreAudio, its duplex or nuthin' */
@ -227,22 +180,7 @@ EngineControl::EngineControl ()
input_device_combo.set_size_request (250, -1);
output_device_combo.set_size_request (250, -1);
/*
if (engine_running()) {
start_button.set_sensitive (false);
} else {
stop_button.set_sensitive (false);
}
start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
*/
button_box.pack_start (start_button, false, false);
button_box.pack_start (stop_button, false, false);
// basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
interface_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::interface_changed));
/* options */
@ -254,46 +192,6 @@ EngineControl::EngineControl ()
realtime_button.set_active (true);
#if PROVIDE_TOO_MANY_OPTIONS
#if !defined(__APPLE__) && !defined(__FreeBSD__)
options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
#else
options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
#endif
strings.clear ();
strings.push_back (_("Ignore"));
strings.push_back ("500 msec");
strings.push_back ("1 sec");
strings.push_back ("2 sec");
strings.push_back ("10 sec");
set_popdown_strings (timeout_combo, strings);
timeout_combo.set_active_text (strings.front ());
label = manage (new Label (_("Client timeout")));
label->set_alignment (1.0, 0.5);
options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
#endif /* PROVIDE_TOO_MANY_OPTIONS */
label = manage (left_aligned_label (_("Number of ports:")));
options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
@ -311,23 +209,6 @@ EngineControl::EngineControl ()
++row;
#endif
find_jack_servers (server_strings);
if (server_strings.empty()) {
fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
/*NOTREACHED*/
}
set_popdown_strings (serverpath_combo, server_strings);
serverpath_combo.set_active_text (server_strings.front());
if (server_strings.size() > 1) {
label = manage (left_aligned_label (_("Server:")));
options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
++row;
}
/* device settings */
device_packer.set_spacings (6);
@ -386,513 +267,92 @@ EngineControl::~EngineControl ()
}
void
EngineControl::build_command_line (vector<string>& cmd)
EngineControl::backend_changed ()
{
string str;
string driver;
bool using_alsa = false;
bool using_coreaudio = false;
bool using_dummy = false;
bool using_ffado = false;
string backend_name = backend_combo.get_active_text();
boost::shared_ptr<ARDOUR::AudioBackend> backend;
/* first, path to jackd */
cmd.push_back (serverpath_combo.get_active_text ());
/* now jackd arguments */
str = timeout_combo.get_active_text ();
if (str != _("Ignore")) {
double secs = 0;
uint32_t msecs;
secs = atof (str);
msecs = (uint32_t) floor (secs * 1000.0);
if (msecs > 0) {
cmd.push_back ("-t");
cmd.push_back (to_string (msecs, std::dec));
}
if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
/* eh? */
return;
}
if (no_memory_lock_button.get_active()) {
cmd.push_back ("-m"); /* no munlock */
}
cmd.push_back ("-p"); /* port max */
cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
if (realtime_button.get_active()) {
cmd.push_back ("-R");
if (backend->requires_driver_selection()) {
vector<string> drivers = backend->enumerate_drivers();
set_popdown_strings (driver_combo, drivers);
driver_combo.set_active_text (drivers.front());
driver_changed ();
} else {
cmd.push_back ("-r"); /* override jackd's default --realtime */
list_devices ();
}
if (unlock_memory_button.get_active()) {
cmd.push_back ("-u");
}
if (verbose_output_button.get_active()) {
cmd.push_back ("-v");
}
/* now add fixed arguments (not user-selectable) */
cmd.push_back ("-T"); // temporary */
/* next the driver */
cmd.push_back ("-d");
driver = driver_combo.get_active_text ();
if (driver == X_("ALSA")) {
using_alsa = true;
cmd.push_back ("alsa");
} else if (driver == X_("OSS")) {
cmd.push_back ("oss");
} else if (driver == X_("CoreAudio")) {
using_coreaudio = true;
cmd.push_back ("coreaudio");
} else if (driver == X_("NetJACK")) {
cmd.push_back ("netjack");
} else if (driver == X_("FreeBoB")) {
cmd.push_back ("freebob");
} else if (driver == X_("FFADO")) {
using_ffado = true;
cmd.push_back ("firewire");
} else if ( driver == X_("Dummy")) {
using_dummy = true;
cmd.push_back ("dummy");
}
/* driver arguments */
if (!using_coreaudio) {
str = audio_mode_combo.get_active_text();
if (str == _("Playback/recording on 1 device")) {
/* relax */
} else if (str == _("Playback/recording on 2 devices")) {
string input_device = get_device_name (driver, input_device_combo.get_active_text());
string output_device = get_device_name (driver, output_device_combo.get_active_text());
if (input_device.empty() || output_device.empty()) {
cmd.clear ();
return;
}
cmd.push_back ("-C");
cmd.push_back (input_device);
cmd.push_back ("-P");
cmd.push_back (output_device);
} else if (str == _("Playback only")) {
cmd.push_back ("-P");
} else if (str == _("Recording only")) {
cmd.push_back ("-C");
}
if (!using_dummy) {
cmd.push_back ("-n");
cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
}
}
cmd.push_back ("-r");
cmd.push_back (to_string (get_rate(), std::dec));
cmd.push_back ("-p");
cmd.push_back (period_size_combo.get_active_text());
if (using_alsa || using_ffado || using_coreaudio) {
double val = input_latency_adjustment.get_value();
if (val) {
cmd.push_back ("-I");
cmd.push_back (to_string ((uint32_t) val, std::dec));
}
val = output_latency_adjustment.get_value();
if (val) {
cmd.push_back ("-O");
cmd.push_back (to_string ((uint32_t) val, std::dec));
}
}
if (using_alsa) {
if (audio_mode_combo.get_active_text() != _("Playback/recording on 2 devices")) {
string device = get_device_name (driver, interface_combo.get_active_text());
if (device.empty()) {
cmd.clear ();
return;
}
cmd.push_back ("-d");
cmd.push_back (device);
}
if (hw_meter_button.get_active()) {
cmd.push_back ("-M");
}
if (hw_monitor_button.get_active()) {
cmd.push_back ("-H");
}
str = dither_mode_combo.get_active_text();
if (str == _("None")) {
} else if (str == _("Triangular")) {
cmd.push_back ("-z triangular");
} else if (str == _("Rectangular")) {
cmd.push_back ("-z rectangular");
} else if (str == _("Shaped")) {
cmd.push_back ("-z shaped");
}
if (force16bit_button.get_active()) {
cmd.push_back ("-S");
}
if (soft_mode_button.get_active()) {
cmd.push_back ("-s");
}
str = midi_driver_combo.get_active_text ();
if (str == _("seq")) {
cmd.push_back ("-X seq");
} else if (str == _("raw")) {
cmd.push_back ("-X raw");
}
} else if (using_coreaudio) {
#ifdef __APPLE__
// note: older versions of the CoreAudio JACK backend use -n instead of -d here
string device = get_device_name (driver, interface_combo.get_active_text());
if (device.empty()) {
cmd.clear ();
return;
}
cmd.push_back ("-d");
cmd.push_back (device);
#endif
}
}
int
EngineControl::setup_engine ()
{
vector<string> args;
std::string cwd = "/tmp";
build_command_line (args);
if (args.empty()) {
return 1; // try again
}
std::string jackdrc_path = Glib::get_home_dir();
jackdrc_path += "/.jackdrc";
ofstream jackdrc (jackdrc_path.c_str());
if (!jackdrc) {
error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
return -1;
}
for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
jackdrc << (*i) << ' ';
}
jackdrc << endl;
jackdrc.close ();
_used = true;
return 0;
}
void
EngineControl::enumerate_devices (const string& driver)
EngineControl::list_devices ()
{
/* note: case matters for the map keys */
boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
assert (backend);
if (driver == "CoreAudio") {
#ifdef __APPLE__
devices[driver] = enumerate_coreaudio_devices ();
#endif
/* now fill out devices, mark sample rates, buffer sizes insensitive */
#if !defined(__APPLE__) && !defined(__FreeBSD__)
} else if (driver == "ALSA") {
devices[driver] = enumerate_alsa_devices ();
} else if (driver == "FreeBOB") {
devices[driver] = enumerate_freebob_devices ();
} else if (driver == "FFADO") {
devices[driver] = enumerate_ffado_devices ();
} else if (driver == "OSS") {
devices[driver] = enumerate_oss_devices ();
} else if (driver == "Dummy") {
devices[driver] = enumerate_dummy_devices ();
} else if (driver == "NetJACK") {
devices[driver] = enumerate_netjack_devices ();
}
#else
}
#endif
vector<string> devices = backend->enumerate_devices ();
set_popdown_strings (interface_combo, devices);
interface_combo.set_active_text (devices.front());
set_popdown_strings (input_device_combo, devices);
input_device_combo.set_active_text (devices.front());
set_popdown_strings (output_device_combo, devices);
output_device_combo.set_active_text (devices.front());
interface_changed ();
}
#ifdef __APPLE__
static OSStatus
getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
{
UInt32 size = sizeof(CFStringRef);
CFStringRef UI;
OSStatus res = AudioDeviceGetProperty(id, 0, false,
kAudioDevicePropertyDeviceUID, &size, &UI);
if (res == noErr)
CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
CFRelease(UI);
return res;
}
vector<string>
EngineControl::enumerate_coreaudio_devices ()
{
vector<string> devs;
// Find out how many Core Audio devices are there, if any...
// (code snippet gently "borrowed" from St?hane Letz jackdmp;)
OSStatus err;
Boolean isWritable;
UInt32 outSize = sizeof(isWritable);
backend_devs.clear ();
err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
&outSize, &isWritable);
if (err == noErr) {
// Calculate the number of device available...
int numCoreDevices = outSize / sizeof(AudioDeviceID);
// Make space for the devices we are about to get...
AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
&outSize, (void *) coreDeviceIDs);
if (err == noErr) {
// Look for the CoreAudio device name...
char coreDeviceName[256];
UInt32 nameSize;
for (int i = 0; i < numCoreDevices; i++) {
nameSize = sizeof (coreDeviceName);
/* enforce duplex devices only */
err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
0, true, kAudioDevicePropertyStreams,
&outSize, &isWritable);
if (err != noErr || outSize == 0) {
continue;
}
err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
0, false, kAudioDevicePropertyStreams,
&outSize, &isWritable);
if (err != noErr || outSize == 0) {
continue;
}
err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
0, true, kAudioDevicePropertyDeviceName,
&outSize, &isWritable);
if (err == noErr) {
err = AudioDeviceGetProperty(coreDeviceIDs[i],
0, true, kAudioDevicePropertyDeviceName,
&nameSize, (void *) coreDeviceName);
if (err == noErr) {
char drivername[128];
// this returns the unique id for the device
// that must be used on the commandline for jack
if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
devs.push_back (coreDeviceName);
backend_devs.push_back (drivername);
}
}
}
}
}
delete [] coreDeviceIDs;
}
if (devs.size() == 0) {
MessageDialog msg (string_compose (_("\
You do not have any audio devices capable of\n\
simultaneous playback and recording.\n\n\
Please use Applications -> Utilities -> Audio MIDI Setup\n\
to create an \"aggregrate\" device, or install a suitable\n\
audio interface.\n\n\
Please send email to Apple and ask them why new Macs\n\
have no duplex audio device.\n\n\
Alternatively, if you really want just playback\n\
or recording but not both, start JACK before running\n\
%1 and choose the relevant device then."
), PROGRAM_NAME),
true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
msg.set_title (_("No suitable audio devices"));
msg.set_position (Gtk::WIN_POS_MOUSE);
msg.run ();
exit (1);
}
return devs;
}
#else
#if !defined(__FreeBSD__)
vector<string>
EngineControl::enumerate_alsa_devices ()
{
vector<string> devs;
snd_ctl_t *handle;
snd_ctl_card_info_t *info;
snd_pcm_info_t *pcminfo;
snd_ctl_card_info_alloca(&info);
snd_pcm_info_alloca(&pcminfo);
string devname;
int cardnum = -1;
int device = -1;
backend_devs.clear ();
while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
devname = "hw:";
devname += to_string (cardnum, std::dec);
if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
snd_pcm_info_set_device (pcminfo, device);
snd_pcm_info_set_subdevice (pcminfo, 0);
snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
devs.push_back (snd_pcm_info_get_name (pcminfo));
devname += ',';
devname += to_string (device, std::dec);
backend_devs.push_back (devname);
}
}
snd_ctl_close(handle);
}
}
return devs;
}
#endif
vector<string>
EngineControl::enumerate_ffado_devices ()
{
vector<string> devs;
backend_devs.clear ();
return devs;
}
vector<string>
EngineControl::enumerate_freebob_devices ()
{
vector<string> devs;
return devs;
}
vector<string>
EngineControl::enumerate_oss_devices ()
{
vector<string> devs;
return devs;
}
vector<string>
EngineControl::enumerate_dummy_devices ()
{
vector<string> devs;
return devs;
}
vector<string>
EngineControl::enumerate_netjack_devices ()
{
vector<string> devs;
return devs;
}
#endif
void
EngineControl::driver_changed ()
{
string driver = driver_combo.get_active_text();
string::size_type maxlen = 0;
int n = 0;
boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
assert (backend);
enumerate_devices (driver);
backend->set_driver (driver_combo.get_active_text());
list_devices ();
}
vector<string>& strings = devices[driver];
void
EngineControl::interface_changed ()
{
boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
assert (backend);
string device_name = interface_combo.get_active_text ();
vector<string> s;
if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
return;
}
/* sample rates */
for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
if ((*i).length() > maxlen) {
maxlen = (*i).length();
vector<float> sr = backend->available_sample_rates (device_name);
for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
char buf[32];
if (fmod (*x, 1000.0f)) {
snprintf (buf, sizeof (buf), "%.1f kHz", (*x)/1000.0);
} else {
snprintf (buf, sizeof (buf), "%.0f kHz", (*x)/1000.0);
}
s.push_back (buf);
}
set_popdown_strings (interface_combo, strings);
set_popdown_strings (input_device_combo, strings);
set_popdown_strings (output_device_combo, strings);
set_popdown_strings (sample_rate_combo, s);
sample_rate_combo.set_active_text (s.front());
if (!strings.empty()) {
interface_combo.set_active_text (strings.front());
input_device_combo.set_active_text (strings.front());
output_device_combo.set_active_text (strings.front());
/* buffer sizes */
s.clear ();
vector<uint32_t> bs = backend->available_buffer_sizes(device_name);
for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
char buf[32];
snprintf (buf, sizeof (buf), "%u", *x);
s.push_back (buf);
}
if (driver == "ALSA") {
soft_mode_button.set_sensitive (true);
force16bit_button.set_sensitive (true);
hw_monitor_button.set_sensitive (true);
hw_meter_button.set_sensitive (true);
monitor_button.set_sensitive (true);
} else {
soft_mode_button.set_sensitive (false);
force16bit_button.set_sensitive (false);
hw_monitor_button.set_sensitive (false);
hw_meter_button.set_sensitive (false);
monitor_button.set_sensitive (false);
}
set_popdown_strings (buffer_size_combo, s);
buffer_size_combo.set_active_text (s.front());
}
uint32_t
@ -912,15 +372,10 @@ void
EngineControl::redisplay_latency ()
{
uint32_t rate = get_rate();
#if defined(__APPLE__) || defined(__FreeBSD__)
float periods = 2;
#else
float periods = periods_adjustment.get_value();
#endif
float period_size = atof (period_size_combo.get_active_text());
float period_size = atof (buffer_size_combo.get_active_text());
char buf[32];
snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
snprintf (buf, sizeof(buf), "%.1fmsec", (2 * period_size) / (rate/1000.0));
latency_label.set_text (buf);
latency_label.set_alignment (0, 0.5);
@ -946,127 +401,6 @@ EngineControl::audio_mode_changed ()
}
}
static bool jack_server_filter(const string& str, void */*arg*/)
{
return str == "jackd" || str == "jackdmp";
}
void
EngineControl::find_jack_servers (vector<string>& strings)
{
#ifdef __APPLE__
/* this magic lets us finds the path to the OSX bundle, and then
we infer JACK's location from there
*/
char execpath[MAXPATHLEN+1];
uint32_t pathsz = sizeof (execpath);
_NSGetExecutablePath (execpath, &pathsz);
string path (Glib::path_get_dirname (execpath));
path += "/jackd";
if (Glib::file_test (path, FILE_TEST_EXISTS)) {
strings.push_back (path);
}
if (getenv ("ARDOUR_WITH_JACK")) {
/* no other options - only use the JACK we supply */
if (strings.empty()) {
fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
/*NOTREACHED*/
}
return;
}
#else
string path;
#endif
PathScanner scanner;
vector<string *> *jack_servers;
std::map<string,int> un;
char *p;
bool need_minimal_path = false;
p = getenv ("PATH");
if (p && *p) {
path = p;
} else {
need_minimal_path = true;
}
#ifdef __APPLE__
// many mac users don't have PATH set up to include
// likely installed locations of JACK
need_minimal_path = true;
#endif
if (need_minimal_path) {
if (path.empty()) {
path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
} else {
path += ":/usr/local/bin:/opt/local/bin";
}
}
#ifdef __APPLE__
// push it back into the environment so that auto-started JACK can find it.
// XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
setenv ("PATH", path.c_str(), 1);
#endif
jack_servers = scanner (path, jack_server_filter, 0, false, true);
if (!jack_servers) {
return;
}
vector<string *>::iterator iter;
for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
string p = **iter;
if (un[p]++ == 0) {
strings.push_back(p);
}
}
}
string
EngineControl::get_device_name (const string& driver, const string& human_readable)
{
vector<string>::iterator n;
vector<string>::iterator i;
if (human_readable.empty()) {
/* this can happen if the user's .ardourrc file has a device name from
another computer system in it
*/
MessageDialog msg (_("You need to choose an audio device first."));
msg.set_position (WIN_POS_MOUSE);
msg.run ();
return string();
}
if (backend_devs.empty()) {
return human_readable;
}
for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
if (human_readable == (*i)) {
return (*n);
}
}
if (i == devices[driver].end()) {
warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
}
return string();
}
XMLNode&
EngineControl::get_state ()
{
@ -1074,6 +408,7 @@ EngineControl::get_state ()
XMLNode* child;
std::string path;
#if 0
child = new XMLNode ("periods");
child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
root->add_child_nocopy (*child);
@ -1165,7 +500,7 @@ EngineControl::get_state ()
child = new XMLNode ("mididriver");
child->add_property ("val", midi_driver_combo.get_active_text());
root->add_child_nocopy (*child);
#endif
return *root;
}
@ -1181,7 +516,7 @@ EngineControl::set_state (const XMLNode& root)
int val;
string strval;
#if 0
if ( (child = root.child ("driver"))){
prop = child->property("val");
@ -1316,4 +651,10 @@ EngineControl::set_state (const XMLNode& root)
midi_driver_combo.set_active_text(strval);
}
}
#endif
}
int
EngineControl::setup_engine ()
{
}

View file

@ -47,80 +47,67 @@ class EngineControl : public Gtk::VBox {
void set_state (const XMLNode&);
private:
Gtk::Adjustment periods_adjustment;
Gtk::SpinButton periods_spinner;
Gtk::Adjustment ports_adjustment;
Gtk::SpinButton ports_spinner;
Gtk::Adjustment input_latency_adjustment;
Gtk::SpinButton input_latency;
Gtk::Adjustment output_latency_adjustment;
Gtk::SpinButton output_latency;
Gtk::Label latency_label;
/* core fields used by all backends */
Gtk::CheckButton realtime_button;
Gtk::CheckButton no_memory_lock_button;
Gtk::CheckButton unlock_memory_button;
Gtk::CheckButton soft_mode_button;
Gtk::CheckButton monitor_button;
Gtk::CheckButton force16bit_button;
Gtk::CheckButton hw_monitor_button;
Gtk::CheckButton hw_meter_button;
Gtk::CheckButton verbose_output_button;
Gtk::ComboBoxText backend_combo;
Gtk::ComboBoxText input_device_combo;
Gtk::ComboBoxText output_device_combo;
Gtk::ComboBoxText sample_rate_combo;
Gtk::ComboBoxText buffer_size_combo;
Gtk::Adjustment input_latency_adjustment;
Gtk::SpinButton input_latency;
Gtk::Adjustment output_latency_adjustment;
Gtk::SpinButton output_latency;
Gtk::Adjustment input_channels_adjustment;
Gtk::SpinButton input_channels;
Gtk::Adjustment output_channels_adjustment;
Gtk::SpinButton output_channels;
Gtk::Adjustment ports_adjustment;
Gtk::SpinButton ports_spinner;
Gtk::Label latency_label;
Gtk::Button start_button;
Gtk::Button stop_button;
Gtk::HButtonBox button_box;
/* JACK specific */
Gtk::ComboBoxText sample_rate_combo;
Gtk::ComboBoxText period_size_combo;
Gtk::CheckButton realtime_button;
Gtk::CheckButton no_memory_lock_button;
Gtk::CheckButton unlock_memory_button;
Gtk::CheckButton soft_mode_button;
Gtk::CheckButton monitor_button;
Gtk::CheckButton force16bit_button;
Gtk::CheckButton hw_monitor_button;
Gtk::CheckButton hw_meter_button;
Gtk::CheckButton verbose_output_button;
Gtk::ComboBoxText preset_combo;
Gtk::ComboBoxText serverpath_combo;
Gtk::ComboBoxText driver_combo;
Gtk::ComboBoxText interface_combo;
Gtk::ComboBoxText timeout_combo;
Gtk::ComboBoxText dither_mode_combo;
Gtk::ComboBoxText audio_mode_combo;
Gtk::ComboBoxText input_device_combo;
Gtk::ComboBoxText output_device_combo;
Gtk::ComboBoxText midi_driver_combo;
Gtk::Table basic_packer;
Gtk::Table options_packer;
Gtk::Table device_packer;
Gtk::HBox basic_hbox;
Gtk::HBox options_hbox;
Gtk::HBox device_hbox;
Gtk::Notebook notebook;
Gtk::ComboBoxText preset_combo;
Gtk::ComboBoxText serverpath_combo;
Gtk::ComboBoxText driver_combo;
Gtk::ComboBoxText interface_combo;
Gtk::ComboBoxText timeout_combo;
Gtk::ComboBoxText dither_mode_combo;
Gtk::ComboBoxText audio_mode_combo;
Gtk::ComboBoxText midi_driver_combo;
bool _used;
Gtk::Table basic_packer;
Gtk::Table options_packer;
Gtk::Table device_packer;
Gtk::HBox basic_hbox;
Gtk::HBox options_hbox;
Gtk::HBox device_hbox;
Gtk::Notebook notebook;
static bool engine_running ();
bool _used;
void driver_changed ();
void build_command_line (std::vector<std::string>&);
static bool engine_running ();
std::map<std::string,std::vector<std::string> > devices;
std::vector<std::string> backend_devs;
void enumerate_devices (const std::string& driver);
void driver_changed ();
void backend_changed ();
#ifdef __APPLE__
std::vector<std::string> enumerate_coreaudio_devices ();
#else
std::vector<std::string> enumerate_alsa_devices ();
std::vector<std::string> enumerate_oss_devices ();
std::vector<std::string> enumerate_netjack_devices ();
std::vector<std::string> enumerate_freebob_devices ();
std::vector<std::string> enumerate_ffado_devices ();
std::vector<std::string> enumerate_dummy_devices ();
#endif
void redisplay_latency ();
uint32_t get_rate();
void audio_mode_changed ();
std::vector<std::string> server_strings;
void find_jack_servers (std::vector<std::string>&);
std::string get_device_name (const std::string& driver, const std::string& human_readable_name);
void redisplay_latency ();
uint32_t get_rate();
void audio_mode_changed ();
void interface_changed ();
void list_devices ();
};
#endif /* __gtk2_ardour_engine_dialog_h__ */

View file

@ -68,6 +68,33 @@ class AudioBackend {
/* Discovering devices and parameters */
/** Return true if this backend requires the selection of a "driver"
* before any device can be selected. Return false otherwise.
*
* Intended mainly to differentiate between meta-APIs like JACK
* which can still expose different backends (such as ALSA or CoreAudio
* or FFADO or netjack) and those like ASIO or CoreAudio which
* do not.
*/
virtual bool requires_driver_selection() const { return false; }
/** If the return value of requires_driver_selection() is true,
* then this function can return the list of known driver names.
*
* If the return value of requires_driver_selection() is false,
* then this function should not be called. If it is called
* its return value is an empty vector of strings.
*/
virtual std::vector<std::string> enumerate_drivers() const { return std::vector<std::string>(); }
/** Returns zero if the backend can successfully use @param name as the
* driver, non-zero otherwise.
*
* Should not be used unless the backend returns true from
* requires_driver_selection()
*/
virtual int set_driver (const std::string& /*drivername*/) { return 0; }
/** Returns a collection of strings identifying devices known
* to this backend. Any of these strings may be used to identify a
* device in other calls to the backend, though any of them may become
@ -358,8 +385,8 @@ struct AudioBackendInfo {
* configured and does not need (re)configuration in order
* to be usable. Return false otherwise.
*
* Note that this may return true if (re)configuration is possible,
* but not required.
* Note that this may return true if (re)configuration, even though
* not currently required, is still possible.
*/
bool (*already_configured)();
};

View file

@ -74,7 +74,8 @@ public:
int discover_backends();
std::vector<const AudioBackendInfo*> available_backends() const;
std::string current_backend_name () const;
int set_backend (const std::string&, const std::string& arg1, const std::string& arg2);
boost::shared_ptr<AudioBackend> set_backend (const std::string&, const std::string& arg1, const std::string& arg2);
boost::shared_ptr<AudioBackend> current_backend() const { return _backend; }
bool setup_required () const;
ProcessThread* main_thread() const { return _main_thread; }
@ -172,13 +173,6 @@ public:
PBD::Signal0<void> Running;
PBD::Signal0<void> Stopped;
/* these two are emitted as we create backends that
can actually be used to do stuff (e.g. register ports)
*/
PBD::Signal0<void> BackendAvailable;
PBD::Signal0<void> BackendRemoved;
static AudioEngine* instance() { return _instance; }
static void destroy();
void died ();

View file

@ -49,7 +49,12 @@ class JACKAudioBackend : public AudioBackend {
bool connected() const;
bool is_realtime () const;
bool requires_driver_selection() const;
std::vector<std::string> enumerate_drivers () const;
int set_driver (const std::string&);
std::vector<std::string> enumerate_devices () const;
std::vector<float> available_sample_rates (const std::string& device) const;
std::vector<uint32_t> available_buffer_sizes (const std::string& device) const;
uint32_t available_input_channel_count (const std::string& device) const;
@ -151,6 +156,7 @@ class JACKAudioBackend : public AudioBackend {
/* pffooo */
std::string _target_driver;
std::string _target_device;
float _target_sample_rate;
uint32_t _target_buffer_size;

View file

@ -561,18 +561,16 @@ AudioEngine::drop_backend ()
if (_backend) {
_backend->stop ();
_backend.reset ();
BackendRemoved(); /* EMIT SIGNAL */
}
}
int
boost::shared_ptr<AudioBackend>
AudioEngine::set_backend (const std::string& name, const std::string& arg1, const std::string& arg2)
{
BackendMap::iterator b = _backends.find (name);
if (b == _backends.end()) {
return -1;
return boost::shared_ptr<AudioBackend>();
}
drop_backend ();
@ -590,12 +588,10 @@ AudioEngine::set_backend (const std::string& name, const std::string& arg1, cons
} catch (exception& e) {
error << string_compose (_("Could not create backend for %1: %2"), name, e.what()) << endmsg;
return -1;
return boost::shared_ptr<AudioBackend>();
}
BackendAvailable (); /* EMIT SIGNAL */
return 0;
return _backend;
}
/* BACKEND PROXY WRAPPERS */

View file

@ -90,16 +90,13 @@ extern "C" {
* must be non-mangled.
*/
using namespace ARDOUR;
AudioBackendInfo descriptor = {
ARDOUR::AudioBackendInfo descriptor = {
"JACK",
instantiate,
deinstantiate,
backend_factory,
portengine_factory,
already_configured
already_configured,
};
}

View file

@ -88,11 +88,31 @@ JACKAudioBackend::is_realtime () const
return jack_is_realtime (_priv_jack);
}
bool
JACKAudioBackend::requires_driver_selection() const
{
return true;
}
vector<string>
JACKAudioBackend::enumerate_drivers () const
{
vector<string> s;
get_jack_audio_driver_names (s);
return s;
}
int
JACKAudioBackend::set_driver (const std::string& name)
{
_target_driver = name;
return 0;
}
vector<string>
JACKAudioBackend::enumerate_devices () const
{
vector<string> devices;
return devices;
return get_jack_device_names_for_audio_driver (_target_driver);
}
vector<float>