more ongoing work on the coreaudio backend
authorRobin Gareus <robin@gareus.org>
Fri, 6 Mar 2015 20:26:07 +0000 (21:26 +0100)
committerRobin Gareus <robin@gareus.org>
Fri, 6 Mar 2015 22:26:05 +0000 (23:26 +0100)
* audio port names
* latency compensation
* xrun reporting
* various fixes and cleanup

libs/backends/coreaudio/coreaudio_backend.cc
libs/backends/coreaudio/coreaudio_backend.h
libs/backends/coreaudio/coreaudio_pcmio.cc
libs/backends/coreaudio/coreaudio_pcmio.h

index fd03ddea50985e43818df37ecf34b655a95350a1..31ee9636e1ee58ec345f82af293a3f4a74e572e9 100644 (file)
@@ -42,12 +42,32 @@ std::vector<AudioBackend::DeviceStatus> CoreAudioBackend::_audio_device_status;
 std::vector<AudioBackend::DeviceStatus> CoreAudioBackend::_midi_device_status;
 
 
+/* static class instance access */
 static void hw_changed_callback_ptr (void *arg)
 {
        CoreAudioBackend *d = static_cast<CoreAudioBackend*> (arg);
        d->hw_changed_callback();
 }
 
+static void error_callback_ptr (void *arg)
+{
+       CoreAudioBackend *d = static_cast<CoreAudioBackend*> (arg);
+       d->error_callback();
+}
+
+static void xrun_callback_ptr (void *arg)
+{
+       CoreAudioBackend *d = static_cast<CoreAudioBackend*> (arg);
+       d->xrun_callback();
+}
+
+static void midi_port_change (void *arg)
+{
+       CoreAudioBackend *d = static_cast<CoreAudioBackend *>(arg);
+       d->coremidi_rediscover ();
+}
+
+
 CoreAudioBackend::CoreAudioBackend (AudioEngine& e, AudioBackendInfo& info)
        : AudioBackend (e, info)
        , _run (false)
@@ -386,18 +406,6 @@ static int process_callback_ptr (void *arg)
        return d->process_callback();
 }
 
-static void error_callback_ptr (void *arg)
-{
-       CoreAudioBackend *d = static_cast<CoreAudioBackend*> (arg);
-       d->error_callback();
-}
-
-static void midi_port_change (void *arg)
-{
-       CoreAudioBackend *d = static_cast<CoreAudioBackend *>(arg);
-       d->coremidi_rediscover ();
-}
-
 int
 CoreAudioBackend::_start (bool for_latency_measurement)
 {
@@ -463,13 +471,13 @@ CoreAudioBackend::_start (bool for_latency_measurement)
                _samples_per_period = _pcmio->sample_per_period();
                PBD::warning << _("CoreAudioBackend: samples per period does not match.") << endmsg;
        }
+#endif
 
-       if (_pcmio->samplerate() != _samplerate) {
-               _samplerate = _pcmio->samplerate();
+       if (_pcmio->current_sample_rate(name_to_id(_audio_device)) != _samplerate) {
+               _samplerate = _pcmio->current_sample_rate(name_to_id(_audio_device));
                engine.sample_rate_change (_samplerate);
                PBD::warning << _("CoreAudioBackend: sample rate does not match.") << endmsg;
        }
-#endif
 
        _measure_latency = for_latency_measurement;
 
@@ -531,6 +539,7 @@ CoreAudioBackend::_start (bool for_latency_measurement)
                return -1;
        }
        _preinit = false;
+       _pcmio->set_xrun_callback (xrun_callback_ptr, this);
 
        return 0;
 }
@@ -856,31 +865,34 @@ int
 CoreAudioBackend::register_system_audio_ports()
 {
        LatencyRange lr;
-       printf("COREAUDIO LATENCY: i:%d, o:%d\n",
-                       _pcmio->get_latency(name_to_id(_audio_device), true),
-                       _pcmio->get_latency(name_to_id(_audio_device), false));
-
-       //TODO set latencies
-       //TODO query port names
 
        const int a_ins = _n_inputs > 0 ? _n_inputs : 2;
        const int a_out = _n_outputs > 0 ? _n_outputs : 2;
 
+       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);
+
+#ifndef NDEBUG
+       printf("COREAUDIO LATENCY: i:%d, o:%d\n",
+                       coreaudio_reported_input_latency,
+                       coreaudio_reported_output_latency);
+#endif
+
        /* audio ports */
