ALSA: allow to select different I/O devices
This adds a basic support to use multiple sound-cards, currently limited to two devices: In/Out with shared settings. Advanced setups still have to resort to using the ARDOUR_ALSA_EXT environment variable
This commit is contained in:
parent
e8efd45c05
commit
b943cec756
@ -782,10 +782,22 @@ AlsaAudioBackend::_start (bool for_latency_measurement)
|
||||
return AudioDeviceInvalidError;
|
||||
}
|
||||
|
||||
std::string slave_device;
|
||||
AudioSlave::DuplexMode slave_duplex = AudioSlave::FullDuplex;
|
||||
|
||||
if (_input_audio_device != _output_audio_device) {
|
||||
if (_input_audio_device != get_standard_device_name(DeviceNone) && _output_audio_device != get_standard_device_name(DeviceNone)) {
|
||||
PBD::error << _("AlsaAudioBackend: Cannot use two different devices.");
|
||||
return AudioDeviceInvalidError;
|
||||
#if 0 /* ideally we'd resample output ...*/
|
||||
slave_device = _output_audio_device;
|
||||
_output_audio_device = get_standard_device_name(DeviceNone);
|
||||
slave_duplex = AudioSlave::HalfDuplexOut;
|
||||
#else
|
||||
/*.. but input is usually a cheap USB device, and keeping
|
||||
* output does auto-connect master-out to the main device. */
|
||||
slave_device = _input_audio_device;
|
||||
_input_audio_device = get_standard_device_name(DeviceNone);
|
||||
slave_duplex = AudioSlave::HalfDuplexIn;
|
||||
#endif
|
||||
}
|
||||
if (_input_audio_device != get_standard_device_name(DeviceNone)) {
|
||||
get_alsa_audio_device_names(devices, HalfDuplexIn);
|
||||
@ -802,15 +814,14 @@ AlsaAudioBackend::_start (bool for_latency_measurement)
|
||||
duplex = 3;
|
||||
}
|
||||
|
||||
for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
|
||||
if (i->first == audio_device) {
|
||||
alsa_device = i->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (alsa_device == "") {
|
||||
std::map<std::string, std::string>::const_iterator di = devices.find (audio_device);
|
||||
|
||||
if (di == devices.end ()) {
|
||||
PBD::error << _("AlsaAudioBackend: Cannot find configured device. Is it still connected?");
|
||||
return AudioDeviceNotAvailableError;
|
||||
} else {
|
||||
alsa_device = di->second;
|
||||
assert (!alsa_device.empty());
|
||||
}
|
||||
|
||||
_device_reservation.acquire_device(alsa_device.c_str());
|
||||
@ -954,26 +965,43 @@ AlsaAudioBackend::_start (bool for_latency_measurement)
|
||||
|
||||
_midi_device_thread_active = listen_for_midi_device_changes ();
|
||||
|
||||
if (!slave_device.empty () && (di = devices.find (slave_device)) != devices.end ()) {
|
||||
std::string dev = di->second;
|
||||
if (add_slave (dev.c_str(), _samplerate, _samples_per_period, _periods_per_cycle, slave_duplex)) {
|
||||
PBD::info << string_compose (_("ALSA slave '%1' added"), dev) << endmsg;
|
||||
} else {
|
||||
PBD::error << string_compose (_("ALSA failed to add '%1' as slave"), dev) << endmsg;
|
||||
}
|
||||
}
|
||||
|
||||
#if 1 // TODO: we need a GUI (and API) for this
|
||||
/* example: ARDOUR_ALSA_EXT="hw:2@48000/512*3;hw:3@44100" */
|
||||
if (NULL != getenv ("ARDOUR_ALSA_EXT")) {
|
||||
boost::char_separator<char> sep (";");
|
||||
boost::tokenizer<boost::char_separator<char> > devs (std::string(getenv ("ARDOUR_ALSA_EXT")), sep);
|
||||
std::string ext (getenv ("ARDOUR_ALSA_EXT"));
|
||||
boost::tokenizer<boost::char_separator<char> > devs (ext, sep);
|
||||
BOOST_FOREACH (const std::string& tmp, devs) {
|
||||
std::string dev (tmp);
|
||||
std::string::size_type n = dev.find ('@');
|
||||
unsigned int sr = _samplerate;
|
||||
unsigned int spp = _samples_per_period;
|
||||
unsigned int duplex = 3; // TODO parse 1: play, 2: capt, 3:both
|
||||
unsigned int ppc = _periods_per_cycle;
|
||||
AudioSlave::DuplexMode duplex = AudioSlave::FullDuplex;
|
||||
std::string::size_type n = dev.find ('@');
|
||||
if (n != std::string::npos) {
|
||||
std::string opt (dev.substr (n + 1));
|
||||
std::string const opt (dev.substr (n + 1));
|
||||
sr = PBD::atoi (opt);
|
||||
dev = dev.substr (0, n);
|
||||
std::string::size_type n = opt.find ('/');
|
||||
if (n != std::string::npos) {
|
||||
spp = PBD::atoi (opt.substr (n + 1));
|
||||
std::string const opt2 (opt.substr (n + 1));
|
||||
spp = PBD::atoi (opt2);
|
||||
std::string::size_type n = opt2.find ('*');
|
||||
if (n != std::string::npos) {
|
||||
ppc = PBD::atoi (opt2.substr (n + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (add_slave (dev.c_str(), sr, spp, duplex)) {
|
||||
if (add_slave (dev.c_str(), sr, spp, ppc, duplex)) {
|
||||
PBD::info << string_compose (_("ALSA slave '%1' added"), dev) << endmsg;
|
||||
} else {
|
||||
PBD::error << string_compose (_("ALSA failed to add '%1' as slave"), dev) << endmsg;
|
||||
@ -2007,11 +2035,12 @@ bool
|
||||
AlsaAudioBackend::add_slave (const char* device,
|
||||
unsigned int slave_rate,
|
||||
unsigned int slave_spp,
|
||||
unsigned int duplex)
|
||||
unsigned int slave_ppc,
|
||||
AudioSlave::DuplexMode duplex)
|
||||
{
|
||||
AudioSlave* s = new AudioSlave (device, duplex,
|
||||
_samplerate, _samples_per_period,
|
||||
slave_rate, slave_spp, 2);
|
||||
slave_rate, slave_spp, slave_ppc);
|
||||
|
||||
if (s->state ()) {
|
||||
// TODO parse error status
|
||||
@ -2064,18 +2093,18 @@ errout:
|
||||
|
||||
AlsaAudioBackend::AudioSlave::AudioSlave (
|
||||
const char* device,
|
||||
unsigned int duplex,
|
||||
DuplexMode duplex,
|
||||
unsigned int master_rate,
|
||||
unsigned int master_samples_per_period,
|
||||
unsigned int slave_rate,
|
||||
unsigned int slave_samples_per_period,
|
||||
unsigned int periods_per_cycle)
|
||||
unsigned int slave_periods_per_cycle)
|
||||
: AlsaDeviceReservation (device)
|
||||
, AlsaAudioSlave (
|
||||
(duplex & 1) ? device : NULL /* playback */,
|
||||
(duplex & 2) ? device : NULL /* capture */,
|
||||
(duplex & HalfDuplexOut) ? device : NULL /* playback */,
|
||||
(duplex & HalfDuplexIn) ? device : NULL /* capture */,
|
||||
master_rate, master_samples_per_period,
|
||||
slave_rate, slave_samples_per_period, periods_per_cycle)
|
||||
slave_rate, slave_samples_per_period, slave_periods_per_cycle)
|
||||
, active (false)
|
||||
, halt (false)
|
||||
, dead (false)
|
||||
|
@ -130,7 +130,7 @@ class AlsaAudioBackend : public AudioBackend, public PortEngineSharedImpl
|
||||
bool is_realtime () const;
|
||||
|
||||
bool use_separate_input_and_output_devices () const { return true; }
|
||||
bool match_input_output_devices_or_none () const { return true; }
|
||||
bool match_input_output_devices_or_none () const { return false; }
|
||||
bool can_set_period_size () const { return true; }
|
||||
|
||||
std::vector<DeviceStatus> enumerate_devices () const;
|
||||
@ -399,17 +399,17 @@ class AlsaAudioBackend : public AudioBackend, public PortEngineSharedImpl
|
||||
void update_systemic_audio_latencies ();
|
||||
void update_systemic_midi_latencies ();
|
||||
|
||||
/* additional re-sampled I/O */
|
||||
bool add_slave (const char* slave_device,
|
||||
unsigned int slave_rate,
|
||||
unsigned int slave_spp,
|
||||
unsigned int duplex = 3);
|
||||
|
||||
class AudioSlave : public AlsaDeviceReservation, public AlsaAudioSlave {
|
||||
public:
|
||||
enum DuplexMode {
|
||||
HalfDuplexIn = 1,
|
||||
HalfDuplexOut = 2,
|
||||
FullDuplex = 3
|
||||
};
|
||||
|
||||
AudioSlave (
|
||||
const char* device,
|
||||
unsigned int duplex,
|
||||
DuplexMode duplex,
|
||||
unsigned int master_rate,
|
||||
unsigned int master_samples_per_period,
|
||||
unsigned int slave_rate,
|
||||
@ -436,6 +436,13 @@ class AlsaAudioBackend : public AudioBackend, public PortEngineSharedImpl
|
||||
void halted ();
|
||||
};
|
||||
|
||||
/* additional re-sampled I/O */
|
||||
bool add_slave (const char* slave_device,
|
||||
unsigned int slave_rate,
|
||||
unsigned int slave_spp,
|
||||
unsigned int slave_ppc,
|
||||
AudioSlave::DuplexMode);
|
||||
|
||||
typedef std::vector<AudioSlave*> AudioSlaves;
|
||||
AudioSlaves _slaves;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user