summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2020-04-10 04:39:58 +0200
committerRobin Gareus <robin@gareus.org>2020-04-10 05:01:33 +0200
commitb943cec756fefab7f840b39c7b9afb86b7388526 (patch)
tree4570a3d6acdb9d4dc52b3591dc38a254f9877f80
parente8efd45c0512cb07a6ac2658575f44c16cc60b69 (diff)
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
-rw-r--r--libs/backends/alsa/alsa_audiobackend.cc73
-rw-r--r--libs/backends/alsa/alsa_audiobackend.h23
2 files changed, 66 insertions, 30 deletions
diff --git a/libs/backends/alsa/alsa_audiobackend.cc b/libs/backends/alsa/alsa_audiobackend.cc
index 2efe2e8976..d8ceb7f796 100644
--- a/libs/backends/alsa/alsa_audiobackend.cc
+++ b/libs/backends/alsa/alsa_audiobackend.cc
@@ -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)
diff --git a/libs/backends/alsa/alsa_audiobackend.h b/libs/backends/alsa/alsa_audiobackend.h
index 49afd7e34e..02d61c6042 100644
--- a/libs/backends/alsa/alsa_audiobackend.h
+++ b/libs/backends/alsa/alsa_audiobackend.h
@@ -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;