Jack2 1.9.10
|
00001 /* 00002 Copyright (C) 2004-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 "JackCoreAudioDriver.h" 00021 #include "JackEngineControl.h" 00022 #include "JackMachThread.h" 00023 #include "JackGraphManager.h" 00024 #include "JackError.h" 00025 #include "JackClientControl.h" 00026 #include "JackDriverLoader.h" 00027 #include "JackGlobals.h" 00028 #include "JackTools.h" 00029 #include "JackLockedEngine.h" 00030 #include "JackAC3Encoder.h" 00031 00032 #include <sstream> 00033 #include <iostream> 00034 #include <CoreServices/CoreServices.h> 00035 #include <CoreFoundation/CFNumber.h> 00036 00037 namespace Jack 00038 { 00039 00040 static void Print4CharCode(const char* msg, long c) 00041 { 00042 UInt32 __4CC_number = (c); 00043 char __4CC_string[5]; 00044 *((SInt32*)__4CC_string) = EndianU32_NtoB(__4CC_number); 00045 __4CC_string[4] = 0; 00046 jack_log("%s'%s'", (msg), __4CC_string); 00047 } 00048 00049 static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) 00050 { 00051 jack_log("- - - - - - - - - - - - - - - - - - - -"); 00052 jack_log(" Sample Rate:%f", inDesc->mSampleRate); 00053 jack_log(" Format ID:%.*s", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); 00054 jack_log(" Format Flags:%lX", inDesc->mFormatFlags); 00055 jack_log(" Bytes per Packet:%ld", inDesc->mBytesPerPacket); 00056 jack_log(" Frames per Packet:%ld", inDesc->mFramesPerPacket); 00057 jack_log(" Bytes per Frame:%ld", inDesc->mBytesPerFrame); 00058 jack_log(" Channels per Frame:%ld", inDesc->mChannelsPerFrame); 00059 jack_log(" Bits per Channel:%ld", inDesc->mBitsPerChannel); 00060 jack_log("- - - - - - - - - - - - - - - - - - - -"); 00061 } 00062 00063 static void printError(OSStatus err) 00064 { 00065 switch (err) { 00066 case kAudioHardwareNoError: 00067 jack_log("error code : kAudioHardwareNoError"); 00068 break; 00069 case kAudioConverterErr_FormatNotSupported: 00070 jack_log("error code : kAudioConverterErr_FormatNotSupported"); 00071 break; 00072 case kAudioConverterErr_OperationNotSupported: 00073 jack_log("error code : kAudioConverterErr_OperationNotSupported"); 00074 break; 00075 case kAudioConverterErr_PropertyNotSupported: 00076 jack_log("error code : kAudioConverterErr_PropertyNotSupported"); 00077 break; 00078 case kAudioConverterErr_InvalidInputSize: 00079 jack_log("error code : kAudioConverterErr_InvalidInputSize"); 00080 break; 00081 case kAudioConverterErr_InvalidOutputSize: 00082 jack_log("error code : kAudioConverterErr_InvalidOutputSize"); 00083 break; 00084 case kAudioConverterErr_UnspecifiedError: 00085 jack_log("error code : kAudioConverterErr_UnspecifiedError"); 00086 break; 00087 case kAudioConverterErr_BadPropertySizeError: 00088 jack_log("error code : kAudioConverterErr_BadPropertySizeError"); 00089 break; 00090 case kAudioConverterErr_RequiresPacketDescriptionsError: 00091 jack_log("error code : kAudioConverterErr_RequiresPacketDescriptionsError"); 00092 break; 00093 case kAudioConverterErr_InputSampleRateOutOfRange: 00094 jack_log("error code : kAudioConverterErr_InputSampleRateOutOfRange"); 00095 break; 00096 case kAudioConverterErr_OutputSampleRateOutOfRange: 00097 jack_log("error code : kAudioConverterErr_OutputSampleRateOutOfRange"); 00098 break; 00099 case kAudioHardwareNotRunningError: 00100 jack_log("error code : kAudioHardwareNotRunningError"); 00101 break; 00102 case kAudioHardwareUnknownPropertyError: 00103 jack_log("error code : kAudioHardwareUnknownPropertyError"); 00104 break; 00105 case kAudioHardwareIllegalOperationError: 00106 jack_log("error code : kAudioHardwareIllegalOperationError"); 00107 break; 00108 case kAudioHardwareBadDeviceError: 00109 jack_log("error code : kAudioHardwareBadDeviceError"); 00110 break; 00111 case kAudioHardwareBadStreamError: 00112 jack_log("error code : kAudioHardwareBadStreamError"); 00113 break; 00114 case kAudioDeviceUnsupportedFormatError: 00115 jack_log("error code : kAudioDeviceUnsupportedFormatError"); 00116 break; 00117 case kAudioDevicePermissionsError: 00118 jack_log("error code : kAudioDevicePermissionsError"); 00119 break; 00120 case kAudioHardwareBadObjectError: 00121 jack_log("error code : kAudioHardwareBadObjectError"); 00122 break; 00123 case kAudioHardwareUnsupportedOperationError: 00124 jack_log("error code : kAudioHardwareUnsupportedOperationError"); 00125 break; 00126 default: 00127 Print4CharCode("error code : unknown ", err); 00128 break; 00129 } 00130 } 00131 00132 static bool CheckAvailableDeviceName(const char* device_name, AudioDeviceID* device_id) 00133 { 00134 UInt32 size; 00135 Boolean isWritable; 00136 int i, deviceNum; 00137 OSStatus err; 00138 00139 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); 00140 if (err != noErr) { 00141 return false; 00142 } 00143 00144 deviceNum = size / sizeof(AudioDeviceID); 00145 AudioDeviceID devices[deviceNum]; 00146 00147 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); 00148 if (err != noErr) { 00149 return false; 00150 } 00151 00152 for (i = 0; i < deviceNum; i++) { 00153 char device_name_aux[256]; 00154 00155 size = 256; 00156 err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name_aux); 00157 if (err != noErr) { 00158 return false; 00159 } 00160 00161 if (strncmp(device_name_aux, device_name, strlen(device_name)) == 0) { 00162 *device_id = devices[i]; 00163 return true; 00164 } 00165 } 00166 00167 return false; 00168 } 00169 00170 static bool CheckAvailableDevice(AudioDeviceID device_id) 00171 { 00172 UInt32 size; 00173 Boolean isWritable; 00174 int i, deviceNum; 00175 OSStatus err; 00176 00177 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); 00178 if (err != noErr) { 00179 return false; 00180 } 00181 00182 deviceNum = size / sizeof(AudioDeviceID); 00183 AudioDeviceID devices[deviceNum]; 00184 00185 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); 00186 if (err != noErr) { 00187 return false; 00188 } 00189 00190 for (i = 0; i < deviceNum; i++) { 00191 if (device_id == devices[i]) { 00192 return true; 00193 } 00194 } 00195 00196 return false; 00197 } 00198 00199 static OSStatus DisplayDeviceNames() 00200 { 00201 UInt32 size; 00202 Boolean isWritable; 00203 int i, deviceNum; 00204 OSStatus err; 00205 CFStringRef UIname; 00206 00207 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); 00208 if (err != noErr) { 00209 return err; 00210 } 00211 00212 deviceNum = size / sizeof(AudioDeviceID); 00213 AudioDeviceID devices[deviceNum]; 00214 00215 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); 00216 if (err != noErr) { 00217 return err; 00218 } 00219 00220 for (i = 0; i < deviceNum; i++) { 00221 char device_name[256]; 00222 char internal_name[256]; 00223 00224 size = sizeof(CFStringRef); 00225 UIname = NULL; 00226 err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); 00227 if (err == noErr) { 00228 CFStringGetCString(UIname, internal_name, 256, CFStringGetSystemEncoding()); 00229 } else { 00230 goto error; 00231 } 00232 00233 size = 256; 00234 err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name); 00235 if (err != noErr) { 00236 return err; 00237 } 00238 00239 jack_info("Device ID = \'%d\' name = \'%s\', internal name = \'%s\' (to be used as -C, -P, or -d parameter)", devices[i], device_name, internal_name); 00240 } 00241 00242 return noErr; 00243 00244 error: 00245 if (UIname != NULL) { 00246 CFRelease(UIname); 00247 } 00248 return err; 00249 } 00250 00251 static CFStringRef GetDeviceName(AudioDeviceID id) 00252 { 00253 UInt32 size = sizeof(CFStringRef); 00254 CFStringRef UIname; 00255 OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); 00256 return (err == noErr) ? UIname : NULL; 00257 } 00258 00259 static void ParseChannelList(const string& list, vector<int>& result, int max_chan) 00260 { 00261 stringstream ss(list); 00262 string token; 00263 int chan; 00264 00265 while (ss >> token) { 00266 istringstream ins; 00267 ins.str(token); 00268 ins >> chan; 00269 if (chan < 0 || chan >= max_chan) { 00270 jack_error("Ignore incorrect channel mapping value = %d", chan); 00271 } else { 00272 result.push_back(chan); 00273 } 00274 } 00275 } 00276 00277 OSStatus JackCoreAudioDriver::Render(void* inRefCon, 00278 AudioUnitRenderActionFlags* ioActionFlags, 00279 const AudioTimeStamp* inTimeStamp, 00280 UInt32 inBusNumber, 00281 UInt32 inNumberFrames, 00282 AudioBufferList* ioData) 00283 { 00284 return static_cast<JackCoreAudioDriver*>(inRefCon)->Render(ioActionFlags, inTimeStamp, ioData); 00285 } 00286 00287 OSStatus JackCoreAudioDriver::Render(AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, AudioBufferList* ioData) 00288 { 00289 fActionFags = ioActionFlags; 00290 fCurrentTime = inTimeStamp; 00291 fDriverOutputData = ioData; 00292 00293 // Setup threaded based log function et get RT thread parameters once... 00294 if (set_threaded_log_function()) { 00295 00296 jack_log("JackCoreAudioDriver::Render : set_threaded_log_function"); 00297 JackMachThread::GetParams(pthread_self(), &fEngineControl->fPeriod, &fEngineControl->fComputation, &fEngineControl->fConstraint); 00298 00299 if (fComputationGrain > 0) { 00300 jack_log("JackCoreAudioDriver::Render : RT thread computation setup to %d percent of period", int(fComputationGrain * 100)); 00301 fEngineControl->fComputation = fEngineControl->fPeriod * fComputationGrain; 00302 } 00303 } 00304 00305 // Signal waiting start function... 00306 fState = true; 00307 00308 CycleTakeBeginTime(); 00309 00310 if (Process() < 0) { 00311 jack_error("Process error, stopping driver"); 00312 NotifyFailure(JackFailure | JackBackendError, "Process error, stopping driver"); // Message length limited to JACK_MESSAGE_SIZE 00313 Stop(); 00314 kill(JackTools::GetPID(), SIGINT); 00315 return kAudioHardwareUnsupportedOperationError; 00316 } else { 00317 return noErr; 00318 } 00319 } 00320 00321 int JackCoreAudioDriver::Read() 00322 { 00323 if (fCaptureChannels > 0) { // Calling AudioUnitRender with no input returns a '????' error (callback setting issue ??), so hack to avoid it here... 00324 return (AudioUnitRender(fAUHAL, fActionFags, fCurrentTime, 1, fEngineControl->fBufferSize, fJackInputData) == noErr) ? 0 : -1; 00325 } else { 00326 return 0; 00327 } 00328 } 00329 00330 int JackCoreAudioDriver::Write() 00331 { 00332 int size = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize; 00333 00334 if (fAC3Encoder) { 00335 00336 // AC3 encoding and SPDIF write 00337 jack_default_audio_sample_t* AC3_inputs[MAX_AC3_CHANNELS]; 00338 jack_default_audio_sample_t* AC3_outputs[2]; 00339 for (int i = 0; i < fPlaybackChannels; i++) { 00340 AC3_inputs[i] = GetOutputBuffer(i); 00341 // If not connected, clear the buffer 00342 if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) == 0) { 00343 memset(AC3_inputs[i], 0, size); 00344 } 00345 } 00346 AC3_outputs[0] = (jack_default_audio_sample_t*)fDriverOutputData->mBuffers[0].mData; 00347 AC3_outputs[1] = (jack_default_audio_sample_t*)fDriverOutputData->mBuffers[1].mData; 00348 fAC3Encoder->Process(AC3_inputs, AC3_outputs, fEngineControl->fBufferSize); 00349 00350 } else { 00351 00352 // Standard write 00353 for (int i = 0; i < fPlaybackChannels; i++) { 00354 if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) { 00355 jack_default_audio_sample_t* buffer = GetOutputBuffer(i); 00356 memcpy((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, buffer, size); 00357 // Monitor ports 00358 if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[i]) > 0) { 00359 memcpy(GetMonitorBuffer(i), buffer, size); 00360 } 00361 } else { 00362 memset((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, 0, size); 00363 } 00364 } 00365 } 00366 return 0; 00367 } 00368 00369 OSStatus JackCoreAudioDriver::SRNotificationCallback(AudioDeviceID inDevice, 00370 UInt32 inChannel, 00371 Boolean isInput, 00372 AudioDevicePropertyID inPropertyID, 00373 void* inClientData) 00374 { 00375 JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; 00376 00377 switch (inPropertyID) { 00378 00379 case kAudioDevicePropertyNominalSampleRate: { 00380 jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); 00381 // Check new sample rate 00382 Float64 tmp_sample_rate; 00383 UInt32 outSize = sizeof(Float64); 00384 OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); 00385 if (err != noErr) { 00386 jack_error("Cannot get current sample rate"); 00387 printError(err); 00388 } else { 00389 jack_log("JackCoreAudioDriver::SRNotificationCallback : checked sample rate = %f", tmp_sample_rate); 00390 } 00391 driver->fState = true; 00392 break; 00393 } 00394 } 00395 00396 return noErr; 00397 } 00398 00399 OSStatus JackCoreAudioDriver::BSNotificationCallback(AudioDeviceID inDevice, 00400 UInt32 inChannel, 00401 Boolean isInput, 00402 AudioDevicePropertyID inPropertyID, 00403 void* inClientData) 00404 { 00405 JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; 00406 00407 switch (inPropertyID) { 00408 00409 case kAudioDevicePropertyBufferFrameSize: { 00410 jack_log("JackCoreAudioDriver::BSNotificationCallback kAudioDevicePropertyBufferFrameSize"); 00411 // Check new buffer size 00412 UInt32 tmp_buffer_size; 00413 UInt32 outSize = sizeof(UInt32); 00414 OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); 00415 if (err != noErr) { 00416 jack_error("Cannot get current buffer size"); 00417 printError(err); 00418 } else { 00419 jack_log("JackCoreAudioDriver::BSNotificationCallback : checked buffer size = %d", tmp_buffer_size); 00420 } 00421 driver->fState = true; 00422 break; 00423 } 00424 } 00425 00426 return noErr; 00427 } 00428 00429 // A better implementation would possibly try to recover in case of hardware device change (see HALLAB HLFilePlayerWindowControllerAudioDevicePropertyListenerProc code) 00430 OSStatus JackCoreAudioDriver::AudioHardwareNotificationCallback(AudioHardwarePropertyID inPropertyID, void* inClientData) 00431 { 00432 JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; 00433 00434 switch (inPropertyID) { 00435 00436 case kAudioHardwarePropertyDevices: { 00437 jack_log("JackCoreAudioDriver::AudioHardwareNotificationCallback kAudioHardwarePropertyDevices"); 00438 DisplayDeviceNames(); 00439 AudioDeviceID captureID, playbackID; 00440 if (CheckAvailableDevice(driver->fDeviceID) || 00441 (CheckAvailableDeviceName(driver->fCaptureUID, &captureID) 00442 && CheckAvailableDeviceName(driver->fPlaybackUID, &playbackID))) { 00443 00444 } 00445 break; 00446 } 00447 } 00448 00449 return noErr; 00450 } 00451 00452 OSStatus JackCoreAudioDriver::DeviceNotificationCallback(AudioDeviceID inDevice, 00453 UInt32 inChannel, 00454 Boolean isInput, 00455 AudioDevicePropertyID inPropertyID, 00456 void* inClientData) 00457 { 00458 JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; 00459 00460 switch (inPropertyID) { 00461 00462 case kAudioDevicePropertyDeviceIsRunning: { 00463 UInt32 isrunning = 0; 00464 UInt32 outsize = sizeof(UInt32); 00465 if (AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyDeviceIsRunning, &outsize, &isrunning) == noErr) { 00466 jack_log("JackCoreAudioDriver::DeviceNotificationCallback kAudioDevicePropertyDeviceIsRunning = %d", isrunning); 00467 } 00468 break; 00469 } 00470 00471 case kAudioDevicePropertyDeviceIsAlive: { 00472 UInt32 isalive = 0; 00473 UInt32 outsize = sizeof(UInt32); 00474 if (AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyDeviceIsAlive, &outsize, &isalive) == noErr) { 00475 jack_log("JackCoreAudioDriver::DeviceNotificationCallback kAudioDevicePropertyDeviceIsAlive = %d", isalive); 00476 } 00477 break; 00478 } 00479 00480 case kAudioDevicePropertyDeviceHasChanged: { 00481 UInt32 hachanged = 0; 00482 UInt32 outsize = sizeof(UInt32); 00483 if (AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyDeviceHasChanged, &outsize, &hachanged) == noErr) { 00484 jack_log("JackCoreAudioDriver::DeviceNotificationCallback kAudioDevicePropertyDeviceHasChanged = %d", hachanged); 00485 } 00486 break; 00487 } 00488 00489 case kAudioDeviceProcessorOverload: { 00490 jack_error("DeviceNotificationCallback kAudioDeviceProcessorOverload"); 00491 jack_time_t cur_time = GetMicroSeconds(); 00492 driver->NotifyXRun(cur_time, float(cur_time - driver->fBeginDateUst)); // Better this value than nothing... 00493 break; 00494 } 00495 00496 case kAudioDevicePropertyStreamConfiguration: { 00497 jack_error("Cannot handle kAudioDevicePropertyStreamConfiguration : server will quit..."); 00498 driver->NotifyFailure(JackFailure | JackBackendError, "Another application has changed the device configuration"); // Message length limited to JACK_MESSAGE_SIZE 00499 driver->CloseAUHAL(); 00500 kill(JackTools::GetPID(), SIGINT); 00501 return kAudioHardwareUnsupportedOperationError; 00502 } 00503 00504 case kAudioDevicePropertyNominalSampleRate: { 00505 Float64 sample_rate = 0; 00506 UInt32 outsize = sizeof(Float64); 00507 OSStatus err = AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outsize, &sample_rate); 00508 if (err != noErr) { 00509 return kAudioHardwareUnsupportedOperationError; 00510 } 00511 00512 char device_name[256]; 00513 const char* digidesign_name = "Digidesign"; 00514 driver->GetDeviceNameFromID(driver->fDeviceID, device_name); 00515 00516 if (sample_rate != driver->fEngineControl->fSampleRate) { 00517 00518 // Digidesign hardware, so "special" code : change the SR again here 00519 if (strncmp(device_name, digidesign_name, 10) == 0) { 00520 00521 jack_log("JackCoreAudioDriver::DeviceNotificationCallback Digidesign HW = %s", device_name); 00522 00523 // Set sample rate again... 00524 sample_rate = driver->fEngineControl->fSampleRate; 00525 err = AudioDeviceSetProperty(driver->fDeviceID, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outsize, &sample_rate); 00526 if (err != noErr) { 00527 jack_error("Cannot set sample rate = %f", sample_rate); 00528 printError(err); 00529 } else { 00530 jack_log("JackCoreAudioDriver::DeviceNotificationCallback : set sample rate = %f", sample_rate); 00531 } 00532 00533 // Check new sample rate again... 00534 outsize = sizeof(Float64); 00535 err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outsize, &sample_rate); 00536 if (err != noErr) { 00537 jack_error("Cannot get current sample rate"); 00538 printError(err); 00539 } else { 00540 jack_log("JackCoreAudioDriver::DeviceNotificationCallback : checked sample rate = %f", sample_rate); 00541 } 00542 return noErr; 00543 00544 } else { 00545 driver->NotifyFailure(JackFailure | JackBackendError, "Another application has changed the sample rate"); // Message length limited to JACK_MESSAGE_SIZE 00546 driver->CloseAUHAL(); 00547 kill(JackTools::GetPID(), SIGINT); 00548 return kAudioHardwareUnsupportedOperationError; 00549 } 00550 } 00551 } 00552 00553 } 00554 return noErr; 00555 } 00556 00557 OSStatus JackCoreAudioDriver::GetDeviceIDFromUID(const char* UID, AudioDeviceID* id) 00558 { 00559 UInt32 size = sizeof(AudioValueTranslation); 00560 CFStringRef inIUD = CFStringCreateWithCString(NULL, UID, CFStringGetSystemEncoding()); 00561 AudioValueTranslation value = { &inIUD, sizeof(CFStringRef), id, sizeof(AudioDeviceID) }; 00562 00563 if (inIUD == NULL) { 00564 return kAudioHardwareUnspecifiedError; 00565 } else { 00566 OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value); 00567 CFRelease(inIUD); 00568 jack_log("JackCoreAudioDriver::GetDeviceIDFromUID %s %ld", UID, *id); 00569 return (*id == kAudioDeviceUnknown) ? kAudioHardwareBadDeviceError : res; 00570 } 00571 } 00572 00573 OSStatus JackCoreAudioDriver::GetDefaultDevice(AudioDeviceID* id) 00574 { 00575 OSStatus res; 00576 UInt32 theSize = sizeof(UInt32); 00577 AudioDeviceID inDefault; 00578 AudioDeviceID outDefault; 00579 00580 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) { 00581 return res; 00582 } 00583 00584 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) { 00585 return res; 00586 } 00587 00588 jack_log("JackCoreAudioDriver::GetDefaultDevice : input = %ld output = %ld", inDefault, outDefault); 00589 00590 // Get the device only if default input and output are the same 00591 if (inDefault != outDefault) { 00592 jack_error("Default input and output devices are not the same !!"); 00593 return kAudioHardwareBadDeviceError; 00594 } else if (inDefault == 0) { 00595 jack_error("Default input and output devices are null !!"); 00596 return kAudioHardwareBadDeviceError; 00597 } else { 00598 *id = inDefault; 00599 return noErr; 00600 } 00601 } 00602 00603 OSStatus JackCoreAudioDriver::GetDefaultInputDevice(AudioDeviceID* id) 00604 { 00605 OSStatus res; 00606 UInt32 theSize = sizeof(UInt32); 00607 AudioDeviceID inDefault; 00608 00609 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) { 00610 return res; 00611 } 00612 00613 if (inDefault == 0) { 00614 jack_error("Error default input device is 0, will take 'Built-in'..."); 00615 if (CheckAvailableDeviceName("Built-in Microphone", id) 00616 || CheckAvailableDeviceName("Built-in Line", id)) { 00617 jack_log("JackCoreAudioDriver::GetDefaultInputDevice : output = %ld", *id); 00618 return noErr; 00619 } else { 00620 jack_error("Cannot find any input device to use..."); 00621 return -1; 00622 } 00623 } 00624 jack_log("JackCoreAudioDriver::GetDefaultInputDevice : input = %ld ", inDefault); 00625 *id = inDefault; 00626 return noErr; 00627 } 00628 00629 OSStatus JackCoreAudioDriver::GetDefaultOutputDevice(AudioDeviceID* id) 00630 { 00631 OSStatus res; 00632 UInt32 theSize = sizeof(UInt32); 00633 AudioDeviceID outDefault; 00634 00635 if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) { 00636 return res; 00637 } 00638 00639 if (outDefault == 0) { 00640 jack_error("Error default ouput device is 0, will take 'Built-in'..."); 00641 if (CheckAvailableDeviceName("Built-in Output", id)) { 00642 jack_log("JackCoreAudioDriver::GetDefaultOutputDevice : output = %ld", *id); 00643 return noErr; 00644 } else { 00645 jack_error("Cannot find any output device to use..."); 00646 return -1; 00647 } 00648 } 00649 jack_log("JackCoreAudioDriver::GetDefaultOutputDevice : output = %ld", outDefault); 00650 *id = outDefault; 00651 return noErr; 00652 } 00653 00654 OSStatus JackCoreAudioDriver::GetDeviceNameFromID(AudioDeviceID id, char* name) 00655 { 00656 UInt32 size = 256; 00657 return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name); 00658 } 00659 00660 OSStatus JackCoreAudioDriver::GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput) 00661 { 00662 OSStatus err = noErr; 00663 UInt32 outSize; 00664 Boolean outWritable; 00665 00666 channelCount = 0; 00667 err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); 00668 if (err == noErr) { 00669 int stream_count = outSize / sizeof(AudioBufferList); 00670 jack_log("JackCoreAudioDriver::GetTotalChannels stream_count = %d", stream_count); 00671 AudioBufferList bufferList[stream_count]; 00672 err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList); 00673 if (err == noErr) { 00674 for (uint i = 0; i < bufferList->mNumberBuffers; i++) { 00675 channelCount += bufferList->mBuffers[i].mNumberChannels; 00676 jack_log("JackCoreAudioDriver::GetTotalChannels stream = %d channels = %d", i, bufferList->mBuffers[i].mNumberChannels); 00677 } 00678 } 00679 } 00680 return err; 00681 } 00682 00683 OSStatus JackCoreAudioDriver::GetStreamLatencies(AudioDeviceID device, bool isInput, vector<int>& latencies) 00684 { 00685 OSStatus err = noErr; 00686 UInt32 outSize1, outSize2, outSize3; 00687 Boolean outWritable; 00688 00689 err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, &outWritable); 00690 if (err == noErr) { 00691 int stream_count = outSize1 / sizeof(UInt32); 00692 AudioStreamID streamIDs[stream_count]; 00693 AudioBufferList bufferList[stream_count]; 00694 UInt32 streamLatency; 00695 outSize2 = sizeof(UInt32); 00696 00697 err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, streamIDs); 00698 if (err != noErr) { 00699 jack_error("GetStreamLatencies kAudioDevicePropertyStreams err = %d", err); 00700 return err; 00701 } 00702 00703 err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, &outWritable); 00704 if (err != noErr) { 00705 jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err); 00706 return err; 00707 } 00708 00709 for (int i = 0; i < stream_count; i++) { 00710 err = AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &outSize2, &streamLatency); 00711 if (err != noErr) { 00712 jack_error("GetStreamLatencies kAudioStreamPropertyLatency err = %d", err); 00713 return err; 00714 } 00715 err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, bufferList); 00716 if (err != noErr) { 00717 jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err); 00718 return err; 00719 } 00720 // Push 'channel' time the stream latency 00721 for (uint k = 0; k < bufferList->mBuffers[i].mNumberChannels; k++) { 00722 latencies.push_back(streamLatency); 00723 } 00724 } 00725 } 00726 return err; 00727 } 00728 00729 bool JackCoreAudioDriver::IsDigitalDevice(AudioDeviceID device) 00730 { 00731 OSStatus err = noErr; 00732 UInt32 outSize1; 00733 bool is_digital = false; 00734 00735 /* Get a list of all the streams on this device */ 00736 AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster }; 00737 err = AudioObjectGetPropertyDataSize(device, &streamsAddress, 0, NULL, &outSize1); 00738 if (err != noErr) { 00739 jack_error("IsDigitalDevice kAudioDevicePropertyStreams err = %d", err); 00740 return false; 00741 } 00742 00743 int stream_count = outSize1 / sizeof(AudioStreamID); 00744 AudioStreamID streamIDs[stream_count]; 00745 00746 err = AudioObjectGetPropertyData(device, &streamsAddress, 0, NULL, &outSize1, streamIDs); 00747 00748 if (err != noErr) { 00749 jack_error("IsDigitalDevice kAudioDevicePropertyStreams list err = %d", err); 00750 return false; 00751 } 00752 00753 AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 }; 00754 00755 for (int i = 0; i < stream_count ; i++) { 00756 00757 /* Find a stream with a cac3 stream */ 00758 int format_num = 0; 00759 00760 /* Retrieve all the stream formats supported by each output stream */ 00761 err = AudioObjectGetPropertyDataSize(streamIDs[i], &physicalFormatsAddress, 0, NULL, &outSize1); 00762 00763 if (err != noErr) { 00764 jack_error("IsDigitalDevice kAudioStreamPropertyAvailablePhysicalFormats err = %d", err); 00765 return false; 00766 } 00767 00768 format_num = outSize1 / sizeof(AudioStreamRangedDescription); 00769 AudioStreamRangedDescription format_list[format_num]; 00770 00771 err = AudioObjectGetPropertyData(streamIDs[i], &physicalFormatsAddress, 0, NULL, &outSize1, format_list); 00772 00773 if (err != noErr) { 00774 jack_error("IsDigitalDevice could not get the list of streamformats err = %d", err); 00775 return false; 00776 } 00777 00778 /* Check if one of the supported formats is a digital format */ 00779 for (int j = 0; j < format_num; j++) { 00780 00781 PrintStreamDesc(&format_list[j].mFormat); 00782 00783 if (format_list[j].mFormat.mFormatID == 'IAC3' || 00784 format_list[j].mFormat.mFormatID == 'iac3' || 00785 format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 || 00786 format_list[j].mFormat.mFormatID == kAudioFormatAC3) 00787 { 00788 is_digital = true; 00789 break; 00790 } 00791 } 00792 } 00793 00794 return is_digital; 00795 } 00796 00797 JackCoreAudioDriver::JackCoreAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) 00798 : JackAudioDriver(name, alias, engine, table), 00799 fAC3Encoder(NULL), 00800 fJackInputData(NULL), 00801 fDriverOutputData(NULL), 00802 fPluginID(0), 00803 fState(false), 00804 fHogged(false), 00805 fIOUsage(1.f), 00806 fComputationGrain(-1.f), 00807 fClockDriftCompensate(false), 00808 fDigitalPlayback(false) 00809 {} 00810 00811 JackCoreAudioDriver::~JackCoreAudioDriver() 00812 { 00813 delete fAC3Encoder; 00814 } 00815 00816 OSStatus JackCoreAudioDriver::DestroyAggregateDevice() 00817 { 00818 OSStatus osErr = noErr; 00819 AudioObjectPropertyAddress pluginAOPA; 00820 pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice; 00821 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; 00822 pluginAOPA.mElement = kAudioObjectPropertyElementMaster; 00823 UInt32 outDataSize; 00824 00825 if (fPluginID > 0) { 00826 00827 osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); 00828 if (osErr != noErr) { 00829 jack_error("DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); 00830 printError(osErr); 00831 return osErr; 00832 } 00833 00834 osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); 00835 if (osErr != noErr) { 00836 jack_error("DestroyAggregateDevice : AudioObjectGetPropertyData error"); 00837 printError(osErr); 00838 return osErr; 00839 } 00840 00841 } 00842 00843 return noErr; 00844 } 00845 00846 OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) 00847 { 00848 OSStatus err = noErr; 00849 AudioObjectID sub_device[32]; 00850 UInt32 outSize = sizeof(sub_device); 00851 00852 err = AudioDeviceGetProperty(captureDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); 00853 vector<AudioDeviceID> captureDeviceIDArray; 00854 00855 jack_log("JackCoreAudioDriver::CreateAggregateDevice : input device %d", captureDeviceID); 00856 00857 if (err != noErr) { 00858 jack_log("JackCoreAudioDriver::CreateAggregateDevice : input device does not have subdevices"); 00859 captureDeviceIDArray.push_back(captureDeviceID); 00860 } else { 00861 int num_devices = outSize / sizeof(AudioObjectID); 00862 jack_log("JackCoreAudioDriver::CreateAggregateDevice : input device has %d subdevices", num_devices); 00863 for (int i = 0; i < num_devices; i++) { 00864 jack_log("JackCoreAudioDriver::CreateAggregateDevice : input sub_device %d", sub_device[i]); 00865 captureDeviceIDArray.push_back(sub_device[i]); 00866 } 00867 } 00868 00869 outSize = sizeof(sub_device); 00870 err = AudioDeviceGetProperty(playbackDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); 00871 vector<AudioDeviceID> playbackDeviceIDArray; 00872 00873 jack_log("JackCoreAudioDriver::CreateAggregateDevice : output device %d", playbackDeviceID); 00874 00875 if (err != noErr) { 00876 jack_log("JackCoreAudioDriver::CreateAggregateDevice : output device does not have subdevices"); 00877 playbackDeviceIDArray.push_back(playbackDeviceID); 00878 } else { 00879 int num_devices = outSize / sizeof(AudioObjectID); 00880 jack_log("JackCoreAudioDriver::CreateAggregateDevice : output device has %d subdevices", num_devices); 00881 for (int i = 0; i < num_devices; i++) { 00882 jack_log("JackCoreAudioDriver::CreateAggregateDevice : output sub_device %d", sub_device[i]); 00883 playbackDeviceIDArray.push_back(sub_device[i]); 00884 } 00885 } 00886 00887 return CreateAggregateDeviceAux(captureDeviceIDArray, playbackDeviceIDArray, samplerate, outAggregateDevice); 00888 } 00889 00890 OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> captureDeviceID, vector<AudioDeviceID> playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) 00891 { 00892 OSStatus osErr = noErr; 00893 UInt32 outSize; 00894 Boolean outWritable; 00895 00896 // Prepare sub-devices for clock drift compensation 00897 // Workaround for bug in the HAL : until 10.6.2 00898 AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 00899 AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 00900 UInt32 theQualifierDataSize = sizeof(AudioObjectID); 00901 AudioClassID inClass = kAudioSubDeviceClassID; 00902 void* theQualifierData = &inClass; 00903 UInt32 subDevicesNum = 0; 00904 00905 //--------------------------------------------------------------------------- 00906 // Setup SR of both devices otherwise creating AD may fail... 00907 //--------------------------------------------------------------------------- 00908 UInt32 keptclockdomain = 0; 00909 UInt32 clockdomain = 0; 00910 outSize = sizeof(UInt32); 00911 bool need_clock_drift_compensation = false; 00912 00913 for (UInt32 i = 0; i < captureDeviceID.size(); i++) { 00914 if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) { 00915 jack_error("CreateAggregateDeviceAux : cannot set SR of input device"); 00916 } else { 00917 // Check clock domain 00918 osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); 00919 if (osErr != 0) { 00920 jack_error("CreateAggregateDeviceAux : kAudioDevicePropertyClockDomain error"); 00921 printError(osErr); 00922 } else { 00923 keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; 00924 jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : input clockdomain = %d", clockdomain); 00925 if (clockdomain != 0 && clockdomain != keptclockdomain) { 00926 jack_error("CreateAggregateDeviceAux : devices do not share the same clock!! clock drift compensation would be needed..."); 00927 need_clock_drift_compensation = true; 00928 } 00929 } 00930 } 00931 } 00932 00933 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { 00934 if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) { 00935 jack_error("CreateAggregateDeviceAux : cannot set SR of output device"); 00936 } else { 00937 // Check clock domain 00938 osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); 00939 if (osErr != 0) { 00940 jack_error("CreateAggregateDeviceAux : kAudioDevicePropertyClockDomain error"); 00941 printError(osErr); 00942 } else { 00943 keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; 00944 jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : output clockdomain = %d", clockdomain); 00945 if (clockdomain != 0 && clockdomain != keptclockdomain) { 00946 jack_error("CreateAggregateDeviceAux : devices do not share the same clock!! clock drift compensation would be needed..."); 00947 need_clock_drift_compensation = true; 00948 } 00949 } 00950 } 00951 } 00952 00953 // If no valid clock domain was found, then assume we have to compensate... 00954 if (keptclockdomain == 0) { 00955 need_clock_drift_compensation = true; 00956 } 00957 00958 //--------------------------------------------------------------------------- 00959 // Start to create a new aggregate by getting the base audio hardware plugin 00960 //--------------------------------------------------------------------------- 00961 00962 char device_name[256]; 00963 for (UInt32 i = 0; i < captureDeviceID.size(); i++) { 00964 GetDeviceNameFromID(captureDeviceID[i], device_name); 00965 jack_info("Separated input = '%s' ", device_name); 00966 } 00967 00968 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { 00969 GetDeviceNameFromID(playbackDeviceID[i], device_name); 00970 jack_info("Separated output = '%s' ", device_name); 00971 } 00972 00973 osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); 00974 if (osErr != noErr) { 00975 jack_error("CreateAggregateDeviceAux : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); 00976 printError(osErr); 00977 return osErr; 00978 } 00979 00980 AudioValueTranslation pluginAVT; 00981 00982 CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio"); 00983 00984 pluginAVT.mInputData = &inBundleRef; 00985 pluginAVT.mInputDataSize = sizeof(inBundleRef); 00986 pluginAVT.mOutputData = &fPluginID; 00987 pluginAVT.mOutputDataSize = sizeof(fPluginID); 00988 00989 osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); 00990 if (osErr != noErr) { 00991 jack_error("CreateAggregateDeviceAux : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); 00992 printError(osErr); 00993 return osErr; 00994 } 00995 00996 //------------------------------------------------- 00997 // Create a CFDictionary for our aggregate device 00998 //------------------------------------------------- 00999 01000 CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 01001 01002 CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex"); 01003 CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex"); 01004 01005 // add the name of the device to the dictionary 01006 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef); 01007 01008 // add our choice of UID for the aggregate device to the dictionary 01009 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef); 01010 01011 // add a "private aggregate key" to the dictionary 01012 int value = 1; 01013 CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value); 01014 01015 SInt32 system; 01016 Gestalt(gestaltSystemVersion, &system); 01017 01018 jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : system version = %x limit = %x", system, 0x00001054); 01019 01020 // Starting with 10.5.4 systems, the AD can be internal... (better) 01021 if (system < 0x00001054) { 01022 jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : public aggregate device...."); 01023 } else { 01024 jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : private aggregate device...."); 01025 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); 01026 } 01027 01028 // Prepare sub-devices for clock drift compensation 01029 CFMutableArrayRef subDevicesArrayClock = NULL; 01030 01031 /* 01032 if (fClockDriftCompensate) { 01033 if (need_clock_drift_compensation) { 01034 jack_info("Clock drift compensation activated..."); 01035 subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 01036 01037 for (UInt32 i = 0; i < captureDeviceID.size(); i++) { 01038 CFStringRef UID = GetDeviceName(captureDeviceID[i]); 01039 if (UID) { 01040 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 01041 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); 01042 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); 01043 //CFRelease(UID); 01044 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); 01045 } 01046 } 01047 01048 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { 01049 CFStringRef UID = GetDeviceName(playbackDeviceID[i]); 01050 if (UID) { 01051 CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 01052 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); 01053 CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); 01054 //CFRelease(UID); 01055 CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); 01056 } 01057 } 01058 01059 // add sub-device clock array for the aggregate device to the dictionary 01060 CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock); 01061 } else { 01062 jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); 01063 } 01064 } 01065 */ 01066 01067 //------------------------------------------------- 01068 // Create a CFMutableArray for our sub-device list 01069 //------------------------------------------------- 01070 01071 // we need to append the UID for each device to a CFMutableArray, so create one here 01072 CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 01073 01074 vector<CFStringRef> captureDeviceUID; 01075 for (UInt32 i = 0; i < captureDeviceID.size(); i++) { 01076 CFStringRef ref = GetDeviceName(captureDeviceID[i]); 01077 if (ref == NULL) { 01078 return -1; 01079 } 01080 captureDeviceUID.push_back(ref); 01081 // input sub-devices in this example, so append the sub-device's UID to the CFArray 01082 CFArrayAppendValue(subDevicesArray, ref); 01083 } 01084 01085 vector<CFStringRef> playbackDeviceUID; 01086 for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { 01087 CFStringRef ref = GetDeviceName(playbackDeviceID[i]); 01088 if (ref == NULL) { 01089 return -1; 01090 } 01091 playbackDeviceUID.push_back(ref); 01092 // output sub-devices in this example, so append the sub-device's UID to the CFArray 01093 CFArrayAppendValue(subDevicesArray, ref); 01094 } 01095 01096 //----------------------------------------------------------------------- 01097 // Feed the dictionary to the plugin, to create a blank aggregate device 01098 //----------------------------------------------------------------------- 01099 01100 AudioObjectPropertyAddress pluginAOPA; 01101 pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice; 01102 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; 01103 pluginAOPA.mElement = kAudioObjectPropertyElementMaster; 01104 UInt32 outDataSize; 01105 01106 osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); 01107 if (osErr != noErr) { 01108 jack_error("CreateAggregateDeviceAux : AudioObjectGetPropertyDataSize error"); 01109 printError(osErr); 01110 goto error; 01111 } 01112 01113 osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); 01114 if (osErr != noErr) { 01115 jack_error("CreateAggregateDeviceAux : AudioObjectGetPropertyData error"); 01116 printError(osErr); 01117 goto error; 01118 } 01119 01120 // pause for a bit to make sure that everything completed correctly 01121 // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created 01122 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); 01123 01124 //------------------------- 01125 // Set the sub-device list 01126 //------------------------- 01127 01128 pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList; 01129 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; 01130 pluginAOPA.mElement = kAudioObjectPropertyElementMaster; 01131 outDataSize = sizeof(CFMutableArrayRef); 01132 osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); 01133 if (osErr != noErr) { 01134 jack_error("CreateAggregateDeviceAux : AudioObjectSetPropertyData for sub-device list error"); 01135 printError(osErr); 01136 goto error; 01137 } 01138 01139 // pause again to give the changes time to take effect 01140 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); 01141 01142 //----------------------- 01143 // Set the master device 01144 //----------------------- 01145 01146 // set the master device manually (this is the device which will act as the master clock for the aggregate device) 01147 // pass in the UID of the device you want to use 01148 pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice; 01149 pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; 01150 pluginAOPA.mElement = kAudioObjectPropertyElementMaster; 01151 outDataSize = sizeof(CFStringRef); 01152 osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]); // First capture is master... 01153 if (osErr != noErr) { 01154 jack_error("CreateAggregateDeviceAux : AudioObjectSetPropertyData for master device error"); 01155 printError(osErr); 01156 goto error; 01157 } 01158 01159 // pause again to give the changes time to take effect 01160 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); 01161 01162 // Prepare sub-devices for clock drift compensation 01163 // Workaround for bug in the HAL : until 10.6.2 01164 01165 if (fClockDriftCompensate) { 01166 if (need_clock_drift_compensation) { 01167 jack_info("Clock drift compensation activated..."); 01168 01169 // Get the property data size 01170 osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize); 01171 if (osErr != noErr) { 01172 jack_error("CreateAggregateDeviceAux kAudioObjectPropertyOwnedObjects error"); 01173 printError(osErr); 01174 } 01175 01176 // Calculate the number of object IDs 01177 subDevicesNum = outSize / sizeof(AudioObjectID); 01178 jack_info("JackCoreAudioDriver::CreateAggregateDeviceAux clock drift compensation, number of sub-devices = %d", subDevicesNum); 01179 AudioObjectID subDevices[subDevicesNum]; 01180 outSize = sizeof(subDevices); 01181 01182 osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices); 01183 if (osErr != noErr) { 01184 jack_error("CreateAggregateDeviceAux kAudioObjectPropertyOwnedObjects error"); 01185 printError(osErr); 01186 } 01187 01188 // Set kAudioSubDevicePropertyDriftCompensation property... 01189 for (UInt32 index = 0; index < subDevicesNum; ++index) { 01190 UInt32 theDriftCompensationValue = 1; 01191 osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); 01192 if (osErr != noErr) { 01193 jack_error("CreateAggregateDeviceAux kAudioSubDevicePropertyDriftCompensation error"); 01194 printError(osErr); 01195 } 01196 } 01197 } else { 01198 jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); 01199 } 01200 } 01201 01202 // pause again to give the changes time to take effect 01203 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); 01204 01205 //---------- 01206 // Clean up 01207 //---------- 01208 01209 // release the private AD key 01210 CFRelease(AggregateDeviceNumberRef); 01211 01212 // release the CF objects we have created - we don't need them any more 01213 CFRelease(aggDeviceDict); 01214 CFRelease(subDevicesArray); 01215 01216 if (subDevicesArrayClock) { 01217 CFRelease(subDevicesArrayClock); 01218 } 01219 01220 // release the device UID 01221 for (UInt32 i = 0; i < captureDeviceUID.size(); i++) { 01222 CFRelease(captureDeviceUID[i]); 01223 } 01224 01225 for (UInt32 i = 0; i < playbackDeviceUID.size(); i++) { 01226 CFRelease(playbackDeviceUID[i]); 01227 } 01228 01229 jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : new aggregate device %ld", *outAggregateDevice); 01230 return noErr; 01231 01232 error: 01233 DestroyAggregateDevice(); 01234 return -1; 01235 } 01236 01237 int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, 01238 const char* playback_driver_uid, 01239 char* capture_driver_name, 01240 char* playback_driver_name, 01241 jack_nframes_t samplerate, 01242 bool ac3_encoding) 01243 { 01244 capture_driver_name[0] = 0; 01245 playback_driver_name[0] = 0; 01246 01247 // Duplex 01248 if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) { 01249 jack_log("JackCoreAudioDriver::SetupDevices : duplex"); 01250 01251 // Same device for capture and playback... 01252 if (strcmp(capture_driver_uid, playback_driver_uid) == 0) { 01253 01254 if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { 01255 jack_log("JackCoreAudioDriver::SetupDevices : will take default in/out"); 01256 if (GetDefaultDevice(&fDeviceID) != noErr) { 01257 jack_error("Cannot open default device"); 01258 return -1; 01259 } 01260 } 01261 01262 if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { 01263 jack_error("Cannot get device name from device ID"); 01264 return -1; 01265 } 01266 01267 if (fHogged) { 01268 if (!TakeHogAux(fDeviceID, false)) { 01269 jack_error("Cannot take hog mode"); 01270 } 01271 if (ac3_encoding) { 01272 fDigitalPlayback = IsDigitalDevice(fDeviceID); 01273 } 01274 } 01275 01276 } else { 01277 01278 // Creates aggregate device 01279 AudioDeviceID captureID = -1; 01280 AudioDeviceID playbackID = -1; 01281 01282 if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { 01283 jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); 01284 if (GetDefaultInputDevice(&captureID) != noErr) { 01285 jack_error("Cannot open default input device"); 01286 return -1; 01287 } 01288 } 01289 01290 if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { 01291 jack_log("JackCoreAudioDriver::SetupDevices : will take default output"); 01292 if (GetDefaultOutputDevice(&playbackID) != noErr) { 01293 jack_error("Cannot open default output device"); 01294 return -1; 01295 } 01296 } 01297 01298 if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { 01299 return -1; 01300 } 01301 01302 GetDeviceNameFromID(captureID, fCaptureUID); 01303 GetDeviceNameFromID(playbackID, fPlaybackUID); 01304 01305 if (fHogged) { 01306 if (!TakeHogAux(captureID, true)) { 01307 jack_error("Cannot take hog mode for capture device"); 01308 } 01309 if (!TakeHogAux(playbackID, false)) { 01310 jack_error("Cannot take hog mode for playback device"); 01311 } 01312 if (ac3_encoding) { 01313 fDigitalPlayback = IsDigitalDevice(playbackID); 01314 } 01315 } 01316 01317 } 01318 01319 // Capture only 01320 } else if (strcmp(capture_driver_uid, "") != 0) { 01321 01322 jack_log("JackCoreAudioDriver::SetupDevices : capture only"); 01323 if (GetDeviceIDFromUID(capture_driver_uid, &fDeviceID) != noErr) { 01324 jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); 01325 if (GetDefaultInputDevice(&fDeviceID) != noErr) { 01326 jack_error("Cannot open default input device"); 01327 return -1; 01328 } 01329 } 01330 01331 if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr) { 01332 jack_error("Cannot get device name from device ID"); 01333 return -1; 01334 } 01335 01336 if (fHogged) { 01337 if (!TakeHogAux(fDeviceID, true)) { 01338 jack_error("Cannot take hog mode for capture device"); 01339 } 01340 } 01341 01342 // Playback only 01343 } else if (strcmp(playback_driver_uid, "") != 0) { 01344 01345 jack_log("JackCoreAudioDriver::SetupDevices : playback only"); 01346 if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { 01347 jack_log("JackCoreAudioDriver::SetupDevices : will take default output"); 01348 if (GetDefaultOutputDevice(&fDeviceID) != noErr) { 01349 jack_error("Cannot open default output device"); 01350 return -1; 01351 } 01352 } 01353 01354 if (GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { 01355 jack_error("Cannot get device name from device ID"); 01356 return -1; 01357 } 01358 01359 if (fHogged) { 01360 if (!TakeHogAux(fDeviceID, false)) { 01361 jack_error("Cannot take hog mode for playback device"); 01362 } 01363 if (ac3_encoding) { 01364 fDigitalPlayback = IsDigitalDevice(fDeviceID); 01365 } 01366 } 01367 01368 // Use default driver in duplex mode 01369 } else { 01370 jack_log("JackCoreAudioDriver::SetupDevices : default driver"); 01371 if (GetDefaultDevice(&fDeviceID) != noErr) { 01372 jack_error("Cannot open default device in duplex mode, so aggregate default input and default output"); 01373 01374 // Creates aggregate device 01375 AudioDeviceID captureID = -1; 01376 AudioDeviceID playbackID = -1; 01377 01378 if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { 01379 jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); 01380 if (GetDefaultInputDevice(&captureID) != noErr) { 01381 jack_error("Cannot open default input device"); 01382 return -1; 01383 } 01384 } 01385 01386 if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { 01387 jack_log("JackCoreAudioDriver::SetupDevices : will take default output"); 01388 if (GetDefaultOutputDevice(&playbackID) != noErr) { 01389 jack_error("Cannot open default output device"); 01390 return -1; 01391 } 01392 } 01393 01394 if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { 01395 return -1; 01396 } 01397 01398 GetDeviceNameFromID(captureID, fCaptureUID); 01399 GetDeviceNameFromID(playbackID, fPlaybackUID); 01400 01401 if (fHogged) { 01402 if (!TakeHogAux(captureID, true)) { 01403 jack_error("Cannot take hog mode for capture device"); 01404 } 01405 if (!TakeHogAux(playbackID, false)) { 01406 jack_error("Cannot take hog mode for playback device"); 01407 } 01408 if (ac3_encoding) { 01409 fDigitalPlayback = IsDigitalDevice(playbackID); 01410 } 01411 } 01412 } 01413 } 01414 01415 return 0; 01416 } 01417 01418 /* 01419 Return the max possible input channels in in_maxChannels and output channels in out_maxChannels. 01420 */ 01421 int JackCoreAudioDriver::SetupChannels(bool capturing, bool playing, int& inchannels, int& outchannels, int& in_maxChannels, int& out_maxChannels, bool strict) 01422 { 01423 OSStatus err = noErr; 01424 01425 jack_log("JackCoreAudioDriver::SetupChannels : fDeviceID = %d", fDeviceID); 01426 01427 if (capturing) { 01428 err = GetTotalChannels(fDeviceID, in_maxChannels, true); 01429 if (err != noErr) { 01430 jack_error("SetupChannels : cannot get input channel number"); 01431 printError(err); 01432 return -1; 01433 } else { 01434 jack_log("JackCoreAudioDriver::SetupChannels : max input channels : %d", in_maxChannels); 01435 } 01436 } 01437 01438 if (playing) { 01439 err = GetTotalChannels(fDeviceID, out_maxChannels, false); 01440 if (err != noErr) { 01441 jack_error("Cannot get output channel number"); 01442 printError(err); 01443 return -1; 01444 } else { 01445 jack_log("JackCoreAudioDriver::SetupChannels : max output channels : %d", out_maxChannels); 01446 } 01447 } 01448 01449 if (inchannels > in_maxChannels) { 01450 jack_error("This device hasn't required input channels inchannels = %d in_maxChannels = %d", inchannels, in_maxChannels); 01451 if (strict) { 01452 return -1; 01453 } 01454 } 01455 01456 if (outchannels > out_maxChannels) { 01457 jack_error("This device hasn't required output channels outchannels = %d out_maxChannels = %d", outchannels, out_maxChannels); 01458 if (strict) { 01459 return -1; 01460 } 01461 } 01462 01463 if (inchannels == -1) { 01464 jack_log("JackCoreAudioDriver::SetupChannels : setup max in channels = %d", in_maxChannels); 01465 inchannels = in_maxChannels; 01466 } 01467 01468 if (outchannels == -1) { 01469 jack_log("JackCoreAudioDriver::SetupChannels : setup max out channels = %d", out_maxChannels); 01470 outchannels = out_maxChannels; 01471 } 01472 01473 return 0; 01474 } 01475 01476 int JackCoreAudioDriver::SetupBufferSize(jack_nframes_t buffer_size) 01477 { 01478 // Setting buffer size 01479 OSStatus err = noErr; 01480 UInt32 tmp_buffer_size = buffer_size; 01481 UInt32 outSize = sizeof(UInt32); 01482 01483 err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); 01484 if (err != noErr) { 01485 jack_error("Cannot get buffer size %ld", buffer_size); 01486 printError(err); 01487 return -1; 01488 } else { 01489 jack_log("JackCoreAudioDriver::SetupBufferSize : current buffer size = %ld", tmp_buffer_size); 01490 } 01491 01492 // If needed, set new buffer size 01493 if (buffer_size != tmp_buffer_size) { 01494 tmp_buffer_size = buffer_size; 01495 01496 // To get BS change notification 01497 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyBufferFrameSize, BSNotificationCallback, this); 01498 if (err != noErr) { 01499 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyBufferFrameSize"); 01500 printError(err); 01501 return -1; 01502 } 01503 01504 // Waiting for BS change notification 01505 int count = 0; 01506 fState = false; 01507 01508 err = AudioDeviceSetProperty(fDeviceID, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, outSize, &tmp_buffer_size); 01509 if (err != noErr) { 01510 jack_error("SetupBufferSize : cannot set buffer size = %ld", tmp_buffer_size); 01511 printError(err); 01512 goto error; 01513 } 01514 01515 while (!fState && count++ < WAIT_NOTIFICATION_COUNTER) { 01516 usleep(100000); 01517 jack_log("JackCoreAudioDriver::SetupBufferSize : wait count = %d", count); 01518 } 01519 01520 if (count >= WAIT_NOTIFICATION_COUNTER) { 01521 jack_error("Did not get buffer size notification..."); 01522 goto error; 01523 } 01524 01525 // Check new buffer size 01526 outSize = sizeof(UInt32); 01527 err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); 01528 if (err != noErr) { 01529 jack_error("Cannot get current buffer size"); 01530 printError(err); 01531 } else { 01532 jack_log("JackCoreAudioDriver::SetupBufferSize : checked buffer size = %ld", tmp_buffer_size); 01533 } 01534 01535 // Remove BS change notification 01536 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyBufferFrameSize, BSNotificationCallback); 01537 } 01538 01539 return 0; 01540 01541 error: 01542 01543 // Remove BS change notification 01544 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyBufferFrameSize, BSNotificationCallback); 01545 return -1; 01546 } 01547 01548 int JackCoreAudioDriver::SetupSampleRate(jack_nframes_t sample_rate) 01549 { 01550 return SetupSampleRateAux(fDeviceID, sample_rate); 01551 } 01552 01553 int JackCoreAudioDriver::SetupSampleRateAux(AudioDeviceID inDevice, jack_nframes_t sample_rate) 01554 { 01555 OSStatus err = noErr; 01556 UInt32 outSize; 01557 Float64 tmp_sample_rate; 01558 01559 // Get sample rate 01560 outSize = sizeof(Float64); 01561 err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); 01562 if (err != noErr) { 01563 jack_error("Cannot get current sample rate"); 01564 printError(err); 01565 return -1; 01566 } else { 01567 jack_log("JackCoreAudioDriver::SetupSampleRateAux : current sample rate = %f", tmp_sample_rate); 01568 } 01569 01570 // If needed, set new sample rate 01571 if (sample_rate != (jack_nframes_t)tmp_sample_rate) { 01572 tmp_sample_rate = (Float64)sample_rate; 01573 01574 // To get SR change notification 01575 err = AudioDeviceAddPropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback, this); 01576 if (err != noErr) { 01577 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); 01578 printError(err); 01579 return -1; 01580 } 01581 01582 // Waiting for SR change notification 01583 int count = 0; 01584 fState = false; 01585 01586 err = AudioDeviceSetProperty(inDevice, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outSize, &tmp_sample_rate); 01587 if (err != noErr) { 01588 jack_error("Cannot set sample rate = %ld", sample_rate); 01589 printError(err); 01590 goto error; 01591 } 01592 01593 while (!fState && count++ < WAIT_NOTIFICATION_COUNTER) { 01594 usleep(100000); 01595 jack_log("JackCoreAudioDriver::SetupSampleRateAux : wait count = %d", count); 01596 } 01597 01598 if (count >= WAIT_NOTIFICATION_COUNTER) { 01599 jack_error("Did not get sample rate notification..."); 01600 goto error; 01601 } 01602 01603 // Check new sample rate 01604 outSize = sizeof(Float64); 01605 err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); 01606 if (err != noErr) { 01607 jack_error("Cannot get current sample rate"); 01608 printError(err); 01609 } else { 01610 jack_log("JackCoreAudioDriver::SetupSampleRateAux : checked sample rate = %f", tmp_sample_rate); 01611 } 01612 01613 // Remove SR change notification 01614 AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback); 01615 } 01616 01617 return 0; 01618 01619 error: 01620 01621 // Remove SR change notification 01622 AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback); 01623 return -1; 01624 } 01625 01626 int JackCoreAudioDriver::OpenAUHAL(bool capturing, 01627 bool playing, 01628 int inchannels, 01629 int outchannels, 01630 int in_maxChannels, 01631 int out_maxChannels, 01632 const vector<int>& chan_in_list, 01633 const vector<int>& chan_out_list, 01634 jack_nframes_t buffer_size, 01635 jack_nframes_t sample_rate) 01636 { 01637 ComponentResult err1; 01638 UInt32 enableIO; 01639 AudioStreamBasicDescription srcFormat, dstFormat; 01640 AudioDeviceID currAudioDeviceID; 01641 UInt32 size; 01642 01643 jack_log("JackCoreAudioDriver::OpenAUHAL : capturing = %d playing = %d inchannels = %d outchannels = %d in_maxChannels = %d out_maxChannels = %d chan_in_list = %d chan_out_list = %d", 01644 capturing, playing, inchannels, outchannels, in_maxChannels, out_maxChannels, chan_in_list.size(), chan_out_list.size()); 01645 01646 if (inchannels == 0 && outchannels == 0) { 01647 jack_error("No input and output channels..."); 01648 return -1; 01649 } 01650 01651 // AUHAL 01652 #ifdef MAC_OS_X_VERSION_10_5 01653 ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; 01654 Component HALOutput = FindNextComponent(NULL, &cd); 01655 err1 = OpenAComponent(HALOutput, &fAUHAL); 01656 if (err1 != noErr) { 01657 jack_error("Error calling OpenAComponent"); 01658 printError(err1); 01659 goto error; 01660 } 01661 #else 01662 AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; 01663 AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd); 01664 err1 = AudioComponentInstanceNew(HALOutput, &fAUHAL); 01665 if (err1 != noErr) { 01666 jack_error("Error calling AudioComponentInstanceNew"); 01667 printError(err1); 01668 goto error; 01669 } 01670 #endif 01671 01672 err1 = AudioUnitInitialize(fAUHAL); 01673 if (err1 != noErr) { 01674 jack_error("Cannot initialize AUHAL unit"); 01675 printError(err1); 01676 goto error; 01677 } 01678 01679 // Start I/O 01680 if (capturing && inchannels > 0) { 01681 enableIO = 1; 01682 jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL input on"); 01683 } else { 01684 enableIO = 0; 01685 jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL input off"); 01686 } 01687 01688 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); 01689 if (err1 != noErr) { 01690 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input"); 01691 printError(err1); 01692 goto error; 01693 } 01694 01695 if (playing && outchannels > 0) { 01696 enableIO = 1; 01697 jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL output on"); 01698 } else { 01699 enableIO = 0; 01700 jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL output off"); 01701 } 01702 01703 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); 01704 if (err1 != noErr) { 01705 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output"); 01706 printError(err1); 01707 goto error; 01708 } 01709 01710 size = sizeof(AudioDeviceID); 01711 err1 = AudioUnitGetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &currAudioDeviceID, &size); 01712 if (err1 != noErr) { 01713 jack_error("Error calling AudioUnitGetProperty - kAudioOutputUnitProperty_CurrentDevice"); 01714 printError(err1); 01715 goto error; 01716 } else { 01717 jack_log("JackCoreAudioDriver::OpenAUHAL : AudioUnitGetPropertyCurrentDevice = %d", currAudioDeviceID); 01718 } 01719 01720 // Setup up choosen device, in both input and output cases 01721 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fDeviceID, sizeof(AudioDeviceID)); 01722 if (err1 != noErr) { 01723 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice"); 01724 printError(err1); 01725 goto error; 01726 } 01727 01728 // Set buffer size 01729 if (capturing && inchannels > 0) { 01730 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&buffer_size, sizeof(UInt32)); 01731 if (err1 != noErr) { 01732 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); 01733 printError(err1); 01734 goto error; 01735 } 01736 } 01737 01738 if (playing && outchannels > 0) { 01739 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&buffer_size, sizeof(UInt32)); 01740 if (err1 != noErr) { 01741 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); 01742 printError(err1); 01743 goto error; 01744 } 01745 } 01746 01747 // Setup input channel map 01748 if (capturing && inchannels > 0 && inchannels <= in_maxChannels) { 01749 SInt32 chanArr[in_maxChannels]; 01750 for (int i = 0; i < in_maxChannels; i++) { 01751 chanArr[i] = -1; 01752 } 01753 // Explicit mapping 01754 if (chan_in_list.size() > 0) { 01755 for (uint i = 0; i < chan_in_list.size(); i++) { 01756 int chan = chan_in_list[i]; 01757 if (chan < in_maxChannels) { 01758 // The wanted JACK input index for the 'chan' channel value 01759 chanArr[chan] = i; 01760 jack_info("Input channel = %d ==> JACK input port = %d", chan, i); 01761 } else { 01762 jack_info("Error input channel number is incorrect : %d", chan); 01763 goto error; 01764 } 01765 } 01766 } else { 01767 for (int i = 0; i < inchannels; i++) { 01768 chanArr[i] = i; 01769 jack_info("Input channel = %d ==> JACK input port = %d", chanArr[i], i); 01770 } 01771 } 01772 01773 AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap , kAudioUnitScope_Input, 1, chanArr, sizeof(SInt32) * in_maxChannels); 01774 if (err1 != noErr) { 01775 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap for input"); 01776 printError(err1); 01777 goto error; 01778 } 01779 } 01780 01781 // Setup output channel map 01782 if (playing && outchannels > 0 && outchannels <= out_maxChannels) { 01783 SInt32 chanArr[out_maxChannels]; 01784 for (int i = 0; i < out_maxChannels; i++) { 01785 chanArr[i] = -1; 01786 } 01787 // Explicit mapping 01788 if (chan_out_list.size() > 0) { 01789 for (uint i = 0; i < chan_out_list.size(); i++) { 01790 int chan = chan_out_list[i]; 01791 if (chan < out_maxChannels) { 01792 // The wanted JACK output index for the 'chan' channel value 01793 chanArr[chan] = i; 01794 jack_info("JACK output port = %d ==> output channel = %d", i, chan); 01795 } else { 01796 jack_info("Error output channel number is incorrect : %d", chan); 01797 goto error; 01798 } 01799 } 01800 } else { 01801 for (int i = 0; i < outchannels; i++) { 01802 chanArr[i] = i; 01803 jack_info("JACK output port = %d ==> output channel = %d", i, chanArr[i]); 01804 } 01805 } 01806 01807 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, chanArr, sizeof(SInt32) * out_maxChannels); 01808 if (err1 != noErr) { 01809 jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap for output"); 01810 printError(err1); 01811 goto error; 01812 } 01813 } 01814 01815 // Setup stream converters 01816 if (capturing && inchannels > 0) { 01817 01818 size = sizeof(AudioStreamBasicDescription); 01819 err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, &size); 01820 if (err1 != noErr) { 01821 jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); 01822 printError(err1); 01823 goto error; 01824 } 01825 PrintStreamDesc(&srcFormat); 01826 01827 jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL input stream converter SR = %ld", sample_rate); 01828 srcFormat.mSampleRate = sample_rate; 01829 srcFormat.mFormatID = kAudioFormatLinearPCM; 01830 srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; 01831 srcFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); 01832 srcFormat.mFramesPerPacket = 1; 01833 srcFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); 01834 srcFormat.mChannelsPerFrame = inchannels; 01835 srcFormat.mBitsPerChannel = 32; 01836 PrintStreamDesc(&srcFormat); 01837 01838 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription)); 01839 if (err1 != noErr) { 01840 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); 01841 printError(err1); 01842 goto error; 01843 } 01844 } 01845 01846 if (playing && outchannels > 0) { 01847 01848 size = sizeof(AudioStreamBasicDescription); 01849 err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, &size); 01850 if (err1 != noErr) { 01851 jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); 01852 printError(err1); 01853 goto error; 01854 } 01855 PrintStreamDesc(&dstFormat); 01856 01857 jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL output stream converter SR = %ld", sample_rate); 01858 dstFormat.mSampleRate = sample_rate; 01859 dstFormat.mFormatID = kAudioFormatLinearPCM; 01860 dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; 01861 dstFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); 01862 dstFormat.mFramesPerPacket = 1; 01863 dstFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); 01864 dstFormat.mChannelsPerFrame = outchannels; 01865 dstFormat.mBitsPerChannel = 32; 01866 PrintStreamDesc(&dstFormat); 01867 01868 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription)); 01869 if (err1 != noErr) { 01870 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); 01871 printError(err1); 01872 goto error; 01873 } 01874 } 01875 01876 // Setup callbacks 01877 if (inchannels > 0 && outchannels == 0) { 01878 AURenderCallbackStruct output; 01879 output.inputProc = Render; 01880 output.inputProcRefCon = this; 01881 err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output)); 01882 if (err1 != noErr) { 01883 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1"); 01884 printError(err1); 01885 goto error; 01886 } 01887 } else { 01888 AURenderCallbackStruct output; 01889 output.inputProc = Render; 01890 output.inputProcRefCon = this; 01891 err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); 01892 if (err1 != noErr) { 01893 jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0"); 01894 printError(err1); 01895 goto error; 01896 } 01897 } 01898 01899 return 0; 01900 01901 error: 01902 CloseAUHAL(); 01903 return -1; 01904 } 01905 01906 int JackCoreAudioDriver::SetupBuffers(int inchannels) 01907 { 01908 // Prepare buffers 01909 fJackInputData = (AudioBufferList*)malloc(sizeof(UInt32) + inchannels * sizeof(AudioBuffer)); 01910 fJackInputData->mNumberBuffers = inchannels; 01911 for (int i = 0; i < inchannels; i++) { 01912 fJackInputData->mBuffers[i].mNumberChannels = 1; 01913 fJackInputData->mBuffers[i].mDataByteSize = fEngineControl->fBufferSize * sizeof(jack_default_audio_sample_t); 01914 } 01915 return 0; 01916 } 01917 01918 void JackCoreAudioDriver::DisposeBuffers() 01919 { 01920 if (fJackInputData) { 01921 free(fJackInputData); 01922 fJackInputData = 0; 01923 } 01924 } 01925 01926 void JackCoreAudioDriver::CloseAUHAL() 01927 { 01928 AudioOutputUnitStop(fAUHAL); 01929 AudioUnitUninitialize(fAUHAL); 01930 CloseComponent(fAUHAL); 01931 } 01932 01933 int JackCoreAudioDriver::AddListeners() 01934 { 01935 OSStatus err = noErr; 01936 01937 // Add listeners 01938 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this); 01939 if (err != noErr) { 01940 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload"); 01941 printError(err); 01942 return -1; 01943 } 01944 01945 err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback, this); 01946 if (err != noErr) { 01947 jack_error("Error calling AudioHardwareAddPropertyListener with kAudioHardwarePropertyDevices"); 01948 printError(err); 01949 return -1; 01950 } 01951 01952 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback, this); 01953 if (err != noErr) { 01954 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); 01955 printError(err); 01956 return -1; 01957 } 01958 01959 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback, this); 01960 if (err != noErr) { 01961 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsRunning"); 01962 printError(err); 01963 return -1; 01964 } 01965 01966 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsAlive, DeviceNotificationCallback, this); 01967 if (err != noErr) { 01968 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsAlive"); 01969 printError(err); 01970 return -1; 01971 } 01972 01973 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceHasChanged, DeviceNotificationCallback, this); 01974 if (err != noErr) { 01975 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceHasChanged"); 01976 printError(err); 01977 return -1; 01978 } 01979 01980 err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); 01981 if (err != noErr) { 01982 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); 01983 printError(err); 01984 return -1; 01985 } 01986 01987 err = AudioDeviceAddPropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); 01988 if (err != noErr) { 01989 jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); 01990 printError(err); 01991 return -1; 01992 } 01993 01994 if (!fEngineControl->fSyncMode && fIOUsage != 1.f) { 01995 UInt32 outSize = sizeof(float); 01996 err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyIOCycleUsage, outSize, &fIOUsage); 01997 if (err != noErr) { 01998 jack_error("Error calling AudioDeviceSetProperty kAudioDevicePropertyIOCycleUsage"); 01999 printError(err); 02000 } 02001 } 02002 02003 return 0; 02004 } 02005 02006 void JackCoreAudioDriver::RemoveListeners() 02007 { 02008 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback); 02009 AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback); 02010 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback); 02011 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback); 02012 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsAlive, DeviceNotificationCallback); 02013 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceHasChanged, DeviceNotificationCallback); 02014 AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); 02015 AudioDeviceRemovePropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); 02016 } 02017 02018 int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, 02019 jack_nframes_t sample_rate, 02020 bool capturing, 02021 bool playing, 02022 int inchannels, 02023 int outchannels, 02024 const char* chan_in_list, 02025 const char* chan_out_list, 02026 bool monitor, 02027 const char* capture_driver_uid, 02028 const char* playback_driver_uid, 02029 jack_nframes_t capture_latency, 02030 jack_nframes_t playback_latency, 02031 int async_output_latency, 02032 int computation_grain, 02033 bool hogged, 02034 bool clock_drift, 02035 bool ac3_encoding, 02036 int ac3_bitrate, 02037 bool ac3_lfe) 02038 { 02039 int in_maxChannels = 0; 02040 int out_maxChannels = 0; 02041 char capture_driver_name[256]; 02042 char playback_driver_name[256]; 02043 02044 fCaptureLatency = capture_latency; 02045 fPlaybackLatency = playback_latency; 02046 fIOUsage = float(async_output_latency) / 100.f; 02047 fComputationGrain = float(computation_grain) / 100.f; 02048 fHogged = hogged; 02049 fClockDriftCompensate = clock_drift; 02050 02051 SInt32 major; 02052 SInt32 minor; 02053 Gestalt(gestaltSystemVersionMajor, &major); 02054 Gestalt(gestaltSystemVersionMinor, &minor); 02055 02056 vector<int> parsed_chan_in_list; 02057 vector<int> parsed_chan_out_list; 02058 02059 // Starting with 10.6 systems, the HAL notification thread is created internally 02060 if (major == 10 && minor >= 6) { 02061 CFRunLoopRef theRunLoop = NULL; 02062 AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 02063 OSStatus osErr = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); 02064 if (osErr != noErr) { 02065 jack_error("Open kAudioHardwarePropertyRunLoop error"); 02066 printError(osErr); 02067 } 02068 } 02069 02070 if (SetupDevices(capture_driver_uid, playback_driver_uid, capture_driver_name, playback_driver_name, sample_rate, ac3_encoding) < 0) { 02071 goto error; 02072 } 02073 02074 // Generic JackAudioDriver Open 02075 if (JackAudioDriver::Open(buffer_size, sample_rate, 02076 capturing, playing, 02077 inchannels, outchannels, 02078 monitor, 02079 capture_driver_name, 02080 playback_driver_name, 02081 capture_latency, 02082 playback_latency) != 0) { 02083 goto error; 02084 } 02085 02086 if (SetupChannels(capturing, playing, inchannels, outchannels, in_maxChannels, out_maxChannels, !ac3_encoding) < 0) { 02087 goto error; 02088 } 02089 02090 ParseChannelList(chan_in_list, parsed_chan_in_list, in_maxChannels); 02091 if (parsed_chan_in_list.size() > 0) { 02092 jack_info("Explicit input channel list size = %d", parsed_chan_in_list.size()); 02093 inchannels = parsed_chan_in_list.size(); 02094 } 02095 02096 ParseChannelList(chan_out_list, parsed_chan_out_list, out_maxChannels); 02097 if (parsed_chan_out_list.size() > 0) { 02098 jack_info("Explicit output channel list size = %d", parsed_chan_out_list.size()); 02099 outchannels = parsed_chan_out_list.size(); 02100 } 02101 02102 if (SetupBufferSize(buffer_size) < 0) { 02103 goto error; 02104 } 02105 02106 if (SetupSampleRate(sample_rate) < 0) { 02107 goto error; 02108 } 02109 02110 if (ac3_encoding) { 02111 02112 if (!fDigitalPlayback) { 02113 jack_error("AC3 encoding can only be used with a digital device"); 02114 goto error; 02115 } 02116 02117 JackAC3EncoderParams params; 02118 memset(¶ms, 0, sizeof(JackAC3EncoderParams)); 02119 params.bitrate = ac3_bitrate; 02120 params.channels = outchannels; 02121 params.sample_rate = sample_rate; 02122 params.lfe = ac3_lfe; 02123 fAC3Encoder = new JackAC3Encoder(params); 02124 02125 if (!fAC3Encoder || !fAC3Encoder->Init(sample_rate)) { 02126 jack_error("Cannot allocate or init AC3 encoder"); 02127 goto error; 02128 } 02129 02130 // Setup AC3 channel number 02131 fPlaybackChannels = outchannels; 02132 if (ac3_lfe) { 02133 fPlaybackChannels++; 02134 } 02135 02136 if (fPlaybackChannels < 2 || fPlaybackChannels > 6) { 02137 jack_error("AC3 encoder channels must be between 2 and 6"); 02138 goto error; 02139 } 02140 02141 // Force real output channel number to 2 02142 outchannels = out_maxChannels = 2; 02143 02144 } else { 02145 fPlaybackChannels = outchannels; 02146 } 02147 02148 // Core driver may have changed the in/out values 02149 fCaptureChannels = inchannels; 02150 02151 if (OpenAUHAL(capturing, playing, inchannels, outchannels, in_maxChannels, out_maxChannels, parsed_chan_in_list, parsed_chan_out_list, buffer_size, sample_rate) < 0) { 02152 goto error; 02153 } 02154 02155 if (capturing && inchannels > 0) { 02156 if (SetupBuffers(inchannels) < 0) { 02157 goto error; 02158 } 02159 } 02160 02161 if (AddListeners() < 0) { 02162 goto error; 02163 } 02164 02165 return noErr; 02166 02167 error: 02168 Close(); 02169 return -1; 02170 } 02171 02172 int JackCoreAudioDriver::Close() 02173 { 02174 jack_log("JackCoreAudioDriver::Close"); 02175 02176 // Generic audio driver close 02177 int res = JackAudioDriver::Close(); 02178 02179 RemoveListeners(); 02180 DisposeBuffers(); 02181 CloseAUHAL(); 02182 DestroyAggregateDevice(); 02183 return res; 02184 } 02185 02186 void JackCoreAudioDriver::UpdateLatencies() 02187 { 02188 UInt32 size; 02189 OSStatus err; 02190 jack_latency_range_t input_range; 02191 jack_latency_range_t output_range; 02192 jack_latency_range_t monitor_range; 02193 02194 // Get Input latency 02195 size = sizeof(UInt32); 02196 UInt32 value1 = 0; 02197 UInt32 value2 = 0; 02198 err = AudioDeviceGetProperty(fDeviceID, 0, true, kAudioDevicePropertyLatency, &size, &value1); 02199 if (err != noErr) { 02200 jack_error("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); 02201 } 02202 err = AudioDeviceGetProperty(fDeviceID, 0, true, kAudioDevicePropertySafetyOffset, &size, &value2); 02203 if (err != noErr) { 02204 jack_error("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); 02205 } 02206 02207 input_range.min = input_range.max = fEngineControl->fBufferSize + value1 + value2 + fCaptureLatency; 02208 02209 // Get input stream latencies 02210 vector<int> input_latencies; 02211 err = GetStreamLatencies(fDeviceID, true, input_latencies); 02212 02213 for (int i = 0; i < fCaptureChannels; i++) { 02214 if (err != noErr) { 02215 input_range.min += input_latencies[i]; 02216 input_range.max += input_latencies[i]; 02217 } 02218 fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &input_range); 02219 } 02220 02221 // Get Output latency 02222 size = sizeof(UInt32); 02223 value1 = 0; 02224 value2 = 0; 02225 err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertyLatency, &size, &value1); 02226 if (err != noErr) { 02227 jack_error("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); 02228 } 02229 err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertySafetyOffset, &size, &value2); 02230 if (err != noErr) { 02231 jack_error("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); 02232 } 02233 02234 // Get output stream latencies 02235 vector<int> output_latencies; 02236 err = GetStreamLatencies(fDeviceID, false, output_latencies); 02237 02238 // Add more latency if "async" mode is used... 02239 output_range.min = output_range.max = fEngineControl->fBufferSize + ((fEngineControl->fSyncMode) 02240 ? 0 : fEngineControl->fBufferSize * fIOUsage) + value1 + value2 + fPlaybackLatency; 02241 02242 for (int i = 0; i < fPlaybackChannels; i++) { 02243 if (err != noErr) { 02244 output_range.min += output_latencies[i]; 02245 output_range.max += output_latencies[i]; 02246 } 02247 fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &output_range); 02248 02249 // Monitor port 02250 if (fWithMonitorPorts) { 02251 monitor_range.min = monitor_range.max = fEngineControl->fBufferSize; 02252 fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &monitor_range); 02253 } 02254 } 02255 } 02256 02257 int JackCoreAudioDriver::Attach() 02258 { 02259 OSStatus err; 02260 JackPort* port; 02261 jack_port_id_t port_index; 02262 UInt32 size; 02263 Boolean isWritable; 02264 char channel_name[64]; 02265 char name[REAL_JACK_PORT_NAME_SIZE]; 02266 char alias[REAL_JACK_PORT_NAME_SIZE]; 02267 02268 jack_log("JackCoreAudioDriver::Attach : fBufferSize %ld fSampleRate %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); 02269 02270 for (int i = 0; i < fCaptureChannels; i++) { 02271 02272 err = AudioDeviceGetPropertyInfo(fDeviceID, i + 1, true, kAudioDevicePropertyChannelName, &size, &isWritable); 02273 if (err != noErr) { 02274 jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetPropertyInfo kAudioDevicePropertyChannelName error"); 02275 } 02276 if (err == noErr && size > 0) { 02277 err = AudioDeviceGetProperty(fDeviceID, i + 1, true, kAudioDevicePropertyChannelName, &size, channel_name); 02278 if (err != noErr) { 02279 jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetProperty kAudioDevicePropertyChannelName error"); 02280 } 02281 snprintf(alias, sizeof(alias), "%s:%s:out_%s%u", fAliasName, fCaptureDriverName, channel_name, i + 1); 02282 } else { 02283 snprintf(alias, sizeof(alias), "%s:%s:out%u", fAliasName, fCaptureDriverName, i + 1); 02284 } 02285 02286 snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, i + 1); 02287 02288 if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { 02289 jack_error("Cannot register port for %s", name); 02290 return -1; 02291 } 02292 02293 port = fGraphManager->GetPort(port_index); 02294 port->SetAlias(alias); 02295 fCapturePortList[i] = port_index; 02296 } 02297 02298 for (int i = 0; i < fPlaybackChannels; i++) { 02299 02300 err = AudioDeviceGetPropertyInfo(fDeviceID, i + 1, false, kAudioDevicePropertyChannelName, &size, &isWritable); 02301 if (err != noErr) { 02302 jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetPropertyInfo kAudioDevicePropertyChannelName error"); 02303 } 02304 if (err == noErr && size > 0) { 02305 err = AudioDeviceGetProperty(fDeviceID, i + 1, false, kAudioDevicePropertyChannelName, &size, channel_name); 02306 if (err != noErr) { 02307 jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetProperty kAudioDevicePropertyChannelName error"); 02308 } 02309 snprintf(alias, sizeof(alias), "%s:%s:in_%s%u", fAliasName, fPlaybackDriverName, channel_name, i + 1); 02310 } else { 02311 snprintf(alias, sizeof(alias), "%s:%s:in%u", fAliasName, fPlaybackDriverName, i + 1); 02312 } 02313 02314 snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, i + 1); 02315 02316 if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { 02317 jack_error("Cannot register port for %s", name); 02318 return -1; 02319 } 02320 02321 port = fGraphManager->GetPort(port_index); 02322 port->SetAlias(alias); 02323 fPlaybackPortList[i] = port_index; 02324 02325 // Monitor ports 02326 if (fWithMonitorPorts) { 02327 jack_log("JackCoreAudioDriver::Attach : create monitor port"); 02328 snprintf(name, sizeof(name), "%s:monitor_%u", fClientControl.fName, i + 1); 02329 if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, MonitorDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { 02330 jack_error("Cannot register monitor port for %s", name); 02331 return -1; 02332 } else { 02333 fMonitorPortList[i] = port_index; 02334 } 02335 } 02336 } 02337 02338 if (fAC3Encoder) { 02339 // Setup specific AC3 channels names 02340 for (int i = 0; i < fPlaybackChannels; i++) { 02341 fAC3Encoder->GetChannelName("coreaudio", "", alias, i); 02342 port = fGraphManager->GetPort(fPlaybackPortList[i]); 02343 port->SetAlias(alias); 02344 } 02345 } 02346 02347 UpdateLatencies(); 02348 02349 // Input buffers do no change : prepare them only once 02350 for (int i = 0; i < fCaptureChannels; i++) { 02351 fJackInputData->mBuffers[i].mData = GetInputBuffer(i); 02352 } 02353 02354 return 0; 02355 } 02356 02357 int JackCoreAudioDriver::Start() 02358 { 02359 jack_log("JackCoreAudioDriver::Start"); 02360 if (JackAudioDriver::Start() == 0) { 02361 02362 // Waiting for Render callback to be called (= driver has started) 02363 fState = false; 02364 int count = 0; 02365 02366 if (AudioOutputUnitStart(fAUHAL) == noErr) { 02367 02368 while (!fState && count++ < WAIT_COUNTER) { 02369 usleep(100000); 02370 jack_log("JackCoreAudioDriver::Start : wait count = %d", count); 02371 } 02372 02373 if (count < WAIT_COUNTER) { 02374 jack_info("CoreAudio driver is running..."); 02375 return 0; 02376 } 02377 02378 jack_error("CoreAudio driver cannot start..."); 02379 } 02380 JackAudioDriver::Stop(); 02381 } 02382 return -1; 02383 } 02384 02385 int JackCoreAudioDriver::Stop() 02386 { 02387 jack_log("JackCoreAudioDriver::Stop"); 02388 int res = (AudioOutputUnitStop(fAUHAL) == noErr) ? 0 : -1; 02389 if (JackAudioDriver::Stop() < 0) { 02390 res = -1; 02391 } 02392 return res; 02393 } 02394 02395 int JackCoreAudioDriver::SetBufferSize(jack_nframes_t buffer_size) 02396 { 02397 if (SetupBufferSize(buffer_size) < 0) { 02398 return -1; 02399 } 02400 02401 JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails 02402 02403 // CoreAudio specific 02404 UpdateLatencies(); 02405 02406 // Input buffers do no change : prepare them only once 02407 for (int i = 0; i < fCaptureChannels; i++) { 02408 fJackInputData->mBuffers[i].mNumberChannels = 1; 02409 fJackInputData->mBuffers[i].mDataByteSize = fEngineControl->fBufferSize * sizeof(jack_default_audio_sample_t); 02410 fJackInputData->mBuffers[i].mData = GetInputBuffer(i); 02411 } 02412 02413 return 0; 02414 } 02415 02416 bool JackCoreAudioDriver::TakeHogAux(AudioDeviceID deviceID, bool isInput) 02417 { 02418 pid_t hog_pid; 02419 UInt32 propSize = sizeof(hog_pid); 02420 02421 OSStatus err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyHogMode, &propSize, &hog_pid); 02422 if (err) { 02423 jack_error("Cannot read hog state..."); 02424 printError(err); 02425 } 02426 02427 jack_log("JackCoreAudioDriver::TakeHogAux : deviceID = %d", deviceID); 02428 02429 if (hog_pid != getpid()) { 02430 hog_pid = getpid(); 02431 err = AudioDeviceSetProperty(deviceID, 0, 0, isInput, kAudioDevicePropertyHogMode, propSize, &hog_pid); 02432 if (err != noErr) { 02433 jack_error("Can't hog device = %d because it's being hogged by another program or cannot be hogged", deviceID); 02434 return false; 02435 } 02436 } 02437 02438 return true; 02439 } 02440 02441 bool JackCoreAudioDriver::TakeHog() 02442 { 02443 OSStatus err = noErr; 02444 AudioObjectID sub_device[32]; 02445 UInt32 outSize = sizeof(sub_device); 02446 err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); 02447 02448 if (err != noErr) { 02449 jack_log("JackCoreAudioDriver::TakeHog : device does not have subdevices"); 02450 return TakeHogAux(fDeviceID, true); 02451 } else { 02452 int num_devices = outSize / sizeof(AudioObjectID); 02453 jack_log("JackCoreAudioDriver::TakeHog : device does has %d subdevices", num_devices); 02454 for (int i = 0; i < num_devices; i++) { 02455 if (!TakeHogAux(sub_device[i], true)) { 02456 return false; 02457 } 02458 } 02459 return true; 02460 } 02461 } 02462 02463 bool JackCoreAudioDriver::IsAggregateDevice(AudioDeviceID device) 02464 { 02465 UInt32 deviceType, outSize = sizeof(UInt32); 02466 OSStatus err = AudioDeviceGetProperty(device, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyTransportType, &outSize, &deviceType); 02467 02468 if (err != noErr) { 02469 jack_log("JackCoreAudioDriver::IsAggregateDevice kAudioDevicePropertyTransportType error"); 02470 return false; 02471 } else { 02472 return (deviceType == kAudioDeviceTransportTypeAggregate); 02473 } 02474 } 02475 02476 02477 } // end of namespace 02478 02479 02480 #ifdef __cplusplus 02481 extern "C" 02482 { 02483 #endif 02484 02485 SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor() 02486 { 02487 jack_driver_desc_t * desc; 02488 jack_driver_desc_filler_t filler; 02489 jack_driver_param_value_t value; 02490 02491 desc = jack_driver_descriptor_construct("coreaudio", JackDriverMaster, "Apple CoreAudio API based audio backend", &filler); 02492 02493 value.i = -1; 02494 jack_driver_descriptor_add_parameter(desc, &filler, "channels", 'c', JackDriverParamInt, &value, NULL, "Maximum number of channels", "Maximum number of channels. If -1, max possible number of channels will be used"); 02495 jack_driver_descriptor_add_parameter(desc, &filler, "in-channels", 'i', JackDriverParamInt, &value, NULL, "Maximum number of input channels", "Maximum number of input channels. If -1, max possible number of input channels will be used"); 02496 jack_driver_descriptor_add_parameter(desc, &filler, "out-channels", 'o', JackDriverParamInt, &value, NULL, "Maximum number of output channels", "Maximum number of output channels. If -1, max possible number of output channels will be used"); 02497 02498 value.str[0] = 0; 02499 jack_driver_descriptor_add_parameter(desc, &filler, "input-list", 'n', JackDriverParamString, &value, NULL, "Input channel list for channel mapping", "List of input channel number to be opened (syntax like : \"0 3 2\")"); 02500 jack_driver_descriptor_add_parameter(desc, &filler, "output-list", 'N', JackDriverParamString, &value, NULL, "Output channel list for channel mapping", "List of output channel number to be opened (syntax like : \"0 3 2\")"); 02501 02502 value.str[0] = 0; 02503 jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Input CoreAudio device name", NULL); 02504 jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamString, &value, NULL, "Output CoreAudio device name", NULL); 02505 02506 value.i = 0; 02507 jack_driver_descriptor_add_parameter(desc, &filler, "monitor", 'm', JackDriverParamBool, &value, NULL, "Provide monitor ports for the output", NULL); 02508 02509 #ifndef __ppc__ 02510 value.i = 0; 02511 jack_driver_descriptor_add_parameter(desc, &filler, "AC3-encoding", 'a', JackDriverParamBool, &value, NULL, "AC3 multi-channels encoding", NULL); 02512 02513 value.i = 448; 02514 jack_driver_descriptor_add_parameter(desc, &filler, "AC3-bitrate", 'b', JackDriverParamUInt, &value, NULL, "AC3 bitrate", NULL); 02515 02516 value.i = 0; 02517 jack_driver_descriptor_add_parameter(desc, &filler, "AC3-LFE", 'f', JackDriverParamBool, &value, NULL, "AC3 LFE channel", NULL); 02518 #endif 02519 value.i = true; 02520 jack_driver_descriptor_add_parameter(desc, &filler, "duplex", 'D', JackDriverParamBool, &value, NULL, "Provide both capture and playback ports", NULL); 02521 02522 value.ui = 44100U; 02523 jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); 02524 02525 value.ui = 256U; 02526 jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); 02527 02528 value.str[0] = 0; 02529 jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "CoreAudio device name", NULL); 02530 02531 value.ui = 0; 02532 jack_driver_descriptor_add_parameter(desc, &filler, "input-latency", 'I', JackDriverParamUInt, &value, NULL, "Extra input latency (frames)", NULL); 02533 jack_driver_descriptor_add_parameter(desc, &filler, "output-latency", 'O', JackDriverParamUInt, &value, NULL, "Extra output latency (frames)", NULL); 02534 02535 value.i = false; 02536 jack_driver_descriptor_add_parameter(desc, &filler, "list-devices", 'l', JackDriverParamBool, &value, NULL, "Display available CoreAudio devices", NULL); 02537 02538 value.i = false; 02539 jack_driver_descriptor_add_parameter(desc, &filler, "hog", 'H', JackDriverParamBool, &value, NULL, "Take exclusive access of the audio device", NULL); 02540 02541 value.ui = 100; 02542 jack_driver_descriptor_add_parameter(desc, &filler, "async-latency", 'L', JackDriverParamUInt, &value, NULL, "Extra output latency in asynchronous mode (percent)", NULL); 02543 02544 value.ui = 100; 02545 jack_driver_descriptor_add_parameter(desc, &filler, "grain", 'G', JackDriverParamUInt, &value, NULL, "Computation grain in RT thread (percent)", NULL); 02546 02547 value.i = false; 02548 jack_driver_descriptor_add_parameter(desc, &filler, "clock-drift", 's', JackDriverParamBool, &value, NULL, "Clock drift compensation", "Whether to compensate clock drift in dynamically created aggregate device"); 02549 02550 return desc; 02551 } 02552 02553 SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) 02554 { 02555 jack_nframes_t srate = 44100; 02556 jack_nframes_t frames_per_interrupt = 256; 02557 bool capture = false; 02558 bool playback = false; 02559 int chan_in = -1; // Default: if not explicitely set, then max possible will be used... 02560 int chan_out = -1; // Default: if not explicitely set, then max possible will be used... 02561 const char* chan_in_list = ""; 02562 const char* chan_out_list = ""; 02563 bool monitor = false; 02564 const char* capture_driver_uid = ""; 02565 const char* playback_driver_uid = ""; 02566 const JSList *node; 02567 const jack_driver_param_t *param; 02568 jack_nframes_t systemic_input_latency = 0; 02569 jack_nframes_t systemic_output_latency = 0; 02570 int async_output_latency = 100; 02571 int computation_grain = -1; 02572 bool hogged = false; 02573 bool clock_drift = false; 02574 bool ac3_encoding = false; 02575 int ac3_bitrate = 448; 02576 bool ac3_lfe = false; 02577 02578 for (node = params; node; node = jack_slist_next(node)) { 02579 param = (const jack_driver_param_t *) node->data; 02580 02581 switch (param->character) { 02582 02583 case 'd': 02584 capture_driver_uid = param->value.str; 02585 playback_driver_uid = param->value.str; 02586 break; 02587 02588 case 'D': 02589 capture = true; 02590 playback = true; 02591 break; 02592 02593 case 'c': 02594 chan_in = chan_out = param->value.i; 02595 break; 02596 02597 case 'i': 02598 chan_in = param->value.i; 02599 break; 02600 02601 case 'o': 02602 chan_out = param->value.i; 02603 break; 02604 02605 case 'n': 02606 chan_in_list = param->value.str; 02607 break; 02608 02609 case 'N': 02610 chan_out_list = param->value.str; 02611 break; 02612 02613 case 'C': 02614 capture = true; 02615 if (strcmp(param->value.str, "none") != 0) { 02616 capture_driver_uid = param->value.str; 02617 } 02618 break; 02619 02620 case 'P': 02621 playback = true; 02622 if (strcmp(param->value.str, "none") != 0) { 02623 playback_driver_uid = param->value.str; 02624 } 02625 break; 02626 02627 case 'm': 02628 monitor = param->value.i; 02629 break; 02630 02631 #ifndef __ppc__ 02632 case 'a': 02633 ac3_encoding = param->value.i; 02634 break; 02635 02636 case 'b': 02637 ac3_bitrate = param->value.i; 02638 break; 02639 02640 case 'f': 02641 ac3_lfe = param->value.i; 02642 break; 02643 #endif 02644 02645 case 'r': 02646 srate = param->value.ui; 02647 break; 02648 02649 case 'p': 02650 frames_per_interrupt = (unsigned int)param->value.ui; 02651 break; 02652 02653 case 'I': 02654 systemic_input_latency = param->value.ui; 02655 break; 02656 02657 case 'O': 02658 systemic_output_latency = param->value.ui; 02659 break; 02660 02661 case 'l': 02662 Jack::DisplayDeviceNames(); 02663 // Stops the server in this case 02664 return NULL; 02665 02666 case 'H': 02667 hogged = true; 02668 break; 02669 02670 case 'L': 02671 async_output_latency = param->value.ui; 02672 break; 02673 02674 case 'G': 02675 computation_grain = param->value.ui; 02676 break; 02677 02678 case 's': 02679 clock_drift = true; 02680 break; 02681 } 02682 } 02683 02684 /* duplex is the default */ 02685 if (!capture && !playback) { 02686 capture = true; 02687 playback = true; 02688 } 02689 02690 if (strcmp(chan_in_list, "") != 0 && chan_in >= 0) { 02691 printf("Input channel list and in channels are both specified, input channel list will take over...\n"); 02692 } 02693 02694 if (strcmp(chan_out_list, "") != 0 && chan_out >= 0) { 02695 printf("Output channel list and out channels are both specified, output channel list will take over...\n"); 02696 } 02697 02698 Jack::JackCoreAudioDriver* driver = new Jack::JackCoreAudioDriver("system", "coreaudio", engine, table); 02699 if (driver->Open(frames_per_interrupt, 02700 srate, capture, 02701 playback, chan_in, 02702 chan_out, chan_in_list, 02703 chan_out_list, monitor, 02704 capture_driver_uid, 02705 playback_driver_uid, 02706 systemic_input_latency, 02707 systemic_output_latency, 02708 async_output_latency, 02709 computation_grain, 02710 hogged, clock_drift, 02711 ac3_encoding, ac3_bitrate, ac3_lfe) == 0) { 02712 return driver; 02713 } else { 02714 delete driver; 02715 return NULL; 02716 } 02717 } 02718 02719 #ifdef __cplusplus 02720 } 02721 #endif 02722 02723