diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp index 16c75589..e22c86d8 100644 --- a/common/JackEngine.cpp +++ b/common/JackEngine.cpp @@ -236,6 +236,7 @@ void JackEngine::NotifyClients(int event, int sync, int value1, int value2) int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum) { + jack_log("JackEngine::NotifyAddClient: name = %s", name); // Notify existing clients of the new client and new client of existing clients. for (int i = 0; i < CLIENT_NUM; i++) { JackClientInterface* old_client = fClientTable[i]; diff --git a/dbus/audio_reserve.c b/dbus/audio_reserve.c index cae5eac9..895ddf09 100644 --- a/dbus/audio_reserve.c +++ b/dbus/audio_reserve.c @@ -67,7 +67,7 @@ SERVER_EXPORT void* audio_acquire(int num) NULL, &error)) < 0) { - jack_error ("Failed to acquire device: %s\n", error.message ? error.message : strerror(-e)); + jack_error("Failed to acquire device: %s", error.message ? error.message : strerror(-e)); return NULL; } @@ -89,5 +89,7 @@ SERVER_EXPORT void audio_release(void* dev) if (device) { jack_info("Release audio card"); rd_release(device); + } else { + jack_info("No audio card to release..."); } } diff --git a/macosx/coreaudio/JackCoreAudioDriver.cpp b/macosx/coreaudio/JackCoreAudioDriver.cpp index 1b2a3bfb..d59aaaca 100644 --- a/macosx/coreaudio/JackCoreAudioDriver.cpp +++ b/macosx/coreaudio/JackCoreAudioDriver.cpp @@ -157,6 +157,14 @@ error: return err; } +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 JackCoreAudioDriver::Render(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, @@ -434,6 +442,136 @@ JackCoreAudioDriver::JackCoreAudioDriver(const char* name, const char* alias, Ja JackCoreAudioDriver::~JackCoreAudioDriver() {} +OSStatus JackCoreAudioDriver::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 + //----------------------- + + osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); + if (osErr != noErr) + return osErr; + + AudioValueTranslation pluginAVT; + + CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio"); + AudioObjectID pluginID; + + pluginAVT.mInputData = &inBundleRef; + pluginAVT.mInputDataSize = sizeof(inBundleRef); + pluginAVT.mOutputData = &pluginID; + pluginAVT.mOutputDataSize = sizeof(pluginID); + + 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); + + //------------------------------------------------- + // 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(pluginID, &pluginAOPA, 0, NULL, &outDataSize); + if (osErr != noErr) + return osErr; + + osErr = AudioObjectGetPropertyData(pluginID, &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 + //---------- + + // 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\n", *outAggregateDevice); + return noErr; +} + int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, const char* playback_driver_uid, char* capture_driver_name, char* playback_driver_name) { capture_driver_name[0] = 0; @@ -442,16 +580,42 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, const char // Duplex if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) { jack_log("JackCoreAudioDriver::Open duplex"); - if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { - jack_log("Will take default in/out"); - if (GetDefaultDevice(&fDeviceID) != noErr) { - jack_error("Cannot open default 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; + */ + + // 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 agregate 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 diff --git a/macosx/coreaudio/JackCoreAudioDriver.h b/macosx/coreaudio/JackCoreAudioDriver.h index f682c3c2..62de09fb 100644 --- a/macosx/coreaudio/JackCoreAudioDriver.h +++ b/macosx/coreaudio/JackCoreAudioDriver.h @@ -115,6 +115,7 @@ class JackCoreAudioDriver : public JackAudioDriver OSStatus GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput); // Setup + OSStatus CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, AudioDeviceID* outAggregateDevice); int SetupDevices(const char* capture_driver_uid, const char* playback_driver_uid, char* capture_driver_name, diff --git a/windows/JackWinNamedPipe.cpp b/windows/JackWinNamedPipe.cpp index c0279479..d3329055 100644 --- a/windows/JackWinNamedPipe.cpp +++ b/windows/JackWinNamedPipe.cpp @@ -119,7 +119,7 @@ JackWinAsyncNamedPipeClient::JackWinAsyncNamedPipeClient() fOverlap.hEvent = CreateEvent(NULL, // default security attribute TRUE, // manual-reset event TRUE, // initial state = signaled - NULL); // unnamed event object + NULL); // unnamed event object } JackWinAsyncNamedPipeClient::JackWinAsyncNamedPipeClient(HANDLE pipe, bool pending)