-       lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : _systemic_audio_input_latency);
-       for (int i = 1; i <= a_ins; ++i) {
+       lr.min = lr.max = _samples_per_period + coreaudio_reported_input_latency + (_measure_latency ? 0 : _systemic_audio_input_latency);
+       for (int i = 0; i < a_ins; ++i) {
                char tmp[64];
-               snprintf(tmp, sizeof(tmp), "system:capture_%d", i);
+               snprintf(tmp, sizeof(tmp), "system:capture_%s", _pcmio->cached_port_name(i, true).c_str());
                PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
                if (!p) return -1;
                set_latency_range (p, false, lr);
                _system_inputs.push_back(static_cast<CoreBackendPort*>(p));
        }
 
-       lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : _systemic_audio_output_latency);
-       for (int i = 1; i <= a_out; ++i) {
+       lr.min = lr.max = _samples_per_period + coreaudio_reported_output_latency + (_measure_latency ? 0 : _systemic_audio_output_latency);
+       for (int i = 0; i < a_out; ++i) {
                char tmp[64];
-               snprintf(tmp, sizeof(tmp), "system:playback_%d", i);
+               snprintf(tmp, sizeof(tmp), "system:playback_%s", _pcmio->cached_port_name(i, false).c_str());
                PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
                if (!p) return -1;
                set_latency_range (p, true, lr);
@@ -1434,7 +1446,7 @@ CoreAudioBackend::process_callback ()
 
        /* get audio */
        i = 0;
-       for (std::vector<CoreBackendPort*>::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it) {
+       for (std::vector<CoreBackendPort*>::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it, ++i) {
                _pcmio->get_capture_channel (i, (float*)((*it)->get_buffer(n_samples)), n_samples);
        }
 
@@ -1480,8 +1492,6 @@ CoreAudioBackend::process_callback ()
        const int64_t elapsed_time = clock2 - clock1;
        _dsp_load = elapsed_time / (float) nominal_time;
 
-       //engine.Xrun (); // TODO, if any
-
        /* port-connection change */
        post_process();
        pthread_mutex_unlock (&_process_callback_mutex);
@@ -1495,6 +1505,12 @@ CoreAudioBackend::error_callback ()
        engine.halted_callback("CoreAudio Process aborted.");
 }
 
+void
+CoreAudioBackend::xrun_callback ()
+{
+       engine.Xrun ();
+}
+
 void
 CoreAudioBackend::hw_changed_callback ()
 {
index 5e243656ead78ba7e4b7d7025b9b0dadc19afdbe..715c067efc9dc77c0f6dd8acb8ece7e32285e239 100644 (file)
@@ -214,6 +214,7 @@ class CoreAudioBackend : public AudioBackend {
                // really private, but needing static access:
                int process_callback();
                void error_callback();
+               void xrun_callback();
                void hw_changed_callback();
 
        protected:
index 29207d4ffa1c8032d25a6f2d99d068d5f5ef1b23..7f9322bc1f58e9d982bacd42eb2109f90251a680 100644 (file)
 #include "coreaudio_pcmio.h"
 
 #ifdef COREAUDIO_108
-static OSStatus hardwarePropertyChangeCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) {
+static OSStatus hw_changed_callback_ptr(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) {
        CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
-       self->hwPropertyChange();
+       self->hw_changed_callback();
        return noErr;
 }
 #else
-static OSStatus hardwarePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* arg) {
+static OSStatus hw_changed_callback_ptr (AudioHardwarePropertyID inPropertyID, void* arg) {
        if (inPropertyID == kAudioHardwarePropertyDevices) {
                CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
-               self->hwPropertyChange();
+               self->hw_changed_callback();
        }
        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(
+               AudioDeviceID inDevice,
+               UInt32 inChannel,
+               Boolean isInput,
+               AudioDevicePropertyID inPropertyID,
+               void* inClientData)
+{
+       CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inClientData);
+       d->xrun_callback();
+       return noErr;
+}
+#endif
+
+static OSStatus render_callback_ptr (
+               void* inRefCon,
+               AudioUnitRenderActionFlags* ioActionFlags,
+               const AudioTimeStamp* inTimeStamp,
+               UInt32 inBusNumber,
+               UInt32 inNumberFrames,
+               AudioBufferList* ioData)
+{
+       CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inRefCon);
+       return d->render_callback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
+}
+
+
 CoreAudioPCM::CoreAudioPCM ()
        : _auhal (0)
        , _device_ids (0)
        , _input_audio_buffer_list (0)
+       , _active_input (0)
+       , _active_output (0)
        , _state (-1)
        , _capture_channels (0)
        , _playback_channels (0)
@@ -47,6 +82,7 @@ CoreAudioPCM::CoreAudioPCM ()
        , _process_callback (0)
        , _error_callback (0)
        , _hw_changed_callback (0)
+       , _xrun_callback (0)
        , _device_ins (0)
        , _device_outs (0)
 {
@@ -61,9 +97,9 @@ CoreAudioPCM::CoreAudioPCM ()
        prop.mSelector = kAudioHardwarePropertyDevices;
        prop.mScope = kAudioObjectPropertyScopeGlobal;
        prop.mElement = 0;
-       AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, hardwarePropertyChangeCallback, this);
+       AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, hw_changed_callback_ptr, this);
 #else
-       AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, hardwarePropertyChangeCallback, this);
+       AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, hw_changed_callback_ptr, this);
 #endif
 }
 
