summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2015-03-07 04:15:02 +0100
committerRobin Gareus <robin@gareus.org>2015-03-07 14:35:32 +0100
commitce3adfd3d4c1a9e346ed9ecdc9e605346f483f98 (patch)
tree13d482217db123247046713aaed3d897a1790262 /libs
parente99599c7db97d6a02e4fb261b91b12eb88599057 (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
Diffstat (limited to 'libs')
-rw-r--r--libs/backends/coreaudio/coreaudio_backend.cc213
-rw-r--r--libs/backends/coreaudio/coreaudio_backend.h12
-rw-r--r--libs/backends/coreaudio/coreaudio_pcmio.cc777
-rw-r--r--libs/backends/coreaudio/coreaudio_pcmio.h61
-rw-r--r--libs/backends/coreaudio/coreaudio_pcmio_aggregate.cc358
-rw-r--r--libs/backends/coreaudio/coremidi_io.cc124
-rw-r--r--libs/backends/coreaudio/coremidi_io.h16
-rw-r--r--libs/backends/coreaudio/wscript2
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"), &notifyProc, 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"), &notifyProc, 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)