coreaudio support for multiple devices

This commit is contained in:
Robin Gareus 2015-05-05 17:28:31 +02:00
parent 4d2bc612f2
commit 1fe738a36e
4 changed files with 134 additions and 23 deletions

View file

@ -38,7 +38,9 @@ using namespace ARDOUR;
static std::string s_instance_name; static std::string s_instance_name;
size_t CoreAudioBackend::_max_buffer_size = 8192; size_t CoreAudioBackend::_max_buffer_size = 8192;
std::vector<std::string> CoreAudioBackend::_midi_options; std::vector<std::string> CoreAudioBackend::_midi_options;
std::vector<AudioBackend::DeviceStatus> CoreAudioBackend::_audio_device_status; std::vector<AudioBackend::DeviceStatus> CoreAudioBackend::_duplex_audio_device_status;
std::vector<AudioBackend::DeviceStatus> CoreAudioBackend::_input_audio_device_status;
std::vector<AudioBackend::DeviceStatus> CoreAudioBackend::_output_audio_device_status;
/* static class instance access */ /* static class instance access */
@ -90,7 +92,8 @@ CoreAudioBackend::CoreAudioBackend (AudioEngine& e, AudioBackendInfo& info)
, _reinit_thread_callback (false) , _reinit_thread_callback (false)
, _measure_latency (false) , _measure_latency (false)
, _last_process_start (0) , _last_process_start (0)
, _audio_device("") , _input_audio_device("")
, _output_audio_device("")
, _midi_driver_option(_("None")) , _midi_driver_option(_("None"))
, _samplerate (48000) , _samplerate (48000)
, _samples_per_period (1024) , _samples_per_period (1024)
@ -142,30 +145,67 @@ CoreAudioBackend::is_realtime () const
std::vector<AudioBackend::DeviceStatus> std::vector<AudioBackend::DeviceStatus>
CoreAudioBackend::enumerate_devices () const CoreAudioBackend::enumerate_devices () const
{ {
_audio_device_status.clear(); _duplex_audio_device_status.clear();
std::map<size_t, std::string> devices; std::map<size_t, std::string> devices;
_pcmio->device_list(devices); _pcmio->duplex_device_list(devices);
for (std::map<size_t, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) { for (std::map<size_t, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
if (_audio_device == "") _audio_device = i->second; if (_input_audio_device == "") _input_audio_device = i->second;
_audio_device_status.push_back (DeviceStatus (i->second, true)); if (_output_audio_device == "") _output_audio_device = i->second;
_duplex_audio_device_status.push_back (DeviceStatus (i->second, true));
} }
return _audio_device_status; return _duplex_audio_device_status;
}
std::vector<AudioBackend::DeviceStatus>
CoreAudioBackend::enumerate_input_devices () const
{
_input_audio_device_status.clear();
std::map<size_t, std::string> devices;
_pcmio->input_device_list(devices);
for (std::map<size_t, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
if (_input_audio_device == "") _input_audio_device = i->second;
_input_audio_device_status.push_back (DeviceStatus (i->second, true));
}
return _input_audio_device_status;
}
std::vector<AudioBackend::DeviceStatus>
CoreAudioBackend::enumerate_output_devices () const
{
_output_audio_device_status.clear();
std::map<size_t, std::string> devices;
_pcmio->output_device_list(devices);
for (std::map<size_t, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
if (_output_audio_device == "") _output_audio_device = i->second;
_output_audio_device_status.push_back (DeviceStatus (i->second, true));
}
return _output_audio_device_status;
} }
std::vector<float> std::vector<float>
CoreAudioBackend::available_sample_rates (const std::string&) const CoreAudioBackend::available_sample_rates (const std::string&) const
{ {
std::vector<float> sr; std::vector<float> sr;
_pcmio->available_sample_rates(name_to_id(_audio_device), sr); std::vector<float> sr_in;
std::vector<float> sr_out;
_pcmio->available_sample_rates(name_to_id(_input_audio_device), sr_in);
_pcmio->available_sample_rates(name_to_id(_output_audio_device), sr_out);
// TODO allow to use different SR per device, tweak aggregate
std::set_intersection(sr_in.begin(), sr_in.end(), sr_out.begin(), sr_out.end(), std::back_inserter(sr));
return sr; return sr;
} }
std::vector<uint32_t> std::vector<uint32_t>
CoreAudioBackend::available_buffer_sizes (const std::string&) const CoreAudioBackend::available_buffer_sizes (const std::string& device) const
{ {
std::vector<uint32_t> bs; std::vector<uint32_t> bs;
_pcmio->available_buffer_sizes(name_to_id(_audio_device), bs); _pcmio->available_buffer_sizes(name_to_id(device), bs);
return bs; return bs;
} }
@ -196,8 +236,27 @@ CoreAudioBackend::can_change_buffer_size_when_running () const
int int
CoreAudioBackend::set_device_name (const std::string& d) CoreAudioBackend::set_device_name (const std::string& d)
{ {
_audio_device = d; int rv = 0;
const float sr = _pcmio->current_sample_rate(name_to_id(_audio_device)); rv |= set_input_device_name (d);
rv |= set_output_device_name (d);
return rv;
}
int
CoreAudioBackend::set_input_device_name (const std::string& d)
{
_input_audio_device = d;
const float sr = _pcmio->current_sample_rate(name_to_id(_input_audio_device));
if (sr > 0) { set_sample_rate(sr); }
return 0;
}
int
CoreAudioBackend::set_output_device_name (const std::string& d)
{
_output_audio_device = d;
// TODO check SR.
const float sr = _pcmio->current_sample_rate(name_to_id(_output_audio_device));
if (sr > 0) { set_sample_rate(sr); } if (sr > 0) { set_sample_rate(sr); }
return 0; return 0;
} }
@ -205,8 +264,10 @@ CoreAudioBackend::set_device_name (const std::string& d)
int int
CoreAudioBackend::set_sample_rate (float sr) CoreAudioBackend::set_sample_rate (float sr)
{ {
if (sr <= 0) { return -1; } std::vector<float> srs = available_sample_rates (/* really ignored */_input_audio_device);
// TODO check if it's in the list of valid SR if (std::find(srs.begin(), srs.end(), sr) == srs.end()) {
return -1;
}
_samplerate = sr; _samplerate = sr;
engine.sample_rate_change (sr); engine.sample_rate_change (sr);
return 0; return 0;
@ -263,7 +324,19 @@ CoreAudioBackend::set_systemic_output_latency (uint32_t sl)
std::string std::string
CoreAudioBackend::device_name () const CoreAudioBackend::device_name () const
{ {
return _audio_device; return "";
}
std::string
CoreAudioBackend::input_device_name () const
{
return _input_audio_device;
}
std::string
CoreAudioBackend::output_device_name () const
{
return _output_audio_device;
} }
float float
@ -339,7 +412,7 @@ CoreAudioBackend::midi_option () const
void void
CoreAudioBackend::launch_control_app () CoreAudioBackend::launch_control_app ()
{ {
_pcmio->launch_control_app(name_to_id(_audio_device)); _pcmio->launch_control_app(name_to_id(_input_audio_device));
} }
/* State Control */ /* State Control */
@ -380,8 +453,8 @@ CoreAudioBackend::_start (bool for_latency_measurement)
_ports.clear(); _ports.clear();
} }
uint32_t device1 = name_to_id(_audio_device); // usually input, but in an aggregate, 1st defines the clock uint32_t device1 = name_to_id(_input_audio_device);
uint32_t device2 = name_to_id(_audio_device); // usually output uint32_t device2 = name_to_id(_output_audio_device);
assert(_active_ca == false); assert(_active_ca == false);
assert(_active_fw == false); assert(_active_fw == false);
@ -395,6 +468,7 @@ CoreAudioBackend::_start (bool for_latency_measurement)
_pcmio->set_sample_rate_callback (sample_rate_callback_ptr, this); _pcmio->set_sample_rate_callback (sample_rate_callback_ptr, this);
_pcmio->pcm_start (device1, device2, _samplerate, _samples_per_period, process_callback_ptr, this); _pcmio->pcm_start (device1, device2, _samplerate, _samples_per_period, process_callback_ptr, this);
printf("STATE: %d\n", _pcmio->state ());
switch (_pcmio->state ()) { switch (_pcmio->state ()) {
case 0: /* OK */ break; case 0: /* OK */ break;
@ -867,8 +941,8 @@ CoreAudioBackend::register_system_audio_ports()
const uint32_t a_ins = _n_inputs; const uint32_t a_ins = _n_inputs;
const uint32_t a_out = _n_outputs; const uint32_t a_out = _n_outputs;
const uint32_t coreaudio_reported_input_latency = _pcmio->get_latency(name_to_id(_audio_device), true); const uint32_t coreaudio_reported_input_latency = _pcmio->get_latency(name_to_id(_input_audio_device), true);
const uint32_t coreaudio_reported_output_latency = _pcmio->get_latency(name_to_id(_audio_device), false); const uint32_t coreaudio_reported_output_latency = _pcmio->get_latency(name_to_id(_output_audio_device), false);
#ifndef NDEBUG #ifndef NDEBUG
printf("COREAUDIO LATENCY: i:%d, o:%d\n", printf("COREAUDIO LATENCY: i:%d, o:%d\n",

View file

@ -165,7 +165,11 @@ class CoreAudioBackend : public AudioBackend {
std::string name () const; std::string name () const;
bool is_realtime () const; bool is_realtime () const;
bool use_separate_input_and_output_devices () const { return true; }
std::vector<DeviceStatus> enumerate_devices () const; std::vector<DeviceStatus> enumerate_devices () const;
std::vector<DeviceStatus> enumerate_input_devices () const;
std::vector<DeviceStatus> enumerate_output_devices () const;
std::vector<float> available_sample_rates (const std::string& device) const; std::vector<float> available_sample_rates (const std::string& device) const;
std::vector<uint32_t> available_buffer_sizes (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; uint32_t available_input_channel_count (const std::string& device) const;
@ -175,6 +179,8 @@ class CoreAudioBackend : public AudioBackend {
bool can_change_buffer_size_when_running () const; bool can_change_buffer_size_when_running () const;
int set_device_name (const std::string&); int set_device_name (const std::string&);
int set_input_device_name (const std::string&);
int set_output_device_name (const std::string&);
int set_sample_rate (float); int set_sample_rate (float);
int set_buffer_size (uint32_t); int set_buffer_size (uint32_t);
int set_interleaved (bool yn); int set_interleaved (bool yn);
@ -189,6 +195,8 @@ class CoreAudioBackend : public AudioBackend {
/* Retrieving parameters */ /* Retrieving parameters */
std::string device_name () const; std::string device_name () const;
std::string input_device_name () const;
std::string output_device_name () const;
float sample_rate () const; float sample_rate () const;
uint32_t buffer_size () const; uint32_t buffer_size () const;
bool interleaved () const; bool interleaved () const;
@ -336,10 +344,13 @@ class CoreAudioBackend : public AudioBackend {
pthread_cond_t _freewheel_signal; pthread_cond_t _freewheel_signal;
static std::vector<std::string> _midi_options; static std::vector<std::string> _midi_options;
static std::vector<AudioBackend::DeviceStatus> _audio_device_status; static std::vector<AudioBackend::DeviceStatus> _input_audio_device_status;
static std::vector<AudioBackend::DeviceStatus> _output_audio_device_status;
static std::vector<AudioBackend::DeviceStatus> _duplex_audio_device_status;
static std::vector<AudioBackend::DeviceStatus> _midi_device_status; static std::vector<AudioBackend::DeviceStatus> _midi_device_status;
mutable std::string _audio_device; mutable std::string _input_audio_device;
mutable std::string _output_audio_device;
std::string _midi_driver_option; std::string _midi_driver_option;
/* audio settings */ /* audio settings */

View file

@ -479,6 +479,15 @@ CoreAudioPCM::get_latency(uint32_t device_id, bool input)
return latency; return latency;
} }
uint32_t
CoreAudioPCM::get_latency(bool input)
{
if (_active_device_id == 0) {
return 0;
}
return get_latency (_active_device_id, input);
}
uint32_t uint32_t
CoreAudioPCM::current_buffer_size_id(AudioDeviceID id) { CoreAudioPCM::current_buffer_size_id(AudioDeviceID id) {
UInt32 buffer_size; UInt32 buffer_size;
@ -629,6 +638,15 @@ CoreAudioPCM::discover()
if (outputChannelCount > 0 || inputChannelCount > 0) { if (outputChannelCount > 0 || inputChannelCount > 0) {
_devices.insert (std::pair<size_t, std::string> (idx, dn)); _devices.insert (std::pair<size_t, std::string> (idx, dn));
} }
if (inputChannelCount > 0) {
_input_devices.insert (std::pair<size_t, std::string> (idx, dn));
}
if (outputChannelCount > 0) {
_output_devices.insert (std::pair<size_t, std::string> (idx, dn));
}
if (outputChannelCount > 0 && inputChannelCount > 0) {
_duplex_devices.insert (std::pair<size_t, std::string> (idx, dn));
}
} }
} }
pthread_mutex_unlock (&_discovery_lock); pthread_mutex_unlock (&_discovery_lock);
@ -695,6 +713,7 @@ CoreAudioPCM::pcm_stop ()
} }
if (_aggregate_plugin_id) { if (_aggregate_plugin_id) {
destroy_aggregate_device(); destroy_aggregate_device();
discover();
} }
AudioUnitUninitialize(_auhal); AudioUnitUninitialize(_auhal);
@ -811,7 +830,7 @@ CoreAudioPCM::pcm_start (
err = AudioUnitInitialize(_auhal); err = AudioUnitInitialize(_auhal);
if (err != noErr) { errorMsg="AudioUnitInitialize"; goto error; } if (err != noErr) { errorMsg="AudioUnitInitialize"; goto error; }
// explicitly change samplerate of the device // explicitly change samplerate of the devices, TODO allow separate rates with aggregates
if (set_device_sample_rate(device_id_in, sample_rate, true)) { if (set_device_sample_rate(device_id_in, sample_rate, true)) {
errorMsg="Failed to set SampleRate, Capture Device"; goto error; errorMsg="Failed to set SampleRate, Capture Device"; goto error;
} }

View file

@ -48,12 +48,16 @@ public:
void discover(); void discover();
void device_list (std::map<size_t, std::string> &devices) const { devices = _devices;} void device_list (std::map<size_t, std::string> &devices) const { devices = _devices;}
void input_device_list (std::map<size_t, std::string> &devices) const { devices = _input_devices;}
void output_device_list (std::map<size_t, std::string> &devices) const { devices = _output_devices;}
void duplex_device_list (std::map<size_t, std::string> &devices) const { devices = _duplex_devices;}
int available_sample_rates (uint32_t device_id, std::vector<float>& sampleRates); int available_sample_rates (uint32_t device_id, std::vector<float>& sampleRates);
int available_buffer_sizes (uint32_t device_id, std::vector<uint32_t>& sampleRates); int available_buffer_sizes (uint32_t device_id, std::vector<uint32_t>& sampleRates);
uint32_t available_channels (uint32_t device_id, bool input); uint32_t available_channels (uint32_t device_id, bool input);
float current_sample_rate (uint32_t device_id, bool input = false); float current_sample_rate (uint32_t device_id, bool input = false);
uint32_t get_latency (uint32_t device_id, bool input); uint32_t get_latency (uint32_t device_id, bool input);
uint32_t get_latency (bool input);
std::string cached_port_name (uint32_t portnum, bool input) const; std::string cached_port_name (uint32_t portnum, bool input) const;
@ -186,6 +190,9 @@ private:
// TODO proper device info struct // TODO proper device info struct
std::map<size_t, std::string> _devices; std::map<size_t, std::string> _devices;
std::map<size_t, std::string> _input_devices;
std::map<size_t, std::string> _output_devices;
std::map<size_t, std::string> _duplex_devices;
uint32_t * _device_ins; uint32_t * _device_ins;
uint32_t * _device_outs; uint32_t * _device_outs;
std::vector<std::string> _input_names; std::vector<std::string> _input_names;