@@ -80,9 +116,9 @@ CoreAudioPCM::~CoreAudioPCM ()
        prop.mSelector = kAudioHardwarePropertyDevices;
        prop.mScope = kAudioObjectPropertyScopeGlobal;
        prop.mElement = 0;
-       AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &hardwarePropertyChangeCallback, this);
+       AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &hw_changed_callback_ptr, this);
 #else
-       AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, hardwarePropertyChangeCallback);
+       AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, hw_changed_callback_ptr);
 #endif
        free(_input_audio_buffer_list);
        pthread_mutex_destroy (&_discovery_lock);
@@ -90,7 +126,8 @@ CoreAudioPCM::~CoreAudioPCM ()
 
 
 void
-CoreAudioPCM::hwPropertyChange() {
+CoreAudioPCM::hw_changed_callback() {
+       printf("CHANGE..\n");
        discover();
        // TODO Filter events..
        if (_hw_changed_callback) {
@@ -305,7 +342,7 @@ CoreAudioPCM::get_stream_latencies(uint32 device_id, bool input, std::vector<uin
                        return;
                }
 #ifndef NDEBUG
-               printf("Stream %d latency: %d\n", i, stream_latency);
+               printf("  ^ Stream %d latency: %d\n", i, stream_latency);
 #endif
                latencies.push_back(stream_latency);
        }
@@ -349,7 +386,8 @@ CoreAudioPCM::get_latency(uint32 device_id, bool input)
        }
 
 #ifndef NDEBUG
-       printf("Base Latency systemic+safetyoffset = %d+%d\n", lat0, latS);
+       printf("%s Latency systemic+safetyoffset = %d + %d\n",
+                       input ? "Input" : "Output", lat0, latS);
 #endif
        latency = lat0 + latS;
 
@@ -542,12 +580,41 @@ CoreAudioPCM::discover()
        pthread_mutex_unlock (&_discovery_lock);
 }
 
+void
+CoreAudioPCM::xrun_callback ()
+{
+#ifndef NDEBUG
+       printf("Coreaudio XRUN\n");
+#endif
+       if (_xrun_callback) {
+               _xrun_callback(_xrun_arg);
+       }
+}
+
 void
 CoreAudioPCM::pcm_stop ()
 {
        if (!_auhal) return;
 
        AudioOutputUnitStop(_auhal);
+       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);
+               }
+#else
+               AudioDeviceRemovePropertyListener(_active_input, 0 , true, kAudioDeviceProcessorOverload, xrun_callback_ptr);
+               AudioDeviceRemovePropertyListener(_active_output, 0 , false, kAudioDeviceProcessorOverload, xrun_callback_ptr);
+#endif
+       }
+
        AudioUnitUninitialize(_auhal);
 #ifdef COREAUDIO_108
        AudioComponentInstanceDispose(_auhal);
@@ -567,6 +634,7 @@ CoreAudioPCM::pcm_stop ()
 
        _error_callback = 0;
        _process_callback = 0;
+       _xrun_callback = 0;
 }
 
 #ifndef NDEBUG
@@ -585,20 +653,6 @@ static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
 }
 #endif
 
