Jack2
1.9.7
|
00001 /* 00002 Copyright (C) 2008 Grame 00003 00004 This program is free software; you can redistribute it and/or modify 00005 it under the terms of the GNU General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or 00007 (at your option) any later version. 00008 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 GNU General Public License for more details. 00013 00014 You should have received a copy of the GNU General Public License 00015 along with this program; if not, write to the Free Software 00016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00017 00018 */ 00019 00020 #include "JackCoreAudioAdapter.h" 00021 #include "JackError.h" 00022 #include <unistd.h> 00023 00024 #include <CoreServices/CoreServices.h> 00025 00026 namespace Jack 00027 { 00028 00029 static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) 00030 { 00031 jack_log("- - - - - - - - - - - - - - - - - - - -"); 00032 jack_log(" Sample Rate:%f", inDesc->mSampleRate); 00033 jack_log(" Format ID:%.*s", (int) sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); 00034 jack_log(" Format Flags:%lX", inDesc->mFormatFlags); 00035 jack_log(" Bytes per Packet:%ld", inDesc->mBytesPerPacket); 00036 jack_log(" Frames per Packet:%ld", inDesc->mFramesPerPacket); 00037 jack_log(" Bytes per Frame:%ld", inDesc->mBytesPerFrame); 00038 jack_log(" Channels per Frame:%ld", inDesc->mChannelsPerFrame); 00039 jack_log(" Bits per Channel:%ld", inDesc->mBitsPerChannel); 00040 jack_log("- - - - - - - - - - - - - - - - - - - -"); 00041 } 00042 00043 static OSStatus DisplayDeviceNames() 00044 { 00045 UInt32 size; 00046 Boolean isWritable; 00047 int i, deviceNum; 00048 OSStatus err; 00049 CFStringRef UIname; 00050 00051 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); 00052 if (err != noErr) 00053 return err; 00054 00055 deviceNum = size / sizeof(AudioDeviceID); 00056 AudioDeviceID devices[deviceNum]; 00057 00058 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); 00059 if (err != noErr) 00060 return err; 00061 00062 for (i = 0; i < deviceNum; i++) { 00063 char device_name[256]; 00064 char internal_name[256]; 00065 00066 size = sizeof(CFStringRef); 00067 UIname = NULL; 00068 err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); 00069 if (err == noErr) { 00070 CFStringGetCString(UIname, internal_name, 256, CFStringGetSystemEncoding()); 00071 } else { 00072 goto error; 00073 } 00074 00075 size = 256; 00076 err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name); 00077 if (err != noErr) 00078 return err; 00079 00080 jack_info("Device name = \'%s\', internal_name = \'%s\' (to be used as -C, -P, or -d parameter)", device_name, internal_name); 00081 } 00082 00083 return noErr; 00084 00085 error: 00086 if (UIname != NULL) 00087 CFRelease(UIname); 00088 return err; 00089 } 00090 00091 static void printError(OSStatus err) 00092 { 00093 switch (err) { 00094 case kAudioHardwareNoError: 00095 jack_log("error code : kAudioHardwareNoError"); 00096 break; 00097 case kAudioConverterErr_FormatNotSupported: 00098 jack_log("error code : kAudioConverterErr_FormatNotSupported"); 00099 break; 00100 case kAudioConverterErr_OperationNotSupported: 00101 jack_log("error code : kAudioConverterErr_OperationNotSupported"); 00102 break; 00103 case kAudioConverterErr_PropertyNotSupported: 00104 jack_log("error code : kAudioConverterErr_PropertyNotSupported"); 00105 break; 00106 case kAudioConverterErr_InvalidInputSize: 00107 jack_log("error code : kAudioConverterErr_InvalidInputSize"); 00108 break; 00109 case kAudioConverterErr_InvalidOutputSize: 00110 jack_log("error code : kAudioConverterErr_InvalidOutputSize"); 00111 break; 00112 case kAudioConverterErr_UnspecifiedError: 00113 jack_log("error code : kAudioConverterErr_UnspecifiedError"); 00114 break; 00115 case kAudioConverterErr_BadPropertySizeError: 00116 jack_log("error code : kAudioConverterErr_BadPropertySizeError"); 00117 break; 00118 case kAudioConverterErr_RequiresPacketDescriptionsError: 00119 jack_log("error code : kAudioConverterErr_RequiresPacketDescriptionsError"); 00120 break; 00121 case kAudioConverterErr_InputSampleRateOutOfRange: 00122 jack_log("error code : kAudioConverterErr_InputSampleRateOutOfRange"); 00123 break; 00124 case kAudioConverterErr_OutputSampleRateOutOfRange: 00125 jack_log("error code : kAudioConverterErr_OutputSampleRateOutOfRange"); 00126 break; 00127 case kAudioHardwareNotRunningError: 00128 jack_log("error code : kAudioHardwareNotRunningError"); 00129 break; 00130 case kAudioHardwareUnknownPropertyError: 00131 jack_log("error code : kAudioHardwareUnknownPropertyError"); 00132 break; 00133 case kAudioHardwareIllegalOperationError: 00134 jack_log("error code : kAudioHardwareIllegalOperationError"); 00135 break; 00136 case kAudioHardwareBadDeviceError: 00137 jack_log("error code : kAudioHardwareBadDeviceError"); 00138 break; 00139 case kAudioHardwareBadStreamError: 00140 jack_log("error code : kAudioHardwareBadStreamError"); 00141 break; 00142 case kAudioDeviceUnsupportedFormatError: 00143 jack_log("error code : kAudioDeviceUnsupportedFormatError"); 00144 break; 00145 case kAudioDevicePermissionsError: 00146 jack_log("error code : kAudioDevicePermissionsError"); 00147 break; 00148 case kAudioHardwareBadObjectError: 00149 jack_log("error code : kAudioHardwareBadObjectError"); 00150 break; 00151 case kAudioHardwareUnsupportedOperationError: 00152 jack_log("error code : kAudioHardwareUnsupportedOperationError"); 00153 break; 00154 default: 00155 jack_log("error code : unknown"); 00156 break; 00157 } 00158 } 00159 00160 OSStatus JackCoreAudioAdapter::SRNotificationCallback(AudioDeviceID inDevice, 00161 UInt32 inChannel, 00162 Boolean isInput, 00163 AudioDevicePropertyID inPropertyID, 00164 void* inClientData) 00165 { 00166 JackCoreAudioAdapter* driver = static_cast<JackCoreAudioAdapter*>(inClientData); 00167 00168 switch (inPropertyID) { 00169 00170 case kAudioDevicePropertyNominalSampleRate: { 00171 jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); 00172 driver->fState = true; 00173 break; 00174 } 00175 } 00176 00177 return noErr; 00178 } 00179 00180 // A better implementation would try to recover in case of hardware device change (see HALLAB HLFilePlayerWindowControllerAudioDevicePropertyListenerProc code) 00181 OSStatus JackCoreAudioAdapter::DeviceNotificationCallback(AudioDeviceID inDevice, 00182 UInt32 inChannel, 00183 Boolean isInput, 00184 AudioDevicePropertyID inPropertyID, 00185 void* inClientData) 00186 { 00187 00188 switch (inPropertyID) { 00189 00190 case kAudioDeviceProcessorOverload: { 00191 jack_error("JackCoreAudioAdapter::DeviceNotificationCallback kAudioDeviceProcessorOverload"); 00192 break; 00193 } 00194 00195 case kAudioDevicePropertyStreamConfiguration: { 00196 jack_error("Cannot handle kAudioDevicePropertyStreamConfiguration"); 00197 return kAudioHardwareUnsupportedOperationError; 00198 } 00199 00200 case kAudioDevicePropertyNominalSampleRate: { 00201 jack_error("Cannot handle kAudioDevicePropertyNominalSampleRate"); 00202 return kAudioHardwareUnsupportedOperationError; 00203 } 00204 00205 } 00206 return noErr; 00207 } 00208 00209 int JackCoreAudioAdapter::AddListeners() 00210 { 00211 OSStatus err = noErr; 00212 00213 // Add listeners 00214 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this); 00215 if (err != noErr) { 00216 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload"); 00217 printError(err); 00218 return -1; 00219 } 00220 00221 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioHardwarePropertyDevices, DeviceNotificationCallback, this); 00222 if (err != noErr) { 00223 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioHardwarePropertyDevices"); 00224 printError(err); 00225 return -1; 00226 } 00227 00228 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback, this); 00229 if (err != noErr) { 00230 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); 00231 printError(err); 00232 return -1; 00233 } 00234 00235 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback, this); 00236 if (err != noErr) { 00237 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsRunning"); 00238 printError(err); 00239 return -1; 00240 } 00241 00242 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); 00243 if (err != noErr) { 00244 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); 00245 printError(err); 00246 return -1; 00247 } 00248 00249 err = AudioDeviceAddPropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); 00250 if (err != noErr) { 00251 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); 00252 printError(err); 00253 return -1; 00254 } 00255 00256 return 0; 00257 } 00258 00259 void JackCoreAudioAdapter::RemoveListeners() 00260 { 00261 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback); 00262 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioHardwarePropertyDevices, DeviceNotificationCallback); 00263 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback); 00264 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback); 00265 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); 00266 AudioDeviceRemovePropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); 00267 } 00268 00269 OSStatus JackCoreAudioAdapter::Render(void *inRefCon, 00270 AudioUnitRenderActionFlags *ioActionFlags, 00271 const AudioTimeStamp *inTimeStamp, 00272 UInt32 inBusNumber, 00273 UInt32 inNumberFrames, 00274 AudioBufferList *ioData) 00275 { 00276 JackCoreAudioAdapter* adapter = static_cast<JackCoreAudioAdapter*>(inRefCon); 00277 AudioUnitRender(adapter->fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, adapter->fInputData); 00278 00279 jack_default_audio_sample_t* inputBuffer[adapter->fCaptureChannels]; 00280 jack_default_audio_sample_t* outputBuffer[adapter->fPlaybackChannels]; 00281 00282 for (int i = 0; i < adapter->fCaptureChannels; i++) { 00283 inputBuffer[i] = (jack_default_audio_sample_t*)adapter->fInputData->mBuffers[i].mData; 00284 } 00285 for (int i = 0; i < adapter->fPlaybackChannels; i++) { 00286 outputBuffer[i] = (jack_default_audio_sample_t*)ioData->mBuffers[i].mData; 00287 } 00288 00289 adapter->PushAndPull((jack_default_audio_sample_t**)inputBuffer, (jack_default_audio_sample_t**)outputBuffer, inNumberFrames); 00290 return noErr; 00291 } 00292 00293 JackCoreAudioAdapter::JackCoreAudioAdapter(jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params) 00294 :JackAudioAdapterInterface(buffer_size, sample_rate), fInputData(0), fCapturing(false), fPlaying(false), fState(false) 00295 { 00296 const JSList* node; 00297 const jack_driver_param_t* param; 00298 int in_nChannels = 0; 00299 int out_nChannels = 0; 00300 char captureName[256]; 00301 char playbackName[256]; 00302 fCaptureUID[0] = 0; 00303 fPlaybackUID[0] = 0; 00304 fClockDriftCompensate = false; 00305 00306 // Default values 00307 fCaptureChannels = -1; 00308 fPlaybackChannels = -1; 00309 00310 SInt32 major; 00311 SInt32 minor; 00312 Gestalt(gestaltSystemVersionMajor, &major); 00313 Gestalt(gestaltSystemVersionMinor, &minor); 00314 00315 // Starting with 10.6 systems, the HAL notification thread is created internally 00316 if (major == 10 && minor >= 6) { 00317 CFRunLoopRef theRunLoop = NULL; 00318 AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 00319 OSStatus theError = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); 00320 if (theError != noErr) { 00321 jack_error("JackCoreAudioAdapter::Open kAudioHardwarePropertyRunLoop error"); 00322 } 00323 } 00324 00325 for (node = params; node; node = jack_slist_next(node)) { 00326 param = (const jack_driver_param_t*) node->data; 00327 00328 switch (param->character) { 00329 00330 case 'c' : 00331 fCaptureChannels = fPlaybackChannels = param->value.ui; 00332 break; 00333 00334 case 'i': 00335 fCaptureChannels = param->value.ui; 00336 break; 00337 00338 case 'o': 00339 fPlaybackChannels = param->value.ui; 00340 break; 00341 00342 case 'C': 00343 fCapturing = true; 00344 strncpy(fCaptureUID, param->value.str, 256); 00345 break; 00346 00347 case 'P': 00348 fPlaying = true; 00349 strncpy(fPlaybackUID, param->value.str, 256); 00350 break; 00351 00352 case 'd': 00353 strncpy(fCaptureUID, param->value.str, 256); 00354 strncpy(fPlaybackUID, param->value.str, 256); 00355 break; 00356 00357 case 'D': 00358 fCapturing = fPlaying = true; 00359 break; 00360 00361 case 'r': 00362 SetAdaptedSampleRate(param->value.ui); 00363 break; 00364 00365 case 'p': 00366 SetAdaptedBufferSize(param->value.ui); 00367 break; 00368 00369 case 'l': 00370 DisplayDeviceNames(); 00371 break; 00372 00373 case 'q': 00374 fQuality = param->value.ui; 00375 break; 00376 00377 case 'g': 00378 fRingbufferCurSize = param->value.ui; 00379 fAdaptative = false; 00380 break; 00381 00382 case 's': 00383 fClockDriftCompensate = true; 00384 break; 00385 } 00386 } 00387 00388 /* duplex is the default */ 00389 if (!fCapturing && !fPlaying) { 00390 fCapturing = true; 00391 fPlaying = true; 00392 } 00393 00394 if (SetupDevices(fCaptureUID, fPlaybackUID, captureName, playbackName, fAdaptedSampleRate) < 0) 00395 throw -1; 00396 00397 if (SetupChannels(fCapturing, fPlaying, fCaptureChannels, fPlaybackChannels, in_nChannels, out_nChannels, true) < 0) 00398 throw -1; 00399 00400 if (SetupBufferSize(fAdaptedBufferSize) < 0) 00401 throw -1; 00402 00403 if (SetupSampleRate(fAdaptedSampleRate) < 0) 00404 throw -1; 00405 00406 if (OpenAUHAL(fCapturing, fPlaying, fCaptureChannels, fPlaybackChannels, in_nChannels, out_nChannels, fAdaptedBufferSize, fAdaptedSampleRate) < 0) 00407 throw -1; 00408 00409 if (fCapturing && fCaptureChannels > 0) 00410 if (SetupBuffers(fCaptureChannels) < 0) 00411 throw -1; 00412 00413 if (AddListeners() < 0) 00414 throw -1; 00415 } 00416 00417 OSStatus JackCoreAudioAdapter::GetDefaultDevice(AudioDeviceID* id) 00418 { 00419 OSStatus res; 00420 UInt32 theSize = sizeof(UInt32); 00421 AudioDeviceID inDefault; 00422 AudioDeviceID outDefault; 00423 00424 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) 00425 return res; 00426 00427 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) 00428 return res; 00429 00430 jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); 00431 00432 // Get the device only if default input and output are the same 00433 if (inDefault == outDefault) { 00434 *id = inDefault; 00435 return noErr; 00436 } else { 00437 jack_error("Default input and output devices are not the same !!"); 00438 return kAudioHardwareBadDeviceError; 00439 } 00440 } 00441 00442 OSStatus JackCoreAudioAdapter::GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput) 00443 { 00444 OSStatus err = noErr; 00445 UInt32 outSize; 00446 Boolean outWritable; 00447 AudioBufferList* bufferList = 0; 00448 00449 channelCount = 0; 00450 err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); 00451 if (err == noErr) { 00452 bufferList = (AudioBufferList*)malloc(outSize); 00453 err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList); 00454 if (err == noErr) { 00455 for (unsigned int i = 0; i < bufferList->mNumberBuffers; i++) 00456 channelCount += bufferList->mBuffers[i].mNumberChannels; 00457 } 00458 00459 if (bufferList) 00460 free(bufferList); 00461 } 00462 00463 return err; 00464 } 00465 00466 OSStatus JackCoreAudioAdapter::GetDeviceIDFromUID(const char* UID, AudioDeviceID* id) 00467 { 00468 UInt32 size = sizeof(AudioValueTranslation); 00469 CFStringRef inIUD = CFStringCreateWithCString(NULL, UID, CFStringGetSystemEncoding()); 00470 AudioValueTranslation value = { &inIUD, sizeof(CFStringRef), id, sizeof(AudioDeviceID) }; 00471 00472 if (inIUD == NULL) { 00473 return kAudioHardwareUnspecifiedError; 00474 } else { 00475 OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value); 00476 CFRelease(inIUD); 00477 jack_log("GetDeviceIDFromUID %s %ld", UID, *id); 00478 return (*id == kAudioDeviceUnknown) ? kAudioHardwareBadDeviceError : res; 00479 } 00480 } 00481 00482 OSStatus JackCoreAudioAdapter::GetDefaultInputDevice(AudioDeviceID* id) 00483 { 00484 OSStatus res; 00485 UInt32 theSize = sizeof(UInt32); 00486 AudioDeviceID inDefault; 00487 00488 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) 00489 return res; 00490 00491 if (inDefault == 0) { 00492 jack_error("Error : input device is 0, please select a correct one !!"); 00493 return -1; 00494 } 00495 jack_log("GetDefaultInputDevice: input = %ld ", inDefault); 00496 *id = inDefault; 00497 return noErr; 00498 } 00499 00500 OSStatus JackCoreAudioAdapter::GetDefaultOutputDevice(AudioDeviceID* id) 00501 { 00502 OSStatus res; 00503 UInt32 theSize = sizeof(UInt32); 00504 AudioDeviceID outDefault; 00505 00506 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) 00507 return res; 00508 00509 if (outDefault == 0) { 00510 jack_error("Error : output device is 0, please select a correct one !!"); 00511 return -1; 00512 } 00513 jack_log("GetDefaultOutputDevice: output = %ld", outDefault); 00514 *id = outDefault; 00515 return noErr; 00516 } 00517 00518 OSStatus JackCoreAudioAdapter::GetDeviceNameFromID(AudioDeviceID id, char* name) 00519 { 00520 UInt32 size = 256; 00521 return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name); 00522 } 00523 00524 // Setup 00525 int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid, 00526 const char* playback_driver_uid, 00527 char* capture_driver_name, 00528 char* playback_driver_name, 00529 jack_nframes_t samplerate) 00530 { 00531 capture_driver_name[0] = 0; 00532 playback_driver_name[0] = 0; 00533 00534 // Duplex 00535 if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) { 00536 00537 // Same device for capture and playback... 00538 if (strcmp(capture_driver_uid, playback_driver_uid) == 0) { 00539 00540 if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { 00541 jack_log("Will take default in/out"); 00542 if (GetDefaultDevice(&fDeviceID) != noErr) { 00543 jack_error("Cannot open default device"); 00544 return -1; 00545 } 00546 } 00547 if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { 00548 jack_error("Cannot get device name from device ID"); 00549 return -1; 00550 } 00551 00552 } else { 00553 00554 // Creates aggregate device 00555 AudioDeviceID captureID, playbackID; 00556 00557 if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { 00558 jack_log("Will take default input"); 00559 if (GetDefaultInputDevice(&captureID) != noErr) { 00560 jack_error("Cannot open default input device"); 00561 return -1; 00562 } 00563 } 00564 00565 if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { 00566 jack_log("Will take default output"); 00567 if (GetDefaultOutputDevice(&playbackID) != noErr) { 00568 jack_error("Cannot open default output device"); 00569 return -1; 00570 } 00571 } 00572 00573 if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) 00574 return -1; 00575 } 00576 00577 // Capture only 00578 } else if (strcmp(capture_driver_uid, "") != 0) { 00579 jack_log("JackCoreAudioAdapter::Open capture only"); 00580 if (GetDeviceIDFromUID(capture_driver_uid, &fDeviceID) != noErr) { 00581 if (GetDefaultInputDevice(&fDeviceID) != noErr) { 00582 jack_error("Cannot open default input device"); 00583 return -1; 00584 } 00585 } 00586 if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr) { 00587 jack_error("Cannot get device name from device ID"); 00588 return -1; 00589 } 00590 00591 // Playback only 00592 } else if (strcmp(playback_driver_uid, "") != 0) { 00593 jack_log("JackCoreAudioAdapter::Open playback only"); 00594 if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { 00595 if (GetDefaultOutputDevice(&fDeviceID) != noErr) { 00596 jack_error("Cannot open default output device"); 00597 return -1; 00598 } 00599 } 00600 if (GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { 00601 jack_error("Cannot get device name from device ID"); 00602 return -1; 00603 } 00604 00605 // Use default driver in duplex mode 00606 } else { 00607 jack_log("JackCoreAudioDriver::Open default driver"); 00608 if (GetDefaultDevice(&fDeviceID) != noErr) { 00609 jack_error("Cannot open default device in duplex mode, so aggregate default input and default output"); 00610 00611 // Creates aggregate device 00612 AudioDeviceID captureID, playbackID; 00613 00614 if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { 00615 jack_log("Will take default input"); 00616 if (GetDefaultInputDevice(&captureID) != noErr) { 00617 jack_error("Cannot open default input device"); 00618 return -1; 00619 } 00620 } 00621 00622 if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { 00623 jack_log("Will take default output"); 00624 if (GetDefaultOutputDevice(&playbackID) != noErr) { 00625 jack_error("Cannot open default output device"); 00626 return -1; 00627 } 00628 } 00629 00630 if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) 00631 return -1; 00632 } 00633 } 00634 00635 return 0; 00636 } 00637 00638 int JackCoreAudioAdapter::SetupChannels(bool capturing, 00639 bool playing, 00640 int& inchannels, 00641 int& outchannels, 00642 int& in_nChannels, 00643 int& out_nChannels, 00644 bool strict) 00645 { 00646 OSStatus err = noErr; 00647 00648 if (capturing) { 00649 err = GetTotalChannels(fDeviceID, in_nChannels, true); 00650 if (err != noErr) { 00651 jack_error("Cannot get input channel number"); 00652 printError(err); 00653 return -1; 00654 } else { 00655 jack_log("Max input channels : %d", in_nChannels); 00656 } 00657 } 00658 00659 if (playing) { 00660 err = GetTotalChannels(fDeviceID, out_nChannels, false); 00661 if (err != noErr) { 00662 jack_error("Cannot get output channel number"); 00663 printError(err); 00664 return -1; 00665 } else { 00666 jack_log("Max output channels : %d", out_nChannels); 00667 } 00668 } 00669 00670 if (inchannels > in_nChannels) { 00671 jack_error("This device hasn't required input channels inchannels = %ld in_nChannels = %ld", inchannels, in_nChannels); 00672 if (strict) 00673 return -1; 00674 } 00675 00676 if (outchannels > out_nChannels) { 00677 jack_error("This device hasn't required output channels outchannels = %ld out_nChannels = %ld", outchannels, out_nChannels); 00678 if (strict) 00679 return -1; 00680 } 00681 00682 if (inchannels == -1) { 00683 jack_log("Setup max in channels = %ld", in_nChannels); 00684 inchannels = in_nChannels; 00685 } 00686 00687 if (outchannels == -1) { 00688 jack_log("Setup max out channels = %ld", out_nChannels); 00689 outchannels = out_nChannels; 00690 } 00691 00692 return 0; 00693 } 00694 00695 int JackCoreAudioAdapter::SetupBufferSize(jack_nframes_t buffer_size) 00696 { 00697 // Setting buffer size 00698 UInt32 outSize = sizeof(UInt32); 00699 OSStatus err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, outSize, &buffer_size); 00700 if (err != noErr) { 00701 jack_error("Cannot set buffer size %ld", buffer_size); 00702 printError(err); 00703 return -1; 00704 } 00705 00706 return 0; 00707 } 00708 00709 int JackCoreAudioAdapter::SetupSampleRate(jack_nframes_t samplerate) 00710 { 00711 return SetupSampleRateAux(fDeviceID, samplerate); 00712 } 00713 00714 int JackCoreAudioAdapter::SetupSampleRateAux(AudioDeviceID inDevice, jack_nframes_t samplerate) 00715 { 00716 OSStatus err = noErr; 00717 UInt32 outSize; 00718 Float64 sampleRate; 00719 00720 // Get sample rate 00721 outSize = sizeof(Float64); 00722 err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &sampleRate); 00723 if (err != noErr) { 00724 jack_error("Cannot get current sample rate"); 00725 printError(err); 00726 return -1; 00727 } else { 00728 jack_log("Current sample rate = %f", sampleRate); 00729 } 00730 00731 // If needed, set new sample rate 00732 if (samplerate != (jack_nframes_t)sampleRate) { 00733 sampleRate = (Float64)samplerate; 00734 00735 // To get SR change notification 00736 err = AudioDeviceAddPropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback, this); 00737 if (err != noErr) { 00738 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); 00739 printError(err); 00740 return -1; 00741 } 00742 err = AudioDeviceSetProperty(inDevice, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outSize, &sampleRate); 00743 if (err != noErr) { 00744 jack_error("Cannot set sample rate = %ld", samplerate); 00745 printError(err); 00746 return -1; 00747 } 00748 00749 // Waiting for SR change notification 00750 int count = 0; 00751 while (!fState && count++ < WAIT_COUNTER) { 00752 usleep(100000); 00753 jack_log("Wait count = %d", count); 00754 } 00755 00756 // Remove SR change notification 00757 AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback); 00758 } 00759 00760 return 0; 00761 } 00762 00763 int JackCoreAudioAdapter::SetupBuffers(int inchannels) 00764 { 00765 jack_log("JackCoreAudioAdapter::SetupBuffers: input = %ld", inchannels); 00766 00767 // Prepare buffers 00768 fInputData = (AudioBufferList*)malloc(sizeof(UInt32) + inchannels * sizeof(AudioBuffer)); 00769 fInputData->mNumberBuffers = inchannels; 00770 for (int i = 0; i < fCaptureChannels; i++) { 00771 fInputData->mBuffers[i].mNumberChannels = 1; 00772 fInputData->mBuffers[i].mDataByteSize = fAdaptedBufferSize * sizeof(jack_default_audio_sample_t); 00773 fInputData->mBuffers[i].mData = malloc(fAdaptedBufferSize * sizeof(jack_default_audio_sample_t)); 00774 } 00775 return 0; 00776 } 00777 00778 void JackCoreAudioAdapter::DisposeBuffers() 00779 { 00780 if (fInputData) { 00781 for (int i = 0; i < fCaptureChannels; i++) 00782 free(fInputData->mBuffers[i].mData); 00783 free(fInputData); 00784 fInputData = 0; 00785 } 00786 } 00787 00788 int JackCoreAudioAdapter::OpenAUHAL(bool capturing, 00789 bool playing, 00790 int inchannels, 00791 int outchannels, 00792 int in_nChannels, 00793 int out_nChannels, 00794 jack_nframes_t buffer_size, 00795 jack_nframes_t samplerate) 00796 { 00797 ComponentResult err1; 00798 UInt32 enableIO; 00799 AudioStreamBasicDescription srcFormat, dstFormat; 00800 AudioDeviceID currAudioDeviceID; 00801 UInt32 size; 00802 00803 jack_log("OpenAUHAL capturing = %d playing = %d inchannels = %d outchannels = %d in_nChannels = %d out_nChannels = %d", capturing, playing, inchannels, outchannels, in_nChannels, out_nChannels); 00804 00805 if (inchannels == 0 && outchannels == 0) { 00806 jack_error("No input and output channels..."); 00807 return -1; 00808 } 00809 00810 // AUHAL 00811 ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; 00812 Component HALOutput = FindNextComponent(NULL, &cd); 00813 00814 err1 = OpenAComponent(HALOutput, &fAUHAL); 00815 if (err1 != noErr) { 00816 jack_error("Error calling OpenAComponent"); 00817 printError(err1); 00818 goto error; 00819 } 00820 00821 err1 = AudioUnitInitialize(fAUHAL); 00822 if (err1 != noErr) { 00823 jack_error("Cannot initialize AUHAL unit"); 00824 printError(err1); 00825 goto error; 00826 } 00827 00828 // Start I/O 00829 if (capturing && inchannels > 0) { 00830 enableIO = 1; 00831 jack_log("Setup AUHAL input on"); 00832 } else { 00833 enableIO = 0; 00834 jack_log("Setup AUHAL input off"); 00835 } 00836 00837 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); 00838 if (err1 != noErr) { 00839 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input"); 00840 printError(err1); 00841 goto error; 00842 } 00843 00844 if (playing && outchannels > 0) { 00845 enableIO = 1; 00846 jack_log("Setup AUHAL output on"); 00847 } else { 00848 enableIO = 0; 00849 jack_log("Setup AUHAL output off"); 00850 } 00851 00852 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); 00853 if (err1 != noErr) { 00854 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output"); 00855 printError(err1); 00856 goto error; 00857 } 00858 00859 size = sizeof(AudioDeviceID); 00860 err1 = AudioUnitGetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &currAudioDeviceID, &size); 00861 if (err1 != noErr) { 00862 jack_error("Error calling AudioUnitGetProperty - kAudioOutputUnitProperty_CurrentDevice"); 00863 printError(err1); 00864 goto error; 00865 } else { 00866 jack_log("AudioUnitGetPropertyCurrentDevice = %d", currAudioDeviceID); 00867 } 00868 00869 // Setup up choosen device, in both input and output cases 00870 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fDeviceID, sizeof(AudioDeviceID)); 00871 if (err1 != noErr) { 00872 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice"); 00873 printError(err1); 00874 goto error; 00875 } 00876 00877 // Set buffer size 00878 if (capturing && inchannels > 0) { 00879 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&buffer_size, sizeof(UInt32)); 00880 if (err1 != noErr) { 00881 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); 00882 printError(err1); 00883 goto error; 00884 } 00885 } 00886 00887 if (playing && outchannels > 0) { 00888 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&buffer_size, sizeof(UInt32)); 00889 if (err1 != noErr) { 00890 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); 00891 printError(err1); 00892 goto error; 00893 } 00894 } 00895 00896 // Setup channel map 00897 if (capturing && inchannels > 0 && inchannels < in_nChannels) { 00898 SInt32 chanArr[in_nChannels]; 00899 for (int i = 0; i < in_nChannels; i++) { 00900 chanArr[i] = -1; 00901 } 00902 for (int i = 0; i < inchannels; i++) { 00903 chanArr[i] = i; 00904 } 00905 AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap , kAudioUnitScope_Input, 1, chanArr, sizeof(SInt32) * in_nChannels); 00906 if (err1 != noErr) { 00907 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 1"); 00908 printError(err1); 00909 goto error; 00910 } 00911 } 00912 00913 if (playing && outchannels > 0 && outchannels < out_nChannels) { 00914 SInt32 chanArr[out_nChannels]; 00915 for (int i = 0; i < out_nChannels; i++) { 00916 chanArr[i] = -1; 00917 } 00918 for (int i = 0; i < outchannels; i++) { 00919 chanArr[i] = i; 00920 } 00921 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, chanArr, sizeof(SInt32) * out_nChannels); 00922 if (err1 != noErr) { 00923 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 0"); 00924 printError(err1); 00925 goto error; 00926 } 00927 } 00928 00929 // Setup stream converters 00930 if (capturing && inchannels > 0) { 00931 00932 size = sizeof(AudioStreamBasicDescription); 00933 err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &srcFormat, &size); 00934 if (err1 != noErr) { 00935 jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); 00936 printError(err1); 00937 goto error; 00938 } 00939 PrintStreamDesc(&srcFormat); 00940 00941 jack_log("Setup AUHAL input stream converter SR = %ld", samplerate); 00942 srcFormat.mSampleRate = samplerate; 00943 srcFormat.mFormatID = kAudioFormatLinearPCM; 00944 srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; 00945 srcFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); 00946 srcFormat.mFramesPerPacket = 1; 00947 srcFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); 00948 srcFormat.mChannelsPerFrame = inchannels; 00949 srcFormat.mBitsPerChannel = 32; 00950 PrintStreamDesc(&srcFormat); 00951 00952 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription)); 00953 00954 if (err1 != noErr) { 00955 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); 00956 printError(err1); 00957 goto error; 00958 } 00959 } 00960 00961 if (playing && outchannels > 0) { 00962 00963 size = sizeof(AudioStreamBasicDescription); 00964 err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &dstFormat, &size); 00965 if (err1 != noErr) { 00966 jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); 00967 printError(err1); 00968 goto error; 00969 } 00970 PrintStreamDesc(&dstFormat); 00971 00972 jack_log("Setup AUHAL output stream converter SR = %ld", samplerate); 00973 dstFormat.mSampleRate = samplerate; 00974 dstFormat.mFormatID = kAudioFormatLinearPCM; 00975 dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; 00976 dstFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); 00977 dstFormat.mFramesPerPacket = 1; 00978 dstFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); 00979 dstFormat.mChannelsPerFrame = outchannels; 00980 dstFormat.mBitsPerChannel = 32; 00981 PrintStreamDesc(&dstFormat); 00982 00983 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription)); 00984 00985 if (err1 != noErr) { 00986 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); 00987 printError(err1); 00988 goto error; 00989 } 00990 } 00991 00992 // Setup callbacks 00993 if (inchannels > 0 && outchannels == 0) { 00994 AURenderCallbackStruct output; 00995 output.inputProc = Render; 00996 output.inputProcRefCon = this; 00997 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output)); 00998 if (err1 != noErr) { 00999 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1"); 01000 printError(err1); 01001 goto error; 01002 } 01003 } else { 01004 AURenderCallbackStruct output; 01005 output.inputProc = Render; 01006 output.inputProcRefCon = this; 01007 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); 01008 if (err1 != noErr) { 01009 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0"); 01010 printError(err1); 01011 goto error; 01012 } 01013 } 01014 01015 return 0; 01016 01017 error: 01018 CloseAUHAL(); 01019 return -1; 01020 } 01021 01022 OSStatus JackCoreAudioAdapter::DestroyAggregateDevice() 01023 { 01024 OSStatus osErr = noErr; 01025 AudioObjectPropertyAddress pluginAOPA; 01026 pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice; 01027 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; 01028 pluginAOPA.mElement = kAudioObjectPropertyElementMaster; 01029 UInt32 outDataSize; 01030 01031 osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); 01032 if (osErr != noErr) { 01033 jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); 01034 printError(osErr); 01035 return osErr; 01036 } 01037 01038 osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); 01039 if (osErr != noErr) { 01040 jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyData error"); 01041 printError(osErr); 01042 return osErr; 01043 } 01044 01045 return noErr; 01046 } 01047 01048 static CFStringRef GetDeviceName(AudioDeviceID id) 01049 { 01050 UInt32 size = sizeof(CFStringRef); 01051 CFStringRef UIname; 01052 OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); 01053 return (err == noErr) ? UIname : NULL; 01054 } 01055 01056 OSStatus JackCoreAudioAdapter::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) 01057 { 01058 OSStatus err = noErr; 01059 AudioObjectID sub_device[32]; 01060 UInt32 outSize = sizeof(sub_device); 01061 01062 err = AudioDeviceGetProperty(captureDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); 01063 vector<AudioDeviceID> captureDeviceIDArray; 01064 01065 if (err != noErr) { 01066 jack_log("Input device does not have subdevices"); 01067 captureDeviceIDArray.push_back(captureDeviceID); 01068 } else { 01069 int num_devices = outSize / sizeof(AudioObjectID); 01070 jack_log("Input device has %d subdevices", num_devices); 01071 for (int i = 0; i < num_devices; i++) { 01072 captureDeviceIDArray.push_back(sub_device[i]); 01073 } 01074 } 01075 01076 err = AudioDeviceGetProperty(playbackDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); 01077 vector<AudioDeviceID> playbackDeviceIDArray; 01078 01079 if (err != noErr) { 01080 jack_log("Output device does not have subdevices"); 01081 playbackDeviceIDArray.push_back(playbackDeviceID); 01082 } else { 01083 int num_devices = outSize / sizeof(AudioObjectID); 01084 jack_log("Output device has %d subdevices", num_devices); 01085 for (int i = 0; i < num_devices; i++) { 01086 playbackDeviceIDArray.push_back(sub_device[i]); 01087 } 01088 } 01089 01090 return CreateAggregateDeviceAux(captureDeviceIDArray, playbackDeviceIDArray, samplerate, outAggregateDevice); 01091 } 01092 01093 OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> captureDeviceID, vector<AudioDeviceID> playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) 01094 { 01095 OSStatus osErr = noErr; 01096 UInt32 outSize; 01097 Boolean outWritable; 01098 01099 // Prepare sub-devices for clock drift compensation 01100 // Workaround for bug in the HAL : until 10.6.2 01101 AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 01102 AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 01103 UInt32 theQualifierDataSize = sizeof(AudioObjectID); 01104 AudioClassID inClass = kAudioSubDeviceClassID; 01105 void* theQualifierData = &inClass; 01106 UInt32 subDevicesNum = 0; 01107 01108 //--------------------------------------------------------------------------- 01109 // Setup SR of both devices otherwise creating AD may fail... 01110 //--------------------------------------------------------------------------- 01111 UInt32 keptclockdomain = 0; 01112 UInt32 clockdomain = 0; 01113 outSize = sizeof(UInt32); 01114 bool need_clock_drift_compensation = false; 01115 01116 for (UInt32 i = 0; i < captureDeviceID.size(); i++) { 01117 if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) { 01118 jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of input device"); 01119 } else { 01120 // Check clock domain 01121 osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); 01122 if (osErr != 0) { 01123 jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); 01124 printError(osErr); 01125 } else { 01126 keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; 01127 jack_log("JackCoreAudioDriver::CreateAggregateDevice : input clockdomain = %d", clockdomain); 01128 if (clockdomain != 0 && clockdomain != keptclockdomain) { 01129 jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); 01130 need_clock_drift_compensation = true; 01131 } 01132 } 01133 } 01134 } 01135 01136 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { 01137 if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) { 01138 jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of output device"); 01139 } else { 01140 // Check clock domain 01141 osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); 01142 if (osErr != 0) { 01143 jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); 01144 printError(osErr); 01145 } else { 01146 keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; 01147 jack_log("JackCoreAudioDriver::CreateAggregateDevice : output clockdomain = %d", clockdomain); 01148 if (clockdomain != 0 && clockdomain != keptclockdomain) { 01149 jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); 01150 need_clock_drift_compensation = true; 01151 } 01152 } 01153 } 01154 } 01155 01156 // If no valid clock domain was found, then assume we have to compensate... 01157 if (keptclockdomain == 0) { 01158 need_clock_drift_compensation = true; 01159 } 01160 01161 //--------------------------------------------------------------------------- 01162 // Start to create a new aggregate by getting the base audio hardware plugin 01163 //--------------------------------------------------------------------------- 01164 01165 char device_name[256]; 01166 for (UInt32 i = 0; i < captureDeviceID.size(); i++) { 01167 GetDeviceNameFromID(captureDeviceID[i], device_name); 01168 jack_info("Separated input = '%s' ", device_name); 01169 } 01170 01171 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { 01172 GetDeviceNameFromID(playbackDeviceID[i], device_name); 01173 jack_info("Separated output = '%s' ", device_name); 01174 } 01175 01176 osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); 01177 if (osErr != noErr) { 01178 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); 01179 printError(osErr); 01180 return osErr; 01181 } 01182 01183 AudioValueTranslation pluginAVT; 01184 01185 CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio"); 01186 01187 pluginAVT.mInputData = &inBundleRef; 01188 pluginAVT.mInputDataSize = sizeof(inBundleRef); 01189 pluginAVT.mOutputData = &fPluginID; 01190 pluginAVT.mOutputDataSize = sizeof(fPluginID); 01191 01192 osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); 01193 if (osErr != noErr) { 01194 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); 01195 printError(osErr); 01196 return osErr; 01197 } 01198 01199 //------------------------------------------------- 01200 // Create a CFDictionary for our aggregate device 01201 //------------------------------------------------- 01202 01203 CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 01204 01205 CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex"); 01206 CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex"); 01207 01208 // add the name of the device to the dictionary 01209 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef); 01210 01211 // add our choice of UID for the aggregate device to the dictionary 01212 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef); 01213 01214 // add a "private aggregate key" to the dictionary 01215 int value = 1; 01216 CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value); 01217 01218 SInt32 system; 01219 Gestalt(gestaltSystemVersion, &system); 01220 01221 jack_log("JackCoreAudioDriver::CreateAggregateDevice : system version = %x limit = %x", system, 0x00001054); 01222 01223 // Starting with 10.5.4 systems, the AD can be internal... (better) 01224 if (system < 0x00001054) { 01225 jack_log("JackCoreAudioDriver::CreateAggregateDevice : public aggregate device...."); 01226 } else { 01227 jack_log("JackCoreAudioDriver::CreateAggregateDevice : private aggregate device...."); 01228 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); 01229 } 01230 01231 // Prepare sub-devices for clock drift compensation 01232 CFMutableArrayRef subDevicesArrayClock = NULL; 01233 01234 /* 01235 if (fClockDriftCompensate) { 01236 if (need_clock_drift_compensation) { 01237 jack_info("Clock drift compensation activated..."); 01238 subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 01239 01240 for (UInt32 i = 0; i < captureDeviceID.size(); i++) { 01241 CFStringRef UID = GetDeviceName(captureDeviceID[i]); 01242 if (UID) { 01243 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 01244 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); 01245 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); 01246 //CFRelease(UID); 01247 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); 01248 } 01249 } 01250 01251 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { 01252 CFStringRef UID = GetDeviceName(playbackDeviceID[i]); 01253 if (UID) { 01254 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 01255 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); 01256 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); 01257 //CFRelease(UID); 01258 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); 01259 } 01260 } 01261 01262 // add sub-device clock array for the aggregate device to the dictionary 01263 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock); 01264 } else { 01265 jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); 01266 } 01267 } 01268 */ 01269 01270 //------------------------------------------------- 01271 // Create a CFMutableArray for our sub-device list 01272 //------------------------------------------------- 01273 01274 // we need to append the UID for each device to a CFMutableArray, so create one here 01275 CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 01276 01277 vector<CFStringRef> captureDeviceUID; 01278 for (UInt32 i = 0; i < captureDeviceID.size(); i++) { 01279 CFStringRef ref = GetDeviceName(captureDeviceID[i]); 01280 if (ref == NULL) 01281 return -1; 01282 captureDeviceUID.push_back(ref); 01283 // input sub-devices in this example, so append the sub-device's UID to the CFArray 01284 CFArrayAppendValue(subDevicesArray, ref); 01285 } 01286 01287 vector<CFStringRef> playbackDeviceUID; 01288 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { 01289 CFStringRef ref = GetDeviceName(playbackDeviceID[i]); 01290 if (ref == NULL) 01291 return -1; 01292 playbackDeviceUID.push_back(ref); 01293 // output sub-devices in this example, so append the sub-device's UID to the CFArray 01294 CFArrayAppendValue(subDevicesArray, ref); 01295 } 01296 01297 //----------------------------------------------------------------------- 01298 // Feed the dictionary to the plugin, to create a blank aggregate device 01299 //----------------------------------------------------------------------- 01300 01301 AudioObjectPropertyAddress pluginAOPA; 01302 pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice; 01303 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; 01304 pluginAOPA.mElement = kAudioObjectPropertyElementMaster; 01305 UInt32 outDataSize; 01306 01307 osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); 01308 if (osErr != noErr) { 01309 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyDataSize error"); 01310 printError(osErr); 01311 goto error; 01312 } 01313 01314 osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); 01315 if (osErr != noErr) { 01316 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyData error"); 01317 printError(osErr); 01318 goto error; 01319 } 01320 01321 // pause for a bit to make sure that everything completed correctly 01322 // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created 01323 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); 01324 01325 //------------------------- 01326 // Set the sub-device list 01327 //------------------------- 01328 01329 pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList; 01330 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; 01331 pluginAOPA.mElement = kAudioObjectPropertyElementMaster; 01332 outDataSize = sizeof(CFMutableArrayRef); 01333 osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); 01334 if (osErr != noErr) { 01335 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error"); 01336 printError(osErr); 01337 goto error; 01338 } 01339 01340 // pause again to give the changes time to take effect 01341 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); 01342 01343 //----------------------- 01344 // Set the master device 01345 //----------------------- 01346 01347 // set the master device manually (this is the device which will act as the master clock for the aggregate device) 01348 // pass in the UID of the device you want to use 01349 pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice; 01350 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; 01351 pluginAOPA.mElement = kAudioObjectPropertyElementMaster; 01352 outDataSize = sizeof(CFStringRef); 01353 osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]); // First apture is master... 01354 if (osErr != noErr) { 01355 jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for master device error"); 01356 printError(osErr); 01357 goto error; 01358 } 01359 01360 // pause again to give the changes time to take effect 01361 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); 01362 01363 // Prepare sub-devices for clock drift compensation 01364 // Workaround for bug in the HAL : until 10.6.2 01365 01366 if (fClockDriftCompensate) { 01367 if (need_clock_drift_compensation) { 01368 jack_info("Clock drift compensation activated..."); 01369 01370 // Get the property data size 01371 osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize); 01372 if (osErr != noErr) { 01373 jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); 01374 printError(osErr); 01375 } 01376 01377 // Calculate the number of object IDs 01378 subDevicesNum = outSize / sizeof(AudioObjectID); 01379 jack_info("JackCoreAudioDriver::CreateAggregateDevice clock drift compensation, number of sub-devices = %d", subDevicesNum); 01380 AudioObjectID subDevices[subDevicesNum]; 01381 outSize = sizeof(subDevices); 01382 01383 osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices); 01384 if (osErr != noErr) { 01385 jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); 01386 printError(osErr); 01387 } 01388 01389 // Set kAudioSubDevicePropertyDriftCompensation property... 01390 for (UInt32 index = 0; index < subDevicesNum; ++index) { 01391 UInt32 theDriftCompensationValue = 1; 01392 osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); 01393 if (osErr != noErr) { 01394 jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error"); 01395 printError(osErr); 01396 } 01397 } 01398 } else { 01399 jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); 01400 } 01401 } 01402 01403 // pause again to give the changes time to take effect 01404 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); 01405 01406 //---------- 01407 // Clean up 01408 //---------- 01409 01410 // release the private AD key 01411 CFRelease(AggregateDeviceNumberRef); 01412 01413 // release the CF objects we have created - we don't need them any more 01414 CFRelease(aggDeviceDict); 01415 CFRelease(subDevicesArray); 01416 01417 if (subDevicesArrayClock) 01418 CFRelease(subDevicesArrayClock); 01419 01420 // release the device UID 01421 for (UInt32 i = 0; i < captureDeviceUID.size(); i++) { 01422 CFRelease(captureDeviceUID[i]); 01423 } 01424 01425 for (UInt32 i = 0; i < playbackDeviceUID.size(); i++) { 01426 CFRelease(playbackDeviceUID[i]); 01427 } 01428 01429 jack_log("New aggregate device %ld", *outAggregateDevice); 01430 return noErr; 01431 01432 error: 01433 DestroyAggregateDevice(); 01434 return -1; 01435 } 01436 01437 01438 bool JackCoreAudioAdapter::IsAggregateDevice(AudioDeviceID device) 01439 { 01440 OSStatus err = noErr; 01441 AudioObjectID sub_device[32]; 01442 UInt32 outSize = sizeof(sub_device); 01443 err = AudioDeviceGetProperty(device, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); 01444 01445 if (err != noErr) { 01446 jack_log("Device does not have subdevices"); 01447 return false; 01448 } else { 01449 int num_devices = outSize / sizeof(AudioObjectID); 01450 jack_log("Device does has %d subdevices", num_devices); 01451 return true; 01452 } 01453 } 01454 01455 void JackCoreAudioAdapter::CloseAUHAL() 01456 { 01457 AudioUnitUninitialize(fAUHAL); 01458 CloseComponent(fAUHAL); 01459 } 01460 01461 int JackCoreAudioAdapter::Open() 01462 { 01463 return (AudioOutputUnitStart(fAUHAL) != noErr) ? -1 : 0; 01464 } 01465 01466 int JackCoreAudioAdapter::Close() 01467 { 01468 #ifdef JACK_MONITOR 01469 fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize); 01470 #endif 01471 AudioOutputUnitStop(fAUHAL); 01472 DisposeBuffers(); 01473 CloseAUHAL(); 01474 RemoveListeners(); 01475 if (fPluginID > 0) 01476 DestroyAggregateDevice(); 01477 return 0; 01478 } 01479 01480 int JackCoreAudioAdapter::SetSampleRate ( jack_nframes_t sample_rate ) { 01481 JackAudioAdapterInterface::SetHostSampleRate ( sample_rate ); 01482 Close(); 01483 return Open(); 01484 } 01485 01486 int JackCoreAudioAdapter::SetBufferSize ( jack_nframes_t buffer_size ) { 01487 JackAudioAdapterInterface::SetHostBufferSize ( buffer_size ); 01488 Close(); 01489 return Open(); 01490 } 01491 01492 } // namespace 01493 01494 #ifdef __cplusplus 01495 extern "C" 01496 { 01497 #endif 01498 01499 SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() 01500 { 01501 jack_driver_desc_t *desc; 01502 unsigned int i; 01503 desc = (jack_driver_desc_t*)calloc(1, sizeof(jack_driver_desc_t)); 01504 01505 strcpy(desc->name, "audioadapter"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 01506 strcpy(desc->desc, "netjack audio <==> net backend adapter"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 01507 01508 desc->nparams = 13; 01509 desc->params = (jack_driver_param_desc_t*)calloc(desc->nparams, sizeof(jack_driver_param_desc_t)); 01510 01511 i = 0; 01512 strcpy(desc->params[i].name, "channels"); 01513 desc->params[i].character = 'c'; 01514 desc->params[i].type = JackDriverParamInt; 01515 desc->params[i].value.ui = -1; 01516 strcpy(desc->params[i].short_desc, "Maximum number of channels"); 01517 strcpy(desc->params[i].long_desc, "Maximum number of channels. If -1, max possible number of channels will be used"); 01518 01519 i++; 01520 strcpy(desc->params[i].name, "inchannels"); 01521 desc->params[i].character = 'i'; 01522 desc->params[i].type = JackDriverParamInt; 01523 desc->params[i].value.ui = -1; 01524 strcpy(desc->params[i].short_desc, "Maximum number of input channels"); 01525 strcpy(desc->params[i].long_desc, "Maximum number of input channels. If -1, max possible number of input channels will be used"); 01526 01527 i++; 01528 strcpy(desc->params[i].name, "outchannels"); 01529 desc->params[i].character = 'o'; 01530 desc->params[i].type = JackDriverParamInt; 01531 desc->params[i].value.ui = -1; 01532 strcpy(desc->params[i].short_desc, "Maximum number of output channels"); 01533 strcpy(desc->params[i].long_desc, "Maximum number of output channels. If -1, max possible number of output channels will be used"); 01534 01535 i++; 01536 strcpy(desc->params[i].name, "capture"); 01537 desc->params[i].character = 'C'; 01538 desc->params[i].type = JackDriverParamString; 01539 strcpy(desc->params[i].short_desc, "Input CoreAudio device name"); 01540 strcpy(desc->params[i].long_desc, desc->params[i].short_desc); 01541 01542 i++; 01543 strcpy(desc->params[i].name, "playback"); 01544 desc->params[i].character = 'P'; 01545 desc->params[i].type = JackDriverParamString; 01546 strcpy(desc->params[i].short_desc, "Output CoreAudio device name"); 01547 strcpy(desc->params[i].long_desc, desc->params[i].short_desc); 01548 01549 i++; 01550 strcpy(desc->params[i].name, "rate"); 01551 desc->params[i].character = 'r'; 01552 desc->params[i].type = JackDriverParamUInt; 01553 desc->params[i].value.ui = 44100U; 01554 strcpy(desc->params[i].short_desc, "Sample rate"); 01555 strcpy(desc->params[i].long_desc, desc->params[i].short_desc); 01556 01557 i++; 01558 strcpy(desc->params[i].name, "period"); 01559 desc->params[i].character = 'p'; 01560 desc->params[i].type = JackDriverParamUInt; 01561 desc->params[i].value.ui = 512U; 01562 strcpy(desc->params[i].short_desc, "Frames per period"); 01563 strcpy(desc->params[i].long_desc, desc->params[i].short_desc); 01564 01565 i++; 01566 strcpy(desc->params[i].name, "duplex"); 01567 desc->params[i].character = 'D'; 01568 desc->params[i].type = JackDriverParamBool; 01569 desc->params[i].value.i = TRUE; 01570 strcpy(desc->params[i].short_desc, "Provide both capture and playback ports"); 01571 strcpy(desc->params[i].long_desc, desc->params[i].short_desc); 01572 01573 i++; 01574 strcpy(desc->params[i].name, "device"); 01575 desc->params[i].character = 'd'; 01576 desc->params[i].type = JackDriverParamString; 01577 strcpy(desc->params[i].short_desc, "CoreAudio device name"); 01578 strcpy(desc->params[i].long_desc, desc->params[i].short_desc); 01579 01580 i++; 01581 strcpy(desc->params[i].name, "list-devices"); 01582 desc->params[i].character = 'l'; 01583 desc->params[i].type = JackDriverParamBool; 01584 desc->params[i].value.i = TRUE; 01585 strcpy(desc->params[i].short_desc, "Display available CoreAudio devices"); 01586 strcpy(desc->params[i].long_desc, desc->params[i].short_desc); 01587 01588 i++; 01589 strcpy(desc->params[i].name, "quality"); 01590 desc->params[i].character = 'q'; 01591 desc->params[i].type = JackDriverParamInt; 01592 desc->params[i].value.ui = 0; 01593 strcpy(desc->params[i].short_desc, "Resample algorithm quality (0 - 4)"); 01594 strcpy(desc->params[i].long_desc, desc->params[i].short_desc); 01595 01596 i++; 01597 strcpy(desc->params[i].name, "ring-buffer"); 01598 desc->params[i].character = 'g'; 01599 desc->params[i].type = JackDriverParamInt; 01600 desc->params[i].value.ui = 32768; 01601 strcpy(desc->params[i].short_desc, "Fixed ringbuffer size"); 01602 strcpy(desc->params[i].long_desc, "Fixed ringbuffer size (if not set => automatic adaptative)"); 01603 01604 i++; 01605 strcpy(desc->params[i].name, "clock-drift"); 01606 desc->params[i].character = 's'; 01607 desc->params[i].type = JackDriverParamBool; 01608 desc->params[i].value.i = FALSE; 01609 strcpy(desc->params[i].short_desc, "Clock drift compensation"); 01610 strcpy(desc->params[i].long_desc, "Whether to compensate clock drift in dynamically created aggregate device"); 01611 01612 return desc; 01613 } 01614 01615 01616 #ifdef __cplusplus 01617 } 01618 #endif 01619