| @@ -25,9 +25,19 @@ Paul Davis | |||
| Jackdmp changes log | |||
| --------------------------- | |||
| 2009-10-27 Stephane Letz <letz@grame.fr> | |||
| * Dynamic system version detection in JackCoreAudioDriver to either create public or private aggregate device. | |||
| 2009-10-26 Stephane Letz <letz@grame.fr> | |||
| * Implement "hog mode" (exclusive access of the audio device) in JackCoreAudioDriver. | |||
| * Fix jack_set_sample_rate_callback to have he same behavior as in JACK1. | |||
| 2009-10-25 Stephane Letz <letz@grame.fr> | |||
| * Improve aggregate device management in JackCoreAudioDriver : now a "private" device only and cleanup properly. | |||
| * Aggregate device code added to JackCoreAudioAdapter. | |||
| 2009-10-23 Stephane Letz <letz@grame.fr> | |||
| @@ -874,6 +874,9 @@ int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg | |||
| GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL); | |||
| fSampleRateArg = arg; | |||
| fSampleRate = callback; | |||
| // Now invoke it | |||
| if (callback) | |||
| callback(GetEngineControl()->fSampleRate, arg); | |||
| return 0; | |||
| } | |||
| } | |||
| @@ -396,7 +396,7 @@ OSStatus JackCoreAudioAdapter::GetDefaultDevice(AudioDeviceID* id) | |||
| jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); | |||
| // Get the device only if default input and ouput are the same | |||
| // Get the device only if default input and output are the same | |||
| if (inDefault == outDefault) { | |||
| *id = inDefault; | |||
| return noErr; | |||
| @@ -491,19 +491,35 @@ int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid, | |||
| // Duplex | |||
| if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) { | |||
| jack_log("JackCoreAudioAdapter::Open duplex"); | |||
| if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { | |||
| if (GetDefaultDevice(&fDeviceID) != noErr) { | |||
| jack_error("Cannot open default device"); | |||
| // Same device for capture and playback... | |||
| if (strcmp(capture_driver_uid, playback_driver_uid) == 0) { | |||
| if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { | |||
| jack_log("Will take default in/out"); | |||
| if (GetDefaultDevice(&fDeviceID) != noErr) { | |||
| jack_error("Cannot open default device"); | |||
| return -1; | |||
| } | |||
| } | |||
| if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { | |||
| jack_error("Cannot get device name from device ID"); | |||
| return -1; | |||
| } | |||
| } | |||
| if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { | |||
| jack_error("Cannot get device name from device ID"); | |||
| return -1; | |||
| } else { | |||
| // Creates aggregate device | |||
| AudioDeviceID captureID, playbackID; | |||
| if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) | |||
| return -1; | |||
| if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) | |||
| return -1; | |||
| if (CreateAggregateDevice(captureID, playbackID, &fDeviceID) != noErr) | |||
| return -1; | |||
| } | |||
| // Capture only | |||
| // Capture only | |||
| } else if (strcmp(capture_driver_uid, "") != 0) { | |||
| jack_log("JackCoreAudioAdapter::Open capture only"); | |||
| if (GetDeviceIDFromUID(capture_driver_uid, &fDeviceID) != noErr) { | |||
| @@ -517,7 +533,7 @@ int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid, | |||
| return -1; | |||
| } | |||
| // Playback only | |||
| // Playback only | |||
| } else if (strcmp(playback_driver_uid, "") != 0) { | |||
| jack_log("JackCoreAudioAdapter::Open playback only"); | |||
| if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { | |||
| @@ -531,7 +547,7 @@ int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid, | |||
| return -1; | |||
| } | |||
| // Use default driver in duplex mode | |||
| // Use default driver in duplex mode | |||
| } else { | |||
| jack_log("JackCoreAudioAdapter::Open default driver"); | |||
| if (GetDefaultDevice(&fDeviceID) != noErr) { | |||
| @@ -903,6 +919,194 @@ int JackCoreAudioAdapter::OpenAUHAL(bool capturing, | |||
| return 0; | |||
| } | |||
| OSStatus JackCoreAudioAdapter::DestroyAggregateDevice() | |||
| { | |||
| OSStatus osErr = noErr; | |||
| AudioObjectPropertyAddress pluginAOPA; | |||
| pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice; | |||
| pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; | |||
| pluginAOPA.mElement = kAudioObjectPropertyElementMaster; | |||
| UInt32 outDataSize; | |||
| osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyData error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| return noErr; | |||
| } | |||
| static CFStringRef GetDeviceName(AudioDeviceID id) | |||
| { | |||
| UInt32 size = sizeof(CFStringRef); | |||
| CFStringRef UIname; | |||
| OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); | |||
| return (err == noErr) ? UIname : NULL; | |||
| } | |||
| OSStatus JackCoreAudioAdapter::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, AudioDeviceID* outAggregateDevice) | |||
| { | |||
| OSStatus osErr = noErr; | |||
| UInt32 outSize; | |||
| Boolean outWritable; | |||
| //--------------------------------------------------------------------------- | |||
| // Start to create a new aggregate by getting the base audio hardware plugin | |||
| //--------------------------------------------------------------------------- | |||
| char capture_name[256]; | |||
| char playback_name[256]; | |||
| GetDeviceNameFromID(captureDeviceID, capture_name); | |||
| GetDeviceNameFromID(playbackDeviceID, playback_name); | |||
| jack_info("Separated input = '%s' and output = '%s' devices, create a private aggregate device to handle them...", capture_name, playback_name); | |||
| osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); | |||
| if (osErr != noErr) | |||
| return osErr; | |||
| AudioValueTranslation pluginAVT; | |||
| CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio"); | |||
| pluginAVT.mInputData = &inBundleRef; | |||
| pluginAVT.mInputDataSize = sizeof(inBundleRef); | |||
| pluginAVT.mOutputData = &fPluginID; | |||
| pluginAVT.mOutputDataSize = sizeof(fPluginID); | |||
| osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); | |||
| if (osErr != noErr) | |||
| return osErr; | |||
| //------------------------------------------------- | |||
| // Create a CFDictionary for our aggregate device | |||
| //------------------------------------------------- | |||
| CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |||
| CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex"); | |||
| CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex"); | |||
| // add the name of the device to the dictionary | |||
| CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef); | |||
| // add our choice of UID for the aggregate device to the dictionary | |||
| CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef); | |||
| // add a "private aggregate key" to the dictionary | |||
| int value = 1; | |||
| CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value); | |||
| SInt32 system; | |||
| Gestalt(gestaltSystemVersion, &system); | |||
| jack_log("JackCoreAudioDriver::CreateAggregateDevice : system version = %x limit = %x", system, 0x00001054); | |||
| // Starting with 10.5.4 systems, the AD can be internal... (better) | |||
| if (system < 0x00001054) { | |||
| jack_log("JackCoreAudioDriver::CreateAggregateDevice : public aggregate device...."); | |||
| } else { | |||
| jack_log("JackCoreAudioDriver::CreateAggregateDevice : private aggregate device...."); | |||
| CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); | |||
| } | |||
| //------------------------------------------------- | |||
| // Create a CFMutableArray for our sub-device list | |||
| //------------------------------------------------- | |||
| CFStringRef captureDeviceUID = GetDeviceName(captureDeviceID); | |||
| CFStringRef playbackDeviceUID = GetDeviceName(playbackDeviceID); | |||
| if (captureDeviceUID == NULL || playbackDeviceUID == NULL) | |||
| return -1; | |||
| // we need to append the UID for each device to a CFMutableArray, so create one here | |||
| CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |||
| // two sub-devices in this example, so append the sub-device's UID to the CFArray | |||
| CFArrayAppendValue(subDevicesArray, captureDeviceUID); | |||
| CFArrayAppendValue(subDevicesArray, playbackDeviceUID); | |||
| //----------------------------------------------------------------------- | |||
| // 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; | |||
| osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); | |||
| if (osErr != noErr) | |||
| return osErr; | |||
| osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); | |||
| if (osErr != noErr) | |||
| return osErr; | |||
| // 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); | |||
| osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); | |||
| if (osErr != noErr) | |||
| return osErr; | |||
| // 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); | |||
| osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID); // capture is master... | |||
| if (osErr != noErr) | |||
| return osErr; | |||
| // pause again to give the changes time to take effect | |||
| CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); | |||
| //---------- | |||
| // Clean up | |||
| //---------- | |||
| CFRelease(AggregateDeviceNumberRef); | |||
| // release the CF objects we have created - we don't need them any more | |||
| CFRelease(aggDeviceDict); | |||
| CFRelease(subDevicesArray); | |||
| // release the device UID | |||
| CFRelease(captureDeviceUID); | |||
| CFRelease(playbackDeviceUID); | |||
| jack_log("New aggregate device %ld", *outAggregateDevice); | |||
| return noErr; | |||
| } | |||
| void JackCoreAudioAdapter::CloseAUHAL() | |||
| { | |||
| AudioUnitUninitialize(fAUHAL); | |||
| @@ -923,6 +1127,8 @@ int JackCoreAudioAdapter::Close() | |||
| DisposeBuffers(); | |||
| CloseAUHAL(); | |||
| RemoveListeners(); | |||
| if (fPluginID > 0) | |||
| DestroyAggregateDevice(); | |||
| return 0; | |||
| } | |||
| @@ -54,7 +54,9 @@ class JackCoreAudioAdapter : public JackAudioAdapterInterface | |||
| bool fCapturing; | |||
| bool fPlaying; | |||
| AudioDeviceID fDeviceID; | |||
| AudioDeviceID fDeviceID; // Used "duplex" device | |||
| AudioObjectID fPluginID; // Used for aggregate device | |||
| bool fState; | |||
| AudioUnitRenderActionFlags* fActionFags; | |||
| @@ -86,6 +88,9 @@ class JackCoreAudioAdapter : public JackAudioAdapterInterface | |||
| OSStatus GetDeviceNameFromID(AudioDeviceID id, char* name); | |||
| // Setup | |||
| OSStatus CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, AudioDeviceID* outAggregateDevice); | |||
| OSStatus DestroyAggregateDevice(); | |||
| int SetupDevices(const char* capture_driver_uid, | |||
| const char* playback_driver_uid, | |||
| char* capture_driver_name, | |||
| @@ -321,7 +321,7 @@ OSStatus JackCoreAudioDriver::GetDefaultDevice(AudioDeviceID* id) | |||
| jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); | |||
| // Get the device only if default input and ouput are the same | |||
| // Get the device only if default input and output are the same | |||
| if (inDefault == outDefault) { | |||
| *id = inDefault; | |||
| return noErr; | |||
| @@ -390,7 +390,14 @@ OSStatus JackCoreAudioDriver::GetTotalChannels(AudioDeviceID device, int& channe | |||
| } | |||
| JackCoreAudioDriver::JackCoreAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) | |||
| : JackAudioDriver(name, alias, engine, table), fJackInputData(NULL), fDriverOutputData(NULL), fPluginID(0), fState(false), fIOUsage(1.f),fComputationGrain(-1.f) | |||
| : JackAudioDriver(name, alias, engine, table), | |||
| fJackInputData(NULL), | |||
| fDriverOutputData(NULL), | |||
| fPluginID(0), | |||
| fState(false), | |||
| fHogged(false), | |||
| fIOUsage(1.f), | |||
| fComputationGrain(-1.f) | |||
| {} | |||
| JackCoreAudioDriver::~JackCoreAudioDriver() | |||
| @@ -431,10 +438,19 @@ OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceI | |||
| //--------------------------------------------------------------------------- | |||
| // Start to create a new aggregate by getting the base audio hardware plugin | |||
| //--------------------------------------------------------------------------- | |||
| char capture_name[256]; | |||
| char playback_name[256]; | |||
| GetDeviceNameFromID(captureDeviceID, capture_name); | |||
| GetDeviceNameFromID(playbackDeviceID, playback_name); | |||
| jack_info("Separated input = '%s' and output = '%s' devices, create a private aggregate device to handle them...", capture_name, playback_name); | |||
| osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); | |||
| if (osErr != noErr) | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| AudioValueTranslation pluginAVT; | |||
| @@ -446,8 +462,11 @@ OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceI | |||
| pluginAVT.mOutputDataSize = sizeof(fPluginID); | |||
| osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); | |||
| if (osErr != noErr) | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| //------------------------------------------------- | |||
| // Create a CFDictionary for our aggregate device | |||
| @@ -467,7 +486,19 @@ OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceI | |||
| // add a "private aggregate key" to the dictionary | |||
| int value = 1; | |||
| CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value); | |||
| CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); | |||
| SInt32 system; | |||
| Gestalt(gestaltSystemVersion, &system); | |||
| jack_log("JackCoreAudioDriver::CreateAggregateDevice : system version = %x limit = %x", system, 0x00001054); | |||
| // Starting with 10.5.4 systems, the AD can be internal... (better) | |||
| if (system < 0x00001054) { | |||
| jack_log("JackCoreAudioDriver::CreateAggregateDevice : public aggregate device...."); | |||
| } else { | |||
| jack_log("JackCoreAudioDriver::CreateAggregateDevice : private aggregate device...."); | |||
| CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); | |||
| } | |||
| //------------------------------------------------- | |||
| // Create a CFMutableArray for our sub-device list | |||
| @@ -497,12 +528,18 @@ OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceI | |||
| UInt32 outDataSize; | |||
| osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); | |||
| if (osErr != noErr) | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyDataSize error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); | |||
| if (osErr != noErr) | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyData error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| // 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 | |||
| @@ -517,9 +554,12 @@ OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceI | |||
| pluginAOPA.mElement = kAudioObjectPropertyElementMaster; | |||
| outDataSize = sizeof(CFMutableArrayRef); | |||
| osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); | |||
| if (osErr != noErr) | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| // pause again to give the changes time to take effect | |||
| CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); | |||
| @@ -534,9 +574,12 @@ OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceI | |||
| pluginAOPA.mElement = kAudioObjectPropertyElementMaster; | |||
| outDataSize = sizeof(CFStringRef); | |||
| osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID); // capture is master... | |||
| if (osErr != noErr) | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for master device error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| // pause again to give the changes time to take effect | |||
| CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); | |||
| @@ -544,6 +587,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceI | |||
| // Clean up | |||
| //---------- | |||
| // release the private AD key | |||
| CFRelease(AggregateDeviceNumberRef); | |||
| // release the CF objects we have created - we don't need them any more | |||
| @@ -584,7 +628,7 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, const char | |||
| } else { | |||
| // Creates agregate device | |||
| // Creates aggregate device | |||
| AudioDeviceID captureID, playbackID; | |||
| if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) | |||
| return -1; | |||
| @@ -594,7 +638,7 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, const char | |||
| return -1; | |||
| } | |||
| // Capture only | |||
| // Capture only | |||
| } else if (strcmp(capture_driver_uid, "") != 0) { | |||
| jack_log("JackCoreAudioDriver::Open capture only"); | |||
| if (GetDeviceIDFromUID(capture_driver_uid, &fDeviceID) != noErr) { | |||
| @@ -609,7 +653,7 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, const char | |||
| return -1; | |||
| } | |||
| // Playback only | |||
| // Playback only | |||
| } else if (strcmp(playback_driver_uid, "") != 0) { | |||
| jack_log("JackCoreAudioDriver::Open playback only"); | |||
| if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { | |||
| @@ -624,7 +668,7 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, const char | |||
| return -1; | |||
| } | |||
| // Use default driver in duplex mode | |||
| // Use default driver in duplex mode | |||
| } else { | |||
| jack_log("JackCoreAudioDriver::Open default driver"); | |||
| if (GetDefaultDevice(&fDeviceID) != noErr) { | |||
| @@ -636,6 +680,14 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, const char | |||
| return -1; | |||
| } | |||
| } | |||
| if (fHogged) { | |||
| if (TakeHog()) { | |||
| jack_info("Device = %ld has been hogged", fDeviceID); | |||
| } else { | |||
| jack_error("Cannot hog device = %ld", fDeviceID); | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -1065,7 +1117,8 @@ int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, | |||
| jack_nframes_t capture_latency, | |||
| jack_nframes_t playback_latency, | |||
| int async_output_latency, | |||
| int computation_grain) | |||
| int computation_grain, | |||
| bool hogged) | |||
| { | |||
| int in_nChannels = 0; | |||
| int out_nChannels = 0; | |||
| @@ -1084,14 +1137,15 @@ int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, | |||
| fPlaybackLatency = playback_latency; | |||
| fIOUsage = float(async_output_latency) / 100.f; | |||
| fComputationGrain = float(computation_grain) / 100.f; | |||
| fHogged = hogged; | |||
| SInt32 major; | |||
| SInt32 minor; | |||
| Gestalt(gestaltSystemVersionMajor, &major); | |||
| Gestalt(gestaltSystemVersionMinor, &minor); | |||
| // Starting with 10.6 systems... | |||
| if (major == 10 && minor >=6) { | |||
| // Starting with 10.6 systems, the HAL notification thread is created internally | |||
| if (major == 10 && minor >= 6) { | |||
| CFRunLoopRef theRunLoop = NULL; | |||
| AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; | |||
| OSStatus theError = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); | |||
| @@ -1328,6 +1382,53 @@ int JackCoreAudioDriver::SetBufferSize(jack_nframes_t buffer_size) | |||
| return 0; | |||
| } | |||
| bool JackCoreAudioDriver::TakeHogAux(AudioDeviceID deviceID, bool isInput) | |||
| { | |||
| pid_t hog_pid; | |||
| OSStatus err; | |||
| UInt32 propSize = sizeof(hog_pid); | |||
| err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyHogMode, &propSize, &hog_pid); | |||
| if (err) { | |||
| jack_error("Cannot read hog state..."); | |||
| printError(err); | |||
| } | |||
| if (hog_pid != getpid()) { | |||
| hog_pid = getpid(); | |||
| err = AudioDeviceSetProperty(deviceID, 0, 0, isInput, kAudioDevicePropertyHogMode, propSize, &hog_pid); | |||
| if (err != noErr) { | |||
| jack_error("Can't hog device = %d because it's being hogged by another program", deviceID); | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| bool JackCoreAudioDriver::TakeHog() | |||
| { | |||
| OSStatus err = noErr; | |||
| AudioObjectID sub_device[32]; | |||
| UInt32 outSize = sizeof(sub_device); | |||
| err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); | |||
| if (err != noErr) { | |||
| jack_log("Device does not have subdevices"); | |||
| return TakeHogAux(fDeviceID, true); | |||
| } else { | |||
| int num_devices = outSize / sizeof(AudioObjectID); | |||
| jack_log("Device does has %d subdevices", num_devices); | |||
| for (int i = 0; i < num_devices; i++) { | |||
| if (!TakeHogAux(sub_device[i], true)) { | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| } | |||
| } // end of namespace | |||
| @@ -1345,7 +1446,7 @@ extern "C" | |||
| strcpy(desc->name, "coreaudio"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 | |||
| strcpy(desc->desc, "Apple CoreAudio API based audio backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 | |||
| desc->nparams = 15; | |||
| desc->nparams = 16; | |||
| desc->params = (jack_driver_param_desc_t*)calloc(desc->nparams, sizeof(jack_driver_param_desc_t)); | |||
| i = 0; | |||
| @@ -1448,16 +1549,24 @@ extern "C" | |||
| strcpy(desc->params[i].name, "list-devices"); | |||
| desc->params[i].character = 'l'; | |||
| desc->params[i].type = JackDriverParamBool; | |||
| desc->params[i].value.i = TRUE; | |||
| desc->params[i].value.i = FALSE; | |||
| strcpy(desc->params[i].short_desc, "Display available CoreAudio devices"); | |||
| strcpy(desc->params[i].long_desc, desc->params[i].short_desc); | |||
| i++; | |||
| strcpy(desc->params[i].name, "hog"); | |||
| desc->params[i].character = 'H'; | |||
| desc->params[i].type = JackDriverParamBool; | |||
| desc->params[i].value.i = FALSE; | |||
| strcpy(desc->params[i].short_desc, "Take exclusive access of the audio device"); | |||
| strcpy(desc->params[i].long_desc, desc->params[i].short_desc); | |||
| i++; | |||
| strcpy(desc->params[i].name, "async-latency"); | |||
| desc->params[i].character = 'L'; | |||
| desc->params[i].type = JackDriverParamUInt; | |||
| desc->params[i].value.i = 100; | |||
| strcpy(desc->params[i].short_desc, "Extra output latency in aynchronous mode (percent)"); | |||
| strcpy(desc->params[i].short_desc, "Extra output latency in asynchronous mode (percent)"); | |||
| strcpy(desc->params[i].long_desc, desc->params[i].short_desc); | |||
| i++; | |||
| @@ -1488,6 +1597,7 @@ extern "C" | |||
| jack_nframes_t systemic_output_latency = 0; | |||
| int async_output_latency = 100; | |||
| int computation_grain = -1; | |||
| bool hogged = false; | |||
| for (node = params; node; node = jack_slist_next(node)) { | |||
| param = (const jack_driver_param_t *) node->data; | |||
| @@ -1554,6 +1664,10 @@ extern "C" | |||
| Jack::DisplayDeviceNames(); | |||
| break; | |||
| case 'H': | |||
| hogged = true; | |||
| break; | |||
| case 'L': | |||
| async_output_latency = param->value.ui; | |||
| break; | |||
| @@ -1572,7 +1686,7 @@ extern "C" | |||
| Jack::JackCoreAudioDriver* driver = new Jack::JackCoreAudioDriver("system", "coreaudio", engine, table); | |||
| if (driver->Open(frames_per_interrupt, srate, capture, playback, chan_in, chan_out, monitor, capture_driver_uid, | |||
| playback_driver_uid, systemic_input_latency, systemic_output_latency, async_output_latency, computation_grain) == 0) { | |||
| playback_driver_uid, systemic_input_latency, systemic_output_latency, async_output_latency, computation_grain, hogged) == 0) { | |||
| return driver; | |||
| } else { | |||
| delete driver; | |||
| @@ -53,13 +53,14 @@ class JackCoreAudioDriver : public JackAudioDriver | |||
| AudioBufferList* fJackInputData; | |||
| AudioBufferList* fDriverOutputData; | |||
| AudioDeviceID fDeviceID; | |||
| AudioObjectID fPluginID; | |||
| AudioDeviceID fDeviceID; // Used "duplex" device | |||
| AudioObjectID fPluginID; // Used for aggregate device | |||
| AudioUnitRenderActionFlags* fActionFags; | |||
| AudioTimeStamp* fCurrentTime; | |||
| bool fState; | |||
| bool fHogged; | |||
| // Initial state | |||
| bool fCapturing; | |||
| @@ -150,6 +151,9 @@ class JackCoreAudioDriver : public JackAudioDriver | |||
| int AddListeners(); | |||
| void RemoveListeners(); | |||
| bool TakeHogAux(AudioDeviceID deviceID, bool isInput); | |||
| bool TakeHog(); | |||
| public: | |||
| @@ -168,7 +172,8 @@ class JackCoreAudioDriver : public JackAudioDriver | |||
| jack_nframes_t capture_latency, | |||
| jack_nframes_t playback_latency, | |||
| int async_output_latency, | |||
| int computation_grain); | |||
| int computation_grain, | |||
| bool hogged); | |||
| int Close(); | |||
| int Attach(); | |||
| @@ -60,10 +60,6 @@ int JackSocketServerChannel::Open(const char* server_name, JackServer* server) | |||
| BuildPoolTable(); | |||
| fServer = server; | |||
| return 0; | |||
| error: | |||
| fRequestListenSocket.Close(); | |||
| return -1; | |||
| } | |||
| void JackSocketServerChannel::Close() | |||