diff options
author | Robin Gareus <robin@gareus.org> | 2015-03-07 04:15:02 +0100 |
---|---|---|
committer | Robin Gareus <robin@gareus.org> | 2015-03-07 14:35:32 +0100 |
commit | ce3adfd3d4c1a9e346ed9ecdc9e605346f483f98 (patch) | |
tree | 13d482217db123247046713aaed3d897a1790262 | |
parent | e99599c7db97d6a02e4fb261b91b12eb88599057 (diff) |
Hard Core Audio
* allow to change buffersizes
* subscribe to buffersize & samplerate changes
* add support for half-duplex devices.
* aggregate Devices (not yet used) code from JACK2
* unify deprecated API wrappers
* properly keep track of MIDI ports
* disable MidiI/O during freewheeling
* various small fixes & cleanup
-rw-r--r-- | libs/backends/coreaudio/coreaudio_backend.cc | 213 | ||||
-rw-r--r-- | libs/backends/coreaudio/coreaudio_backend.h | 12 | ||||
-rw-r--r-- | libs/backends/coreaudio/coreaudio_pcmio.cc | 777 | ||||
-rw-r--r-- | libs/backends/coreaudio/coreaudio_pcmio.h | 61 | ||||
-rw-r--r-- | libs/backends/coreaudio/coreaudio_pcmio_aggregate.cc | 358 | ||||
-rw-r--r-- | libs/backends/coreaudio/coremidi_io.cc | 124 | ||||
-rw-r--r-- | libs/backends/coreaudio/coremidi_io.h | 16 | ||||
-rw-r--r-- | libs/backends/coreaudio/wscript | 2 |
8 files changed, 1134 insertions, 429 deletions
diff --git a/libs/backends/coreaudio/coreaudio_backend.cc b/libs/backends/coreaudio/coreaudio_backend.cc index 31ee9636e1..5a46514094 100644 --- a/libs/backends/coreaudio/coreaudio_backend.cc +++ b/libs/backends/coreaudio/coreaudio_backend.cc @@ -61,6 +61,18 @@ static void xrun_callback_ptr (void *arg) d->xrun_callback(); } +static void buffer_size_callback_ptr (void *arg) +{ + CoreAudioBackend *d = static_cast<CoreAudioBackend*> (arg); + d->buffer_size_callback(); +} + +static void sample_rate_callback_ptr (void *arg) +{ + CoreAudioBackend *d = static_cast<CoreAudioBackend*> (arg); + d->sample_rate_callback(); +} + static void midi_port_change (void *arg) { CoreAudioBackend *d = static_cast<CoreAudioBackend *>(arg); @@ -99,7 +111,6 @@ CoreAudioBackend::CoreAudioBackend (AudioEngine& e, AudioBackendInfo& info) _pcmio->set_hw_changed_callback (hw_changed_callback_ptr, this); _pcmio->discover(); - _midiio->discover(); } CoreAudioBackend::~CoreAudioBackend () @@ -175,7 +186,7 @@ CoreAudioBackend::can_change_sample_rate_when_running () const bool CoreAudioBackend::can_change_buffer_size_when_running () const { - return false; + return true; } int @@ -204,6 +215,7 @@ CoreAudioBackend::set_buffer_size (uint32_t bs) return -1; } _samples_per_period = bs; + _pcmio->set_samples_per_period(bs); engine.buffer_size_change (bs); return 0; } @@ -428,7 +440,8 @@ CoreAudioBackend::_start (bool for_latency_measurement) _ports.clear(); } - uint32_t device_id = name_to_id(_audio_device); + uint32_t device1 = name_to_id(_audio_device); // usually input, but in an aggregate, 1st defines the clock + uint32_t device2 = name_to_id(_audio_device); // usually output assert(_active_ca == false); assert(_active_fw == false); @@ -437,7 +450,10 @@ CoreAudioBackend::_start (bool for_latency_measurement) _reinit_thread_callback = true; _pcmio->set_error_callback (error_callback_ptr, this); - _pcmio->pcm_start (device_id, device_id, _samplerate, _samples_per_period, process_callback_ptr, this); + _pcmio->set_buffer_size_callback (buffer_size_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); switch (_pcmio->state ()) { case 0: /* OK */ break; @@ -454,7 +470,7 @@ CoreAudioBackend::_start (bool for_latency_measurement) } else { _n_outputs = std::min (_n_outputs, _pcmio->n_playback_channels ()); } - PBD::warning << _("CoreAudioBackend: adjusted output channel count to match device.") << endmsg; + PBD::info << _("CoreAudioBackend: adjusted output channel count to match device.") << endmsg; } if (_n_inputs != _pcmio->n_capture_channels ()) { @@ -463,18 +479,16 @@ CoreAudioBackend::_start (bool for_latency_measurement) } else { _n_inputs = std::min (_n_inputs, _pcmio->n_capture_channels ()); } - PBD::warning << _("CoreAudioBackend: adjusted input channel count to match device.") << endmsg; + PBD::info << _("CoreAudioBackend: adjusted input channel count to match device.") << endmsg; } -#if 0 // TODO - if (_pcmio->sample_per_period() != _samples_per_period) { - _samples_per_period = _pcmio->sample_per_period(); + if (_pcmio->samples_per_period() != _samples_per_period) { + _samples_per_period = _pcmio->samples_per_period(); PBD::warning << _("CoreAudioBackend: samples per period does not match.") << endmsg; } -#endif - if (_pcmio->current_sample_rate(name_to_id(_audio_device)) != _samplerate) { - _samplerate = _pcmio->current_sample_rate(name_to_id(_audio_device)); + if (_pcmio->sample_rate() != _samplerate) { + _samplerate = _pcmio->sample_rate(); engine.sample_rate_change (_samplerate); PBD::warning << _("CoreAudioBackend: sample rate does not match.") << endmsg; } @@ -486,8 +500,9 @@ CoreAudioBackend::_start (bool for_latency_measurement) _port_change_flag = false; if (_midi_driver_option == _("CoreMidi")) { + _midiio->set_enabled(true); _midiio->set_port_changed_callback(midi_port_change, this); - _midiio->discover(); + _midiio->start(); } if (register_system_audio_ports()) { @@ -555,6 +570,7 @@ CoreAudioBackend::stop () _run = false; _pcmio->pcm_stop(); _midiio->set_port_changed_callback(NULL, NULL); + _midiio->stop(); if (pthread_join (_freeewheel_thread, &status)) { PBD::error << _("CoreAudioBackend: failed to terminate.") << endmsg; @@ -866,8 +882,8 @@ CoreAudioBackend::register_system_audio_ports() { LatencyRange lr; - const int a_ins = _n_inputs > 0 ? _n_inputs : 2; - const int a_out = _n_outputs > 0 ? _n_outputs : 2; + const int a_ins = _n_inputs; + const int 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_output_latency = _pcmio->get_latency(name_to_id(_audio_device), false); @@ -901,67 +917,100 @@ CoreAudioBackend::register_system_audio_ports() return 0; } -int -CoreAudioBackend::register_system_midi_ports() +void +CoreAudioBackend::coremidi_rediscover() { - int midi_ins = _system_midi_out.size(); - int midi_outs = _system_midi_in.size(); + if (!_run) { return; } + assert(_midi_driver_option == _("CoreMidi")); - //TODO query port names - for (uint32_t i = midi_ins; i < _midiio->n_midi_outputs(); ++i) { - char tmp[64]; - snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", ++midi_ins); - PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal)); + pthread_mutex_lock (&_process_callback_mutex); + + for (std::vector<CoreBackendPort*>::iterator it = _system_midi_out.begin (); it != _system_midi_out.end ();) { + bool found = false; + for (int i = 0; i < _midiio->n_midi_outputs(); ++i) { + if ((*it)->name() == _midiio->port_name(i, false)) { + found = true; + break; + } + } + if (found) { + ++it; + } else { +#ifndef NDEBUG + printf("unregister MIDI Output: %s\n", (*it)->name().c_str()); +#endif + _port_change_flag = true; + unregister_port((*it)); + it = _system_midi_out.erase(it); + } + } + + for (std::vector<CoreBackendPort*>::iterator it = _system_midi_in.begin (); it != _system_midi_in.end ();) { + bool found = false; + for (int i = 0; i < _midiio->n_midi_inputs(); ++i) { + if ((*it)->name() == _midiio->port_name(i, true)) { + found = true; + break; + } + } + if (found) { + ++it; + } else { +#ifndef NDEBUG + printf("unregister MIDI Input: %s\n", (*it)->name().c_str()); +#endif + _port_change_flag = true; + unregister_port((*it)); + it = _system_midi_in.erase(it); + } + } + + for (int i = 0; i < _midiio->n_midi_inputs(); ++i) { + std::string name = _midiio->port_name(i, true); + if (find_port_in(_system_midi_in, name)) { + continue; + } + +#ifndef NDEBUG + printf("register MIDI Input: %s\n", name.c_str()); +#endif + PortHandle p = add_port(name, DataType::MIDI, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal)); if (!p) { + fprintf(stderr, "failed to register MIDI IN: %s\n", name.c_str()); continue; } LatencyRange lr; lr.min = lr.max = _samples_per_period; // TODO add per-port midi-systemic latency set_latency_range (p, false, lr); - _system_midi_out.push_back(static_cast<CoreBackendPort*>(p)); + _system_midi_in.push_back(static_cast<CoreBackendPort*>(p)); + _port_change_flag = true; } - for (uint32_t i = midi_outs; i < _midiio->n_midi_inputs(); ++i) { - char tmp[64]; - snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", ++midi_outs); - PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal)); + for (int i = 0; i < _midiio->n_midi_outputs(); ++i) { + std::string name = _midiio->port_name(i, false); + if (find_port_in(_system_midi_out, name)) { + continue; + } + +#ifndef NDEBUG + printf("register MIDI OUT: %s\n", name.c_str()); +#endif + PortHandle p = add_port(name, DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal)); if (!p) { + fprintf(stderr, "failed to register MIDI OUT: %s\n", name.c_str()); continue; } LatencyRange lr; lr.min = lr.max = _samples_per_period; // TODO add per-port midi-systemic latency set_latency_range (p, false, lr); - _system_midi_in.push_back(static_cast<CoreBackendPort*>(p)); - } - - return 0; -} - -void -CoreAudioBackend::coremidi_rediscover() -{ - if (!_run) { return; } - assert(_midi_driver_option == _("CoreMidi")); - - pthread_mutex_lock (&_process_callback_mutex); - - // TODO maintain device-specific connections, rather - // than re-map. - while (_system_midi_out.size() > _midiio->n_midi_outputs()) { - CoreBackendPort* p = _system_midi_out.back(); - _system_midi_out.pop_back(); - unregister_port(p); + _system_midi_out.push_back(static_cast<CoreBackendPort*>(p)); + _port_change_flag = true; } - while (_system_midi_in.size() > _midiio->n_midi_inputs()) { - CoreBackendPort* p = _system_midi_in.back(); - _system_midi_in.pop_back(); - unregister_port(p); - } - register_system_midi_ports(); + assert(_system_midi_out.size() == _midiio->n_midi_outputs()); + assert(_system_midi_in.size() == _midiio->n_midi_inputs()); - _port_change_flag = true; pthread_mutex_unlock (&_process_callback_mutex); } @@ -1339,6 +1388,7 @@ CoreAudioBackend::freewheel_thread () // handshake w/ coreaudio _reinit_thread_callback = true; _freewheel_ack = false; + _midiio->set_enabled(true); } engine.freewheel_callback (_freewheeling); @@ -1356,8 +1406,12 @@ CoreAudioBackend::freewheel_thread () first_run = false; _main_thread = pthread_self(); AudioEngine::thread_init_callback (this); + _midiio->set_enabled(false); } + post_process(); + + pthread_mutex_lock (&_process_callback_mutex); // Freewheelin' for (std::vector<CoreBackendPort*>::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it) { memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample)); @@ -1367,17 +1421,18 @@ CoreAudioBackend::freewheel_thread () } if (engine.process_callback (_samples_per_period)) { + pthread_mutex_unlock (&_process_callback_mutex); break; } + + pthread_mutex_unlock (&_process_callback_mutex); _dsp_load = 1.0; Glib::usleep (100); // don't hog cpu - - post_process(); } _active_fw = false; - if (_run && _freewheel) { + if (_run) { engine.halted_callback("CoreAudio Freehweeling aborted."); } return 0; @@ -1412,16 +1467,10 @@ CoreAudioBackend::process_callback () manager.graph_order_callback(); } - const uint32_t n_samples = _pcmio->n_samples(); + /* port-connection change */ + post_process(); -#if 0 // here in RT callback ?? XXX - if (_samples_per_period != n_samples) { - printf("CoreAudio Adjust SPP %zu -> %d\n", _samples_per_period, n_samples); - _samples_per_period = n_samples; - engine.buffer_size_change (_samples_per_period); - // TODO update latencies - } -#endif + const uint32_t n_samples = _pcmio->n_samples(); // cycle-length in usec const int64_t nominal_time = 1e6 * n_samples / _samplerate; @@ -1492,8 +1541,6 @@ CoreAudioBackend::process_callback () const int64_t elapsed_time = clock2 - clock1; _dsp_load = elapsed_time / (float) nominal_time; - /* port-connection change */ - post_process(); pthread_mutex_unlock (&_process_callback_mutex); return 0; } @@ -1502,7 +1549,11 @@ void CoreAudioBackend::error_callback () { _pcmio->set_error_callback (NULL, NULL); + _pcmio->set_sample_rate_callback (NULL, NULL); + _pcmio->set_xrun_callback (NULL, NULL); + _midiio->set_port_changed_callback(NULL, NULL); engine.halted_callback("CoreAudio Process aborted."); + _active_ca = false; } void @@ -1512,6 +1563,28 @@ CoreAudioBackend::xrun_callback () } void +CoreAudioBackend::buffer_size_callback () +{ + uint32_t bs = _pcmio->samples_per_period(); + if (bs == _samples_per_period) { + return; + } + _samples_per_period = bs; + engine.buffer_size_change (_samples_per_period); +} + +void +CoreAudioBackend::sample_rate_callback () +{ + _pcmio->set_error_callback (NULL, NULL); + _pcmio->set_sample_rate_callback (NULL, NULL); + _pcmio->set_xrun_callback (NULL, NULL); + _midiio->set_port_changed_callback(NULL, NULL); + engine.halted_callback("Sample Rate Changed."); + stop(); +} + +void CoreAudioBackend::hw_changed_callback () { _reinit_thread_callback = true; diff --git a/libs/backends/coreaudio/coreaudio_backend.h b/libs/backends/coreaudio/coreaudio_backend.h index 715c067efc..cfdf20625f 100644 --- a/libs/backends/coreaudio/coreaudio_backend.h +++ b/libs/backends/coreaudio/coreaudio_backend.h @@ -215,6 +215,8 @@ class CoreAudioBackend : public AudioBackend { int process_callback(); void error_callback(); void xrun_callback(); + void buffer_size_callback(); + void sample_rate_callback(); void hw_changed_callback(); protected: @@ -376,7 +378,6 @@ class CoreAudioBackend : public AudioBackend { /* port engine */ PortHandle add_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags); int register_system_audio_ports (); - int register_system_midi_ports (); void unregister_ports (bool system_only = false); std::vector<CoreBackendPort *> _ports; @@ -423,6 +424,15 @@ class CoreAudioBackend : public AudioBackend { return NULL; } + CoreBackendPort * find_port_in (std::vector<CoreBackendPort *> plist, const std::string& port_name) const { + for (std::vector<CoreBackendPort*>::const_iterator it = plist.begin (); it != plist.end (); ++it) { + if ((*it)->name () == port_name) { + return *it; + } + } + return NULL; + } + }; // class CoreAudioBackend } // namespace diff --git a/libs/backends/coreaudio/coreaudio_pcmio.cc b/libs/backends/coreaudio/coreaudio_pcmio.cc index 7f9322bc1f..10edbe5aca 100644 --- a/libs/backends/coreaudio/coreaudio_pcmio.cc +++ b/libs/backends/coreaudio/coreaudio_pcmio.cc @@ -19,13 +19,132 @@ #include <glibmm.h> #include "coreaudio_pcmio.h" +/* abstraction for deprecated CoreAudio */ + +static OSStatus GetPropertyWrapper ( + AudioDeviceID id, UInt32 elem, bool input, AudioDevicePropertyID prop, UInt32* size, void * data) +{ +#ifdef COREAUDIO_108 + AudioObjectPropertyAddress property_address; + property_address.mSelector = prop; + switch (prop) { + case kAudioDevicePropertyBufferFrameSize: + case kAudioDevicePropertyBufferFrameSizeRange: + property_address.mScope = kAudioObjectPropertyScopeGlobal; + break; + default: + property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput; + break; + } + property_address.mElement = kAudioObjectPropertyElementMaster; + return AudioObjectGetPropertyData(id, &property_address, elem, NULL, size, data); +#else + return AudioDeviceGetProperty(id, elem, input, prop, size, data); +#endif +} + +static OSStatus SetPropertyWrapper ( + AudioDeviceID id, const AudioTimeStamp* when, UInt32 chn, bool input, AudioDevicePropertyID prop, UInt32 size, void * data) +{ +#ifdef COREAUDIO_108 + AudioObjectPropertyAddress property_address; + property_address.mSelector = prop; + property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput; + property_address.mElement = kAudioObjectPropertyElementMaster; + return AudioObjectSetPropertyData (id, &property_address, 0, NULL, size, data); +#else + return AudioDeviceSetProperty (id, when, chn, input, prop, size, data); +#endif +} + +static OSStatus GetHardwarePropertyInfoWrapper (AudioDevicePropertyID prop, UInt32* size) +{ +#ifdef COREAUDIO_108 + AudioObjectPropertyAddress property_address; + property_address.mSelector = prop; + property_address.mScope = kAudioObjectPropertyScopeGlobal; + property_address.mElement = kAudioObjectPropertyElementMaster; + return AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &property_address, 0, NULL, size); +#else + Boolean outWritable; + return AudioHardwareGetPropertyInfo(prop, size, &outWritable); +#endif +} + +static OSStatus GetHardwarePropertyWrapper (AudioDevicePropertyID prop, UInt32* size, void *d) +{ +#ifdef COREAUDIO_108 + AudioObjectPropertyAddress property_address; + property_address.mSelector = prop; + property_address.mScope = kAudioObjectPropertyScopeGlobal; + property_address.mElement = kAudioObjectPropertyElementMaster; + return AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, size, d); +#else + return AudioHardwareGetProperty (kAudioHardwarePropertyDevices, size, d); +#endif +} + +static OSStatus GetPropertyInfoWrapper (AudioDeviceID id, UInt32 elem, bool input, AudioDevicePropertyID prop, UInt32* size) +{ #ifdef COREAUDIO_108 -static OSStatus hw_changed_callback_ptr(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) { + AudioObjectPropertyAddress property_address; + property_address.mSelector = prop; + property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput; + property_address.mElement = elem; + return AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, size); +#else + Boolean outWritable; + return AudioDeviceGetPropertyInfo(id, elem, input, prop, size, &outWritable); +#endif +} + +static OSStatus GetDeviceNameFromID(AudioDeviceID id, char* name) +{ + UInt32 size = 256; + return GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyDeviceName, &size, name); +} + +static CFStringRef GetDeviceName(AudioDeviceID id) +{ + UInt32 size = sizeof(CFStringRef); + CFStringRef UIname; + OSStatus err = GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyDeviceUID, &size, &UIname); + return (err == noErr) ? UIname : NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "coreaudio_pcmio_aggregate.cc" + +/* callbacks */ + +#ifdef COREAUDIO_108 + +static OSStatus property_callback_ptr (AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) { CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg); - self->hw_changed_callback(); + for (UInt32 i = 0; i < inNumberAddresses; ++i) { + switch (inAddresses[i].mSelector) { + case kAudioHardwarePropertyDevices: + self->hw_changed_callback(); + break; + case kAudioDeviceProcessorOverload: + self->xrun_callback(); + break; + case kAudioDevicePropertyBufferFrameSize: + self->buffer_size_callback(); + break; + case kAudioDevicePropertyNominalSampleRate: + self->sample_rate_callback(); + break; + default: + break; + } + } return noErr; } + #else + static OSStatus hw_changed_callback_ptr (AudioHardwarePropertyID inPropertyID, void* arg) { if (inPropertyID == kAudioHardwarePropertyDevices) { CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg); @@ -33,16 +152,8 @@ static OSStatus hw_changed_callback_ptr (AudioHardwarePropertyID inPropertyID, v } return noErr; } -#endif -#ifdef COREAUDIO_108 -static OSStatus xrun_callback_ptr(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) { - CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg); - self->xrun_callback(); - return noErr; -} -#else -static OSStatus xrun_callback_ptr( +static OSStatus property_callback_ptr ( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, @@ -50,9 +161,20 @@ static OSStatus xrun_callback_ptr( void* inClientData) { CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inClientData); - d->xrun_callback(); + switch (inPropertyID) { + case kAudioDeviceProcessorOverload: + d->xrun_callback(); + break; + case kAudioDevicePropertyBufferFrameSize: + d->buffer_size_callback(); + break; + case kAudioDevicePropertyNominalSampleRate: + d->sample_rate_callback(); + break; + } return noErr; } + #endif static OSStatus render_callback_ptr ( @@ -68,12 +190,28 @@ static OSStatus render_callback_ptr ( } +static OSStatus add_listener (AudioDeviceID id, AudioDevicePropertyID selector, void *arg) { +#ifdef COREAUDIO_108 + AudioObjectPropertyAddress property_address; + property_address.mSelector = selector; + property_address.mScope = kAudioObjectPropertyScopeGlobal; + property_address.mElement = 0; + return AudioObjectAddPropertyListener(id, &property_address, property_callback_ptr, arg); +#else + return AudioDeviceAddPropertyListener(id, 0, true, selector, property_callback_ptr, arg); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// + CoreAudioPCM::CoreAudioPCM () : _auhal (0) , _device_ids (0) , _input_audio_buffer_list (0) - , _active_input (0) - , _active_output (0) + , _active_device_id (0) + , _aggregate_device_id (0) + , _aggregate_plugin_id (0) , _state (-1) , _capture_channels (0) , _playback_channels (0) @@ -83,6 +221,8 @@ CoreAudioPCM::CoreAudioPCM () , _error_callback (0) , _hw_changed_callback (0) , _xrun_callback (0) + , _buffer_size_callback (0) + , _sample_rate_callback (0) , _device_ins (0) , _device_outs (0) { @@ -93,11 +233,10 @@ CoreAudioPCM::CoreAudioPCM () AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioHardwarePropertyDevices }; AudioObjectSetPropertyData (kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); - AudioObjectPropertyAddress prop; - prop.mSelector = kAudioHardwarePropertyDevices; - prop.mScope = kAudioObjectPropertyScopeGlobal; - prop.mElement = 0; - AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, hw_changed_callback_ptr, this); + property.mSelector = kAudioHardwarePropertyDevices; + property.mScope = kAudioObjectPropertyScopeGlobal; + property.mElement = 0; + AudioObjectAddPropertyListener(kAudioObjectSystemObject, &property, property_callback_ptr, this); #else AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, hw_changed_callback_ptr, this); #endif @@ -116,7 +255,7 @@ CoreAudioPCM::~CoreAudioPCM () prop.mSelector = kAudioHardwarePropertyDevices; prop.mScope = kAudioObjectPropertyScopeGlobal; prop.mElement = 0; - AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &hw_changed_callback_ptr, this); + AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &property_callback_ptr, this); #else AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, hw_changed_callback_ptr); #endif @@ -127,9 +266,10 @@ CoreAudioPCM::~CoreAudioPCM () void CoreAudioPCM::hw_changed_callback() { - printf("CHANGE..\n"); +#ifndef NDEBUG + printf("CoreAudio HW change..\n"); +#endif discover(); - // TODO Filter events.. if (_hw_changed_callback) { _hw_changed_callback(_hw_changed_arg); } @@ -147,15 +287,7 @@ CoreAudioPCM::available_sample_rates(uint32_t device_id, std::vector<float>& sam return -1; } -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress property_address; - property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; - property_address.mScope = kAudioDevicePropertyScopeOutput; - property_address.mElement = kAudioObjectPropertyElementMaster; - err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size); -#else - err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &size, NULL); -#endif + err = GetPropertyInfoWrapper (_device_ids[device_id], 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size); if (err != kAudioHardwareNoError) { return -1; @@ -164,11 +296,7 @@ CoreAudioPCM::available_sample_rates(uint32_t device_id, std::vector<float>& sam int numRates = size / sizeof(AudioValueRange); AudioValueRange* supportedRates = new AudioValueRange[numRates]; -#ifdef COREAUDIO_108 - err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, supportedRates); -#else - err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &size, supportedRates); -#endif + err = GetPropertyWrapper (_device_ids[device_id], 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, supportedRates); if (err != kAudioHardwareNoError) { delete [] supportedRates; @@ -205,15 +333,8 @@ CoreAudioPCM::available_buffer_sizes(uint32_t device_id, std::vector<uint32_t>& AudioValueRange supportedRange; size = sizeof (AudioValueRange); -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress property_address; - property_address.mSelector = kAudioDevicePropertyBufferFrameSizeRange; - err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &supportedRange); -#else - err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &size, &supportedRange); -#endif - - if (err != kAudioHardwareNoError) { + err = GetPropertyWrapper (_device_ids[device_id], 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &size, &supportedRange); + if (err != noErr) { return -1; } @@ -246,11 +367,7 @@ CoreAudioPCM::available_channels(uint32_t device_id, bool input) } /* query number of inputs */ -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress property_address; - property_address.mSelector = kAudioDevicePropertyStreamConfiguration; - property_address.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size); + err = GetPropertyInfoWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreamConfiguration, &size); if (kAudioHardwareNoError != err) { fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed: %i\n", err); return 0; @@ -259,23 +376,8 @@ CoreAudioPCM::available_channels(uint32_t device_id, bool input) bufferList = (AudioBufferList *)(malloc(size)); assert(bufferList); if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); return 0; } - - err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, bufferList); - -#else - err = AudioDeviceGetPropertyInfo (_device_ids[device_id], 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &size, NULL); - if (kAudioHardwareNoError != err) { - fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed: %i\n", err); - return 0; - } - - bufferList = (AudioBufferList *)(malloc(size)); - assert(bufferList); - if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); return 0; } - bufferList->mNumberBuffers = 0; - err = AudioDeviceGetProperty(_device_ids[device_id], 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &size, bufferList); -#endif + err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufferList); if(kAudioHardwareNoError != err) { fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed: %i\n", err); @@ -291,7 +393,7 @@ CoreAudioPCM::available_channels(uint32_t device_id, bool input) } void -CoreAudioPCM::get_stream_latencies(uint32 device_id, bool input, std::vector<uint32>& latencies) +CoreAudioPCM::get_stream_latencies(uint32_t device_id, bool input, std::vector<uint32_t>& latencies) { OSStatus err; UInt32 size = 0; @@ -300,42 +402,29 @@ CoreAudioPCM::get_stream_latencies(uint32 device_id, bool input, std::vector<uin return; } -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress property_address; - property_address.mSelector = kAudioDevicePropertyStreams; - property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput; - property_address.mElement = kAudioObjectPropertyElementMaster; - err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size); -#else - Boolean outWritable; - const int elem = input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT; - err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, elem, kAudioDevicePropertyStreams, &size, &outWritable); -#endif - if (err != noErr) { - return; - } + err = GetPropertyInfoWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreams, &size); + if (err != noErr) { return; } - uint32 stream_count = size / sizeof(UInt32); + uint32_t stream_count = size / sizeof(UInt32); AudioStreamID streamIDs[stream_count]; -#ifdef COREAUDIO_108 - err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &streamIDs); -#else - err = AudioDeviceGetProperty(_device_ids[device_id], 0, elem, kAudioDevicePropertyStreams, &size, streamIDs); -#endif + err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreams, &size, &streamIDs); if (err != noErr) { fprintf(stderr, "GetStreamLatencies kAudioDevicePropertyStreams\n"); return; } - for (uint32 i = 0; i < stream_count; i++) { + for (uint32_t i = 0; i < stream_count; i++) { UInt32 stream_latency; size = sizeof(UInt32); #ifdef COREAUDIO_108 + AudioObjectPropertyAddress property_address; property_address.mSelector = kAudioDevicePropertyStreams; + property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput; + property_address.mElement = i; // ?? err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &stream_latency); #else - err = AudioStreamGetProperty(streamIDs[i], elem, kAudioStreamPropertyLatency, &size, &stream_latency); + err = AudioStreamGetProperty(streamIDs[i], input, kAudioStreamPropertyLatency, &size, &stream_latency); #endif if (err != noErr) { fprintf(stderr, "GetStreamLatencies kAudioStreamPropertyLatency\n"); @@ -349,10 +438,10 @@ CoreAudioPCM::get_stream_latencies(uint32 device_id, bool input, std::vector<uin } uint32_t -CoreAudioPCM::get_latency(uint32 device_id, bool input) +CoreAudioPCM::get_latency(uint32_t device_id, bool input) { OSStatus err; - uint32 latency = 0; + uint32_t latency = 0; UInt32 size = sizeof(UInt32); UInt32 lat0 = 0; UInt32 latS = 0; @@ -361,26 +450,12 @@ CoreAudioPCM::get_latency(uint32 device_id, bool input) return 0; } -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress property_address; - property_address.mSelector = kAudioDevicePropertyLatency; - property_address.mScope = input? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - property_address.mElement = 0; - err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &lat0); -#else - const int elem = input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT; - err = AudioDeviceGetProperty(_device_ids[device_id], 0, elem, kAudioDevicePropertyLatency, &size, &lat0); -#endif + err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyLatency, &size, &lat0); if (err != kAudioHardwareNoError) { fprintf(stderr, "GetLatency kAudioDevicePropertyLatency\n"); } -#ifdef COREAUDIO_108 - property_address.mSelector = kAudioDevicePropertySafetyOffset; - err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &latS); -#else - err = AudioDeviceGetProperty(_device_ids[device_id], 0, elem, kAudioDevicePropertySafetyOffset, &size, &latS); -#endif + err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertySafetyOffset, &size, &latS); if (err != kAudioHardwareNoError) { fprintf(stderr, "GetLatency kAudioDevicePropertySafetyOffset\n"); } @@ -392,7 +467,7 @@ CoreAudioPCM::get_latency(uint32 device_id, bool input) latency = lat0 + latS; uint32_t max_stream_latency = 0; - std::vector<uint32> stream_latencies; + std::vector<uint32_t> stream_latencies; get_stream_latencies(device_id, input, stream_latencies); for (size_t i = 0; i < stream_latencies.size(); ++i) { max_stream_latency = std::max(max_stream_latency, stream_latencies[i]); @@ -402,76 +477,64 @@ CoreAudioPCM::get_latency(uint32 device_id, bool input) return latency; } - +uint32_t +CoreAudioPCM::current_buffer_size_id(AudioDeviceID id) { + UInt32 buffer_size; + UInt32 size = sizeof(UInt32); + OSStatus err; + err = GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyBufferFrameSize, &size, &buffer_size); + if (err != noErr) { + return _samples_per_period; + } + return buffer_size; +} float -CoreAudioPCM::current_sample_rate(uint32 device_id, bool input) { +CoreAudioPCM::current_sample_rate_id(AudioDeviceID id, bool input) { OSStatus err; UInt32 size = 0; - - if (device_id >= _n_devices) { - return -1; - } - - float sample_rate = 0; - Float64 rate; size = sizeof (rate); -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress property_address; - property_address.mSelector = kAudioDevicePropertyNominalSampleRate; - property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput; - err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &rate); -#else - err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyNominalSampleRate, &size, &rate); -#endif - - if (err == kAudioHardwareNoError) { - sample_rate = rate; + err = GetPropertyWrapper(id, 0, input, kAudioDevicePropertyNominalSampleRate, &size, &rate); + if (err == noErr) { + return rate; } + return 0; +} - // prefer input, if vailable - -#ifdef COREAUDIO_108 - property_address.mSelector = kAudioDevicePropertyNominalSampleRate; - property_address.mScope = kAudioDevicePropertyScopeInput; - err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &rate); -#else - err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, AUHAL_INPUT_ELEMENT, kAudioDevicePropertyNominalSampleRate, &size, &rate); -#endif - - if (err == kAudioHardwareNoError) { - sample_rate = rate; +float +CoreAudioPCM::current_sample_rate(uint32_t device_id, bool input) { + if (device_id >= _n_devices) { + return -1; } + return current_sample_rate_id(_device_ids[device_id], input); +} - return sample_rate; +float +CoreAudioPCM::sample_rate() { + if (_active_device_id == 0) { + return 0; + } + return current_sample_rate_id(_active_device_id, _playback_channels > 0 ? false : true); } int -CoreAudioPCM::set_device_sample_rate (uint32 device_id, float rate, bool input) +CoreAudioPCM::set_device_sample_rate_id (AudioDeviceID id, float rate, bool input) { std::vector<int>::iterator intIter; OSStatus err; UInt32 size = 0; - if (current_sample_rate(device_id, input) == rate) { + if (current_sample_rate_id(id, input) == rate) { return 0; } Float64 newNominalRate = rate; size = sizeof (Float64); -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress property_address; - property_address.mSelector = kAudioDevicePropertyNominalSampleRate; - property_address.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - property_address.mElement = kAudioObjectPropertyElementMaster; - err = AudioObjectSetPropertyData (_device_ids[device_id], &property_address, 0, NULL, size, &newNominalRate); -#else - err = AudioDeviceSetProperty(_device_ids[device_id], NULL, 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyNominalSampleRate, size, &newNominalRate); -#endif + err = SetPropertyWrapper(id, NULL, 0, input, kAudioDevicePropertyNominalSampleRate, size, &newNominalRate); if (err != noErr) { fprintf(stderr, "CoreAudioPCM: failed to set samplerate\n"); return 0; @@ -479,7 +542,7 @@ CoreAudioPCM::set_device_sample_rate (uint32 device_id, float rate, bool input) int timeout = 3000; // 3 sec while (--timeout > 0) { - if (current_sample_rate(device_id) == rate) { + if (current_sample_rate_id(id, input) == rate) { break; } Glib::usleep (1000); @@ -494,6 +557,12 @@ CoreAudioPCM::set_device_sample_rate (uint32 device_id, float rate, bool input) return 0; } +int +CoreAudioPCM::set_device_sample_rate (AudioDeviceID device_id, float rate, bool input) +{ + return set_device_sample_rate_id(_device_ids[device_id], rate, input); +} + void CoreAudioPCM::discover() { @@ -511,15 +580,7 @@ CoreAudioPCM::discover() } _devices.clear(); -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress property_address; - property_address.mSelector = kAudioHardwarePropertyDevices; - property_address.mScope = kAudioObjectPropertyScopeGlobal; - property_address.mElement = kAudioObjectPropertyElementMaster; - err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &property_address, 0, NULL, &size); -#else - err = AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &size, NULL); -#endif + err = GetHardwarePropertyInfoWrapper (kAudioHardwarePropertyDevices, &size); _n_devices = size / sizeof (AudioDeviceID); size = _n_devices * sizeof (AudioDeviceID); @@ -538,24 +599,12 @@ CoreAudioPCM::discover() return; } - -#ifdef COREAUDIO_108 - property_address.mSelector = kAudioHardwarePropertyDevices; - err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, &size, _device_ids); -#else - err = AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &size, _device_ids); -#endif + err = GetHardwarePropertyWrapper (kAudioHardwarePropertyDevices, &size, _device_ids); for (size_t idx = 0; idx < _n_devices; ++idx) { size = 64; char deviceName[64]; -#ifdef COREAUDIO_108 - property_address.mSelector = kAudioDevicePropertyDeviceName; - property_address.mScope = kAudioDevicePropertyScopeOutput; - err = AudioObjectGetPropertyData(_device_ids[idx], &property_address, 0, NULL, &size, deviceName); -#else - err = AudioDeviceGetProperty(_device_ids[idx], 0, 0, kAudioDevicePropertyDeviceName, &size, deviceName); -#endif + err = GetPropertyWrapper (_device_ids[idx], 0, 0, kAudioDevicePropertyDeviceName, &size, deviceName); if (kAudioHardwareNoError != err) { fprintf(stderr, "CoreAudioPCM: device name query failed: %i\n", err); @@ -570,9 +619,9 @@ CoreAudioPCM::discover() _device_ins[idx] = inputChannelCount; _device_outs[idx] = outputChannelCount; #ifndef NDEBUG - printf("CoreAudio Device: #%ld '%s' in:%d out:%d\n", idx, deviceName, inputChannelCount, outputChannelCount); + printf("CoreAudio Device: #%ld (id:%u) '%s' in:%d out:%d\n", idx, _device_ids[idx], deviceName, inputChannelCount, outputChannelCount); #endif - if (outputChannelCount > 0 && inputChannelCount > 0) { + if (outputChannelCount > 0 || inputChannelCount > 0) { _devices.insert (std::pair<size_t, std::string> (idx, dn)); } } @@ -592,6 +641,27 @@ CoreAudioPCM::xrun_callback () } void +CoreAudioPCM::buffer_size_callback () +{ + _samples_per_period = current_buffer_size_id(_active_device_id); + + if (_buffer_size_callback) { + _buffer_size_callback(_buffer_size_arg); + } +} + +void +CoreAudioPCM::sample_rate_callback () +{ +#ifndef NDEBUG + printf("Sample Rate Changed!\n"); +#endif + if (_sample_rate_callback) { + _sample_rate_callback(_sample_rate_arg); + } +} + +void CoreAudioPCM::pcm_stop () { if (!_auhal) return; @@ -600,20 +670,27 @@ CoreAudioPCM::pcm_stop () if (_state == 0) { #ifdef COREAUDIO_108 AudioObjectPropertyAddress prop; - prop.mSelector = kAudioDeviceProcessorOverload; prop.mScope = kAudioObjectPropertyScopeGlobal; prop.mElement = 0; - if (_active_output > 0) { - AudioObjectRemovePropertyListener(_active_input, &prop, &xrun_callback_ptr, this); - } - if (_active_input > 0 && _active_output != _active_input) { - AudioObjectRemovePropertyListener(_active_output, &prop, &xrun_callback_ptr, this); + if (_active_device_id > 0) { + prop.mSelector = kAudioDeviceProcessorOverload; + AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this); + prop.mSelector = kAudioDevicePropertyBufferFrameSize; + AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this); + prop.mSelector = kAudioDevicePropertyNominalSampleRate; + AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this); } #else - AudioDeviceRemovePropertyListener(_active_input, 0 , true, kAudioDeviceProcessorOverload, xrun_callback_ptr); - AudioDeviceRemovePropertyListener(_active_output, 0 , false, kAudioDeviceProcessorOverload, xrun_callback_ptr); + if (_active_device_id > 0) { + AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDeviceProcessorOverload, property_callback_ptr); + AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyBufferFrameSize, property_callback_ptr); + AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyNominalSampleRate, property_callback_ptr); + } #endif } + if (_aggregate_plugin_id) { + destroy_aggregate_device(); + } AudioUnitUninitialize(_auhal); #ifdef COREAUDIO_108 @@ -625,6 +702,9 @@ CoreAudioPCM::pcm_stop () _state = -1; _capture_channels = 0; _playback_channels = 0; + _aggregate_plugin_id = 0; + _aggregate_device_id = 0; + _active_device_id = 0; free(_input_audio_buffer_list); _input_audio_buffer_list = 0; @@ -654,6 +734,31 @@ static void PrintStreamDesc (AudioStreamBasicDescription *inDesc) #endif int +CoreAudioPCM::set_device_buffer_size_id (AudioDeviceID id, uint32_t samples_per_period) +{ + OSStatus err; + UInt32 uint32val; + + uint32val = samples_per_period; + err = SetPropertyWrapper(id, NULL, 0, true, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val); + if (err != noErr) { return -1; } + err = SetPropertyWrapper(id, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val); + if (err != noErr) { return -1; } + return 0; +} + +int +CoreAudioPCM::set_samples_per_period (uint32_t n_samples) +{ + + if (_state != 0 || _active_device_id == 0) { + return -1; + } + set_device_buffer_size_id (_active_device_id, n_samples); + return 0; +} + +int CoreAudioPCM::pcm_start ( uint32_t device_id_in, uint32_t device_id_out, uint32_t sample_rate, uint32_t samples_per_period, @@ -664,18 +769,31 @@ CoreAudioPCM::pcm_start ( std::string errorMsg; _state = -2; + // TODO add "none' device to force half-duplex + if (device_id_out >= _n_devices || device_id_in >= _n_devices) { return -1; } + pthread_mutex_lock (&_discovery_lock); + _process_callback = process_callback; _process_arg = process_arg; - _max_samples_per_period = samples_per_period; + _samples_per_period = samples_per_period; _cur_samples_per_period = 0; - _active_input = _active_output = 0; + _active_device_id = 0; + _capture_channels = 0; + _playback_channels = 0; + + const uint32_t chn_in = _device_ins[device_id_in] + ((device_id_out != device_id_in) ? _device_ins[device_id_out] : 0); + const uint32_t chn_out = _device_outs[device_id_out] + ((device_id_out != device_id_in) ? _device_outs[device_id_in] : 0); + + assert (chn_in > 0 || chn_out > 0); ComponentResult err; UInt32 uint32val; + UInt32 size; + AudioDeviceID device_id; AudioStreamBasicDescription srcFormat, dstFormat; AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; @@ -697,142 +815,153 @@ CoreAudioPCM::pcm_start ( } // explicitly request device buffer size - uint32val = samples_per_period; -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress property_address; - property_address.mSelector = kAudioDevicePropertyBufferFrameSize; - property_address.mScope = kAudioDevicePropertyScopeInput; - property_address.mElement = kAudioObjectPropertyElementMaster; - err = AudioObjectSetPropertyData (_device_ids[device_id_in], &property_address, 0, NULL, sizeof(UInt32), &uint32val); - if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Input"; goto error; } + if (set_device_buffer_size_id(_device_ids[device_id_in], samples_per_period)) { + errorMsg="kAudioDevicePropertyBufferFrameSize, Input"; goto error; + } + if (set_device_buffer_size_id(_device_ids[device_id_out], samples_per_period)) { + errorMsg="kAudioDevicePropertyBufferFrameSize, Output"; goto error; + } - property_address.mScope = kAudioDevicePropertyScopeOutput; - err = AudioObjectSetPropertyData (_device_ids[device_id_out], &property_address, 0, NULL, sizeof(UInt32), &uint32val); - if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Output"; goto error; } -#else - err = AudioDeviceSetProperty(_device_ids[device_id_in], NULL, 0, AUHAL_INPUT_ELEMENT, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val); - if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Input"; goto error; } - err = AudioDeviceSetProperty(_device_ids[device_id_out], NULL, 0, AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val); - if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Output"; goto error; } -#endif + // create aggregate device.. + if (_device_ids[device_id_in] != _device_ids[device_id_out]) { + if (0 == create_aggregate_device(_device_ids[device_id_in], _device_ids[device_id_out], sample_rate, &_aggregate_device_id)) { + device_id = _aggregate_device_id; + } else { + _aggregate_device_id = 0; + _aggregate_plugin_id = 0; + errorMsg="Cannot create Aggregate Device"; goto error; + } + } else { + device_id = _device_ids[device_id_out]; + } + + if (device_id_out != device_id_in) { + assert(_aggregate_device_id > 0); + } - uint32val = 1; + // enableIO to progress further + uint32val = (chn_in > 0) ? 1 : 0; err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &uint32val, sizeof(UInt32)); if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Input"; goto error; } - uint32val = 1; + + uint32val = (chn_out > 0) ? 1 : 0; err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, &uint32val, sizeof(UInt32)); if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Output"; goto error; } - err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, &_device_ids[device_id_out], sizeof(AudioDeviceID)); - if (err != noErr) { errorMsg="kAudioOutputUnitProperty_CurrentDevice, Output"; goto error; } - - err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, &_device_ids[device_id_in], sizeof(AudioDeviceID)); + err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID)); if (err != noErr) { errorMsg="kAudioOutputUnitProperty_CurrentDevice, Input"; goto error; } - // Set buffer size - err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, (UInt32*)&_max_samples_per_period, sizeof(UInt32)); - if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Input"; goto error; } - err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, (UInt32*)&_max_samples_per_period, sizeof(UInt32)); - if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Output"; goto error; } - - // set sample format - srcFormat.mSampleRate = sample_rate; - srcFormat.mFormatID = kAudioFormatLinearPCM; - srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; - srcFormat.mBytesPerPacket = sizeof(float); - srcFormat.mFramesPerPacket = 1; - srcFormat.mBytesPerFrame = sizeof(float); - srcFormat.mChannelsPerFrame = _device_ins[device_id_in]; - srcFormat.mBitsPerChannel = 32; - -#if 0 - property_address = { kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster }; - err = AudioObjectSetPropertyData (_device_ids[device_id_in], &property_address, 0, NULL, sizeof(AudioStreamBasicDescription), &srcFormat); -#else - err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, sizeof(AudioStreamBasicDescription)); -#endif - if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat, Output"; goto error; } - - dstFormat.mSampleRate = sample_rate; - dstFormat.mFormatID = kAudioFormatLinearPCM; - dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; - dstFormat.mBytesPerPacket = sizeof(float); - dstFormat.mFramesPerPacket = 1; - dstFormat.mBytesPerFrame = sizeof(float); - dstFormat.mChannelsPerFrame = _device_outs[device_id_out]; - dstFormat.mBitsPerChannel = 32; + if (chn_in > 0) { + // set sample format + srcFormat.mSampleRate = sample_rate; + srcFormat.mFormatID = kAudioFormatLinearPCM; + srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; + srcFormat.mBytesPerPacket = sizeof(float); + srcFormat.mFramesPerPacket = 1; + srcFormat.mBytesPerFrame = sizeof(float); + srcFormat.mChannelsPerFrame = chn_in; + srcFormat.mBitsPerChannel = 32; + + err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, sizeof(AudioStreamBasicDescription)); + if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat, Output"; goto error; } + + err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32)); + if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Input"; goto error; } + } -#if 0 - property_address = { kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput, 0 }; - err = AudioObjectSetPropertyData (_device_ids[device_id_out], &property_address, 0, NULL, sizeof(AudioStreamBasicDescription), &dstFormat); -#else - err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, sizeof(AudioStreamBasicDescription)); -#endif - if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; goto error; } + if (chn_out > 0) { + dstFormat.mSampleRate = sample_rate; + dstFormat.mFormatID = kAudioFormatLinearPCM; + dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; + dstFormat.mBytesPerPacket = sizeof(float); + dstFormat.mFramesPerPacket = 1; + dstFormat.mBytesPerFrame = sizeof(float); + dstFormat.mChannelsPerFrame = chn_out; + dstFormat.mBitsPerChannel = 32; + + err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, sizeof(AudioStreamBasicDescription)); + if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; goto error; } + + err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32)); + if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Output"; goto error; } + } /* read back stream descriptions */ - UInt32 size; - size = sizeof(AudioStreamBasicDescription); - err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size); - if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Output"; goto error; } - _capture_channels = srcFormat.mChannelsPerFrame; + if (chn_in > 0) { + size = sizeof(AudioStreamBasicDescription); + err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size); + if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Output"; goto error; } + _capture_channels = srcFormat.mChannelsPerFrame; #ifndef NDEBUG - PrintStreamDesc(&srcFormat); + PrintStreamDesc(&srcFormat); #endif + } - size = sizeof(AudioStreamBasicDescription); - err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, &size); - if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Input"; goto error; } - _playback_channels = dstFormat.mChannelsPerFrame; + if (chn_out > 0) { + size = sizeof(AudioStreamBasicDescription); + err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, &size); + if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Input"; goto error; } + _playback_channels = dstFormat.mChannelsPerFrame; #ifndef NDEBUG - PrintStreamDesc(&dstFormat); + PrintStreamDesc(&dstFormat); #endif + } /* prepare buffers for input */ - _input_audio_buffer_list = (AudioBufferList*)malloc(sizeof(UInt32) + _capture_channels * sizeof(AudioBuffer)); - assert(_input_audio_buffer_list); - if (!_input_audio_buffer_list) { errorMsg="Out of Memory."; goto error; } + if (_capture_channels > 0) { + _input_audio_buffer_list = (AudioBufferList*)malloc(sizeof(UInt32) + _capture_channels * sizeof(AudioBuffer)); + assert(_input_audio_buffer_list); + if (!_input_audio_buffer_list) { errorMsg="Out of Memory."; goto error; } + } - _active_input = _device_ids[device_id_in]; - _active_output = _device_ids[device_id_out]; + _active_device_id = device_id; -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress prop; - prop.mSelector = kAudioDeviceProcessorOverload; - prop.mScope = kAudioObjectPropertyScopeGlobal; - prop.mElement = 0; - AudioObjectAddPropertyListener(_active_output, &prop, xrun_callback_ptr, this); - if (err != noErr) { errorMsg="kAudioDeviceProcessorOverload, Output"; goto error; } - if (_active_input != _active_output) { - AudioObjectAddPropertyListener(_active_input, &prop, xrun_callback_ptr, this); - if (err != noErr) { errorMsg="kAudioDeviceProcessorOverload, Input"; goto error; } - } -#else - err = AudioDeviceAddPropertyListener(_device_ids[device_id_out], 0 , false, kAudioDeviceProcessorOverload, xrun_callback_ptr, this); - if (err != noErr) { errorMsg="kAudioDeviceProcessorOverload, Output"; goto error; } - err = AudioDeviceAddPropertyListener(_device_ids[device_id_in], 0 , true, kAudioDeviceProcessorOverload, xrun_callback_ptr, this); - if (err != noErr) { errorMsg="kAudioDeviceProcessorOverload, Input"; goto error; } -#endif + // add Listeners + err = add_listener (_active_device_id, kAudioDeviceProcessorOverload, this); + if (err != noErr) { errorMsg="kAudioDeviceProcessorOverload, Listen"; goto error; } - // Setup callbacks + err = add_listener (_active_device_id, kAudioDevicePropertyBufferFrameSize, this); + if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Listen"; goto error; } + + err = add_listener (_active_device_id, kAudioDevicePropertyNominalSampleRate, this); + if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Listen"; goto error; } + + _samples_per_period = current_buffer_size_id(_active_device_id); + + // Setup callback AURenderCallbackStruct renderCallback; memset (&renderCallback, 0, sizeof (renderCallback)); renderCallback.inputProc = render_callback_ptr; renderCallback.inputProcRefCon = this; - err = AudioUnitSetProperty(_auhal, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, - &renderCallback, sizeof (renderCallback)); + if (_playback_channels == 0) { + err = AudioUnitSetProperty(_auhal, + kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Output, 1, + &renderCallback, sizeof (renderCallback)); + } else { + err = AudioUnitSetProperty(_auhal, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Output, 0, + &renderCallback, sizeof (renderCallback)); + } + if (err != noErr) { errorMsg="kAudioUnitProperty_SetRenderCallback"; goto error; } /* setup complete, now get going.. */ if (AudioOutputUnitStart(_auhal) == noErr) { _input_names.clear(); _output_names.clear(); - cache_port_names( device_id_in, true); - cache_port_names( device_id_out, false); + cache_port_names (device_id, true); + cache_port_names (device_id, false); _state = 0; + pthread_mutex_unlock (&_discovery_lock); + + // kick device + if (set_device_buffer_size_id(_active_device_id, samples_per_period)) { + errorMsg="kAudioDevicePropertyBufferFrameSize"; goto error; + } + return 0; } @@ -841,15 +970,15 @@ error: fprintf(stderr, "CoreaudioPCM Error: %c%c%c%c %s\n", rv[0], rv[1], rv[2], rv[3], errorMsg.c_str()); pcm_stop(); _state = -3; - _active_input = _active_output = 0; + _active_device_id = 0; + pthread_mutex_unlock (&_discovery_lock); return -1; } void -CoreAudioPCM::cache_port_names(uint32 device_id, bool input) +CoreAudioPCM::cache_port_names(AudioDeviceID id, bool input) { uint32_t n_chn; - assert (device_id < _n_devices); if (input) { n_chn = _capture_channels; @@ -860,8 +989,6 @@ CoreAudioPCM::cache_port_names(uint32 device_id, bool input) AudioObjectPropertyAddress property_address; property_address.mSelector = kAudioObjectPropertyElementName; property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput; -#else - const int elem = input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT; #endif for (uint32_t c = 0; c < n_chn; ++c) { @@ -872,9 +999,9 @@ CoreAudioPCM::cache_port_names(uint32 device_id, bool input) #ifdef COREAUDIO_108 property_address.mElement = c + 1; - err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size); + err = AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, &size); #else - err = AudioDeviceGetPropertyInfo (_device_ids[device_id], c + 1, elem, + err = AudioDeviceGetPropertyInfo (id, c + 1, input, kAudioDevicePropertyChannelNameCFString, &size, NULL); @@ -882,9 +1009,9 @@ CoreAudioPCM::cache_port_names(uint32 device_id, bool input) if (err == kAudioHardwareNoError) { #ifdef COREAUDIO_108 - err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, c + 1, NULL, &size, &name); + err = AudioObjectGetPropertyData(id, &property_address, c + 1, NULL, &size, &name); #else - err = AudioDeviceGetProperty (_device_ids[device_id], c + 1, elem, + err = AudioDeviceGetProperty (id, c + 1, input, kAudioDevicePropertyChannelNameCFString, &size, &name); @@ -923,17 +1050,17 @@ CoreAudioPCM::cache_port_names(uint32 device_id, bool input) } std::string -CoreAudioPCM::cached_port_name(uint32 port, bool input) const +CoreAudioPCM::cached_port_name(uint32_t port, bool input) const { if (_state != 0) { return ""; } if (input) { - if (port > _input_names.size()) { + if (port >= _input_names.size()) { return ""; } return _input_names[port]; } else { - if (port > _output_names.size()) { + if (port >= _output_names.size()) { return ""; } return _output_names[port]; @@ -949,22 +1076,34 @@ CoreAudioPCM::render_callback ( UInt32 inNumberFrames, AudioBufferList* ioData) { - OSStatus retVal = 0; + OSStatus retVal = kAudioHardwareNoError; + + if (_samples_per_period < inNumberFrames) { +#ifndef NDEBUG + printf("samples per period exceeds configured value, skip cycle. %d < %d\n", _samples_per_period, inNumberFrames); +#endif + for (uint32_t i = 0; _playback_channels > 0 && i < ioData->mNumberBuffers; ++i) { + float* ob = (float*) ioData->mBuffers[i].mData; + memset(ob, 0, sizeof(float) * inNumberFrames); + } + return noErr; + } - assert(_max_samples_per_period >= inNumberFrames); - assert(ioData->mNumberBuffers = _playback_channels); + assert(_playback_channels == 0 || ioData->mNumberBuffers == _playback_channels); _cur_samples_per_period = inNumberFrames; - _input_audio_buffer_list->mNumberBuffers = _capture_channels; - for (uint32_t i = 0; i < _capture_channels; ++i) { - _input_audio_buffer_list->mBuffers[i].mNumberChannels = 1; - _input_audio_buffer_list->mBuffers[i].mDataByteSize = inNumberFrames * sizeof(float); - _input_audio_buffer_list->mBuffers[i].mData = NULL; - } + if (_capture_channels > 0) { + _input_audio_buffer_list->mNumberBuffers = _capture_channels; + for (uint32_t i = 0; i < _capture_channels; ++i) { + _input_audio_buffer_list->mBuffers[i].mNumberChannels = 1; + _input_audio_buffer_list->mBuffers[i].mDataByteSize = inNumberFrames * sizeof(float); + _input_audio_buffer_list->mBuffers[i].mData = NULL; + } - retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, _input_audio_buffer_list); + retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, _input_audio_buffer_list); + } if (retVal != kAudioHardwareNoError) { #if 0 @@ -989,7 +1128,7 @@ CoreAudioPCM::render_callback ( _in_process = false; - if (rv != 0) { + if (rv != 0 && _playback_channels > 0) { // clear output for (uint32_t i = 0; i < ioData->mNumberBuffers; ++i) { float* ob = (float*) ioData->mBuffers[i].mData; @@ -1017,7 +1156,7 @@ CoreAudioPCM::set_playback_channel (uint32_t chn, const float *output, uint32_t return -1; } - assert(_output_audio_buffer_list->mNumberBuffers > chn); + assert(_output_audio_buffer_list && _output_audio_buffer_list->mNumberBuffers > chn); memcpy((void*)_output_audio_buffer_list->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples); return 0; } @@ -1034,15 +1173,7 @@ CoreAudioPCM::launch_control_app (uint32_t device_id) UInt32 size = sizeof (config_app); OSStatus err; -#ifdef COREAUDIO_108 - AudioObjectPropertyAddress property_address; - property_address.mSelector = kAudioDevicePropertyConfigurationApplication; - property_address.mScope = kAudioDevicePropertyScopeOutput; - property_address.mElement = kAudioObjectPropertyElementMaster; - err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &config_app); -#else - err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyConfigurationApplication, &size, &config_app); -#endif + err = GetPropertyWrapper(_device_ids[device_id], 0, false, kAudioDevicePropertyConfigurationApplication, &size, &config_app); if (kAudioHardwareNoError != err) { return; } diff --git a/libs/backends/coreaudio/coreaudio_pcmio.h b/libs/backends/coreaudio/coreaudio_pcmio.h index 6bb3d08356..63ae495cbf 100644 --- a/libs/backends/coreaudio/coreaudio_pcmio.h +++ b/libs/backends/coreaudio/coreaudio_pcmio.h @@ -42,15 +42,20 @@ public: uint32_t n_capture_channels (void) const { return _capture_channels; } 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;} 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); uint32_t available_channels (uint32_t device_id, bool input); - float current_sample_rate(uint32 device_id, bool input = false); - uint32_t get_latency(uint32 device_id, bool input); + float current_sample_rate (uint32_t device_id, bool input = false); + uint32_t get_latency (uint32_t device_id, bool input); - std::string cached_port_name(uint32_t portnum, bool input) const; + std::string cached_port_name (uint32_t portnum, bool input) const; + + float sample_rate (); + uint32_t samples_per_period () const { return _samples_per_period; }; + + int set_samples_per_period (uint32_t); void launch_control_app (uint32_t device_id); @@ -79,6 +84,7 @@ public: _hw_changed_callback = callback; _hw_changed_arg = arg; } + void set_xrun_callback ( void ( callback (void*)), void * arg @@ -86,6 +92,20 @@ public: _xrun_callback = callback; _xrun_arg = arg; } + void set_buffer_size_callback ( + void ( callback (void*)), + void * arg + ) { + _buffer_size_callback = callback; + _buffer_size_arg = arg; + } + void set_sample_rate_callback ( + void ( callback (void*)), + void * arg + ) { + _sample_rate_callback = callback; + _sample_rate_arg = arg; + } // must be called from process_callback; int get_capture_channel (uint32_t chn, float *input, uint32_t n_samples); @@ -101,24 +121,39 @@ public: AudioBufferList* ioData); void xrun_callback (); + void buffer_size_callback (); + void sample_rate_callback (); void hw_changed_callback (); private: - int set_device_sample_rate (uint32 device_id, float rate, bool input); - void get_stream_latencies(uint32 device_id, bool input, std::vector<uint32>& latencies); - void cache_port_names(uint32 device_id, bool input); + float current_sample_rate_id (AudioDeviceID id, bool input); + uint32_t current_buffer_size_id (AudioDeviceID id); + int set_device_sample_rate_id (AudioDeviceID id, float rate, bool input); + int set_device_buffer_size_id (AudioDeviceID id, uint32_t samples_per_period); + int set_device_sample_rate (uint32_t device_id, float rate, bool input); + void get_stream_latencies (uint32_t device_id, bool input, std::vector<uint32_t>& latencies); + void cache_port_names (AudioDeviceID id, bool input); + + void destroy_aggregate_device(); + int create_aggregate_device( + AudioDeviceID input_device_id, + AudioDeviceID output_device_id, + uint32_t sample_rate, + AudioDeviceID *created_device); + AudioUnit _auhal; AudioDeviceID* _device_ids; AudioBufferList* _input_audio_buffer_list; AudioBufferList* _output_audio_buffer_list; - AudioDeviceID _active_input; - AudioDeviceID _active_output; + AudioDeviceID _active_device_id; + AudioDeviceID _aggregate_device_id; + AudioDeviceID _aggregate_plugin_id; int _state; - uint32_t _max_samples_per_period; + uint32_t _samples_per_period; uint32_t _cur_samples_per_period; uint32_t _capture_channels; uint32_t _playback_channels; @@ -137,6 +172,12 @@ private: void (* _xrun_callback) (void*); void * _xrun_arg; + void (* _buffer_size_callback) (void*); + void * _buffer_size_arg; + + void (* _sample_rate_callback) (void*); + void * _sample_rate_arg; + // TODO proper device info struct std::map<size_t, std::string> _devices; diff --git a/libs/backends/coreaudio/coreaudio_pcmio_aggregate.cc b/libs/backends/coreaudio/coreaudio_pcmio_aggregate.cc new file mode 100644 index 0000000000..907bdae0b8 --- /dev/null +++ b/libs/backends/coreaudio/coreaudio_pcmio_aggregate.cc @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2015 Robin Gareus <robin@gareus.org> + * Copyright (C) 2004-2008 Grame + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <vector> + +void +CoreAudioPCM::destroy_aggregate_device () +{ + if (_aggregate_plugin_id == 0) { + return; + } + + OSStatus err; + + AudioObjectPropertyAddress property_address; + property_address.mSelector = kAudioPlugInDestroyAggregateDevice; + property_address.mScope = kAudioObjectPropertyScopeGlobal; + property_address.mElement = kAudioObjectPropertyElementMaster; + UInt32 outDataSize; + + err = AudioObjectGetPropertyDataSize(_aggregate_plugin_id, &property_address, 0, NULL, &outDataSize); + if (err != noErr) { + fprintf(stderr, "DestroyAggregateDevice : AudioObjectGetPropertyDataSize error\n"); + return; + } + + err = AudioObjectGetPropertyData(_aggregate_plugin_id, &property_address, 0, NULL, &outDataSize, &_aggregate_device_id); + if (err != noErr) { + fprintf(stderr, "DestroyAggregateDevice : AudioObjectGetPropertyData error\n"); + return; + } +#ifndef NDEBUG + printf("DestroyAggregateDevice : OK (plugin: %u device:%u)\n", _aggregate_plugin_id, _aggregate_device_id); +#endif +} + +int +CoreAudioPCM::create_aggregate_device ( + AudioDeviceID input_device_id, + AudioDeviceID output_device_id, + uint32_t sample_rate, + AudioDeviceID* created_device) +{ + OSStatus err; + AudioObjectID sub_device[32]; + UInt32 size = sizeof(sub_device); + + /* look up sub-devices */ + err = GetPropertyWrapper (input_device_id, 0, 0, kAudioAggregateDevicePropertyActiveSubDeviceList, &size, sub_device); + std::vector<AudioDeviceID> input_device_ids; + + if (err != noErr) { + input_device_ids.push_back(input_device_id); + } else { + uint32_t num_devices = size / sizeof(AudioObjectID); + for (uint32_t i = 0; i < num_devices; ++i) { + input_device_ids.push_back(sub_device[i]); + } + } + + size = sizeof(sub_device); + err = GetPropertyWrapper (output_device_id, 0, 0, kAudioAggregateDevicePropertyActiveSubDeviceList, &size, sub_device); + std::vector<AudioDeviceID> output_device_ids; + + if (err != noErr) { + output_device_ids.push_back(output_device_id); + } else { + uint32_t num_devices = size / sizeof(AudioObjectID); + for (uint32_t i = 0; i < num_devices; ++i) { + output_device_ids.push_back(sub_device[i]); + } + } + + //--------------------------------------------------------------------------- + // Setup SR of both devices otherwise creating AD may fail... + //--------------------------------------------------------------------------- + UInt32 keptclockdomain = 0; + UInt32 clockdomain = 0; + size = sizeof(UInt32); + bool need_clock_drift_compensation = false; + + for (size_t i = 0; i < input_device_ids.size(); ++i) { + set_device_sample_rate_id(input_device_ids[i], sample_rate, true); + + // Check clock domain + err = GetPropertyWrapper (input_device_ids[i], 0, 0, kAudioDevicePropertyClockDomain, &size, &clockdomain); + if (err == noErr) { + keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; + if (clockdomain != 0 && clockdomain != keptclockdomain) { +#ifndef NDEBUG + printf("AggregateDevice: devices do not share the same clock.\n"); +#endif + need_clock_drift_compensation = true; + } + } + } + + for (UInt32 i = 0; i < output_device_ids.size(); i++) { + set_device_sample_rate_id(output_device_ids[i], sample_rate, true); + + // Check clock domain + err = GetPropertyWrapper (output_device_ids[i], 0, 0, kAudioDevicePropertyClockDomain, &size, &clockdomain); + if (err == noErr) { + keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; + if (clockdomain != 0 && clockdomain != keptclockdomain) { +#ifndef NDEBUG + printf("AggregateDevice: devices do not share the same clock.\n"); +#endif + need_clock_drift_compensation = true; + } + } + } + + // If no valid clock domain was found, then assume we have to compensate... + if (keptclockdomain == 0) { + need_clock_drift_compensation = true; + } + + //--------------------------------------------------------------------------- + // Start to create a new aggregate by getting the base audio hardware plugin + //--------------------------------------------------------------------------- + +#ifndef NDEBUG + char device_name[256]; + for (size_t i = 0; i < input_device_ids.size(); ++i) { + GetDeviceNameFromID(input_device_ids[i], device_name); + printf("Separated input = '%s'\n", device_name); + } + + for (size_t i = 0; i < output_device_ids.size(); ++i) { + GetDeviceNameFromID(output_device_ids[i], device_name); + printf("Separated output = '%s'\n", device_name); + } +#endif + + err = GetHardwarePropertyInfoWrapper (kAudioHardwarePropertyPlugInForBundleID, &size); + if (err != noErr) { + fprintf(stderr, "AggregateDevice: AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error\n"); + return -1; + } + + AudioValueTranslation pluginAVT; + + CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio"); + + pluginAVT.mInputData = &inBundleRef; + pluginAVT.mInputDataSize = sizeof(inBundleRef); + pluginAVT.mOutputData = &_aggregate_plugin_id; + pluginAVT.mOutputDataSize = sizeof(AudioDeviceID); + + err = GetHardwarePropertyWrapper (kAudioHardwarePropertyPlugInForBundleID, &size, &pluginAVT); + if (err != noErr) { + fprintf(stderr, "AggregateDevice: AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error\n"); + return -1; + } + + //------------------------------------------------- + // Create a CFDictionary for our aggregate device + //------------------------------------------------- + + CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFStringRef AggregateDeviceNameRef = CFSTR("ArdourDuplex"); + CFStringRef AggregateDeviceUIDRef = CFSTR("com.ardour.CoreAudio"); + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef); + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef); + + // hide from list + int value = 1; + CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value); + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); + + //------------------------------------------------- + // Create a CFMutableArray for our sub-device list + //------------------------------------------------- + + // we need to append the UID for each device to a CFMutableArray, so create one here + CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + std::vector<CFStringRef> captureDeviceUID; + for (UInt32 i = 0; i < input_device_ids.size(); i++) { + CFStringRef ref = GetDeviceName(input_device_ids[i]); + if (ref == NULL) { + return -1; + } + captureDeviceUID.push_back(ref); + CFArrayAppendValue(subDevicesArray, ref); + } + + std::vector<CFStringRef> playbackDeviceUID; + for (UInt32 i = 0; i < output_device_ids.size(); i++) { + CFStringRef ref = GetDeviceName(output_device_ids[i]); + if (ref == NULL) { + return -1; + } + playbackDeviceUID.push_back(ref); + CFArrayAppendValue(subDevicesArray, ref); + } + + //----------------------------------------------------------------------- + // Feed the dictionary to the plugin, to create a blank aggregate device + //----------------------------------------------------------------------- + + AudioObjectPropertyAddress pluginAOPA; + pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + UInt32 outDataSize; + + err = AudioObjectGetPropertyDataSize(_aggregate_plugin_id, &pluginAOPA, 0, NULL, &outDataSize); + if (err != noErr) { + fprintf(stderr, "AggregateDevice: AudioObjectGetPropertyDataSize error\n"); + goto error; + } + + err = AudioObjectGetPropertyData(_aggregate_plugin_id, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, created_device); + if (err != noErr) { + fprintf(stderr, "AggregateDevice: AudioObjectGetPropertyData error\n"); + goto error; + } + + // pause for a bit to make sure that everything completed correctly + // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + //------------------------- + // Set the sub-device list + //------------------------- + + pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + outDataSize = sizeof(CFMutableArrayRef); + err = AudioObjectSetPropertyData(*created_device, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); + if (err != noErr) { + fprintf(stderr, "AggregateDevice: AudioObjectSetPropertyData for sub-device list error\n"); + goto error; + } + + // pause again to give the changes time to take effect + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + //----------------------- + // Set the master device + //----------------------- + + // set the master device manually (this is the device which will act as the master clock for the aggregate device) + // pass in the UID of the device you want to use + pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + outDataSize = sizeof(CFStringRef); + err = AudioObjectSetPropertyData(*created_device, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]); + if (err != noErr) { + fprintf(stderr, "AggregateDevice: AudioObjectSetPropertyData for playback-master device error\n"); + // try playback + err = AudioObjectSetPropertyData(*created_device, &pluginAOPA, 0, NULL, outDataSize, &playbackDeviceUID[0]); + } + if (err != noErr) { + fprintf(stderr, "AggregateDevice: AudioObjectSetPropertyData for capture-master device error\n"); + goto error; + } + + // pause again to give the changes time to take effect + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + // Prepare sub-devices for clock drift compensation + // Workaround for bug in the HAL : until 10.6.2 + if (need_clock_drift_compensation) { + + AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + UInt32 theQualifierDataSize = sizeof(AudioObjectID); + AudioClassID inClass = kAudioSubDeviceClassID; + void* theQualifierData = &inClass; + UInt32 subDevicesNum = 0; + +#ifndef NDEBUG + printf("Clock drift compensation activated...\n"); +#endif + + // Get the property data size + err = AudioObjectGetPropertyDataSize(*created_device, &theAddressOwned, theQualifierDataSize, theQualifierData, &size); + if (err != noErr) { + fprintf(stderr, "AggregateDevice: kAudioObjectPropertyOwnedObjects error\n"); + } + + // Calculate the number of object IDs + subDevicesNum = size / sizeof(AudioObjectID); +#ifndef NDEBUG + printf("AggregateDevice: clock drift compensation, number of sub-devices = %d\n", subDevicesNum); +#endif + AudioObjectID subDevices[subDevicesNum]; + size = sizeof(subDevices); + + err = AudioObjectGetPropertyData(*created_device, &theAddressOwned, theQualifierDataSize, theQualifierData, &size, subDevices); + if (err != noErr) { + fprintf(stderr, "AggregateDevice: kAudioObjectPropertyOwnedObjects error\n"); + } + + // Set kAudioSubDevicePropertyDriftCompensation property... + for (UInt32 index = 0; index < subDevicesNum; ++index) { + UInt32 theDriftCompensationValue = 1; + err = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); + if (err != noErr) { + fprintf(stderr, "AggregateDevice: kAudioSubDevicePropertyDriftCompensation error\n"); + } + } + } + + // pause again to give the changes time to take effect + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + //---------- + // Clean up + //---------- + + // release the private AD key + CFRelease(AggregateDeviceNumberRef); + + // release the CF objects we have created - we don't need them any more + CFRelease(aggDeviceDict); + CFRelease(subDevicesArray); + + // release the device UID + for (size_t i = 0; i < captureDeviceUID.size(); ++i) { + CFRelease(captureDeviceUID[i]); + } + + for (size_t i = 0; i < playbackDeviceUID.size(); ++i) { + CFRelease(playbackDeviceUID[i]); + } + +#ifndef NDEBUG + printf("AggregateDevice: new aggregate device %u\n", *created_device); +#endif + return 0; + +error: + destroy_aggregate_device(); + return -1; +} diff --git a/libs/backends/coreaudio/coremidi_io.cc b/libs/backends/coreaudio/coremidi_io.cc index 750fd76e55..c3bd868165 100644 --- a/libs/backends/coreaudio/coremidi_io.cc +++ b/libs/backends/coreaudio/coremidi_io.cc @@ -16,6 +16,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <sstream> #include "coremidi_io.h" #include <CoreAudio/HostTime.h> @@ -25,12 +26,16 @@ static void notifyProc (const MIDINotification *message, void *refCon) { } static void midiInputCallback(const MIDIPacketList *list, void *procRef, void *srcRef) { - // TODO skip while freewheeling + CoreMidiIo *self = static_cast<CoreMidiIo*> (procRef); + if (!self || !self->enabled()) { + // skip while freewheeling + return; + } RingBuffer<uint8_t> * rb = static_cast<RingBuffer < uint8_t > *> (srcRef); if (!rb) return; for (UInt32 i = 0; i < list->numPackets; i++) { const MIDIPacket *packet = &list->packet[i]; - if (rb->write_space() < sizeof(MIDIPacket)) { + if (rb->write_space() < sizeof(MIDIPacket)) { fprintf(stderr, "CoreMIDI: dropped MIDI event\n"); continue; } @@ -38,8 +43,19 @@ static void midiInputCallback(const MIDIPacketList *list, void *procRef, void *s } } +static std::string getDisplayName(MIDIObjectRef object) +{ + CFStringRef name = nil; + if (noErr != MIDIObjectGetStringProperty(object, kMIDIPropertyDisplayName, &name) && name) { + return ""; + } + std::string rv (CFStringGetCStringPtr(name, kCFStringEncodingUTF8)); + CFRelease(name); + + return rv; +} -CoreMidiIo::CoreMidiIo() +CoreMidiIo::CoreMidiIo() : _midi_client (0) , _input_endpoints (0) , _output_endpoints (0) @@ -51,25 +67,28 @@ CoreMidiIo::CoreMidiIo() , _n_midi_out (0) , _time_at_cycle_start (0) , _active (false) + , _enabled (true) + , _run (false) , _changed_callback (0) , _changed_arg (0) { - OSStatus err; - err = MIDIClientCreate(CFSTR("Ardour"), ¬ifyProc, this, &_midi_client); - if (noErr != err) { - fprintf(stderr, "Creating Midi Client failed\n"); - } - + pthread_mutex_init (&_discovery_lock, 0); } -CoreMidiIo::~CoreMidiIo() +CoreMidiIo::~CoreMidiIo() { + pthread_mutex_lock (&_discovery_lock); cleanup(); - MIDIClientDispose(_midi_client); _midi_client = 0; + if (_midi_client) { + MIDIClientDispose(_midi_client); + _midi_client = 0; + } + pthread_mutex_unlock (&_discovery_lock); + pthread_mutex_destroy (&_discovery_lock); } void -CoreMidiIo::cleanup() +CoreMidiIo::cleanup() { _active = false; for (uint32_t i = 0 ; i < _n_midi_in ; ++i) { @@ -93,13 +112,13 @@ CoreMidiIo::cleanup() } void -CoreMidiIo::start_cycle() +CoreMidiIo::start_cycle() { _time_at_cycle_start = AudioGetCurrentHostTime(); } void -CoreMidiIo::notify_proc(const MIDINotification *message) +CoreMidiIo::notify_proc(const MIDINotification *message) { switch(message->messageID) { case kMIDIMsgSetupChanged: @@ -150,7 +169,7 @@ CoreMidiIo::recv_event (uint32_t port, double cycle_time_us, uint64_t &time, uin MIDIPacket packet; size_t rv = _rb[port]->read((uint8_t*)&packet, sizeof(MIDIPacket)); assert(rv == sizeof(MIDIPacket)); - _input_queue[port].push_back(boost::shared_ptr<CoreMIDIPacket>(new _CoreMIDIPacket (&packet))); + _input_queue[port].push_back(boost::shared_ptr<CoreMIDIPacket>(new _CoreMIDIPacket (&packet))); } UInt64 start = _time_at_cycle_start; @@ -208,12 +227,68 @@ CoreMidiIo::send_event (uint32_t port, double reltime_us, const uint8_t *d, cons return 0; } +std::string +CoreMidiIo::port_name (uint32_t port, bool input) +{ + std::stringstream ss; + std::string pn; + // XXX including the number will not yield persistent port-names + // when disconnecting devices in the middle. + if (input) { + ss << "system:midi_capture_" << port; + if (port < _n_midi_in) { + pn = getDisplayName(_input_endpoints[port]); + } + } else { + ss << "system:midi_playback_" << port; + if (port < _n_midi_out) { + pn = getDisplayName(_output_endpoints[port]); + } + } + if (!pn.empty()) { + ss << " - " << pn; + } + return ss.str(); +} + void -CoreMidiIo::discover() +CoreMidiIo::start () { + _run = true; + if (!_midi_client) { + OSStatus err; + err = MIDIClientCreate(CFSTR("Ardour"), ¬ifyProc, this, &_midi_client); + if (noErr != err) { + fprintf(stderr, "Creating Midi Client failed\n"); + } + } + discover(); +} + +void +CoreMidiIo::stop () { + _run = false; + pthread_mutex_lock (&_discovery_lock); cleanup(); + pthread_mutex_unlock (&_discovery_lock); +#if 0 + if (_midi_client) { + MIDIClientDispose(_midi_client); + _midi_client = 0; + } +#endif +} + +void +CoreMidiIo::discover() +{ + if (!_run || !_midi_client) { return; } + + if (pthread_mutex_trylock (&_discovery_lock)) { + return; + } - assert(!_active && _midi_client); + cleanup(); ItemCount srcCount = MIDIGetNumberOfSources(); ItemCount dstCount = MIDIGetNumberOfDestinations(); @@ -232,16 +307,19 @@ CoreMidiIo::discover() for (ItemCount i = 0; i < srcCount; i++) { OSStatus err; MIDIEndpointRef src = MIDIGetSource(i); + if (!src) continue; +#ifndef NDEBUG + printf("MIDI IN DEVICE: %s\n", getDisplayName(src).c_str()); +#endif + CFStringRef port_name; port_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("midi_capture_%lu"), i); err = MIDIInputPortCreate (_midi_client, port_name, midiInputCallback, this, &_input_ports[_n_midi_in]); if (noErr != err) { fprintf(stderr, "Cannot create Midi Output\n"); - // TODO handle errors continue; } - // TODO get device name/ID _rb[_n_midi_in] = new RingBuffer<uint8_t>(1024 * sizeof(MIDIPacket)); _input_queue[_n_midi_in] = CoreMIDIQueue(); MIDIPortConnectSource(_input_ports[_n_midi_in], src, (void*) _rb[_n_midi_in]); @@ -259,10 +337,13 @@ CoreMidiIo::discover() err = MIDIOutputPortCreate (_midi_client, port_name, &_output_ports[_n_midi_out]); if (noErr != err) { fprintf(stderr, "Cannot create Midi Output\n"); - // TODO handle errors continue; } - // TODO get device name/ID + +#ifndef NDEBUG + printf("MIDI OUT DEVICE: %s\n", getDisplayName(dst).c_str()); +#endif + MIDIPortConnectSource(_output_ports[_n_midi_out], dst, NULL); CFRelease(port_name); _output_endpoints[_n_midi_out] = dst; @@ -274,4 +355,5 @@ CoreMidiIo::discover() } _active = true; + pthread_mutex_unlock (&_discovery_lock); } diff --git a/libs/backends/coreaudio/coremidi_io.h b/libs/backends/coreaudio/coremidi_io.h index 9edc77aa9d..de4f9be0bb 100644 --- a/libs/backends/coreaudio/coremidi_io.h +++ b/libs/backends/coreaudio/coremidi_io.h @@ -64,8 +64,9 @@ public: CoreMidiIo (void); ~CoreMidiIo (void); - // TODO explicit start/stop, add/remove devices as needed. - void discover (); + void start (); + void stop (); + void start_cycle (); int send_event (uint32_t, double, const uint8_t *, const size_t); @@ -73,15 +74,20 @@ public: uint32_t n_midi_inputs (void) const { return _n_midi_in; } uint32_t n_midi_outputs (void) const { return _n_midi_out; } + std::string port_name (uint32_t, bool input); void notify_proc (const MIDINotification *message); + void set_enabled (bool yn = true) { _enabled = yn; } + bool enabled (void) const { return _active && _enabled; } + void set_port_changed_callback (void (changed_callback (void*)), void *arg) { _changed_callback = changed_callback; _changed_arg = arg; } private: + void discover (); void cleanup (); MIDIClientRef _midi_client; @@ -97,8 +103,12 @@ private: uint32_t _n_midi_out; MIDITimeStamp _time_at_cycle_start; - bool _active; + bool _active; // internal deactivate during discovery etc + bool _enabled; // temporary disable, e.g. during freewheeli + bool _run; // general status void (* _changed_callback) (void*); void * _changed_arg; + + pthread_mutex_t _discovery_lock; }; diff --git a/libs/backends/coreaudio/wscript b/libs/backends/coreaudio/wscript index a4f9ee69f3..82f892a1f2 100644 --- a/libs/backends/coreaudio/wscript +++ b/libs/backends/coreaudio/wscript @@ -32,7 +32,7 @@ def build(bld): obj.framework = [ 'CoreAudio', 'AudioToolbox', 'CoreServices', 'CoreMidi' ] obj.install_path = os.path.join(bld.env['LIBDIR'], 'backends') obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"', - 'ARDOURBACKEND_DLL_EXPORTS', 'COREAUDIO_108' + 'ARDOURBACKEND_DLL_EXPORTS' ] # use new coreaudio API (the old one was deprecated in 10.6, yet still works) |