* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <glibmm.h>
#include "coreaudio_pcmio.h"
-#include <string>
-
+#ifdef COREAUDIO_108
+static OSStatus hardwarePropertyChangeCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) {
+ CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
+ self->hwPropertyChange();
+ return noErr;
+}
+#else
static OSStatus hardwarePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* arg) {
if (inPropertyID == kAudioHardwarePropertyDevices) {
CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
}
return noErr;
}
+#endif
CoreAudioPCM::CoreAudioPCM ()
: _auhal (0)
- , _deviceIDs (0)
- , _inputAudioBufferList (0)
+ , _device_ids (0)
+ , _input_audio_buffer_list (0)
, _state (-1)
, _capture_channels (0)
, _playback_channels (0)
, _in_process (false)
- , _numDevices (0)
+ , _n_devices (0)
, _process_callback (0)
, _error_callback (0)
+ , _hw_changed_callback (0)
, _device_ins (0)
, _device_outs (0)
{
-#ifdef COREAUDIO_108 // TODO
+ pthread_mutex_init (&_discovery_lock, 0);
+
+#ifdef COREAUDIO_108
CFRunLoopRef theRunLoop = NULL;
AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioHardwarePropertyDevices };
AudioObjectSetPropertyData (kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
-#endif
+
+ AudioObjectPropertyAddress prop;
+ prop.mSelector = kAudioHardwarePropertyDevices;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = 0;
+ AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, hardwarePropertyChangeCallback, this);
+#else
AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, hardwarePropertyChangeCallback, this);
+#endif
}
CoreAudioPCM::~CoreAudioPCM ()
if (_state == 0) {
pcm_stop();
}
- delete _deviceIDs;
+ delete _device_ids;
free(_device_ins);
free(_device_outs);
+#ifdef COREAUDIO_108
+ AudioObjectPropertyAddress prop;
+ prop.mSelector = kAudioHardwarePropertyDevices;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = 0;
+ AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &hardwarePropertyChangeCallback, this);
+#else
AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, hardwarePropertyChangeCallback);
- free(_inputAudioBufferList);
+#endif
+ free(_input_audio_buffer_list);
+ pthread_mutex_destroy (&_discovery_lock);
}
void
CoreAudioPCM::hwPropertyChange() {
- printf("hardwarePropertyChangeCallback\n");
discover();
+ // TODO Filter events..
+ if (_hw_changed_callback) {
+ _hw_changed_callback(_hw_changed_arg);
+ }
}
-void
-CoreAudioPCM::discover() {
- OSStatus err;
- UInt32 propSize = 0;
- // TODO trymutex lock.
+int
+CoreAudioPCM::available_sample_rates(uint32_t device_id, std::vector<float>& sampleRates)
+{
+ OSStatus err;
+ UInt32 size = 0;
+ sampleRates.clear();
- if (_deviceIDs) {
- delete _deviceIDs; _deviceIDs = 0;
- free(_device_ins); _device_ins = 0;
- free(_device_outs); _device_outs = 0;
+ if (device_id >= _n_devices) {
+ return -1;
}
- _devices.clear();
#ifdef COREAUDIO_108
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mSelector = kAudioHardwarePropertyDevices;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propSize);
+ 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 = AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &propSize, NULL);
+ err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &size, NULL);
#endif
- _numDevices = propSize / sizeof (AudioDeviceID);
- propSize = _numDevices * sizeof (AudioDeviceID);
+ if (err != kAudioHardwareNoError) {
+ return -1;
+ }
- _deviceIDs = new AudioDeviceID[_numDevices];
- _device_ins = (uint32_t*) calloc(_numDevices, sizeof(uint32_t));
- _device_outs = (uint32_t*) calloc(_numDevices, sizeof(uint32_t));
+ int numRates = size / sizeof(AudioValueRange);
+ AudioValueRange* supportedRates = new AudioValueRange[numRates];
#ifdef COREAUDIO_108
- propertyAddress.mSelector = kAudioHardwarePropertyDevices;
- err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propSize, _deviceIDs);
+ err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, supportedRates);
#else
- err = AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &propSize, _deviceIDs);
+ err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &size, supportedRates);
#endif
- for (size_t deviceIndex = 0; deviceIndex < _numDevices; deviceIndex++) {
- propSize = 64;
- char deviceName[64];
+ if (err != kAudioHardwareNoError) {
+ delete [] supportedRates;
+ return -1;
+ }
+
+ static const float ardourRates[] = { 8000.0, 22050.0, 24000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0};
+
+ for(uint32_t i = 0; i < sizeof(ardourRates)/sizeof(float); ++i) {
+ for(uint32_t j = 0; j < numRates; ++j) {
+ if ((supportedRates[j].mMinimum <= ardourRates[i]) &&
+ (supportedRates[j].mMaximum >= ardourRates[i])) {
+ sampleRates.push_back (ardourRates[i]);
+ break;
+ }
+ }
+ }
+
+ delete [] supportedRates;
+ return 0;
+}
+
+int
+CoreAudioPCM::available_buffer_sizes(uint32_t device_id, std::vector<uint32_t>& bufferSizes)
+{
+ OSStatus err;
+ UInt32 size = 0;
+ bufferSizes.clear();
+
+ if (device_id >= _n_devices) {
+ return -1;
+ }
+
+ AudioValueRange supportedRange;
+ size = sizeof (AudioValueRange);
+
#ifdef COREAUDIO_108
- propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- err = AudioObjectGetPropertyData(_deviceIDs[deviceIndex], &propertyAddress, 0, NULL, &propSize, deviceName);
+ AudioObjectPropertyAddress property_address;
+ property_address.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
+ err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &supportedRange);
#else
- err = AudioDeviceGetProperty(_deviceIDs[deviceIndex], 0, 0, kAudioDevicePropertyDeviceName, &propSize, deviceName);
+ err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &size, &supportedRange);
#endif
- if (kAudioHardwareNoError != err) {
- fprintf(stderr, "device name query failed: %i\n", err);
- continue;
+ if (err != kAudioHardwareNoError) {
+ return -1;
+ }
+
+ static const uint32_t ardourSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
+
+ for(uint32_t i = 0; i < sizeof(ardourSizes)/sizeof(uint32_t); ++i) {
+ if ((supportedRange.mMinimum <= ardourSizes[i]) &&
+ (supportedRange.mMaximum >= ardourSizes[i])) {
+ bufferSizes.push_back (ardourSizes[i]);
}
+ }
- UInt32 size;
- UInt32 outputChannelCount = 0;
- UInt32 inputChannelCount = 0;
- AudioBufferList *bufferList = NULL;
+ if (bufferSizes.empty()) {
+ bufferSizes.push_back ((uint32_t)supportedRange.mMinimum);
+ bufferSizes.push_back ((uint32_t)supportedRange.mMaximum);
+ }
+ return 0;
+}
- /* query number of inputs */
+uint32_t
+CoreAudioPCM::available_channels(uint32_t device_id, bool input)
+{
+ OSStatus err;
+ UInt32 size = 0;
+ AudioBufferList *bufferList = NULL;
+ uint32_t channel_count = 0;
+
+ if (device_id >= _n_devices) {
+ return 0;
+ }
+
+ /* query number of inputs */
#ifdef COREAUDIO_108
- size = 0;
- propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- err = AudioObjectGetPropertyDataSize(_deviceIDs[deviceIndex], &propertyAddress, 0, NULL, &size);
- if (kAudioHardwareNoError != err) {
- fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
- continue;
- }
+ AudioObjectPropertyAddress property_address;
+ property_address.mSelector = kAudioDevicePropertyStreamConfiguration;
+ property_address.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
+ err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size);
+ 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"); break; }
+ bufferList = (AudioBufferList *)(malloc(size));
+ assert(bufferList);
+ if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); return 0; }
- err = AudioObjectGetPropertyData(_deviceIDs[deviceIndex], &propertyAddress, 0, NULL, &size, bufferList);
+ err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, bufferList);
#else
- err = AudioDeviceGetPropertyInfo (_deviceIDs[deviceIndex], 0, AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
- if (kAudioHardwareNoError != err) {
- fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
- continue;
- }
- bufferList = (AudioBufferList *)(malloc(size));
- assert(bufferList);
- if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); break; }
+ 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->mNumberBuffers = 0;
- err = AudioDeviceGetProperty(_deviceIDs[deviceIndex], 0, AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
+ 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
- if(kAudioHardwareNoError != err) {
- fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
- free(bufferList);
- continue;
- }
- for(UInt32 j = 0; j < bufferList->mNumberBuffers; ++j) {
- outputChannelCount += bufferList->mBuffers[j].mNumberChannels;
- }
+ if(kAudioHardwareNoError != err) {
+ fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
free(bufferList);
+ return 0;
+ }
+ for(UInt32 j = 0; j < bufferList->mNumberBuffers; ++j) {
+ channel_count += bufferList->mBuffers[j].mNumberChannels;
+ }
+ free(bufferList);
+ return channel_count;
+}
+
+void
+CoreAudioPCM::get_stream_latencies(uint32 device_id, bool input, std::vector<uint32>& latencies)
+{
+ OSStatus err;
+ UInt32 size = 0;
+
+ if (device_id >= _n_devices) {
+ return;
+ }
- /* query number of inputs */
#ifdef COREAUDIO_108
- size = 0;
- propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
- propertyAddress.mScope = kAudioDevicePropertyScopeInput;
- err = AudioObjectGetPropertyDataSize(_deviceIDs[deviceIndex], &propertyAddress, 0, NULL, &size);
- if (kAudioHardwareNoError != err) {
- fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
- continue;
- }
+ 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;
+ }
- bufferList = (AudioBufferList *)(malloc(size));
- assert(bufferList);
- if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); break; }
+ uint32 stream_count = size / sizeof(UInt32);
+ AudioStreamID streamIDs[stream_count];
- err = AudioObjectGetPropertyData(_deviceIDs[deviceIndex], &propertyAddress, 0, NULL, &size, bufferList);
+#ifdef COREAUDIO_108
+ err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &streamIDs);
#else
- err = AudioDeviceGetPropertyInfo (_deviceIDs[deviceIndex], 0, AUHAL_INPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
- if (kAudioHardwareNoError != err) {
- fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
- continue;
- }
- bufferList = (AudioBufferList *)(malloc(size));
- assert(bufferList);
- if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); break; }
+ err = AudioDeviceGetProperty(_device_ids[device_id], 0, elem, kAudioDevicePropertyStreams, &size, streamIDs);
+#endif
+ if (err != noErr) {
+ fprintf(stderr, "GetStreamLatencies kAudioDevicePropertyStreams\n");
+ return;
+ }
- bufferList->mNumberBuffers = 0;
- err = AudioDeviceGetProperty(_deviceIDs[deviceIndex], 0, AUHAL_INPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
+ for (uint32 i = 0; i < stream_count; i++) {
+ UInt32 stream_latency;
+ size = sizeof(UInt32);
+#ifdef COREAUDIO_108
+ property_address.mSelector = kAudioDevicePropertyStreams;
+ err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &stream_latency);
+#else
+ err = AudioStreamGetProperty(streamIDs[i], elem, kAudioStreamPropertyLatency, &size, &stream_latency);
#endif
- if(kAudioHardwareNoError != err) {
- fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
- free(bufferList);
- continue;
+ if (err != noErr) {
+ fprintf(stderr, "GetStreamLatencies kAudioStreamPropertyLatency\n");
+ return;
}
+#ifndef NDEBUG
+ printf("Stream %d latency: %d\n", i, stream_latency);
+#endif
+ latencies.push_back(stream_latency);
+ }
+}
+
+uint32_t
+CoreAudioPCM::get_latency(uint32 device_id, bool input)
+{
+ OSStatus err;
+ uint32 latency = 0;
+ UInt32 size = sizeof(UInt32);
+ UInt32 lat0 = 0;
+ UInt32 latS = 0;
+
+ if (device_id >= _n_devices) {
+ 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
+ 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
+ if (err != kAudioHardwareNoError) {
+ fprintf(stderr, "GetLatency kAudioDevicePropertySafetyOffset\n");
+ }
+
+#ifndef NDEBUG
+ printf("Base Latency systemic+safetyoffset = %d+%d\n", lat0, latS);
+#endif
+ latency = lat0 + latS;
+
+ uint32_t max_stream_latency = 0;
+ std::vector<uint32> 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]);
+ }
+ latency += max_stream_latency;
+
+ return latency;
+}
+
+
+
+
+float
+CoreAudioPCM::current_sample_rate(uint32 device_id, bool input) {
+ OSStatus err;
+ UInt32 size = 0;
+
+ if (device_id >= _n_devices) {
+ return -1;
+ }
+
+ float sample_rate = 0;
+
+ Float64 rate;
+ size = sizeof (rate);
- for(UInt32 j = 0; j < bufferList->mNumberBuffers; ++j) {
- inputChannelCount += bufferList->mBuffers[j].mNumberChannels;
+#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;
+ }
+
+ // 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;
+ }
+
+ return sample_rate;
+}
+
+int
+CoreAudioPCM::set_device_sample_rate (uint32 device_id, float rate, bool input)
+{
+ std::vector<int>::iterator intIter;
+ OSStatus err;
+ UInt32 size = 0;
+
+ if (current_sample_rate(device_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
+ if (err != noErr) {
+ fprintf(stderr, "CoreAudioPCM: failed to set samplerate\n");
+ return 0;
+ }
+
+ int timeout = 3000; // 3 sec
+ while (--timeout > 0) {
+ if (current_sample_rate(device_id) == rate) {
+ break;
}
- free(bufferList);
+ Glib::usleep (1000);
+ }
+ fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate took %d ms.\n", (3000 - timeout));
+
+ if (timeout == 0) {
+ fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate timed out.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+CoreAudioPCM::discover()
+{
+ OSStatus err;
+ UInt32 size = 0;
+
+ if (pthread_mutex_trylock (&_discovery_lock)) {
+ return;
+ }
+
+ if (_device_ids) {
+ delete _device_ids; _device_ids = 0;
+ free(_device_ins); _device_ins = 0;
+ free(_device_outs); _device_outs = 0;
+ }
+ _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
+
+ _n_devices = size / sizeof (AudioDeviceID);
+ size = _n_devices * sizeof (AudioDeviceID);
+
+ _device_ids = new AudioDeviceID[_n_devices];
+ _device_ins = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
+ _device_outs = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
+
+ assert(_device_ins && _device_outs && _device_ids);
+ if (!_device_ins || !_device_ins || !_device_ids) {
+ fprintf(stderr, "OUT OF MEMORY\n");
+ _device_ids = 0;
+ _device_ins = 0;
+ _device_outs = 0;
+ pthread_mutex_unlock (&_discovery_lock);
+ 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
+
+ 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
+
+ if (kAudioHardwareNoError != err) {
+ fprintf(stderr, "CoreAudioPCM: device name query failed: %i\n", err);
+ continue;
+ }
+
+ UInt32 inputChannelCount = available_channels(idx, true);
+ UInt32 outputChannelCount = available_channels(idx, false);
+
{
std::string dn = deviceName;
- _device_ins[deviceIndex] = inputChannelCount;
- _device_outs[deviceIndex] = outputChannelCount;
- printf("CoreAudio Device: #%ld '%s' in:%d out:%d\n", deviceIndex, deviceName, inputChannelCount, outputChannelCount);
+ _device_ins[idx] = inputChannelCount;
+ _device_outs[idx] = outputChannelCount;
+#ifndef NDEBUG
+ printf("CoreAudio Device: #%ld '%s' in:%d out:%d\n", idx, deviceName, inputChannelCount, outputChannelCount);
+#endif
if (outputChannelCount > 0 && inputChannelCount > 0) {
- _devices.insert (std::pair<size_t, std::string> (deviceIndex, dn));
+ _devices.insert (std::pair<size_t, std::string> (idx, dn));
}
}
}
+ pthread_mutex_unlock (&_discovery_lock);
}
void
CoreAudioPCM::pcm_stop ()
{
- printf("CoreAudioPCM::pcm_stop\n");
if (!_auhal) return;
AudioOutputUnitStop(_auhal);
_capture_channels = 0;
_playback_channels = 0;
- free(_inputAudioBufferList);
- _inputAudioBufferList = 0;
+ free(_input_audio_buffer_list);
+ _input_audio_buffer_list = 0;
+
+ _input_names.clear();
+ _output_names.clear();
_error_callback = 0;
_process_callback = 0;
#ifndef NDEBUG
static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
{
- printf ("- - - - - - - - - - - - - - - - - - - -\n");
- printf (" Sample Rate:%f", inDesc->mSampleRate);
- printf (" Format ID:%.*s\n", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID);
- printf (" Format Flags:%X\n", inDesc->mFormatFlags);
- printf (" Bytes per Packet:%d\n", inDesc->mBytesPerPacket);
- printf (" Frames per Packet:%d\n", inDesc->mFramesPerPacket);
- printf (" Bytes per Frame:%d\n", inDesc->mBytesPerFrame);
- printf (" Channels per Frame:%d\n", inDesc->mChannelsPerFrame);
- printf (" Bits per Channel:%d\n", inDesc->mBitsPerChannel);
- printf ("- - - - - - - - - - - - - - - - - - - -\n");
+ printf ("- - - - - - - - - - - - - - - - - - - -\n");
+ printf (" Sample Rate:%f", inDesc->mSampleRate);
+ printf (" Format ID:%.*s\n", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID);
+ printf (" Format Flags:%X\n", inDesc->mFormatFlags);
+ printf (" Bytes per Packet:%d\n", inDesc->mBytesPerPacket);
+ printf (" Frames per Packet:%d\n", inDesc->mFramesPerPacket);
+ printf (" Bytes per Frame:%d\n", inDesc->mBytesPerFrame);
+ printf (" Channels per Frame:%d\n", inDesc->mChannelsPerFrame);
+ printf (" Bits per Channel:%d\n", inDesc->mBitsPerChannel);
+ printf ("- - - - - - - - - - - - - - - - - - - -\n");
}
#endif
int (process_callback (void*)), void *process_arg)
{
- assert(_deviceIDs);
+ assert(_device_ids);
+ std::string errorMsg;
_state = -2;
- if (device_id_out >= _numDevices || device_id_in >= _numDevices) {
+ if (device_id_out >= _n_devices || device_id_in >= _n_devices) {
return -1;
}
_cur_samples_per_period = 0;
ComponentResult err;
- UInt32 enableIO;
+ UInt32 uint32val;
AudioStreamBasicDescription srcFormat, dstFormat;
-
+
AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd);
- if (!HALOutput) { goto error; }
+ if (!HALOutput) { errorMsg="AudioComponentFindNext"; goto error; }
err = AudioComponentInstanceNew(HALOutput, &_auhal);
- if (err != noErr) { goto error; }
+ if (err != noErr) { errorMsg="AudioComponentInstanceNew"; goto error; }
err = AudioUnitInitialize(_auhal);
- if (err != noErr) { goto error; }
+ if (err != noErr) { errorMsg="AudioUnitInitialize"; goto error; }
- enableIO = 1;
- err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &enableIO, sizeof(enableIO));
- if (err != noErr) { goto error; }
- enableIO = 1;
- err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, &enableIO, sizeof(enableIO));
- if (err != noErr) { goto error; }
+ // explicitly change samplerate of the device
+ if (set_device_sample_rate(device_id_in, sample_rate, true)) {
+ errorMsg="Failed to set SampleRate, Capture Device"; goto error;
+ }
+ if (set_device_sample_rate(device_id_out, sample_rate, false)) {
+ errorMsg="Failed to set SampleRate, Playback Device"; goto error;
+ }
- err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, &_deviceIDs[device_id_out], sizeof(AudioDeviceID));
- if (err != noErr) { goto error; }
+ // 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; }
+
+ 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
+
+ uint32val = 1;
+ err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &uint32val, sizeof(UInt32));
+ if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Input"; goto error; }
+ uint32val = 1;
+ 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_INPUT_ELEMENT, &_deviceIDs[device_id_in], sizeof(AudioDeviceID));
- if (err != noErr) { 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));
+ 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) { goto error; }
+ 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) { goto error; }
-
+ if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Output"; goto error; }
// set sample format
srcFormat.mSampleRate = sample_rate;
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));
- if (err != noErr) { goto error; }
+#endif
+ if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat, Output"; goto error; }
dstFormat.mSampleRate = sample_rate;
dstFormat.mFormatID = kAudioFormatLinearPCM;
dstFormat.mChannelsPerFrame = _device_outs[device_id_out];
dstFormat.mBitsPerChannel = 32;
+#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));
- if (err != noErr) { goto error; }
+#endif
+ if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; goto error; }
UInt32 size;
size = sizeof(AudioStreamBasicDescription);
err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size);
- if (err != noErr) { goto error; }
+ if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Output"; goto error; }
_capture_channels = srcFormat.mChannelsPerFrame;
#ifndef NDEBUG
PrintStreamDesc(&srcFormat);
size = sizeof(AudioStreamBasicDescription);
err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, &size);
- if (err != noErr) { goto error; }
+ if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Input"; goto error; }
_playback_channels = dstFormat.mChannelsPerFrame;
#ifndef NDEBUG
PrintStreamDesc(&dstFormat);
#endif
- _inputAudioBufferList = (AudioBufferList*)malloc(sizeof(UInt32) + _capture_channels * sizeof(AudioBuffer));
+ _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; }
// Setup callbacks
AURenderCallbackStruct renderCallback;
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT,
&renderCallback, sizeof (renderCallback));
- if (err != noErr) { goto error; }
-
- printf("SETUP OK..\n");
+ if (err != noErr) { errorMsg="kAudioUnitProperty_SetRenderCallback"; goto error; }
if (AudioOutputUnitStart(_auhal) == noErr) {
- printf("Coreaudio Started..\n");
+ _input_names.clear();
+ _output_names.clear();
+ cache_port_names( device_id_in, true);
+ cache_port_names( device_id_out, false);
_state = 0;
return 0;
}
error:
+ char *rv = (char*)&err;
+ 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;
return -1;
}
+void
+CoreAudioPCM::cache_port_names(uint32 device_id, bool input)
+{
+ uint32_t n_chn;
+ assert (device_id < _n_devices);
+
+ if (input) {
+ n_chn = _capture_channels;
+ } else {
+ n_chn = _playback_channels;;
+ }
+#ifdef COREAUDIO_108
+ 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) {
+ CFStringRef name = NULL;
+ std::stringstream ss;
+ UInt32 size = 0;
+ OSStatus err;
+
+#ifdef COREAUDIO_108
+ property_address.mElement = c + 1;
+ err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size);
+#else
+ err = AudioDeviceGetPropertyInfo (_device_ids[device_id], c + 1, elem,
+ kAudioDevicePropertyChannelNameCFString,
+ &size,
+ NULL);
+#endif
+
+ if (err == kAudioHardwareNoError) {
+#ifdef COREAUDIO_108
+ err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, c + 1, NULL, &size, &name);
+#else
+ err = AudioDeviceGetProperty (_device_ids[device_id], c + 1, elem,
+ kAudioDevicePropertyChannelNameCFString,
+ &size,
+ &name);
+#endif
+ }
+
+ bool decoded = false;
+ char* cstr_name = 0;
+ if (err == kAudioHardwareNoError) {
+ CFIndex length = CFStringGetLength(name);
+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
+ cstr_name = new char[maxSize];
+ decoded = CFStringGetCString(name, cstr_name, maxSize, kCFStringEncodingUTF8);
+ }
+
+ 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);
+ }
+ }
+
+ printf("%s %d Name: %s\n", input ? "Input" : "Output", c+1, ss.str().c_str());
+
+ if (input) {
+ _input_names.push_back (ss.str());
+ } else {
+ _output_names.push_back (ss.str());
+ }
+
+ if (name) {
+ CFRelease (name);
+ }
+ delete [] cstr_name;
+ }
+}
+
+std::string
+CoreAudioPCM::cached_port_name(uint32 port, bool input) const
+{
+ if (_state != 0) { return ""; }
+
+ if (input) {
+ if (port > _input_names.size()) {
+ return "";
+ }
+ return _input_names[port];
+ } else {
+ if (port > _output_names.size()) {
+ return "";
+ }
+ return _output_names[port];
+ }
+}
+
OSStatus
CoreAudioPCM::render_callback (
_cur_samples_per_period = inNumberFrames;
- _inputAudioBufferList->mNumberBuffers = _capture_channels;
- for (int i = 0; i < _capture_channels; ++i) {
- _inputAudioBufferList->mBuffers[i].mNumberChannels = 1;
- _inputAudioBufferList->mBuffers[i].mDataByteSize = inNumberFrames * sizeof(float);
- _inputAudioBufferList->mBuffers[i].mData = NULL;
+ _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, _inputAudioBufferList);
+ retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, _input_audio_buffer_list);
- if (retVal != kAudioHardwareNoError) {
+ if (retVal != kAudioHardwareNoError) {
+#if 0
char *rv = (char*)&retVal;
printf("ERR %c%c%c%c\n", rv[0], rv[1], rv[2], rv[3]);
+#endif
if (_error_callback) {
_error_callback(_error_arg);
}
return retVal;
}
- _outputAudioBufferList = ioData;
+ _output_audio_buffer_list = ioData;
_in_process = true;
if (rv != 0) {
// clear output
- for (int i = 0; i < ioData->mNumberBuffers; ++i) {
+ for (uint32_t i = 0; i < ioData->mNumberBuffers; ++i) {
float* ob = (float*) ioData->mBuffers[i].mData;
memset(ob, 0, sizeof(float) * inNumberFrames);
}
if (!_in_process || chn > _capture_channels || n_samples > _cur_samples_per_period) {
return -1;
}
- assert(_inputAudioBufferList->mNumberBuffers > chn);
- memcpy((void*)input, (void*)_inputAudioBufferList->mBuffers[chn].mData, sizeof(float) * n_samples);
+ assert(_input_audio_buffer_list->mNumberBuffers > chn);
+ memcpy((void*)input, (void*)_input_audio_buffer_list->mBuffers[chn].mData, sizeof(float) * n_samples);
return 0;
}
return -1;
}
- assert(_outputAudioBufferList->mNumberBuffers > chn);
- memcpy((void*)_outputAudioBufferList->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples);
+ assert(_output_audio_buffer_list->mNumberBuffers > chn);
+ memcpy((void*)_output_audio_buffer_list->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples);
return 0;
}
+
+
+void
+CoreAudioPCM::launch_control_app (uint32_t device_id)
+{
+ if (device_id >= _n_devices) {
+ return;
+ }
+
+ CFStringRef config_app = NULL;
+ 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
+ if (kAudioHardwareNoError != err) {
+ return;
+ }
+
+ FSRef appFSRef;
+ if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, config_app, NULL, &appFSRef, NULL)) {
+ LSOpenFSRef(&appFSRef, NULL);
+ } else {
+ // open default AudioMIDISetup if device app is not found
+ CFStringRef audioMidiSetup = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.audio.AudioMIDISetup", kCFStringEncodingMacRoman);
+ if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, audioMidiSetup, NULL, &appFSRef, NULL)) {
+ LSOpenFSRef(&appFSRef, NULL);
+ }
+ }
+ if (config_app) {
+ CFRelease (config_app);
+ }
+}