Jack2 1.9.6

JackCoreAudioAdapter.cpp

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     float* inputBuffer[adapter->fCaptureChannels];
00280     float* outputBuffer[adapter->fPlaybackChannels];
00281  
00282     for (int i = 0; i < adapter->fCaptureChannels; i++) {
00283         inputBuffer[i] = (float*)adapter->fInputData->mBuffers[i].mData;
00284     }
00285     for (int i = 0; i < adapter->fPlaybackChannels; i++) {
00286         outputBuffer[i] = (float*)ioData->mBuffers[i].mData;
00287     }
00288     
00289     adapter->PushAndPull((float**)inputBuffer, (float**)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     jack_log("GetDefaultInputDevice: input = %ld ", inDefault);
00492     *id = inDefault;
00493     return noErr;
00494 }
00495 
00496 OSStatus JackCoreAudioAdapter::GetDefaultOutputDevice(AudioDeviceID* id)
00497 {
00498     OSStatus res;
00499     UInt32 theSize = sizeof(UInt32);
00500     AudioDeviceID outDefault;
00501 
00502     if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr)
00503         return res;
00504 
00505     jack_log("GetDefaultOutputDevice: output = %ld", outDefault);
00506     *id = outDefault;
00507     return noErr;
00508 }
00509 
00510 OSStatus JackCoreAudioAdapter::GetDeviceNameFromID(AudioDeviceID id, char* name)
00511 {
00512     UInt32 size = 256;
00513     return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name);
00514 }
00515 
00516 // Setup
00517 int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid,
00518                                         const char* playback_driver_uid,
00519                                         char* capture_driver_name,
00520                                         char* playback_driver_name,
00521                                         jack_nframes_t samplerate)
00522 {
00523     capture_driver_name[0] = 0;
00524     playback_driver_name[0] = 0;
00525 
00526     // Duplex
00527     if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) {
00528     
00529         // Same device for capture and playback...
00530         if (strcmp(capture_driver_uid, playback_driver_uid) == 0)  {
00531             
00532             if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) {
00533                 jack_log("Will take default in/out");
00534                 if (GetDefaultDevice(&fDeviceID) != noErr) {
00535                     jack_error("Cannot open default device");
00536                     return -1;
00537                 }
00538             }
00539             if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) {
00540                 jack_error("Cannot get device name from device ID");
00541                 return -1;
00542             }
00543             
00544         } else {
00545         
00546             // Creates aggregate device
00547             AudioDeviceID captureID, playbackID;
00548             
00549             if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) {
00550                 jack_log("Will take default input");
00551                 if (GetDefaultInputDevice(&captureID) != noErr) {
00552                     jack_error("Cannot open default input device");
00553                     return -1;
00554                 }
00555             }
00556             
00557             if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) {
00558                 jack_log("Will take default output");
00559                 if (GetDefaultOutputDevice(&playbackID) != noErr) {
00560                     jack_error("Cannot open default output device");
00561                     return -1;
00562                 }
00563             }
00564             
00565             if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr)
00566                 return -1;
00567         }
00568 
00569     // Capture only
00570     } else if (strcmp(capture_driver_uid, "") != 0) {
00571         jack_log("JackCoreAudioAdapter::Open capture only");
00572         if (GetDeviceIDFromUID(capture_driver_uid, &fDeviceID) != noErr) {
00573             if (GetDefaultInputDevice(&fDeviceID) != noErr) {
00574                 jack_error("Cannot open default input device");
00575                 return -1;
00576             }
00577         }
00578         if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr) {
00579             jack_error("Cannot get device name from device ID");
00580             return -1;
00581         }
00582 
00583     // Playback only
00584     } else if (strcmp(playback_driver_uid, "") != 0) {
00585         jack_log("JackCoreAudioAdapter::Open playback only");
00586         if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) {
00587             if (GetDefaultOutputDevice(&fDeviceID) != noErr) {
00588                 jack_error("Cannot open default output device");
00589                 return -1;
00590             }
00591         }
00592         if (GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) {
00593             jack_error("Cannot get device name from device ID");
00594             return -1;
00595         }
00596 
00597     // Use default driver in duplex mode
00598     } else {
00599         jack_log("JackCoreAudioDriver::Open default driver");
00600         if (GetDefaultDevice(&fDeviceID) != noErr) {
00601             jack_error("Cannot open default device in duplex mode, so aggregate default input and default output");
00602             
00603             // Creates aggregate device
00604             AudioDeviceID captureID, playbackID;
00605             
00606             if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) {
00607                 jack_log("Will take default input");
00608                 if (GetDefaultInputDevice(&captureID) != noErr) {
00609                     jack_error("Cannot open default input device");
00610                     return -1;
00611                 }
00612             }
00613             
00614             if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) {
00615                 jack_log("Will take default output");
00616                 if (GetDefaultOutputDevice(&playbackID) != noErr) {
00617                     jack_error("Cannot open default output device");
00618                     return -1;
00619                 }
00620             }
00621             
00622             if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr)
00623                 return -1;
00624         }
00625     }
00626 
00627     return 0;
00628 }
00629 
00630 int JackCoreAudioAdapter::SetupChannels(bool capturing,
00631                                         bool playing,
00632                                         int& inchannels,
00633                                         int& outchannels,
00634                                         int& in_nChannels,
00635                                         int& out_nChannels,
00636                                         bool strict)
00637 {
00638     OSStatus err = noErr;
00639 
00640     if (capturing) {
00641         err = GetTotalChannels(fDeviceID, in_nChannels, true);
00642         if (err != noErr) {
00643             jack_error("Cannot get input channel number");
00644             printError(err);
00645             return -1;
00646         } else {
00647             jack_log("Max input channels : %d", in_nChannels);
00648         }
00649     }
00650 
00651     if (playing) {
00652         err = GetTotalChannels(fDeviceID, out_nChannels, false);
00653         if (err != noErr) {
00654             jack_error("Cannot get output channel number");
00655             printError(err);
00656             return -1;
00657         } else {
00658             jack_log("Max output channels : %d", out_nChannels);
00659         }
00660     }
00661 
00662     if (inchannels > in_nChannels) {
00663         jack_error("This device hasn't required input channels inchannels = %ld in_nChannels = %ld", inchannels, in_nChannels);
00664         if (strict)
00665             return -1;
00666     }
00667 
00668     if (outchannels > out_nChannels) {
00669         jack_error("This device hasn't required output channels outchannels = %ld out_nChannels = %ld", outchannels, out_nChannels);
00670         if (strict)
00671             return -1;
00672     }
00673 
00674     if (inchannels == -1) {
00675         jack_log("Setup max in channels = %ld", in_nChannels);
00676         inchannels = in_nChannels;
00677     }
00678 
00679     if (outchannels == -1) {
00680         jack_log("Setup max out channels = %ld", out_nChannels);
00681         outchannels = out_nChannels;
00682     }
00683  
00684     return 0;
00685 }
00686 
00687 int JackCoreAudioAdapter::SetupBufferSize(jack_nframes_t buffer_size)
00688 {
00689     // Setting buffer size
00690     UInt32 outSize = sizeof(UInt32);
00691     OSStatus err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, outSize, &buffer_size);
00692     if (err != noErr) {
00693         jack_error("Cannot set buffer size %ld", buffer_size);
00694         printError(err);
00695         return -1;
00696     }
00697     
00698     return 0;
00699 }
00700 
00701 int JackCoreAudioAdapter::SetupSampleRate(jack_nframes_t samplerate)
00702 {
00703     return SetupSampleRateAux(fDeviceID, samplerate);
00704 }
00705 
00706 int JackCoreAudioAdapter::SetupSampleRateAux(AudioDeviceID inDevice, jack_nframes_t samplerate)
00707 {
00708     OSStatus err = noErr;
00709     UInt32 outSize;
00710     Float64 sampleRate;
00711     
00712     // Get sample rate
00713     outSize =  sizeof(Float64);
00714     err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &sampleRate);
00715     if (err != noErr) {
00716         jack_error("Cannot get current sample rate");
00717         printError(err);
00718         return -1;
00719     }
00720     
00721     // If needed, set new sample rate
00722     if (samplerate != (jack_nframes_t)sampleRate) {
00723         sampleRate = (Float64)samplerate;
00724         
00725         // To get SR change notification
00726         err = AudioDeviceAddPropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback, this);
00727         if (err != noErr) {
00728             jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate");
00729             printError(err);
00730             return -1;
00731         }
00732         err = AudioDeviceSetProperty(inDevice, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outSize, &sampleRate);
00733         if (err != noErr) {
00734             jack_error("Cannot set sample rate = %ld", samplerate);
00735             printError(err);
00736             return -1;
00737         }
00738         
00739         // Waiting for SR change notification
00740         int count = 0;
00741         while (!fState && count++ < WAIT_COUNTER) {
00742             usleep(100000);
00743             jack_log("Wait count = %d", count);
00744         }
00745         
00746         // Remove SR change notification
00747         AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback);
00748     }
00749     
00750     return 0;
00751 }
00752 
00753 int JackCoreAudioAdapter::SetupBuffers(int inchannels)
00754 {
00755     jack_log("JackCoreAudioAdapter::SetupBuffers: input = %ld", inchannels);
00756 
00757     // Prepare buffers
00758     fInputData = (AudioBufferList*)malloc(sizeof(UInt32) + inchannels * sizeof(AudioBuffer));
00759     fInputData->mNumberBuffers = inchannels;
00760     for (int i = 0; i < fCaptureChannels; i++) {
00761         fInputData->mBuffers[i].mNumberChannels = 1;
00762         fInputData->mBuffers[i].mDataByteSize = fAdaptedBufferSize * sizeof(float);
00763         fInputData->mBuffers[i].mData = malloc(fAdaptedBufferSize * sizeof(float));
00764     }
00765     return 0;
00766 }
00767 
00768 void JackCoreAudioAdapter::DisposeBuffers()
00769 {
00770     if (fInputData) {
00771         for (int i = 0; i < fCaptureChannels; i++)
00772             free(fInputData->mBuffers[i].mData);
00773         free(fInputData);
00774         fInputData = 0;
00775     }
00776 }
00777 
00778 int JackCoreAudioAdapter::OpenAUHAL(bool capturing,
00779                                    bool playing,
00780                                    int inchannels,
00781                                    int outchannels,
00782                                    int in_nChannels,
00783                                    int out_nChannels,
00784                                    jack_nframes_t buffer_size,
00785                                    jack_nframes_t samplerate)
00786 {
00787     ComponentResult err1;
00788     UInt32 enableIO;
00789     AudioStreamBasicDescription srcFormat, dstFormat;
00790     AudioDeviceID currAudioDeviceID;
00791     UInt32 size;
00792 
00793     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);
00794 
00795     if (inchannels == 0 && outchannels == 0) {
00796         jack_error("No input and output channels...");
00797         return -1;
00798     }
00799          
00800     // AUHAL
00801     ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
00802     Component HALOutput = FindNextComponent(NULL, &cd);
00803 
00804     err1 = OpenAComponent(HALOutput, &fAUHAL);
00805     if (err1 != noErr) {
00806         jack_error("Error calling OpenAComponent");
00807         printError(err1);
00808         goto error;
00809     }
00810 
00811     err1 = AudioUnitInitialize(fAUHAL);
00812     if (err1 != noErr) {
00813         jack_error("Cannot initialize AUHAL unit");
00814         printError(err1);
00815         goto error;
00816     }
00817 
00818     // Start I/O
00819     if (capturing && inchannels > 0) {
00820         enableIO = 1;
00821         jack_log("Setup AUHAL input on");
00822     } else {
00823         enableIO = 0;
00824         jack_log("Setup AUHAL input off");
00825     }
00826     
00827     err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO));
00828     if (err1 != noErr) {
00829         jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input");
00830         printError(err1);
00831         goto error;
00832     }
00833 
00834     if (playing && outchannels > 0) {
00835         enableIO = 1;
00836         jack_log("Setup AUHAL output on");
00837     } else {
00838         enableIO = 0;
00839         jack_log("Setup AUHAL output off");
00840     }
00841     
00842     err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO));
00843     if (err1 != noErr) {
00844         jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output");
00845         printError(err1);
00846         goto error;
00847     }
00848     
00849     size = sizeof(AudioDeviceID);
00850     err1 = AudioUnitGetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &currAudioDeviceID, &size);
00851     if (err1 != noErr) {
00852         jack_error("Error calling AudioUnitGetProperty - kAudioOutputUnitProperty_CurrentDevice");
00853         printError(err1);
00854         goto error;
00855     } else {
00856         jack_log("AudioUnitGetPropertyCurrentDevice = %d", currAudioDeviceID);
00857     }
00858 
00859     // Setup up choosen device, in both input and output cases
00860     err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fDeviceID, sizeof(AudioDeviceID));
00861     if (err1 != noErr) {
00862         jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice");
00863         printError(err1);
00864         goto error;
00865     }
00866   
00867     // Set buffer size
00868     if (capturing && inchannels > 0) {
00869         err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&buffer_size, sizeof(UInt32));
00870         if (err1 != noErr) {
00871             jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice");
00872             printError(err1);
00873             goto error;
00874         }
00875     }
00876 
00877     if (playing && outchannels > 0) {
00878         err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&buffer_size, sizeof(UInt32));
00879         if (err1 != noErr) {
00880             jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice");
00881             printError(err1);
00882             goto error;
00883         }
00884     }
00885 
00886     // Setup channel map
00887     if (capturing && inchannels > 0 && inchannels < in_nChannels) {
00888         SInt32 chanArr[in_nChannels];
00889         for (int i = 0; i < in_nChannels; i++) {
00890             chanArr[i] = -1;
00891         }
00892         for (int i = 0; i < inchannels; i++) {
00893             chanArr[i] = i;
00894         }
00895         AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap , kAudioUnitScope_Input, 1, chanArr, sizeof(SInt32) * in_nChannels);
00896         if (err1 != noErr) {
00897             jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 1");
00898             printError(err1);
00899             goto error;
00900         }
00901     }
00902 
00903     if (playing && outchannels > 0 && outchannels < out_nChannels) {
00904         SInt32 chanArr[out_nChannels];
00905         for (int i = 0; i < out_nChannels; i++) {
00906             chanArr[i] = -1;
00907         }
00908         for (int i = 0; i < outchannels; i++) {
00909             chanArr[i] = i;
00910         }
00911         err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, chanArr, sizeof(SInt32) * out_nChannels);
00912         if (err1 != noErr) {
00913             jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 0");
00914             printError(err1);
00915             goto error;
00916         }
00917     }
00918 
00919     // Setup stream converters
00920     if (capturing && inchannels > 0) {
00921     
00922         size = sizeof(AudioStreamBasicDescription);
00923         err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &srcFormat, &size);
00924         if (err1 != noErr) {
00925             jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input");
00926             printError(err1);
00927             goto error;
00928         }
00929         PrintStreamDesc(&srcFormat);
00930               
00931         jack_log("Setup AUHAL input stream converter SR = %ld", samplerate);
00932         srcFormat.mSampleRate = samplerate;
00933         srcFormat.mFormatID = kAudioFormatLinearPCM;
00934         srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
00935         srcFormat.mBytesPerPacket = sizeof(float);
00936         srcFormat.mFramesPerPacket = 1;
00937         srcFormat.mBytesPerFrame = sizeof(float);
00938         srcFormat.mChannelsPerFrame = inchannels;
00939         srcFormat.mBitsPerChannel = 32;
00940         PrintStreamDesc(&srcFormat);
00941       
00942         err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription));
00943         
00944         if (err1 != noErr) {
00945             jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input");
00946             printError(err1);
00947             goto error;
00948         }
00949     }
00950 
00951     if (playing && outchannels > 0) {
00952     
00953         size = sizeof(AudioStreamBasicDescription);
00954         err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &dstFormat, &size);
00955         if (err1 != noErr) {
00956             jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output");
00957             printError(err1);
00958             goto error;
00959         }
00960         PrintStreamDesc(&dstFormat);
00961          
00962         jack_log("Setup AUHAL output stream converter SR = %ld", samplerate);
00963         dstFormat.mSampleRate = samplerate;
00964         dstFormat.mFormatID = kAudioFormatLinearPCM;
00965         dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
00966         dstFormat.mBytesPerPacket = sizeof(float);
00967         dstFormat.mFramesPerPacket = 1;
00968         dstFormat.mBytesPerFrame = sizeof(float);
00969         dstFormat.mChannelsPerFrame = outchannels;
00970         dstFormat.mBitsPerChannel = 32;
00971         PrintStreamDesc(&dstFormat);
00972        
00973         err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription));
00974         
00975         if (err1 != noErr) {
00976             jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output");
00977             printError(err1);
00978             goto error;
00979         }
00980     }
00981 
00982     // Setup callbacks
00983     if (inchannels > 0 && outchannels == 0) {
00984         AURenderCallbackStruct output;
00985         output.inputProc = Render;
00986         output.inputProcRefCon = this;
00987         err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output));
00988         if (err1 != noErr) {
00989             jack_error("Error calling  AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1");
00990             printError(err1);
00991             goto error;
00992         }
00993     } else {
00994         AURenderCallbackStruct output;
00995         output.inputProc = Render;
00996         output.inputProcRefCon = this;
00997         err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output));
00998         if (err1 != noErr) {
00999             jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0");
01000             printError(err1);
01001             goto error;
01002         }
01003     }
01004 
01005     return 0;
01006     
01007 error:
01008     CloseAUHAL();
01009     return -1;
01010 }
01011 
01012 OSStatus JackCoreAudioAdapter::DestroyAggregateDevice() 
01013 {
01014     OSStatus osErr = noErr;
01015     AudioObjectPropertyAddress pluginAOPA;
01016     pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice;
01017     pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
01018     pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
01019     UInt32 outDataSize;
01020   
01021     osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize);
01022     if (osErr != noErr) {
01023         jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error");
01024         printError(osErr);
01025         return osErr;
01026     }
01027         
01028     osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID);
01029     if (osErr != noErr) {
01030         jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyData error");
01031         printError(osErr);
01032         return osErr;
01033     }
01034     
01035     return noErr;
01036 }
01037 
01038 static CFStringRef GetDeviceName(AudioDeviceID id)
01039 {
01040     UInt32 size = sizeof(CFStringRef);
01041     CFStringRef UIname;
01042     OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname);
01043     return (err == noErr) ? UIname : NULL;
01044 }
01045 
01046 OSStatus JackCoreAudioAdapter::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) 
01047 {
01048     OSStatus err = noErr;
01049     AudioObjectID sub_device[32];
01050     UInt32 outSize = sizeof(sub_device);
01051     
01052     err = AudioDeviceGetProperty(captureDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device);
01053     vector<AudioDeviceID> captureDeviceIDArray;
01054     
01055     if (err != noErr) {
01056         jack_log("Input device does not have subdevices");
01057         captureDeviceIDArray.push_back(captureDeviceID);
01058     } else {
01059         int num_devices = outSize / sizeof(AudioObjectID);
01060         jack_log("Input device has %d subdevices", num_devices);
01061         for (int i = 0; i < num_devices; i++) {
01062             captureDeviceIDArray.push_back(sub_device[i]);
01063         }
01064     }
01065     
01066     err = AudioDeviceGetProperty(playbackDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device);    
01067     vector<AudioDeviceID> playbackDeviceIDArray;
01068     
01069     if (err != noErr) {
01070         jack_log("Output device does not have subdevices");
01071         playbackDeviceIDArray.push_back(playbackDeviceID);
01072     } else {
01073         int num_devices = outSize / sizeof(AudioObjectID);
01074         jack_log("Output device has %d subdevices", num_devices);
01075         for (int i = 0; i < num_devices; i++) {
01076             playbackDeviceIDArray.push_back(sub_device[i]);
01077         }
01078     }
01079     
01080     return CreateAggregateDeviceAux(captureDeviceIDArray, playbackDeviceIDArray, samplerate, outAggregateDevice);
01081 }
01082     
01083 OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector<AudioDeviceID> captureDeviceID, vector<AudioDeviceID> playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) 
01084 {
01085     OSStatus osErr = noErr;
01086     UInt32 outSize;
01087     Boolean outWritable;
01088     
01089     // Prepare sub-devices for clock drift compensation
01090     // Workaround for bug in the HAL : until 10.6.2
01091     AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
01092     AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
01093     UInt32 theQualifierDataSize = sizeof(AudioObjectID);
01094     AudioClassID inClass = kAudioSubDeviceClassID;
01095     void* theQualifierData = &inClass;
01096     UInt32 subDevicesNum = 0;
01097     
01098     //---------------------------------------------------------------------------
01099     // Setup SR of both devices otherwise creating AD may fail...
01100     //---------------------------------------------------------------------------
01101     UInt32 keptclockdomain = 0;
01102     UInt32 clockdomain = 0;
01103     outSize = sizeof(UInt32);
01104     bool need_clock_drift_compensation = false;
01105     
01106     for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
01107         if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) {
01108             jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of input device");
01109         } else  {
01110             // Check clock domain
01111             osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); 
01112             if (osErr != 0) {
01113                 jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error");
01114                 printError(osErr);
01115             } else {
01116                 keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; 
01117                 jack_log("JackCoreAudioDriver::CreateAggregateDevice : input clockdomain = %d", clockdomain);
01118                 if (clockdomain != 0 && clockdomain != keptclockdomain) {
01119                     jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...");
01120                     need_clock_drift_compensation = true;
01121                 }
01122             }
01123         }
01124     }
01125     
01126     for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
01127         if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) {
01128             jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of output device");
01129         } else {
01130             // Check clock domain
01131             osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); 
01132             if (osErr != 0) {
01133                 jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error");
01134                 printError(osErr);
01135             } else {
01136                 keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; 
01137                 jack_log("JackCoreAudioDriver::CreateAggregateDevice : output clockdomain = %d", clockdomain);
01138                 if (clockdomain != 0 && clockdomain != keptclockdomain) {
01139                     jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed...");
01140                     need_clock_drift_compensation = true;
01141                 }
01142             }
01143         }
01144     }
01145     
01146     // If no valid clock domain was found, then assume we have to compensate...
01147     if (keptclockdomain == 0) {
01148         need_clock_drift_compensation = true;
01149     }
01150     
01151     //---------------------------------------------------------------------------
01152     // Start to create a new aggregate by getting the base audio hardware plugin
01153     //---------------------------------------------------------------------------
01154     
01155     char device_name[256];
01156     for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
01157         GetDeviceNameFromID(captureDeviceID[i], device_name);
01158         jack_info("Separated input = '%s' ", device_name);
01159     }
01160     
01161     for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
01162         GetDeviceNameFromID(playbackDeviceID[i], device_name);
01163         jack_info("Separated output = '%s' ", device_name);
01164     }
01165     
01166     osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable);
01167     if (osErr != noErr) {
01168         jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error");
01169         printError(osErr);
01170         return osErr;
01171     }
01172     
01173     AudioValueTranslation pluginAVT;
01174     
01175     CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio");
01176     
01177     pluginAVT.mInputData = &inBundleRef;
01178     pluginAVT.mInputDataSize = sizeof(inBundleRef);
01179     pluginAVT.mOutputData = &fPluginID;
01180     pluginAVT.mOutputDataSize = sizeof(fPluginID);
01181     
01182     osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT);
01183     if (osErr != noErr) {
01184         jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error");
01185         printError(osErr);
01186         return osErr;
01187     }
01188     
01189     //-------------------------------------------------
01190     // Create a CFDictionary for our aggregate device
01191     //-------------------------------------------------
01192     
01193     CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
01194     
01195     CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex");
01196     CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex");
01197     
01198     // add the name of the device to the dictionary
01199     CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef);
01200     
01201     // add our choice of UID for the aggregate device to the dictionary
01202     CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef);
01203     
01204     // add a "private aggregate key" to the dictionary
01205     int value = 1;
01206     CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
01207     
01208     SInt32 system;
01209     Gestalt(gestaltSystemVersion, &system);
01210     
01211     jack_log("JackCoreAudioDriver::CreateAggregateDevice : system version = %x limit = %x", system, 0x00001054);
01212     
01213     // Starting with 10.5.4 systems, the AD can be internal... (better)
01214     if (system < 0x00001054) {
01215         jack_log("JackCoreAudioDriver::CreateAggregateDevice : public aggregate device....");
01216     } else {
01217         jack_log("JackCoreAudioDriver::CreateAggregateDevice : private aggregate device....");
01218         CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef);
01219     }
01220     
01221     // Prepare sub-devices for clock drift compensation
01222     CFMutableArrayRef subDevicesArrayClock = NULL;
01223     
01224     /*
01225      if (fClockDriftCompensate) {
01226      if (need_clock_drift_compensation) {
01227      jack_info("Clock drift compensation activated...");
01228      subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
01229      
01230      for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
01231      CFStringRef UID = GetDeviceName(captureDeviceID[i]);
01232      if (UID) {
01233      CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
01234      CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
01235      CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
01236      //CFRelease(UID);
01237      CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
01238      }
01239      }
01240      
01241      for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
01242      CFStringRef UID = GetDeviceName(playbackDeviceID[i]);
01243      if (UID) {
01244      CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
01245      CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID);
01246      CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef);
01247      //CFRelease(UID);
01248      CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict);
01249      }
01250      }
01251      
01252      // add sub-device clock array for the aggregate device to the dictionary
01253      CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock);
01254      } else {
01255      jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)");
01256      }
01257      }
01258      */
01259     
01260     //-------------------------------------------------
01261     // Create a CFMutableArray for our sub-device list
01262     //-------------------------------------------------
01263     
01264     // we need to append the UID for each device to a CFMutableArray, so create one here
01265     CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
01266     
01267     vector<CFStringRef> captureDeviceUID;
01268     for (UInt32 i = 0; i < captureDeviceID.size(); i++) {
01269         CFStringRef ref = GetDeviceName(captureDeviceID[i]);
01270         if (ref == NULL)
01271             return -1;
01272         captureDeviceUID.push_back(ref);
01273         // input sub-devices in this example, so append the sub-device's UID to the CFArray
01274         CFArrayAppendValue(subDevicesArray, ref);
01275     }
01276     
01277     vector<CFStringRef> playbackDeviceUID;
01278     for (UInt32 i = 0; i < playbackDeviceID.size(); i++) {
01279         CFStringRef ref = GetDeviceName(playbackDeviceID[i]);
01280         if (ref == NULL)
01281             return -1;
01282         playbackDeviceUID.push_back(ref);
01283         // output sub-devices in this example, so append the sub-device's UID to the CFArray
01284         CFArrayAppendValue(subDevicesArray, ref);
01285     }
01286     
01287     //-----------------------------------------------------------------------
01288     // Feed the dictionary to the plugin, to create a blank aggregate device
01289     //-----------------------------------------------------------------------
01290     
01291     AudioObjectPropertyAddress pluginAOPA;
01292     pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice;
01293     pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
01294     pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
01295     UInt32 outDataSize;
01296     
01297     osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize);
01298     if (osErr != noErr) {
01299         jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyDataSize error");
01300         printError(osErr);
01301         goto error;
01302     }
01303     
01304     osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice);
01305     if (osErr != noErr) {
01306         jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyData error");
01307         printError(osErr);
01308         goto error;
01309     }
01310     
01311     // pause for a bit to make sure that everything completed correctly
01312     // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created
01313     CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
01314     
01315     //-------------------------
01316     // Set the sub-device list
01317     //-------------------------
01318     
01319     pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList;
01320     pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
01321     pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
01322     outDataSize = sizeof(CFMutableArrayRef);
01323     osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray);
01324     if (osErr != noErr) {
01325         jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error");
01326         printError(osErr);
01327         goto error;
01328     }
01329     
01330     // pause again to give the changes time to take effect
01331     CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
01332     
01333     //-----------------------
01334     // Set the master device
01335     //-----------------------
01336     
01337     // set the master device manually (this is the device which will act as the master clock for the aggregate device)
01338     // pass in the UID of the device you want to use
01339     pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice;
01340     pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal;
01341     pluginAOPA.mElement = kAudioObjectPropertyElementMaster;
01342     outDataSize = sizeof(CFStringRef);
01343     osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]);  // First apture is master...
01344     if (osErr != noErr) {
01345         jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for master device error");
01346         printError(osErr);
01347         goto error;
01348     }
01349     
01350     // pause again to give the changes time to take effect
01351     CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
01352     
01353     // Prepare sub-devices for clock drift compensation
01354     // Workaround for bug in the HAL : until 10.6.2
01355     
01356     if (fClockDriftCompensate) {
01357         if (need_clock_drift_compensation) {
01358             jack_info("Clock drift compensation activated...");
01359             
01360             // Get the property data size
01361             osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize);
01362             if (osErr != noErr) {
01363                 jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error");
01364                 printError(osErr);
01365             }
01366             
01367             //  Calculate the number of object IDs
01368             subDevicesNum = outSize / sizeof(AudioObjectID);
01369             jack_info("JackCoreAudioDriver::CreateAggregateDevice clock drift compensation, number of sub-devices = %d", subDevicesNum);
01370             AudioObjectID subDevices[subDevicesNum];
01371             outSize = sizeof(subDevices);
01372             
01373             osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices);
01374             if (osErr != noErr) {
01375                 jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error");
01376                 printError(osErr);
01377             }
01378             
01379             // Set kAudioSubDevicePropertyDriftCompensation property...
01380             for (UInt32 index = 0; index < subDevicesNum; ++index) {
01381                 UInt32 theDriftCompensationValue = 1;
01382                 osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue);
01383                 if (osErr != noErr) {
01384                     jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error");
01385                     printError(osErr);
01386                 }
01387             }
01388         } else {
01389             jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)");
01390         }
01391     }    
01392     
01393     // pause again to give the changes time to take effect
01394     CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
01395     
01396     //----------
01397     // Clean up
01398     //----------
01399     
01400     // release the private AD key
01401     CFRelease(AggregateDeviceNumberRef);
01402     
01403     // release the CF objects we have created - we don't need them any more
01404     CFRelease(aggDeviceDict);
01405     CFRelease(subDevicesArray);
01406     
01407     if (subDevicesArrayClock)
01408         CFRelease(subDevicesArrayClock);
01409     
01410     // release the device UID
01411     for (UInt32 i = 0; i < captureDeviceUID.size(); i++) {
01412         CFRelease(captureDeviceUID[i]);
01413     }
01414     
01415     for (UInt32 i = 0; i < playbackDeviceUID.size(); i++) {
01416         CFRelease(playbackDeviceUID[i]);
01417     }
01418     
01419     jack_log("New aggregate device %ld", *outAggregateDevice);
01420     return noErr;
01421     
01422 error:
01423     DestroyAggregateDevice();
01424     return -1;
01425 }
01426     
01427     
01428 bool JackCoreAudioAdapter::IsAggregateDevice(AudioDeviceID device)
01429 {
01430     OSStatus err = noErr;
01431     AudioObjectID sub_device[32];
01432     UInt32 outSize = sizeof(sub_device);
01433     err = AudioDeviceGetProperty(device, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device);
01434     
01435     if (err != noErr) {
01436         jack_log("Device does not have subdevices");
01437         return false;
01438     } else {
01439         int num_devices = outSize / sizeof(AudioObjectID);
01440         jack_log("Device does has %d subdevices", num_devices);
01441         return true;
01442     }
01443 }
01444 
01445 void JackCoreAudioAdapter::CloseAUHAL()
01446 {
01447     AudioUnitUninitialize(fAUHAL);
01448     CloseComponent(fAUHAL);
01449 }
01450 
01451 int JackCoreAudioAdapter::Open()
01452 {
01453     return (AudioOutputUnitStart(fAUHAL) != noErr)  ? -1 : 0;
01454 }
01455 
01456 int JackCoreAudioAdapter::Close()
01457 {
01458 #ifdef JACK_MONITOR
01459     fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize);
01460 #endif
01461     AudioOutputUnitStop(fAUHAL);
01462     DisposeBuffers();
01463     CloseAUHAL();
01464     RemoveListeners();
01465     if (fPluginID > 0)
01466         DestroyAggregateDevice();
01467     return 0;
01468 }
01469 
01470 int JackCoreAudioAdapter::SetSampleRate ( jack_nframes_t sample_rate ) {
01471     JackAudioAdapterInterface::SetHostSampleRate ( sample_rate );
01472     Close();
01473     return Open();
01474 }
01475 
01476 int JackCoreAudioAdapter::SetBufferSize ( jack_nframes_t buffer_size ) {
01477     JackAudioAdapterInterface::SetHostBufferSize ( buffer_size );
01478     Close();
01479     return Open();
01480 }
01481 
01482 } // namespace
01483 
01484 #ifdef __cplusplus
01485 extern "C"
01486 {
01487 #endif
01488 
01489    SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor()
01490    {
01491         jack_driver_desc_t *desc;
01492         unsigned int i;
01493         desc = (jack_driver_desc_t*)calloc(1, sizeof(jack_driver_desc_t));
01494 
01495         strcpy(desc->name, "audioadapter");                            // size MUST be less then JACK_DRIVER_NAME_MAX + 1
01496         strcpy(desc->desc, "netjack audio <==> net backend adapter");  // size MUST be less then JACK_DRIVER_PARAM_DESC + 1
01497      
01498         desc->nparams = 13;
01499         desc->params = (jack_driver_param_desc_t*)calloc(desc->nparams, sizeof(jack_driver_param_desc_t));
01500 
01501         i = 0;
01502         strcpy(desc->params[i].name, "channels");
01503         desc->params[i].character = 'c';
01504         desc->params[i].type = JackDriverParamInt;
01505         desc->params[i].value.ui = -1;
01506         strcpy(desc->params[i].short_desc, "Maximum number of channels");
01507         strcpy(desc->params[i].long_desc, "Maximum number of channels. If -1, max possible number of channels will be used");
01508 
01509         i++;
01510         strcpy(desc->params[i].name, "inchannels");
01511         desc->params[i].character = 'i';
01512         desc->params[i].type = JackDriverParamInt;
01513         desc->params[i].value.ui = -1;
01514         strcpy(desc->params[i].short_desc, "Maximum number of input channels");
01515         strcpy(desc->params[i].long_desc, "Maximum number of input channels. If -1, max possible number of input channels will be used");
01516 
01517         i++;
01518         strcpy(desc->params[i].name, "outchannels");
01519         desc->params[i].character = 'o';
01520         desc->params[i].type = JackDriverParamInt;
01521         desc->params[i].value.ui = -1;
01522         strcpy(desc->params[i].short_desc, "Maximum number of output channels");
01523         strcpy(desc->params[i].long_desc, "Maximum number of output channels. If -1, max possible number of output channels will be used");
01524 
01525         i++;
01526         strcpy(desc->params[i].name, "capture");
01527         desc->params[i].character = 'C';
01528         desc->params[i].type = JackDriverParamString;
01529         strcpy(desc->params[i].short_desc, "Input CoreAudio device name");
01530         strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
01531 
01532         i++;
01533         strcpy(desc->params[i].name, "playback");
01534         desc->params[i].character = 'P';
01535         desc->params[i].type = JackDriverParamString;
01536         strcpy(desc->params[i].short_desc, "Output CoreAudio device name");
01537         strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
01538 
01539         i++;
01540         strcpy(desc->params[i].name, "rate");
01541         desc->params[i].character = 'r';
01542         desc->params[i].type = JackDriverParamUInt;
01543         desc->params[i].value.ui = 44100U;
01544         strcpy(desc->params[i].short_desc, "Sample rate");
01545         strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
01546 
01547         i++;
01548         strcpy(desc->params[i].name, "period");
01549         desc->params[i].character = 'p';
01550         desc->params[i].type = JackDriverParamUInt;
01551         desc->params[i].value.ui = 512U;
01552         strcpy(desc->params[i].short_desc, "Frames per period");
01553         strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
01554 
01555         i++;
01556         strcpy(desc->params[i].name, "duplex");
01557         desc->params[i].character = 'D';
01558         desc->params[i].type = JackDriverParamBool;
01559         desc->params[i].value.i = TRUE;
01560         strcpy(desc->params[i].short_desc, "Provide both capture and playback ports");
01561         strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
01562 
01563         i++;
01564         strcpy(desc->params[i].name, "device");
01565         desc->params[i].character = 'd';
01566         desc->params[i].type = JackDriverParamString;
01567         strcpy(desc->params[i].short_desc, "CoreAudio device name");
01568         strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
01569 
01570         i++;
01571         strcpy(desc->params[i].name, "list-devices");
01572         desc->params[i].character = 'l';
01573         desc->params[i].type = JackDriverParamBool;
01574         desc->params[i].value.i = TRUE;
01575         strcpy(desc->params[i].short_desc, "Display available CoreAudio devices");
01576         strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
01577         
01578         i++;
01579         strcpy(desc->params[i].name, "quality");
01580         desc->params[i].character = 'q';
01581         desc->params[i].type = JackDriverParamInt;
01582         desc->params[i].value.ui = 0;
01583         strcpy(desc->params[i].short_desc, "Resample algorithm quality (0 - 4)");
01584         strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
01585         
01586         i++;
01587         strcpy(desc->params[i].name, "ring-buffer");
01588         desc->params[i].character = 'g';
01589         desc->params[i].type = JackDriverParamInt;
01590         desc->params[i].value.ui = 32768;
01591         strcpy(desc->params[i].short_desc, "Fixed ringbuffer size");
01592         strcpy(desc->params[i].long_desc, "Fixed ringbuffer size (if not set => automatic adaptative)");
01593        
01594         i++;
01595         strcpy(desc->params[i].name, "clock-drift");
01596         desc->params[i].character = 's';
01597         desc->params[i].type = JackDriverParamBool;
01598         desc->params[i].value.i = FALSE;
01599         strcpy(desc->params[i].short_desc, "Clock drift compensation");
01600         strcpy(desc->params[i].long_desc, "Whether to compensate clock drift in dynamically created aggregate device");
01601    
01602         return desc;
01603     }
01604 
01605 
01606 #ifdef __cplusplus
01607 }
01608 #endif
01609