-static OSStatus render_callback_ptr (
-               void* inRefCon,
-               AudioUnitRenderActionFlags* ioActionFlags,
-               const AudioTimeStamp* inTimeStamp,
-               UInt32 inBusNumber,
-               UInt32 inNumberFrames,
-               AudioBufferList* ioData)
-{
-       CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inRefCon);
-       return d->render_callback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
-}
-
-
-
 int
 CoreAudioPCM::pcm_start (
                uint32_t device_id_in, uint32_t device_id_out,
@@ -618,6 +672,7 @@ CoreAudioPCM::pcm_start (
        _process_arg = process_arg;
        _max_samples_per_period = samples_per_period;
        _cur_samples_per_period = 0;
+       _active_input = _active_output = 0;
 
        ComponentResult err;
        UInt32 uint32val;
@@ -715,6 +770,7 @@ CoreAudioPCM::pcm_start (
 #endif
        if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; goto error; }
 
+       /* read back stream descriptions */
        UInt32 size;
        size = sizeof(AudioStreamBasicDescription);
        err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size);
@@ -733,10 +789,32 @@ CoreAudioPCM::pcm_start (
        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; }
 
+       _active_input = _device_ids[device_id_in];
+       _active_output = _device_ids[device_id_out];
+
+#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
+
        // Setup callbacks
        AURenderCallbackStruct renderCallback;
        memset (&renderCallback, 0, sizeof (renderCallback));
@@ -748,6 +826,7 @@ CoreAudioPCM::pcm_start (
                        &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();
@@ -762,6 +841,7 @@ 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;
        return -1;
 }
 
@@ -810,7 +890,7 @@ CoreAudioPCM::cache_port_names(uint32 device_id, bool input)
                                        &name);
 #endif
                }
+
                bool decoded = false;
                char* cstr_name = 0;
                if (err == kAudioHardwareNoError) {
@@ -820,19 +900,14 @@ CoreAudioPCM::cache_port_names(uint32 device_id, bool input)
                        decoded = CFStringGetCString(name, cstr_name, maxSize, kCFStringEncodingUTF8);
                }
 
-               ss << (c + 1) << " - ";
+               ss << (c + 1);
 
                if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) {
-                       ss << cstr_name;
-               } else {
-                       if (input) {
-                               ss << "Input " << (c + 1);
-                       } else {
-                               ss << "Output " << (c + 1);
-                       }
+                       ss << " - " <<  cstr_name;
                }
-
+#if 0
                printf("%s %d Name: %s\n", input ? "Input" : "Output", c+1, ss.str().c_str());
+#endif
 
                if (input) {
                        _input_names.push_back (ss.str());
index b249dedbf3cbe89de9abe76bca877dea926c4ff0..6bb3d083565f5880abe2618e2b278fb19df36de8 100644 (file)
@@ -64,7 +64,6 @@ public:
                        void * process_arg
                        );
 
-       // TODO: combine callbacks below, add a enum type
        void     set_error_callback (
                        void ( error_callback (void*)),
                        void * error_arg
@@ -80,6 +79,13 @@ public:
                _hw_changed_callback = callback;
                _hw_changed_arg = arg;
        }
+       void     set_xrun_callback (
+                       void ( callback (void*)),
+                       void * arg
+                       ) {
+               _xrun_callback = callback;
+               _xrun_arg = arg;
+       }
 
        // must be called from process_callback;
        int      get_capture_channel (uint32_t chn, float *input, uint32_t n_samples);
@@ -94,7 +100,8 @@ public:
                        UInt32 inNumberFrames,
                        AudioBufferList* ioData);
 
-       void hwPropertyChange();
+       void xrun_callback ();
+       void hw_changed_callback ();
 
 private:
        int set_device_sample_rate (uint32 device_id, float rate, bool input);
@@ -106,6 +113,9 @@ private:
        AudioBufferList* _input_audio_buffer_list;
        AudioBufferList* _output_audio_buffer_list;
 
+       AudioDeviceID _active_input;
+       AudioDeviceID _active_output;
+
        int _state;
 
        uint32_t _max_samples_per_period;
@@ -124,6 +134,10 @@ private:
        void (* _hw_changed_callback) (void*);
        void  * _hw_changed_arg;
 
+       void (* _xrun_callback) (void*);
+       void  * _xrun_arg;
+
+
        // TODO proper device info struct
        std::map<size_t, std::string> _devices;
        uint32_t * _device_ins;