Jack2 1.9.7
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     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 

Generated for Jack2 by doxygen 1.7.4