1/* 2 * Copyright (c) 1998-2014 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23#include "IOAudioDebug.h" 24#include "IOAudioStream.h" 25#include "IOAudioEngine.h" 26#include "IOAudioEngineUserClient.h" 27#include "IOAudioControl.h" 28#include "IOAudioTypes.h" 29#include "IOAudioDefines.h" 30 31#include <IOKit/IOLib.h> 32#include <IOKit/IOWorkLoop.h> 33#include <IOKit/IOCommandGate.h> 34 35#include <libkern/c++/OSSymbol.h> 36#include <libkern/c++/OSNumber.h> 37#include <libkern/c++/OSArray.h> 38#include <libkern/c++/OSDictionary.h> 39 40typedef struct IOAudioStreamFormatExtensionDesc { 41 UInt32 version; 42 UInt32 flags; 43 UInt32 framesPerPacket; 44 UInt32 bytesPerPacket; 45} IOAudioStreamFormatExtensionDesc; 46 47typedef struct IOAudioStreamFormatDesc { 48 IOAudioStreamFormat format; 49 IOAudioSampleRate minimumSampleRate; 50 IOAudioSampleRate maximumSampleRate; 51 IOAudioStream::AudioIOFunction *ioFunctionList; 52 UInt32 numIOFunctions; 53 IOAudioStreamFormatExtensionDesc formatExtension; 54} IOAudioStreamFormatDesc; 55 56#define super IOService 57OSDefineMetaClassAndStructors(IOAudioStream, IOService) 58 59OSMetaClassDefineReservedUsed(IOAudioStream, 0); 60OSMetaClassDefineReservedUsed(IOAudioStream, 1); 61OSMetaClassDefineReservedUsed(IOAudioStream, 2); 62OSMetaClassDefineReservedUsed(IOAudioStream, 3); 63OSMetaClassDefineReservedUsed(IOAudioStream, 4); 64OSMetaClassDefineReservedUsed(IOAudioStream, 5); 65OSMetaClassDefineReservedUsed(IOAudioStream, 6); 66OSMetaClassDefineReservedUsed(IOAudioStream, 7); 67OSMetaClassDefineReservedUsed(IOAudioStream, 8); 68OSMetaClassDefineReservedUsed(IOAudioStream, 9); 69OSMetaClassDefineReservedUsed(IOAudioStream, 10); 70OSMetaClassDefineReservedUsed(IOAudioStream, 11); 71 72OSMetaClassDefineReservedUnused(IOAudioStream, 12); 73OSMetaClassDefineReservedUnused(IOAudioStream, 13); 74OSMetaClassDefineReservedUnused(IOAudioStream, 14); 75OSMetaClassDefineReservedUnused(IOAudioStream, 15); 76OSMetaClassDefineReservedUnused(IOAudioStream, 16); 77OSMetaClassDefineReservedUnused(IOAudioStream, 17); 78OSMetaClassDefineReservedUnused(IOAudioStream, 18); 79OSMetaClassDefineReservedUnused(IOAudioStream, 19); 80OSMetaClassDefineReservedUnused(IOAudioStream, 20); 81OSMetaClassDefineReservedUnused(IOAudioStream, 21); 82OSMetaClassDefineReservedUnused(IOAudioStream, 22); 83OSMetaClassDefineReservedUnused(IOAudioStream, 23); 84OSMetaClassDefineReservedUnused(IOAudioStream, 24); 85OSMetaClassDefineReservedUnused(IOAudioStream, 25); 86OSMetaClassDefineReservedUnused(IOAudioStream, 26); 87OSMetaClassDefineReservedUnused(IOAudioStream, 27); 88OSMetaClassDefineReservedUnused(IOAudioStream, 28); 89OSMetaClassDefineReservedUnused(IOAudioStream, 29); 90OSMetaClassDefineReservedUnused(IOAudioStream, 30); 91OSMetaClassDefineReservedUnused(IOAudioStream, 31); 92OSMetaClassDefineReservedUnused(IOAudioStream, 32); 93OSMetaClassDefineReservedUnused(IOAudioStream, 33); 94OSMetaClassDefineReservedUnused(IOAudioStream, 34); 95OSMetaClassDefineReservedUnused(IOAudioStream, 35); 96OSMetaClassDefineReservedUnused(IOAudioStream, 36); 97OSMetaClassDefineReservedUnused(IOAudioStream, 37); 98OSMetaClassDefineReservedUnused(IOAudioStream, 38); 99OSMetaClassDefineReservedUnused(IOAudioStream, 39); 100OSMetaClassDefineReservedUnused(IOAudioStream, 40); 101OSMetaClassDefineReservedUnused(IOAudioStream, 41); 102OSMetaClassDefineReservedUnused(IOAudioStream, 42); 103OSMetaClassDefineReservedUnused(IOAudioStream, 43); 104OSMetaClassDefineReservedUnused(IOAudioStream, 44); 105OSMetaClassDefineReservedUnused(IOAudioStream, 45); 106OSMetaClassDefineReservedUnused(IOAudioStream, 46); 107OSMetaClassDefineReservedUnused(IOAudioStream, 47); 108 109// New code added here: 110 111#define CMPSAMPLERATE(left, right) ((left.whole < right->whole) ? -1 : (left.whole == right->whole) ? (left.fraction < right->fraction) ? -1 : 0 : 1) 112 113// <rdar://problem/5994776> Amount of frames allowed to go over the pseudo mix buffer size. We use the source buffer as a mix buffer in encoded format mode. 114// But we can't clip an arbitrary amount of data. We need to limit it to what the size of the source buffer is. The problem seems to 115// be that the source buffer size is hidden by some VBR change. The true size of the source buffer is saved off an then readded after we can use it. 116// So through imperical testing it looks like the source buffer size is 4 times the IOBufferSize. We set it to 3 times to be safe. 117 118#define kMixBufferMaxSize ( 2043 ) // Limit to 2 pages but there is 16 bytes taken out of the sample buffer for VBR stuff 119 120bool IOAudioStream::validateFormat(IOAudioStreamFormat *streamFormat, IOAudioStreamFormatExtension *formatExtension, IOAudioStreamFormatDesc *formatDesc, const IOAudioSampleRate *sampleRate) 121{ 122 bool foundFormat = false; 123 124 audioDebugIOLog(3, "+ IOAudioStream[%p]::validateFormat(%p, %p, %p)\n", this, streamFormat, formatExtension, formatDesc); 125 126 127 if (streamFormat && availableFormats && (numAvailableFormats > 0) && sampleRate) { 128 UInt32 formatIndex; 129 130 for (formatIndex = 0; formatIndex < numAvailableFormats; formatIndex++) { 131 audioDebugIOLog(3, " %ld: streamFormat->fNumChannels = %ld\n", (long int)availableFormats[formatIndex].format.fNumChannels, (long int)streamFormat->fNumChannels); 132 audioDebugIOLog(3, " 0x%lx: streamFormat->fSampleFormat = 0x%lx\n", (long unsigned int)availableFormats[formatIndex].format.fSampleFormat, (long unsigned int)streamFormat->fSampleFormat); 133 audioDebugIOLog(3, " 0x%lx: streamFormat->fNumericRepresentation = 0x%lx\n", (long unsigned int)availableFormats[formatIndex].format.fNumericRepresentation, (long unsigned int)streamFormat->fNumericRepresentation); 134 audioDebugIOLog(3, " %d: streamFormat->fBitDepth = %d\n", availableFormats[formatIndex].format.fBitDepth, streamFormat->fBitDepth); 135 audioDebugIOLog(3, " %d: streamFormat->fBitWidth = %d\n", availableFormats[formatIndex].format.fBitWidth, streamFormat->fBitWidth); 136 audioDebugIOLog(3, " %d: streamFormat->fAlignment = %d\n", availableFormats[formatIndex].format.fAlignment, streamFormat->fAlignment); 137 audioDebugIOLog(3, " %d: streamFormat->fByteOrder = %d\n", availableFormats[formatIndex].format.fByteOrder, streamFormat->fByteOrder); 138 if ((availableFormats[formatIndex].format.fNumChannels == streamFormat->fNumChannels) 139 && (availableFormats[formatIndex].format.fSampleFormat == streamFormat->fSampleFormat) 140 && (availableFormats[formatIndex].format.fNumericRepresentation == streamFormat->fNumericRepresentation) 141 && (availableFormats[formatIndex].format.fBitDepth == streamFormat->fBitDepth) 142 && (availableFormats[formatIndex].format.fBitWidth == streamFormat->fBitWidth) 143 && (availableFormats[formatIndex].format.fAlignment == streamFormat->fAlignment) 144 && (availableFormats[formatIndex].format.fByteOrder == streamFormat->fByteOrder) 145 && (availableFormats[formatIndex].format.fIsMixable == streamFormat->fIsMixable)) { 146 147 bool passSRCheck = true; 148 if (0 != sampleRate->whole) { 149 if ((CMPSAMPLERATE (availableFormats[formatIndex].minimumSampleRate, sampleRate) > 0) || (CMPSAMPLERATE (availableFormats[formatIndex].maximumSampleRate, sampleRate) < 0)) { 150 passSRCheck = false; 151 } 152 } 153 if (passSRCheck) { 154 // <rdar://10957396> Only update the tag if required 155 if ( streamFormat->fDriverTag != availableFormats[formatIndex].format.fDriverTag ) { 156 streamFormat->fDriverTag = availableFormats[formatIndex].format.fDriverTag; 157 } 158 159 // streamFormat->fIsMixable = availableFormats[formatIndex].format.fIsMixable; 160 if (formatDesc) { 161 memcpy(formatDesc, &availableFormats[formatIndex], sizeof(IOAudioStreamFormatDesc)); 162 } 163 foundFormat = true; 164 break; 165 } 166 } 167 } 168 } 169 170 audioDebugIOLog(3, "- IOAudioStream[%p]::validateFormat(%p, %p, %p) returns %d\n", this, streamFormat, formatExtension, formatDesc, foundFormat ); 171 return foundFormat; 172} 173 174const IOAudioStreamFormatExtension *IOAudioStream::getFormatExtension() 175{ 176 assert(reserved); 177 return &reserved->streamFormatExtension; 178} 179 180IOReturn IOAudioStream::setFormat(const IOAudioStreamFormat *streamFormat, const IOAudioStreamFormatExtension *formatExtension, bool callDriver) 181{ 182 IOReturn result = kIOReturnSuccess; 183 OSDictionary *formatDict = NULL; 184 IOAudioStreamFormatExtension validFormatExtension; 185 186 if (streamFormat) { 187 if (!formatExtension) { 188 IOAudioStreamFormatDesc formatDesc; 189 validateFormat((IOAudioStreamFormat *)streamFormat, NULL, &formatDesc); 190 memcpy (&validFormatExtension, &formatDesc.formatExtension, sizeof (validFormatExtension)); 191 } else { 192 validFormatExtension = *formatExtension; 193 } 194 if ( (formatDict = createDictionaryFromFormat(streamFormat, &validFormatExtension)) ) { 195 result = setFormat(streamFormat, &validFormatExtension, formatDict, callDriver); 196 formatDict->release(); 197 } else { 198 result = kIOReturnError; 199 } 200 } else { 201 result = kIOReturnBadArgument; 202 } 203 204 return result; 205} 206 207// <rdar://8121989> Restructured for single point of entry and single point of exit so that 208// the indentifier post processing tool can properly insert scope when post processing a log file 209// obtained via fwkpfv. 210 211IOReturn IOAudioStream::setFormat(const IOAudioStreamFormat *streamFormat, const IOAudioStreamFormatExtension *formatExtension, OSDictionary *formatDict, bool callDriver) 212{ 213 IOReturn result = kIOReturnSuccess; 214 IOAudioStreamFormat validFormat; 215 IOAudioStreamFormatDesc formatDesc; 216 IOAudioStreamFormatExtension validFormatExtension; 217 const IOAudioSampleRate *requestedSampleRate = NULL; 218 OSDictionary *sampleRateDict; 219 220 audioDebugIOLog(3, "+ IOAudioStream[%p]::setFormat(%p, %p)\n", this, streamFormat, formatDict); 221 222 if (!streamFormat || !formatDict) 223 { 224 result = kIOReturnBadArgument; 225 } 226 else 227 { 228 #ifdef DEBUG 229 setProperty("IOAudioStreamPendingFormat", formatDict); 230 #endif 231 232 validFormat = *streamFormat; 233 if (NULL != formatExtension) { 234 validFormatExtension = *formatExtension; 235 } else { 236 validFormatExtension.fVersion = kFormatExtensionCurrentVersion; 237 validFormatExtension.fFlags = 0; 238 validFormatExtension.fFramesPerPacket = 1; 239 validFormatExtension.fBytesPerPacket = streamFormat->fNumChannels * (streamFormat->fBitWidth / 8); 240 } 241 242 sampleRateDict = OSDynamicCast(OSDictionary, formatDict->getObject(kIOAudioSampleRateKey)); 243 if (sampleRateDict) { 244 requestedSampleRate = IOAudioEngine::createSampleRateFromDictionary(sampleRateDict); 245 } else { 246 requestedSampleRate = audioEngine->getSampleRate(); 247 } 248 249 if (validateFormat(&validFormat, &validFormatExtension, &formatDesc, requestedSampleRate)) { 250 // OSDictionary *sampleRateDict; 251 IOAudioSampleRate *newSampleRate = NULL; 252 OSSet *userClientsToLock; 253 254 sampleRateDict = OSDynamicCast(OSDictionary, formatDict->getObject(kIOAudioSampleRateKey)); 255 if (sampleRateDict) { 256 const IOAudioSampleRate *currentSampleRate; 257 258 newSampleRate = IOAudioEngine::createSampleRateFromDictionary(sampleRateDict); 259 currentSampleRate = audioEngine->getSampleRate(); 260 if (newSampleRate && (newSampleRate->whole == currentSampleRate->whole) && (newSampleRate->fraction == currentSampleRate->fraction)) { 261 newSampleRate = NULL; 262 } 263 } 264 265 // In order to avoid deadlocks, we need to ensure we hold all of the user client locks 266 // before making calls while holding our IO lock. Everything works fine as long 267 // as the order of the locks is workLoop -> user client -> stream. 268 // Any other order is sure to cause trouble. 269 270 // Because we pause the engine while doing the format change, the user clients will be removed 271 // from our list before we complete. Therefore, we must make a copy of the list to allow 272 // all of the clients to be unlocked when we are done. 273 userClientsToLock = OSSet::withCapacity(numClients); 274 if (userClientsToLock) { 275 OSCollectionIterator *clientIterator; 276 IOAudioClientBuffer *clientBuf; 277 IOAudioEngineUserClient *userClient; 278 279 lockStreamForIO(); // <rdar://13186726> 280 281 clientBuf = userClientList; 282 while (clientBuf) { 283 assert(clientBuf->userClient); 284 285 userClientsToLock->setObject(clientBuf->userClient); 286 clientBuf = clientBuf->nextClient; 287 } 288 289 unlockStreamForIO(); // <rdar://13186726> 290 291 clientIterator = OSCollectionIterator::withCollection(userClientsToLock); 292 if (!clientIterator) { 293 userClientsToLock->release(); 294 result = kIOReturnNoMemory; 295 goto Done; 296 } 297 298 while ( (userClient = (IOAudioEngineUserClient *)clientIterator->getNextObject()) ) { 299 userClient->lockBuffers(); 300 } 301 302 clientIterator->release(); 303 304 lockStreamForIO(); 305 306 audioEngine->pauseAudioEngine(); 307 308 if (callDriver) { 309 result = audioEngine->performFormatChange(this, &validFormat, &validFormatExtension, newSampleRate); 310 if ( result == kIOReturnUnsupported ) 311 { 312 result = audioEngine->performFormatChange(this, &validFormat, newSampleRate); 313 } 314 } 315 316 if (result == kIOReturnSuccess) { 317 OSDictionary *newFormatDict; 318 319 if (formatDesc.ioFunctionList && (formatDesc.numIOFunctions > 0)) { 320 setIOFunctionList(formatDesc.ioFunctionList, formatDesc.numIOFunctions); 321 } 322 323 newFormatDict = createDictionaryFromFormat(&validFormat, &validFormatExtension); 324 if (newFormatDict) { 325 UInt32 oldNumChannels; 326 327 if (mixBuffer != NULL) { 328 // If we have a mix buffer and the new format is not mixable, free the mix buffer 329 if (!validFormat.fIsMixable) { 330 setMixBuffer(NULL, 0); 331 } else if (streamAllocatedMixBuffer && (format.fNumChannels != validFormat.fNumChannels)) { // We need to reallocate the mix buffer 332 UInt32 newMixBufSize; 333 334 assert(audioEngine); 335 newMixBufSize = validFormat.fNumChannels * kIOAudioEngineDefaultMixBufferSampleSize * audioEngine->numSampleFramesPerBuffer; 336 337 if (newMixBufSize > 0) { 338 void *newMixBuf = IOMallocAligned(newMixBufSize, 32); 339 if (newMixBuf) { 340 setMixBuffer(newMixBuf, newMixBufSize); 341 streamAllocatedMixBuffer = true; 342 } 343 } 344 } 345 } 346 347 oldNumChannels = format.fNumChannels; 348 349 format = validFormat; 350 setProperty(kIOAudioStreamFormatKey, newFormatDict); 351 newFormatDict->release(); 352 353 if (format.fNumChannels != oldNumChannels) { 354 audioEngine->updateChannelNumbers(); 355 } 356 357 if (newSampleRate) { 358 audioEngine->setSampleRate(newSampleRate); 359 } 360 } else { 361 result = kIOReturnError; 362 } 363 } else { 364 if ( kIOReturnNotReady != result ) { // <rdar://8094567> 365 IOLog("IOAudioStream<%p>::setFormat(0x%p, 0x%p) - audio engine unable to change format\n", this, streamFormat, formatDict); 366 } 367 } 368 369 if (result == kIOReturnSuccess) { 370 audioEngine->sendFormatChangeNotification(this); 371 } 372 373 audioEngine->resumeAudioEngine(); 374 375 unlockStreamForIO(); 376 377 // Unlock all of the user clients we originally locked 378 assert(userClientsToLock); 379 clientIterator = OSCollectionIterator::withCollection(userClientsToLock); 380 if (clientIterator) { 381 while ( (userClient = (IOAudioEngineUserClient *)clientIterator->getNextObject()) ) { 382 userClient->unlockBuffers(); 383 } 384 clientIterator->release(); 385 } else { 386 // Uh oh... we're in trouble now! 387 // We have to unlock the clients, but we can't get an iterator on the collection. 388 // All existing clients will now hang trying to play audio 389 result = kIOReturnNoMemory; 390 } 391 392 userClientsToLock->release(); 393 } else { 394 result = kIOReturnNoMemory; 395 } 396 } else { 397 IOLog("IOAudioStream<0x%p>::setFormat(0x%p, 0x%p) - invalid format.\n", this, streamFormat, formatDict); 398 result = kIOReturnBadArgument; 399 } 400 } 401 402Done: 403 404 audioDebugIOLog(3, "IOAudioStream[%p]::setFormat(%p, %p) returns 0x%lx", this, streamFormat, formatDict, (long unsigned int)result); 405 406 return result; 407} 408 409void IOAudioStream::addAvailableFormat(const IOAudioStreamFormat *streamFormat, const IOAudioStreamFormatExtension *formatExtension, const IOAudioSampleRate *minRate, const IOAudioSampleRate *maxRate, const AudioIOFunction *ioFunctionList, UInt32 numFunctions) 410{ 411 assert(availableFormatDictionaries); 412 413 if (streamFormat && minRate && maxRate) { 414 IOAudioStreamFormatDesc *newAvailableFormatList; 415 IOAudioStreamFormatExtension localFormatExtension; 416 417 newAvailableFormatList = (IOAudioStreamFormatDesc *)IOMallocAligned((numAvailableFormats+1) * sizeof(IOAudioStreamFormatDesc), sizeof (IOAudioStreamFormatDesc *)); 418 if (newAvailableFormatList) { 419 if (availableFormats && (numAvailableFormats > 0)) { 420 memcpy(newAvailableFormatList, availableFormats, numAvailableFormats * sizeof(IOAudioStreamFormatDesc)); 421 } 422 newAvailableFormatList[numAvailableFormats].format = *streamFormat; 423 newAvailableFormatList[numAvailableFormats].minimumSampleRate = *minRate; 424 newAvailableFormatList[numAvailableFormats].maximumSampleRate = *maxRate; 425 if (formatExtension) { 426 localFormatExtension = *formatExtension; 427 newAvailableFormatList[numAvailableFormats].formatExtension.flags = formatExtension->fFlags; 428 newAvailableFormatList[numAvailableFormats].formatExtension.framesPerPacket = formatExtension->fFramesPerPacket; 429 newAvailableFormatList[numAvailableFormats].formatExtension.bytesPerPacket = formatExtension->fBytesPerPacket; 430 } else { 431 newAvailableFormatList[numAvailableFormats].formatExtension.flags = localFormatExtension.fFlags = 0; 432 newAvailableFormatList[numAvailableFormats].formatExtension.framesPerPacket = localFormatExtension.fFramesPerPacket = 1; 433 newAvailableFormatList[numAvailableFormats].formatExtension.bytesPerPacket = localFormatExtension.fBytesPerPacket = streamFormat->fNumChannels * (streamFormat->fBitWidth / 8); 434 } 435 436 if (ioFunctionList && (numFunctions > 0)) { 437 newAvailableFormatList[numAvailableFormats].ioFunctionList = (AudioIOFunction *)IOMallocAligned(numFunctions * sizeof(AudioIOFunction), sizeof (AudioIOFunction *)); 438 newAvailableFormatList[numAvailableFormats].numIOFunctions = numFunctions; 439 memcpy(newAvailableFormatList[numAvailableFormats].ioFunctionList, ioFunctionList, numFunctions * sizeof(AudioIOFunction)); 440 } else { 441 newAvailableFormatList[numAvailableFormats].ioFunctionList = NULL; 442 newAvailableFormatList[numAvailableFormats].numIOFunctions = 0; 443 } 444 445 IOFreeAligned(availableFormats, numAvailableFormats * sizeof(IOAudioStreamFormatDesc)); 446 availableFormats = newAvailableFormatList; 447 numAvailableFormats++; 448 } 449 450 OSDictionary *formatDict = createDictionaryFromFormat(streamFormat, &localFormatExtension); 451 if (formatDict) { 452 OSDictionary *sampleRateDict; 453 454 sampleRateDict = IOAudioEngine::createDictionaryFromSampleRate(minRate); 455 if (sampleRateDict) { 456 formatDict->setObject(gMinimumSampleRateKey, sampleRateDict); 457 sampleRateDict->release(); 458 459 sampleRateDict = IOAudioEngine::createDictionaryFromSampleRate(maxRate); 460 if (sampleRateDict) { 461 OSArray *newAvailableFormats; 462 OSArray *oldAvailableFormats; 463 464 oldAvailableFormats = availableFormatDictionaries; 465 newAvailableFormats = OSDynamicCast(OSArray, availableFormatDictionaries->copyCollection()); // copyCollection() does a deep copy 466 467 if (newAvailableFormats) { 468 formatDict->setObject(gMaximumSampleRateKey, sampleRateDict); 469 newAvailableFormats->setObject(formatDict); 470 availableFormatDictionaries = newAvailableFormats; 471 setProperty(kIOAudioStreamAvailableFormatsKey, availableFormatDictionaries); 472 oldAvailableFormats->release(); 473 if (streamFormat->fNumChannels > maxNumChannels) { 474 maxNumChannels = streamFormat->fNumChannels; 475 } 476 } 477 478 sampleRateDict->release(); 479 } 480 } 481 formatDict->release(); 482 } 483 } 484} 485 486void IOAudioStream::addAvailableFormat(const IOAudioStreamFormat *streamFormat, const IOAudioStreamFormatExtension *formatExtension, const IOAudioSampleRate *minRate, const IOAudioSampleRate *maxRate, AudioIOFunction ioFunction) 487{ 488 addAvailableFormat(streamFormat, formatExtension, minRate, maxRate, &ioFunction, 1); 489} 490 491bool IOAudioStream::validateFormat(IOAudioStreamFormat *streamFormat, IOAudioStreamFormatExtension *formatExtension, IOAudioStreamFormatDesc *formatDesc) 492{ 493 return validateFormat(streamFormat, formatExtension, formatDesc, audioEngine->getSampleRate()); 494} 495 496void IOAudioStream::setTerminalType(const UInt32 terminalType) 497{ 498 if (terminalType) { 499 setProperty(kIOAudioStreamTerminalTypeKey, terminalType, 32); 500 } 501} 502 503IOReturn IOAudioStream::mixOutputSamples(const void *sourceBuf, void *mixBuf, UInt32 firstSampleFrame, UInt32 numSampleFrames, const IOAudioStreamFormat *streamFormat, IOAudioStream *audioStream) 504{ 505 bcopy (sourceBuf, (float *)mixBuf + (firstSampleFrame * streamFormat->fNumChannels), numSampleFrames * sizeof (float) * streamFormat->fNumChannels); 506 507 return kIOReturnSuccess; 508} 509 510void IOAudioStream::setSampleLatency(UInt32 numSamples) 511{ 512 audioDebugIOLog(3, "+-IOAudioStream[%p]::setSampleLatency(0x%lx)\n", this, (long unsigned int)numSamples); 513 setProperty(kIOAudioStreamSampleLatencyKey, numSamples, sizeof(UInt32)*8); 514} 515 516UInt32 IOAudioStream::getNumSampleFramesRead() 517{ 518 assert(reserved); 519 audioDebugIOLog(4, "+-IOAudioStream[%p]::getNumSampleFramesRead() returns %ld\n", this, (long unsigned int)reserved->mSampleFramesReadByEngine); 520 return reserved->mSampleFramesReadByEngine; 521} 522 523void IOAudioStream::setDefaultNumSampleFramesRead(UInt32 inDefaultNumFramesRead) 524{ 525 assert(reserved); 526 audioDebugIOLog(4, "+-IOAudioStream[%p]::setDefaultNumSampleFramesRead(%ld)\n", this, (long unsigned int)inDefaultNumFramesRead); 527 reserved->mSampleFramesReadByEngine = inDefaultNumFramesRead; 528} 529 530// Original code from here on: 531const OSSymbol *IOAudioStream::gDirectionKey = NULL; 532const OSSymbol *IOAudioStream::gNumChannelsKey = NULL; 533const OSSymbol *IOAudioStream::gSampleFormatKey = NULL; 534const OSSymbol *IOAudioStream::gNumericRepresentationKey = NULL; 535const OSSymbol *IOAudioStream::gBitDepthKey = NULL; 536const OSSymbol *IOAudioStream::gBitWidthKey = NULL; 537const OSSymbol *IOAudioStream::gAlignmentKey = NULL; 538const OSSymbol *IOAudioStream::gByteOrderKey = NULL; 539const OSSymbol *IOAudioStream::gIsMixableKey = NULL; 540const OSSymbol *IOAudioStream::gDriverTagKey = NULL; 541const OSSymbol *IOAudioStream::gMinimumSampleRateKey = NULL; 542const OSSymbol *IOAudioStream::gMaximumSampleRateKey = NULL; 543 544void IOAudioStream::initKeys() 545{ 546 if (!gNumChannelsKey) { 547 gNumChannelsKey = OSSymbol::withCString(kIOAudioStreamNumChannelsKey); 548 gSampleFormatKey = OSSymbol::withCString(kIOAudioStreamSampleFormatKey); 549 gNumericRepresentationKey = OSSymbol::withCString(kIOAudioStreamNumericRepresentationKey); 550 gBitDepthKey = OSSymbol::withCString(kIOAudioStreamBitDepthKey); 551 gBitWidthKey = OSSymbol::withCString(kIOAudioStreamBitWidthKey); 552 gAlignmentKey = OSSymbol::withCString(kIOAudioStreamAlignmentKey); 553 gByteOrderKey = OSSymbol::withCString(kIOAudioStreamByteOrderKey); 554 gIsMixableKey = OSSymbol::withCString(kIOAudioStreamIsMixableKey); 555 gDriverTagKey = OSSymbol::withCString(kIOAudioStreamDriverTagKey); 556 557 gDirectionKey = OSSymbol::withCString(kIOAudioStreamDirectionKey); 558 559 gMinimumSampleRateKey = OSSymbol::withCString(kIOAudioStreamMinimumSampleRateKey); 560 gMaximumSampleRateKey = OSSymbol::withCString(kIOAudioStreamMaximumSampleRateKey); 561 } 562} 563 564OSDictionary *IOAudioStream::createDictionaryFromFormat(const IOAudioStreamFormat *streamFormat, const IOAudioStreamFormatExtension *formatExtension, OSDictionary *formatDict) 565{ 566 OSDictionary *newDict = NULL; 567 568 if (streamFormat) { 569 if (formatDict) { 570 newDict = formatDict; 571 } else { 572 newDict = OSDictionary::withCapacity(7); 573 } 574 575 if (newDict) { 576 OSNumber *num; 577 578 if (!gNumChannelsKey) { 579 initKeys(); 580 } 581 582 num = OSNumber::withNumber(streamFormat->fNumChannels, 32); 583 newDict->setObject(gNumChannelsKey, num); 584 num->release(); 585 586 num = OSNumber::withNumber(streamFormat->fSampleFormat, 32); 587 newDict->setObject(gSampleFormatKey, num); 588 num->release(); 589 590 num = OSNumber::withNumber(streamFormat->fNumericRepresentation, 32); 591 newDict->setObject(gNumericRepresentationKey, num); 592 num->release(); 593 594 num = OSNumber::withNumber(streamFormat->fBitDepth, 8); 595 newDict->setObject(gBitDepthKey, num); 596 num->release(); 597 598 num = OSNumber::withNumber(streamFormat->fBitWidth, 8); 599 newDict->setObject(gBitWidthKey, num); 600 num->release(); 601 602 num = OSNumber::withNumber(streamFormat->fAlignment, 8); 603 newDict->setObject(gAlignmentKey, num); 604 num->release(); 605 606 num = OSNumber::withNumber(streamFormat->fByteOrder, 8); 607 newDict->setObject(gByteOrderKey, num); 608 num->release(); 609 610 num = OSNumber::withNumber(streamFormat->fIsMixable, 8); 611 newDict->setObject(gIsMixableKey, num); 612 num->release(); 613 614 num = OSNumber::withNumber(streamFormat->fDriverTag, 32); 615 newDict->setObject(gDriverTagKey, num); 616 num->release(); 617 618 if (formatExtension && formatExtension->fVersion >= kFormatExtensionCurrentVersion) { 619 num = OSNumber::withNumber(formatExtension->fFlags, 32); 620 newDict->setObject(kIOAudioStreamFormatFlagsKey, num); 621 num->release(); 622 623 num = OSNumber::withNumber(formatExtension->fFramesPerPacket, 32); 624 newDict->setObject(kIOAudioStreamFramesPerPacketKey, num); 625 num->release(); 626 627 num = OSNumber::withNumber(formatExtension->fBytesPerPacket, 32); 628 newDict->setObject(kIOAudioStreamBytesPerPacketKey, num); 629 num->release(); 630 } 631 } 632 } 633 634 635 return newDict; 636} 637 638IOAudioStreamFormat *IOAudioStream::createFormatFromDictionary(const OSDictionary *formatDict, IOAudioStreamFormat *streamFormat, IOAudioStreamFormatExtension *formatExtension) 639{ 640 IOAudioStreamFormat *format = NULL; 641 static IOAudioStreamFormat staticFormat; 642 643 if (formatDict) { 644 if (streamFormat) { 645 format = streamFormat; 646 } else { 647 format = &staticFormat; 648 } 649 650 if (format) { 651 OSNumber *num; 652 653 if (!gNumChannelsKey) { 654 initKeys(); 655 } 656 657 bzero(format, sizeof(IOAudioStreamFormat)); 658 659 num = OSDynamicCast(OSNumber, formatDict->getObject(gNumChannelsKey)); 660 if (num) { 661 format->fNumChannels = num->unsigned32BitValue(); 662 } 663 664 num = OSDynamicCast(OSNumber, formatDict->getObject(gSampleFormatKey)); 665 if (num) { 666 format->fSampleFormat = num->unsigned32BitValue(); 667 } 668 669 num = OSDynamicCast(OSNumber, formatDict->getObject(gNumericRepresentationKey)); 670 if (num) { 671 format->fNumericRepresentation = num->unsigned32BitValue(); 672 } 673 674 num = OSDynamicCast(OSNumber, formatDict->getObject(gBitDepthKey)); 675 if (num) { 676 format->fBitDepth = num->unsigned8BitValue(); 677 } 678 679 num = OSDynamicCast(OSNumber, formatDict->getObject(gBitWidthKey)); 680 if (num) { 681 format->fBitWidth = num->unsigned8BitValue(); 682 } 683 684 num = OSDynamicCast(OSNumber, formatDict->getObject(gAlignmentKey)); 685 if (num) { 686 format->fAlignment = num->unsigned8BitValue(); 687 } 688 689 num = OSDynamicCast(OSNumber, formatDict->getObject(gByteOrderKey)); 690 if (num) { 691 format->fByteOrder = num->unsigned8BitValue(); 692 } 693 694 num = OSDynamicCast(OSNumber, formatDict->getObject(gIsMixableKey)); 695 if (num) { 696 format->fIsMixable = num->unsigned8BitValue(); 697 } 698 699 num = OSDynamicCast(OSNumber, formatDict->getObject(gDriverTagKey)); 700 if (num) { 701 format->fDriverTag = num->unsigned32BitValue(); 702 } 703 704 if (formatExtension) { 705 formatExtension->fVersion = kFormatExtensionCurrentVersion; 706 707 num = OSDynamicCast(OSNumber, formatDict->getObject(kIOAudioStreamFormatFlagsKey)); 708 if (num) { 709 formatExtension->fFlags = num->unsigned32BitValue(); 710 } 711 712 num = OSDynamicCast(OSNumber, formatDict->getObject(kIOAudioStreamFramesPerPacketKey)); 713 if (num) { 714 formatExtension->fFramesPerPacket = num->unsigned32BitValue(); 715 } 716 717 num = OSDynamicCast(OSNumber, formatDict->getObject(kIOAudioStreamBytesPerPacketKey)); 718 if (num) { 719 formatExtension->fBytesPerPacket = num->unsigned32BitValue(); 720 } 721 722 } 723 } 724 } 725 726 return format; 727} 728 729 730bool IOAudioStream::initWithAudioEngine(IOAudioEngine *engine, IOAudioStreamDirection dir, UInt32 startChannelID, const char *streamDescription, OSDictionary *properties) 731{ 732 UInt32 streamID; 733 734 if (!gNumChannelsKey) { 735 initKeys(); 736 } 737 738 if (!engine) { 739 return false; 740 } 741 742 if (!super::init(properties)) { 743 return false; 744 } 745 746 audioEngine = engine; 747 748 reserved = (ExpansionData *)IOMalloc (sizeof(struct ExpansionData)); 749 if (!reserved) { 750 return false; 751 } 752 753 workLoop = audioEngine->getWorkLoop(); 754 if (!workLoop) { 755 return false; 756 } 757 758 workLoop->retain(); 759 760 commandGate = IOCommandGate::commandGate(this); 761 if (!commandGate) { 762 return false; 763 } 764 765 streamIOLock = IORecursiveLockAlloc(); 766 if (!streamIOLock) { 767 return false; 768 } 769 770 setDirection(dir); 771 772 startingChannelID = startChannelID; 773 setProperty(kIOAudioStreamStartingChannelIDKey, startingChannelID, sizeof(UInt32)*8); 774 775 maxNumChannels = 0; 776 777 if (streamDescription) { 778 setProperty(kIOAudioStreamDescriptionKey, streamDescription); 779 } 780 781 availableFormatDictionaries = OSArray::withCapacity(1); 782 if (!availableFormatDictionaries) { 783 return false; 784 } 785 setProperty(kIOAudioStreamAvailableFormatsKey, availableFormatDictionaries); 786 787 // This needs to change to passing up a token rather than the "this" pointer. 788 streamID = engine->getNextStreamID (this); 789 setProperty(kIOAudioStreamIDKey, streamID, sizeof(UInt32)*8); 790// setProperty(kIOAudioStreamIDKey, (UInt32)this, sizeof(UInt32)*8); 791 792 streamAvailable = true; 793 setProperty(kIOAudioStreamAvailableKey, (UInt8)1, sizeof(UInt8)*8); 794 795 numClients = 0; 796 updateNumClients(); 797 798 resetClipInfo(); 799 800 clientBufferListStart = NULL; 801 clientBufferListEnd = NULL; 802 803 userClientList = NULL; 804 805 audioIOFunctions = NULL; 806 numIOFunctions = false; 807 808 streamAllocatedMixBuffer = false; 809 810 workLoop->addEventSource(commandGate); 811 812 return true; 813} 814 815void IOAudioStream::free() 816{ 817 if (availableFormatDictionaries) { 818 availableFormatDictionaries->release(); 819 availableFormatDictionaries = NULL; 820 } 821 822 if (mixBuffer && streamAllocatedMixBuffer) { 823 IOFreeAligned(mixBuffer, mixBufferSize); 824 mixBuffer = NULL; 825 mixBufferSize = 0; 826 } 827 828 if (defaultAudioControls) { 829 removeDefaultAudioControls(); 830 defaultAudioControls->release(); 831 defaultAudioControls = NULL; 832 } 833 834 if (commandGate) { 835 if (workLoop) { 836 workLoop->removeEventSource(commandGate); 837 } 838 839 commandGate->release(); 840 commandGate = NULL; 841 } 842 843 if (workLoop) { 844 workLoop->release(); 845 workLoop = NULL; 846 } 847 848 if (streamIOLock) { 849 IORecursiveLockFree(streamIOLock); 850 streamIOLock = NULL; 851 } 852 853 if (audioIOFunctions && (numIOFunctions > 0)) { 854 IOFreeAligned(audioIOFunctions, numIOFunctions * sizeof(AudioIOFunction)); 855 audioIOFunctions = NULL; 856 numIOFunctions = 0; 857 } 858 859 if (availableFormats && (numAvailableFormats > 0)) { 860 UInt32 formatNum; 861 862 for (formatNum = 0; formatNum < numAvailableFormats; formatNum++) { 863 if (availableFormats[formatNum].ioFunctionList && (availableFormats[formatNum].numIOFunctions > 0)) { 864 IOFreeAligned(availableFormats[formatNum].ioFunctionList, availableFormats[formatNum].numIOFunctions * sizeof(AudioIOFunction)); 865 } 866 } 867 868 IOFreeAligned(availableFormats, numAvailableFormats * sizeof(IOAudioStreamFormatDesc)); 869 availableFormats = NULL; 870 numAvailableFormats = 0; 871 } 872 873 if (reserved) { 874 IOFree (reserved, sizeof(struct ExpansionData)); 875 } 876 877 super::free(); 878} 879 880void IOAudioStream::stop(IOService *provider) 881{ 882 if (commandGate) { 883 if (workLoop) { 884 workLoop->removeEventSource(commandGate); 885 } 886 887 commandGate->release(); 888 commandGate = NULL; 889 } 890 891 super::stop(provider); 892} 893 894IOWorkLoop *IOAudioStream::getWorkLoop() const 895{ 896 return workLoop; 897} 898 899IOReturn IOAudioStream::setProperties(OSObject *properties) 900{ 901 OSDictionary *props; 902 IOReturn result = kIOReturnSuccess; 903 904 audioDebugIOLog(3, "+ IOAudioStream[%p]::setProperties(%p)\n", this, properties); 905 906 if (properties && (props = OSDynamicCast(OSDictionary, properties))) { 907 OSCollectionIterator *iterator; 908 OSObject *iteratorKey; 909 910 iterator = OSCollectionIterator::withCollection(props); 911 if (iterator) { 912 while ( (iteratorKey = iterator->getNextObject()) ) { 913 OSSymbol *key; 914 915 key = OSDynamicCast(OSSymbol, iteratorKey); 916 if (key && key->isEqualTo(kIOAudioStreamFormatKey)) { 917 OSDictionary *formatDict = OSDynamicCast(OSDictionary, props->getObject(key)); 918 if (formatDict) { 919 assert(workLoop); // <rdar://8568040,8691669> 920 result = workLoop->runAction(_setFormatAction, this, formatDict); // <rdar://8568040,8691669> 921 } 922 } 923 } 924 iterator->release(); 925 } else { 926 result = kIOReturnError; 927 } 928 } else { 929 result = kIOReturnBadArgument; 930 } 931 932 audioDebugIOLog(3, "- IOAudioStream[%p]::setProperties(%p) returns 0x%lX\n", this, properties, (long unsigned int)result ); 933 return result; 934} 935 936void IOAudioStream::setDirection(IOAudioStreamDirection dir) 937{ 938 direction = dir; 939 setProperty(kIOAudioStreamDirectionKey, direction, 8); 940} 941 942IOAudioStreamDirection IOAudioStream::getDirection() 943{ 944 return direction; 945} 946 947void IOAudioStream::setSampleBuffer(void *buffer, UInt32 size) 948{ 949 lockStreamForIO(); 950 951 sampleBuffer = buffer; 952 953 if (sampleBuffer) { 954 sampleBufferSize = size; 955 bzero(sampleBuffer, sampleBufferSize); 956 } else { 957 sampleBufferSize = 0; 958 } 959 960 unlockStreamForIO(); 961} 962 963void *IOAudioStream::getSampleBuffer() 964{ 965 return sampleBuffer; 966} 967 968UInt32 IOAudioStream::getSampleBufferSize() 969{ 970 return sampleBufferSize; 971} 972 973void IOAudioStream::setMixBuffer(void *buffer, UInt32 size) 974{ 975 lockStreamForIO(); 976 977 if (mixBuffer && streamAllocatedMixBuffer) { 978 IOFreeAligned(mixBuffer, mixBufferSize); 979 mixBuffer = NULL; 980 mixBufferSize = 0; 981 streamAllocatedMixBuffer = false; 982 } 983 984 mixBuffer = buffer; 985 986 if (mixBuffer) { 987 mixBufferSize = size; 988 bzero(mixBuffer, mixBufferSize); 989 } else { 990 mixBufferSize = 0; 991 } 992 993 unlockStreamForIO(); 994} 995 996void *IOAudioStream::getMixBuffer() 997{ 998 return mixBuffer; 999} 1000 1001UInt32 IOAudioStream::getMixBufferSize() 1002{ 1003 return mixBufferSize; 1004} 1005 1006void IOAudioStream::numSampleFramesPerBufferChanged() 1007{ 1008 if (mixBuffer && streamAllocatedMixBuffer) { 1009 setMixBuffer(NULL, 0); 1010 } 1011} 1012 1013void IOAudioStream::clearSampleBuffer() 1014{ 1015 if (sampleBuffer && (sampleBufferSize > 0)) { 1016 bzero(sampleBuffer, sampleBufferSize); 1017 } 1018 1019 if (mixBuffer && (mixBufferSize > 0)) { 1020 bzero(mixBuffer, mixBufferSize); 1021 } 1022} 1023 1024void IOAudioStream::setIOFunction(AudioIOFunction ioFunction) 1025{ 1026 setIOFunctionList(&ioFunction, 1); 1027} 1028 1029void IOAudioStream::setIOFunctionList(const AudioIOFunction *ioFunctionList, UInt32 numFunctions) 1030{ 1031 lockStreamForIO(); 1032 1033 if (audioIOFunctions && (numIOFunctions > 0)) { 1034 IOFreeAligned(audioIOFunctions, numIOFunctions * sizeof(AudioIOFunction)); 1035 audioIOFunctions = NULL; 1036 numIOFunctions = 0; 1037 } 1038 1039 if (ioFunctionList && (numFunctions != 0)) { 1040 audioIOFunctions = (AudioIOFunction *)IOMallocAligned(numFunctions * sizeof(AudioIOFunction), sizeof (AudioIOFunction *)); 1041 if (audioIOFunctions) { 1042 memcpy(audioIOFunctions, ioFunctionList, numFunctions * sizeof(AudioIOFunction)); 1043 numIOFunctions = numFunctions; 1044 } 1045 } 1046 1047 unlockStreamForIO(); 1048} 1049 1050const IOAudioStreamFormat *IOAudioStream::getFormat() 1051{ 1052 return &format; 1053} 1054 1055// <rdar://8568040,8691669> 1056IOReturn IOAudioStream::_setFormatAction(OSObject *target, void *arg0, void *arg1, void *arg2, void *arg3) 1057{ 1058 IOReturn result = kIOReturnBadArgument; 1059 1060 if (target) { 1061 IOAudioStream *stream = OSDynamicCast(IOAudioStream, target); 1062 if (stream) { 1063 if (stream->commandGate) { 1064 result = stream->commandGate->runAction(setFormatAction, arg0, arg1, arg2, arg3); 1065 } else { 1066 result = kIOReturnError; 1067 } 1068 } 1069 } 1070 1071 return result; 1072} 1073 1074IOReturn IOAudioStream::setFormatAction(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) 1075{ 1076 IOReturn result = kIOReturnBadArgument; 1077 1078 if (owner) { 1079 IOAudioStream *audioStream = OSDynamicCast(IOAudioStream, owner); 1080 if (audioStream) { 1081 result = audioStream->setFormat((OSDictionary *)arg1); 1082 } 1083 } 1084 1085 return result; 1086} 1087 1088IOReturn IOAudioStream::setFormat(const IOAudioStreamFormat *streamFormat, bool callDriver) 1089{ 1090 return setFormat(streamFormat, (IOAudioStreamFormatExtension *)NULL, callDriver); 1091} 1092 1093IOReturn IOAudioStream::setFormat(OSDictionary *formatDict) 1094{ 1095 IOReturn result = kIOReturnSuccess; 1096 1097 if (formatDict) { 1098 IOAudioStreamFormat streamFormat; 1099 IOAudioStreamFormatExtension formatExtension; 1100 if (createFormatFromDictionary(formatDict, &streamFormat, &formatExtension)) { 1101 result = setFormat(&streamFormat, &formatExtension, formatDict); 1102 } else { 1103 result = kIOReturnBadArgument; 1104 } 1105 } else { 1106 result = kIOReturnBadArgument; 1107 } 1108 1109 return result; 1110} 1111 1112IOReturn IOAudioStream::setFormat(const IOAudioStreamFormat *streamFormat, OSDictionary *formatDict, bool callDriver) 1113{ 1114 return setFormat(streamFormat, NULL, formatDict, callDriver); 1115} 1116 1117IOReturn IOAudioStream::hardwareFormatChanged(const IOAudioStreamFormat *streamFormat) 1118{ 1119 assert(reserved); 1120 return setFormat(streamFormat, &reserved->streamFormatExtension, false); 1121} 1122 1123void IOAudioStream::addAvailableFormat(const IOAudioStreamFormat *streamFormat, const IOAudioSampleRate *minRate, const IOAudioSampleRate *maxRate, AudioIOFunction ioFunction) 1124{ 1125 addAvailableFormat(streamFormat, NULL, minRate, maxRate, &ioFunction, 1); 1126} 1127 1128void IOAudioStream::addAvailableFormat(const IOAudioStreamFormat *streamFormat, const IOAudioSampleRate *minRate, const IOAudioSampleRate *maxRate, const AudioIOFunction *ioFunctionList, UInt32 numFunctions) 1129{ 1130 addAvailableFormat(streamFormat, NULL, minRate, maxRate, ioFunctionList, numFunctions); 1131} 1132 1133void IOAudioStream::clearAvailableFormats() 1134{ 1135 OSArray* oldAvailableFormats; 1136 OSArray* clearedAvailableFormats; 1137 1138 assert(availableFormatDictionaries); 1139 1140 oldAvailableFormats = availableFormatDictionaries; 1141 1142 clearedAvailableFormats = OSArray::withCapacity(1); 1143 if (!clearedAvailableFormats) { 1144 return; 1145 } 1146 availableFormatDictionaries = clearedAvailableFormats; 1147 setProperty(kIOAudioStreamAvailableFormatsKey, availableFormatDictionaries); 1148 1149 oldAvailableFormats->release(); 1150 1151 // <rdar://9059646> Clean up the available formats array. 1152 if (availableFormats && (numAvailableFormats > 0)) { 1153 IOFreeAligned(availableFormats, numAvailableFormats * sizeof(IOAudioStreamFormatDesc)); 1154 } 1155 availableFormats = NULL; 1156 numAvailableFormats = 0; 1157} 1158 1159bool IOAudioStream::validateFormat(IOAudioStreamFormat *streamFormat, IOAudioStreamFormatDesc *formatDesc) 1160{ 1161 return validateFormat(streamFormat, NULL, formatDesc); 1162} 1163 1164UInt32 IOAudioStream::getStartingChannelID() 1165{ 1166 return startingChannelID; 1167} 1168 1169UInt32 IOAudioStream::getMaxNumChannels() 1170{ 1171 return maxNumChannels; 1172} 1173 1174void IOAudioStream::setStartingChannelNumber(UInt32 channelNumber) 1175{ 1176 setProperty(kIOAudioStreamStartingChannelNumberKey, channelNumber, sizeof(UInt32)*8); 1177} 1178 1179void IOAudioStream::updateNumClients() 1180{ 1181 setProperty(kIOAudioStreamNumClientsKey, numClients, sizeof(UInt32)*8); 1182} 1183 1184IOReturn IOAudioStream::addClient(IOAudioClientBuffer *clientBuffer) 1185{ 1186 IOReturn result = kIOReturnBadArgument; 1187 1188 audioDebugIOLog(3, "+ IOAudioStream[%p]::addClient(%p)\n", this, clientBuffer); 1189 1190 if (clientBuffer) { 1191 assert(clientBuffer->audioStream == this); 1192 1193 lockStreamForIO(); 1194 1195 // <rdar://11731381> Make sure this buffer is not in the list 1196 bool bufferInList = false; 1197 if ((clientBuffer->nextClip == NULL) && (clientBuffer->previousClip == NULL) && (clientBuffer != clientBufferListStart) && (clientBuffer->nextClient == NULL) && (clientBuffer != userClientList)) { 1198 1199 // <rdar://11731381> Make sure that the clientBuffer is not at the end of the list. 1200 IOAudioClientBuffer *tmpClientBuffer = userClientList; 1201 while (tmpClientBuffer && (tmpClientBuffer != clientBuffer)) { 1202 tmpClientBuffer = tmpClientBuffer->nextClient; 1203 } 1204 if (tmpClientBuffer) { 1205 audioDebugIOLog(3, " clientBuffer %p is already in the list.\n", tmpClientBuffer); 1206 bufferInList = true; 1207 } 1208 } 1209 else { 1210 audioDebugIOLog(3, " unexpected clientBuffer values (%p, %p, %p, %p, %p, %p)\n", clientBuffer->nextClip, clientBuffer->previousClip, clientBuffer, clientBufferListStart, clientBuffer->nextClient, userClientList ); 1211 bufferInList = true; 1212 } 1213 1214 if (!bufferInList) { // <rdar://11731381> 1215 1216 // It's OK to allow a new client if this is a mixable format 1217 // or if its not mixable but we don't have any clients 1218 // or if we are an input stream 1219 if (format.fIsMixable || (numClients == 0) || (getDirection() == kIOAudioStreamDirectionInput)) { 1220 numClients++; 1221 updateNumClients(); 1222 1223 clientBuffer->nextClient = userClientList; 1224 userClientList = clientBuffer; 1225 1226 if (getDirection() == kIOAudioStreamDirectionOutput) { 1227 1228 clientBuffer->mixedPosition.fLoopCount = 0; 1229 clientBuffer->mixedPosition.fSampleFrame = 0; 1230 1231 clientBuffer->previousClip = NULL; 1232 clientBuffer->nextClip = NULL; 1233 1234 if (!mixBuffer && format.fIsMixable && sampleBuffer && (sampleBufferSize > 0)) { 1235 assert(audioEngine); 1236 1237 UInt32 mixBufSize = format.fNumChannels * kIOAudioEngineDefaultMixBufferSampleSize * audioEngine->numSampleFramesPerBuffer; 1238 1239 if (mixBufSize > 0) { 1240 void *mixBuf = IOMallocAligned(mixBufSize, 32); 1241 if (mixBuf) { 1242 setMixBuffer(mixBuf, mixBufSize); 1243 streamAllocatedMixBuffer = true; 1244 } 1245 } 1246 } 1247 } 1248 1249 result = kIOReturnSuccess; 1250 } else { 1251 audioDebugIOLog(3, " clientBuffer invalid (%d, %d, %d).\n", format.fIsMixable, numClients, getDirection()); 1252 result = kIOReturnExclusiveAccess; 1253 } 1254 } 1255 else { 1256 // <rdar://13412666> Return success if the buffers are already registered 1257 result = kIOReturnSuccess; 1258 } 1259 1260 unlockStreamForIO(); 1261 } 1262 1263 audioDebugIOLog(3, "- IOAudioStream[%p]::addClient(%p) returns 0x%lX\n", this, clientBuffer, (long unsigned int)result ); 1264 return result; 1265} 1266 1267void IOAudioStream::removeClient(IOAudioClientBuffer *clientBuffer) 1268{ 1269 audioDebugIOLog(3, "+ IOAudioStream[%p]::removeClient(%p)\n", this, clientBuffer); 1270 1271 if (clientBuffer) { 1272 IOAudioClientBuffer *tmpClientBuffer, *previousClientBuffer = NULL; 1273 1274 assert(clientBuffer->audioStream == this); 1275 1276 lockStreamForIO(); 1277 1278 tmpClientBuffer = userClientList; 1279 while (tmpClientBuffer && (tmpClientBuffer != clientBuffer)) { 1280 previousClientBuffer = tmpClientBuffer; 1281 tmpClientBuffer = tmpClientBuffer->nextClient; 1282 } 1283 1284 if (tmpClientBuffer) { 1285 if (previousClientBuffer) { 1286 previousClientBuffer->nextClient = tmpClientBuffer->nextClient; 1287 } else { 1288 assert(tmpClientBuffer == userClientList); 1289 userClientList = tmpClientBuffer->nextClient; 1290 } 1291 1292 tmpClientBuffer->nextClient = NULL; 1293 1294 numClients--; 1295 updateNumClients(); 1296 } 1297 1298 // Make sure the buffer is in the list 1299 if ((clientBuffer->nextClip != NULL) || (clientBuffer->previousClip != NULL) || (clientBuffer == clientBufferListStart)) { 1300 if (getDirection() == kIOAudioStreamDirectionOutput) { 1301 if (numClients == 0) { 1302 resetClipInfo(); 1303 } 1304 1305 if (clientBuffer->previousClip != NULL) { 1306 clientBuffer->previousClip->nextClip = clientBuffer->nextClip; 1307 } 1308 1309 if (clientBuffer->nextClip != NULL) { 1310 clientBuffer->nextClip->previousClip = clientBuffer->previousClip; 1311 } 1312 1313 if (clientBufferListEnd == clientBuffer) { 1314 assert(clientBuffer->nextClip == NULL); 1315 clientBufferListEnd = clientBuffer->previousClip; 1316 } 1317 1318 if (clientBufferListStart == clientBuffer) { 1319 assert(clientBuffer->previousClip == NULL); 1320 clientBufferListStart = clientBuffer->nextClip; 1321 if (clientBufferListStart != NULL) { 1322 clipIfNecessary(); 1323 } 1324 } 1325 } 1326 } 1327 1328 // clear these values for bug 2851917 1329 clientBuffer->previousClip = NULL; 1330 clientBuffer->nextClip = NULL; 1331 clientBuffer->nextClient = NULL; 1332 unlockStreamForIO(); 1333 } 1334 1335 audioDebugIOLog(3, "- IOAudioStream[%p]::removeClient(%p)\n", this, clientBuffer); 1336 return; 1337} 1338 1339UInt32 IOAudioStream::getNumClients() 1340{ 1341 return numClients; 1342} 1343 1344void dumpList(IOAudioClientBuffer *start) 1345{ 1346 IOAudioClientBuffer *tmp; 1347 1348 tmp = start; 1349 while (tmp) { 1350 audioDebugIOLog(3, " (%lx,%lx)\n", (long unsigned int)tmp->mixedPosition.fLoopCount, (long unsigned int)tmp->mixedPosition.fSampleFrame); 1351 tmp = tmp->nextClip; 1352 } 1353} 1354 1355void validateList(IOAudioClientBuffer *start) 1356{ 1357 IOAudioClientBuffer *tmp; 1358 1359 tmp = start; 1360 while (tmp) { 1361 if (tmp->nextClip && (CMP_IOAUDIOENGINEPOSITION(&tmp->mixedPosition, &tmp->nextClip->mixedPosition) > 0)) { 1362 audioDebugIOLog(3, "+-IOAudioStream: ERROR - client buffer list not sorted!\n"); 1363 dumpList(start); 1364 break; 1365 } 1366 tmp = tmp->nextClip; 1367 } 1368} 1369 1370IOReturn IOAudioStream::readInputSamples(IOAudioClientBuffer *clientBuffer, UInt32 firstSampleFrame) 1371{ 1372 IOReturn result = kIOReturnError; 1373 1374 assert(audioEngine); 1375 assert(getDirection() == kIOAudioStreamDirectionInput); 1376 assert(reserved); 1377 1378 if (clientBuffer) { 1379 UInt32 numWrappedFrames = 0; 1380 UInt32 numReadFrames = 0; 1381 UInt32 numSampleFramesPerBuffer; 1382 1383 numSampleFramesPerBuffer = audioEngine->getNumSampleFramesPerBuffer(); 1384 1385 if ((firstSampleFrame + clientBuffer->numSampleFrames) > numSampleFramesPerBuffer) { 1386 numWrappedFrames = clientBuffer->numSampleFrames - (numSampleFramesPerBuffer - firstSampleFrame); 1387 } 1388 1389 if (audioIOFunctions && (numIOFunctions != 0)) { 1390 UInt32 functionNum; 1391 1392 for (functionNum = 0; functionNum < numIOFunctions; functionNum++) { 1393 if (audioIOFunctions[functionNum]) { 1394 result = audioIOFunctions[functionNum](sampleBuffer, clientBuffer->sourceBuffer, firstSampleFrame, clientBuffer->numSampleFrames - numWrappedFrames, &format, this); 1395 if (result != kIOReturnSuccess) { 1396 break; 1397 } 1398 } 1399 } 1400 1401 if (numWrappedFrames > 0) { 1402 for (functionNum = 0; functionNum < numIOFunctions; functionNum++) { 1403 if (audioIOFunctions[functionNum]) { 1404 result = audioIOFunctions[functionNum](sampleBuffer, &((float *)clientBuffer->sourceBuffer)[(numSampleFramesPerBuffer - firstSampleFrame) * format.fNumChannels], 0, numWrappedFrames, &format, this); 1405 if (result != kIOReturnSuccess) { 1406 break; 1407 } 1408 } 1409 } 1410 } 1411 } else { 1412 numReadFrames = clientBuffer->numSampleFrames - numWrappedFrames; 1413 // numReadFrames passed by reference, value may or may not be modified by the engine. 1414 result = audioEngine->convertInputSamplesVBR(sampleBuffer, clientBuffer->sourceBuffer, firstSampleFrame, numReadFrames, &format, this); 1415 // override the default value set before this call with driver actual value 1416 reserved->mSampleFramesReadByEngine = numReadFrames; 1417 1418 if ((result == kIOReturnSuccess) && (numWrappedFrames > 0)) { 1419 numReadFrames = numWrappedFrames; 1420 if (format.fIsMixable) { // <rdar://8572755> 1421 // Use float format to compute offset for destination buffer 1422 result = audioEngine->convertInputSamplesVBR(sampleBuffer, &((float *)clientBuffer->sourceBuffer)[(numSampleFramesPerBuffer - firstSampleFrame) * format.fNumChannels], 0, numReadFrames, &format, this); 1423 } else { 1424 // Use native format to compute offset for destination buffer 1425 result = audioEngine->convertInputSamplesVBR(sampleBuffer, ((UInt8 *)clientBuffer->sourceBuffer) + ((numSampleFramesPerBuffer - firstSampleFrame) * format.fNumChannels * (format.fBitWidth / 8)), 0, numReadFrames, &format, this); 1426 } 1427 reserved->mSampleFramesReadByEngine += numReadFrames; 1428 } 1429 } 1430 } else { 1431 result = kIOReturnBadArgument; 1432 } 1433 1434 return result; 1435} 1436 1437IOReturn IOAudioStream::processOutputSamples(IOAudioClientBuffer *clientBuffer, UInt32 firstSampleFrame, UInt32 loopCount, bool samplesAvailable) 1438{ 1439 IOReturn result = kIOReturnSuccess; 1440 1441 //audioDebugIOLog(6, "IOAudioStream[%p]::processOutputSamples(%p, 0x%lx)\n", this, clientBuffer, firstSampleFrame); 1442 //audioDebugIOLog(6, "m(%lx,%lx,%lx)\n", loopCount, firstSampleFrame, clientBuffer->numSampleFrames); 1443 1444 assert(direction == kIOAudioStreamDirectionOutput); 1445 if (clientBuffer) { 1446 // We can go ahead if we have a mix buffer or if the format is not mixable 1447 if (mixBuffer || !format.fIsMixable) { 1448 UInt32 numSampleFramesPerBuffer = audioEngine->getNumSampleFramesPerBuffer(); 1449 UInt32 nextSampleFrame = 0; 1450 UInt32 mixBufferWrapped = false; 1451 UInt32 numSamplesToMix = 0; 1452 IOAudioClientBuffer *tmpBuf = NULL; 1453 1454 assert(audioEngine); 1455 1456 1457 1458 // If we haven't mixed any samples for this client yet, 1459 // we have to figure out which loop those samples belong to 1460 if (IOAUDIOENGINEPOSITION_IS_ZERO(&clientBuffer->mixedPosition)) { 1461 clientBuffer->mixedPosition.fSampleFrame = firstSampleFrame; 1462 clientBuffer->mixedPosition.fLoopCount = loopCount; 1463 } else { 1464 // If firstSampleFrame is not the same as the previous mixed position sample frame, 1465 // then adjust it to the firstSampleFrame - looping if necessary 1466 if ((clientBuffer->mixedPosition.fSampleFrame != firstSampleFrame) || (clientBuffer->mixedPosition.fLoopCount != loopCount)) { 1467 audioDebugIOLog(6, "IOAudioStream[%p]::processOutputSamples(%p) - Mix start position (%lx,%lx) is not previous mixed position (%lx,%lx)\n", 1468 this, 1469 clientBuffer, 1470 (long unsigned int)loopCount, 1471 (long unsigned int)firstSampleFrame, 1472 (long unsigned int)clientBuffer->mixedPosition.fLoopCount, 1473 (long unsigned int)clientBuffer->mixedPosition.fSampleFrame); 1474 clientBuffer->mixedPosition.fLoopCount = loopCount; 1475 clientBuffer->mixedPosition.fSampleFrame = firstSampleFrame; 1476 } 1477 1478 // Check to see if the first sample frame is more than one buffer behind the last mixed position 1479 // of all of the buffers. We need to deal with the case where we didn't get any samples 1480 // for this buffer for more than a buffer cycle. In that case, we need to jump to 1481 // the loop that the last buffer is on. This assumes that a client never gets more than one 1482 // buffer cycle ahead of the playback head 1483 if ((clientBuffer != clientBufferListEnd) && 1484 (clientBufferListEnd != NULL) && 1485 ((clientBufferListEnd->mixedPosition.fLoopCount > (clientBuffer->mixedPosition.fLoopCount + 1)) || 1486 ((clientBufferListEnd->mixedPosition.fLoopCount == (clientBuffer->mixedPosition.fLoopCount + 1)) && 1487 (clientBufferListEnd->mixedPosition.fSampleFrame > clientBuffer->mixedPosition.fSampleFrame)))) { 1488 // Adjust the loop count to be on the loop before the last mixed position 1489 if (clientBuffer->mixedPosition.fSampleFrame > clientBufferListEnd->mixedPosition.fSampleFrame) { 1490 clientBuffer->mixedPosition.fLoopCount = clientBufferListEnd->mixedPosition.fLoopCount - 1; 1491 } else { 1492 clientBuffer->mixedPosition.fLoopCount = clientBufferListEnd->mixedPosition.fLoopCount; 1493 } 1494 audioDebugIOLog(6, "IOAudioStream[%p]::processOutputSamples(%p) - more than one buffer behind (%lx,%lx) adjusting to (%lx,%lx)\n", 1495 this, 1496 clientBuffer, 1497 (long unsigned int)clientBufferListEnd->mixedPosition.fLoopCount, 1498 (long unsigned int)clientBufferListEnd->mixedPosition.fSampleFrame, 1499 (long unsigned int)clientBuffer->mixedPosition.fLoopCount, 1500 (long unsigned int)firstSampleFrame); 1501 //dumpList(clientBufferListStart); 1502 } 1503 } 1504 1505 // If we've already clipped, we need to verify all of the samples are after the clipped position 1506 // Those that are not will be discarded - they can't be played 1507 if (!IOAUDIOENGINEPOSITION_IS_ZERO(&clippedPosition)) { 1508 if (clientBuffer->mixedPosition.fLoopCount == clippedPosition.fLoopCount) { 1509 if (clientBuffer->mixedPosition.fSampleFrame < clippedPosition.fSampleFrame) { 1510 audioEngine->resetClipPosition(this, clientBuffer->mixedPosition.fSampleFrame); 1511 1512#ifdef DEBUG 1513 UInt32 samplesMissed; 1514 samplesMissed = clippedPosition.fSampleFrame - clientBuffer->mixedPosition.fSampleFrame; 1515 audioDebugIOLog(6, "IOAudioStream[%p]::processOutputSamples(%p) - Reset clip position (%lx,%lx)->(%lx,%lx) - %lx samples.\n", 1516 this, 1517 clientBuffer, 1518 (long unsigned int)clippedPosition.fLoopCount, 1519 (long unsigned int)clippedPosition.fSampleFrame, 1520 (long unsigned int)clientBuffer->mixedPosition.fLoopCount, 1521 (long unsigned int)clientBuffer->mixedPosition.fSampleFrame, 1522 (long unsigned int)samplesMissed); 1523#endif 1524 1525 clippedPosition = clientBuffer->mixedPosition; 1526 } 1527 } else if (clientBuffer->mixedPosition.fLoopCount < clippedPosition.fLoopCount) { 1528 audioEngine->resetClipPosition(this, clientBuffer->mixedPosition.fSampleFrame); 1529 1530#ifdef DEBUG 1531 UInt32 samplesMissed; 1532 samplesMissed = (clippedPosition.fLoopCount - clientBuffer->mixedPosition.fLoopCount - 1) * numSampleFramesPerBuffer; 1533 samplesMissed += clippedPosition.fSampleFrame + numSampleFramesPerBuffer - clientBuffer->mixedPosition.fSampleFrame; 1534 audioDebugIOLog(6, "IOAudioStream[%p]::processOutputSamples(%p) - Reset clip position (%lx,%lx)->(%lx,%lx) - %lx samples.\n", 1535 this, 1536 clientBuffer, 1537 (long unsigned int)clippedPosition.fLoopCount, 1538 (long unsigned int)clippedPosition.fSampleFrame, 1539 (long unsigned int)clientBuffer->mixedPosition.fLoopCount, 1540 (long unsigned int)clientBuffer->mixedPosition.fSampleFrame, 1541 (long unsigned int)samplesMissed); 1542#endif 1543 1544 clippedPosition = clientBuffer->mixedPosition; 1545 } 1546 } 1547 1548 // We only need to mix samples if there are samples available 1549 // If the watchdog timer was responsible for this call, then 1550 // there won't be any samples, so there's no point in mixing 1551 // or resetting the clip position 1552 if (samplesAvailable) { 1553 numSamplesToMix = clientBuffer->numSampleFrames; 1554 } 1555 1556 if (numSamplesToMix > 0) { 1557/* 1558#ifdef DEBUG 1559 UInt32 currentSampleFrame = audioEngine->getCurrentSampleFrame(); 1560 if (currentSampleFrame > firstSampleFrame) { 1561 if ((firstSampleFrame + clientBuffer->numSampleFrames) > currentSampleFrame) { 1562 //audioDebugIOLog(6, "IOAudioStream[%p]::processOutputSamples(%p) - Error: Some samples already played: first=%lx num=%lx curr=%lx\n", this, clientBuffer, firstSampleFrame, clientBuffer->numSampleFrames, currentSampleFrame); 1563 audioDebugIOLog(6, "mix() missed first=%lx num=%lx curr=%lx\n", firstSampleFrame, clientBuffer->numSampleFrames, currentSampleFrame); 1564 } 1565 } else { 1566 if ((clientBuffer->numSampleFrames + firstSampleFrame) > (currentSampleFrame + numSampleFramesPerBuffer)) { 1567 //audioDebugIOLog(6, "IOAudioStream[%p]::processOutputSamples(%p) - Error: Some samples already played: first=%lx num=%lx curr=%lx\n", this, clientBuffer, firstSampleFrame, clientBuffer->numSampleFrames, currentSampleFrame); 1568 audioDebugIOLog(6, "mix() missed first=%lx num=%lx curr=%lx\n", firstSampleFrame, clientBuffer->numSampleFrames, currentSampleFrame); 1569 } 1570 } 1571#endif 1572*/ 1573 1574 // Check if the buffer wraps 1575 if (numSampleFramesPerBuffer > (firstSampleFrame + numSamplesToMix)) { // No wrap 1576 if (format.fIsMixable) { 1577 if (numClients == 1) { 1578 result = mixOutputSamples (clientBuffer->sourceBuffer, mixBuffer, firstSampleFrame, numSamplesToMix, &format, this); 1579 } else { 1580 result = audioEngine->mixOutputSamples(clientBuffer->sourceBuffer, mixBuffer, firstSampleFrame, numSamplesToMix, &format, this); 1581 } 1582 } else { 1583 result = kIOReturnSuccess; 1584 } 1585 nextSampleFrame = firstSampleFrame + numSamplesToMix; 1586 } else { // Buffer wraps around 1587 mixBufferWrapped = true; 1588 if (format.fIsMixable) { 1589 if (numClients == 1) { 1590 result = mixOutputSamples (clientBuffer->sourceBuffer, mixBuffer, firstSampleFrame, numSampleFramesPerBuffer - firstSampleFrame, &format, this); 1591 } else { 1592 result = audioEngine->mixOutputSamples(clientBuffer->sourceBuffer, mixBuffer, firstSampleFrame, numSampleFramesPerBuffer - firstSampleFrame, &format, this); 1593 } 1594 } else { 1595 result = kIOReturnSuccess; 1596 } 1597 if (result != kIOReturnSuccess) { 1598 IOLog("IOAudioStream[%p]::processOutputSamples(%p) - Error: 0x%x returned from audioEngine->mixOutputSamples(%p, %p, 0x%lx, 0x%lx, %p, %p)\n", this, clientBuffer, result, clientBuffer->sourceBuffer, mixBuffer, (long unsigned int)firstSampleFrame,(long unsigned int) numSampleFramesPerBuffer - firstSampleFrame, &format, this); 1599 } 1600 nextSampleFrame = numSamplesToMix - (numSampleFramesPerBuffer - firstSampleFrame); 1601 if (format.fIsMixable) { 1602 if (numClients == 1) { 1603 result = mixOutputSamples (((float *)clientBuffer->sourceBuffer) + ((numSampleFramesPerBuffer - firstSampleFrame) * format.fNumChannels), mixBuffer, 0, nextSampleFrame, &format, this); 1604 } else { 1605 result = audioEngine->mixOutputSamples(((float *)clientBuffer->sourceBuffer) + ((numSampleFramesPerBuffer - firstSampleFrame) * format.fNumChannels), mixBuffer, 0, nextSampleFrame, &format, this); 1606 } 1607 } else { 1608 result = kIOReturnSuccess; 1609 } 1610 } 1611 1612 if (result == kIOReturnSuccess) { 1613 // Reset startingSampleFrame and startingLoopCount if we haven't clipped 1614 // anything yet and this buffer mixed samples before the previous 1615 // starting frame 1616 if (IOAUDIOENGINEPOSITION_IS_ZERO(&clippedPosition)) { 1617 if (IOAUDIOENGINEPOSITION_IS_ZERO(&startingPosition) || 1618 (clientBuffer->mixedPosition.fLoopCount < startingPosition.fLoopCount) || 1619 ((clientBuffer->mixedPosition.fLoopCount == startingPosition.fLoopCount) && (firstSampleFrame < startingPosition.fSampleFrame))) { 1620 1621 startingPosition.fLoopCount = clientBuffer->mixedPosition.fLoopCount; 1622 startingPosition.fSampleFrame = firstSampleFrame; 1623 } 1624 } 1625 } else { 1626 IOLog("IOAudioStream[%p]::processOutputSamples(%p) - Error: 0x%lx returned from audioEngine->mixOutputSamples(%p, %p, 0x%lx, 0x%lx, %p, %p)\n", this, clientBuffer, (long unsigned int)result, clientBuffer->sourceBuffer, mixBuffer, (long unsigned int)firstSampleFrame,(long unsigned int) numSampleFramesPerBuffer - firstSampleFrame, &format, this); 1627 } 1628 1629 if (mixBufferWrapped) { 1630 clientBuffer->mixedPosition.fLoopCount++; 1631 } 1632 clientBuffer->mixedPosition.fSampleFrame = nextSampleFrame; 1633 1634 } else { // We missed all of the samples 1635 clientBuffer->mixedPosition.fSampleFrame += clientBuffer->numSampleFrames; 1636 if (clientBuffer->mixedPosition.fSampleFrame >= numSampleFramesPerBuffer) { 1637 clientBuffer->mixedPosition.fSampleFrame -= numSampleFramesPerBuffer; 1638 clientBuffer->mixedPosition.fLoopCount++; 1639 } 1640 } 1641 1642 // If this buffer isn't in the list yet, then we look at the beginning of the list 1643 if ((clientBuffer->nextClip == NULL) && (clientBuffer->previousClip == NULL) && (clientBuffer != clientBufferListStart)) { 1644 // If the buffer has mixed past the first buffer in the list, then we can start at the beginning 1645 // If not, then tmpBuf is just NULL and we insert at the beginning 1646 if ((clientBufferListStart != NULL) && (CMP_IOAUDIOENGINEPOSITION(&clientBuffer->mixedPosition, &clientBufferListStart->mixedPosition) > 0)) { 1647 tmpBuf = clientBufferListStart; 1648 } 1649 } else { // Otherwise, we look forward from the current position 1650 tmpBuf = clientBuffer; 1651 } 1652 1653 // Add it to the beginning if the buffer is new and has not mixed past any other buffers 1654 if (tmpBuf == NULL) { 1655 assert(clientBuffer->nextClip == NULL); 1656 assert(clientBuffer->previousClip == NULL); 1657 1658 clientBuffer->nextClip = clientBufferListStart; 1659 clientBufferListStart = clientBuffer; 1660 1661 if (clientBuffer->nextClip == NULL) { 1662 clientBufferListEnd = clientBuffer; 1663 } else { 1664 clientBuffer->nextClip->previousClip = clientBuffer; 1665 } 1666 } else { 1667 //Find the insertion point for the new location for this buffer 1668 while ((tmpBuf->nextClip != NULL) && (CMP_IOAUDIOENGINEPOSITION(&clientBuffer->mixedPosition, &tmpBuf->nextClip->mixedPosition) > 0)) { 1669 tmpBuf = tmpBuf->nextClip; 1670 } 1671 1672 if (tmpBuf != clientBuffer) { 1673 // If the buffer is to change position, move updated client buffer to its new sorted position 1674 // First remove the client buffer from its current position 1675 if (clientBuffer->previousClip != NULL) { 1676 clientBuffer->previousClip->nextClip = clientBuffer->nextClip; 1677 } else if (clientBuffer == clientBufferListStart) { // If we don't have a previous clip set, we may be the starting entry 1678 clientBufferListStart = clientBuffer->nextClip; 1679 } // If we have don't have a previousClip set and are not the start, then this is the first time this buffer is being mixed 1680 1681 if (clientBuffer->nextClip != NULL) { 1682 clientBuffer->nextClip->previousClip = clientBuffer->previousClip; 1683 } else if (clientBuffer == clientBufferListEnd) { // If we don't have a next clip set, we may be the last entry 1684 // We should never get here, because we only are moving this buffer forward 1685 // and that is impossible if it is the last one 1686 clientBufferListEnd = clientBuffer->previousClip; 1687 } // If we don't have a next clip and are not the end, then this is the first time this buffer is being mixed 1688 1689 // Insert it after tmpBuf 1690 clientBuffer->nextClip = tmpBuf->nextClip; 1691 clientBuffer->previousClip= tmpBuf; 1692 tmpBuf->nextClip = clientBuffer; 1693 if (clientBuffer->nextClip) { 1694 clientBuffer->nextClip->previousClip = clientBuffer; 1695 } 1696 if (clientBuffer->nextClip == NULL) { 1697 assert(clientBufferListEnd == tmpBuf); 1698 clientBufferListEnd = clientBuffer; 1699 } 1700 1701#ifdef DEBUG 1702 validateList(clientBufferListStart); 1703#endif 1704 } 1705 } 1706 1707 // We should attempt to clip if we mixed some samples of if we 1708 // were called as a result of the watchdog timer (indicated 1709 // by samplesAvailable being false) 1710 if ((numSamplesToMix > 0) || !samplesAvailable) { 1711 if (!format.fIsMixable) { 1712 mixBuffer = clientBuffer->sourceBuffer; 1713 } 1714 1715 reserved->mClipOutputStatus = kIOReturnSuccess; 1716 1717 clipIfNecessary(); 1718 if (!format.fIsMixable) { 1719 mixBuffer = NULL; 1720 } 1721 1722 // gets set based on IOAudioEngine::clipOutputSamples return value inside IOAudioStream::clipOutputSamples 1723 result = reserved->mClipOutputStatus; 1724 } 1725 } else { 1726 IOLog("IOAudioStream[%p]::processOutputSamples(%p) - Internal Error: No mix buffer\n", this, clientBuffer); 1727 result = kIOReturnError; 1728 } 1729 } else { 1730 result = kIOReturnBadArgument; 1731 } 1732 1733 return result; 1734} 1735 1736void IOAudioStream::resetClipInfo() 1737{ 1738 startingPosition.fLoopCount = 0; 1739 startingPosition.fSampleFrame = 0; 1740 clippedPosition.fLoopCount = 0; 1741 clippedPosition.fSampleFrame = 0; 1742} 1743 1744void IOAudioStream::clipIfNecessary() 1745{ 1746 //audioDebugIOLog(6, "IOAudioStream[%p]::clipIfNecessary()\n", this); 1747 1748 if (clientBufferListStart != NULL) { 1749 // Only try to clip if there is not an unmixed buffer 1750 if (!IOAUDIOENGINEPOSITION_IS_ZERO(&clientBufferListStart->mixedPosition)) { 1751 1752 // Check to see if we've clipped any samples yet 1753 if (IOAUDIOENGINEPOSITION_IS_ZERO(&clippedPosition)) { 1754 clippedPosition = startingPosition; 1755 } 1756 1757#ifdef DEBUG 1758 IOAudioClientBuffer *tmp; 1759 1760 tmp = clientBufferListStart->nextClip; 1761 while (tmp) { 1762 if ((tmp->mixedPosition.fLoopCount > (clippedPosition.fLoopCount + 1)) || 1763 ((tmp->mixedPosition.fLoopCount == clippedPosition.fLoopCount) && 1764 (tmp->mixedPosition.fSampleFrame > clippedPosition.fSampleFrame))) { 1765 1766 if (clientBufferListStart->mixedPosition.fSampleFrame > clippedPosition.fSampleFrame) { 1767 if ((tmp->mixedPosition.fSampleFrame > clippedPosition.fSampleFrame) && 1768 (clientBufferListStart->mixedPosition.fSampleFrame > tmp->mixedPosition.fSampleFrame)) { 1769 audioDebugIOLog(6, "IOAudioStream[%p]::clipIfNecessary() - Error: Clipping across future buffer boundary - glitching! (%lx,%lx)->(%lx,%lx) buf=(%lx,%lx)\n", 1770 this, 1771 (long unsigned int)clippedPosition.fLoopCount, 1772 (long unsigned int)clippedPosition.fSampleFrame, 1773 (long unsigned int)clientBufferListStart->mixedPosition.fLoopCount, 1774 (long unsigned int)clientBufferListStart->mixedPosition.fSampleFrame, 1775 (long unsigned int)tmp->mixedPosition.fLoopCount, 1776 (long unsigned int)tmp->mixedPosition.fSampleFrame); 1777 dumpList(clientBufferListStart); 1778 break; 1779 } 1780 } else if (clippedPosition.fSampleFrame > clientBufferListStart->mixedPosition.fSampleFrame) { 1781 if ((tmp->mixedPosition.fSampleFrame < clientBufferListStart->mixedPosition.fSampleFrame) || 1782 (tmp->mixedPosition.fSampleFrame > clippedPosition.fSampleFrame)) { 1783 audioDebugIOLog(6, "IOAudioStream[%p]::clipIfNecessary() - Error: Clipping across future buffer boundary - glitching! (%lx,%lx)->(%lx,%lx) buf=(%lx,%lx)\n", 1784 this, 1785 (long unsigned int)clippedPosition.fLoopCount, 1786 (long unsigned int)clippedPosition.fSampleFrame, 1787 (long unsigned int)clientBufferListStart->mixedPosition.fLoopCount, 1788 (long unsigned int)clientBufferListStart->mixedPosition.fSampleFrame, 1789 (long unsigned int)tmp->mixedPosition.fLoopCount, 1790 (long unsigned int)tmp->mixedPosition.fSampleFrame); 1791 dumpList(clientBufferListStart); 1792 break; 1793 } 1794 } 1795 } 1796 tmp = tmp->nextClip; 1797 } 1798#endif 1799 1800 // Check to see if it is on the same loop as the starting position 1801 // If not, adjust it to the same loop 1802 if (((clientBufferListStart->mixedPosition.fLoopCount == (clippedPosition.fLoopCount + 1)) && 1803 (clientBufferListStart->mixedPosition.fSampleFrame >= clippedPosition.fSampleFrame)) || 1804 (clientBufferListStart->mixedPosition.fLoopCount > (clippedPosition.fLoopCount + 1))) { 1805 IOLog("IOAudioStream[%p]::clipIfNecessary() - Error: attempting to clip to a position more than one buffer ahead of last clip position (%lx,%lx)->(%lx,%lx).\n", this, (long unsigned int)clippedPosition.fLoopCount, (long unsigned int)clippedPosition.fSampleFrame, (long unsigned int) clientBufferListStart->mixedPosition.fLoopCount,(long unsigned int) clientBufferListStart->mixedPosition.fSampleFrame); 1806 if (clientBufferListStart->mixedPosition.fSampleFrame >= clippedPosition.fSampleFrame) { 1807 clippedPosition.fLoopCount = clientBufferListStart->mixedPosition.fLoopCount; 1808 } else { 1809 clippedPosition.fLoopCount = clientBufferListStart->mixedPosition.fLoopCount - 1; 1810 } 1811 IOLog("IOAudioStream[%p]::clipIfNecessary() - adjusting clipped position to (%lx,%lx)\n", this, (long unsigned int) clippedPosition.fLoopCount,(long unsigned int) clippedPosition.fSampleFrame); 1812 } 1813 1814 // Add a test to see if we'd be clipping more samples than delivered because the HAL might skip some samples around a loop increment 1815 // If the HAL skipped samples around a loop increment, then just start from where it wants to 1816 if (clientBufferListStart->mixedPosition.fLoopCount + 1 == clippedPosition.fLoopCount && (clientBufferListStart->numSampleFrames < audioEngine->getNumSampleFramesPerBuffer() - clippedPosition.fSampleFrame)) { 1817 clientBufferListStart->mixedPosition.fLoopCount = clippedPosition.fLoopCount; 1818 IOLog ("clip position is off %ld < %ld - %ld \n",(long int) clientBufferListStart->numSampleFrames,(long int) audioEngine->getNumSampleFramesPerBuffer(),(long int) clippedPosition.fSampleFrame); 1819 } 1820/* 1821 static UInt32 lastSampleFrame; 1822 if (clippedPosition.fSampleFrame != lastSampleFrame) { 1823 audioDebugIOLog(3, "Family sample frames wrong %ld %ld\n", clippedPosition.fSampleFrame, lastSampleFrame); 1824 } 1825 lastSampleFrame = clippedPosition.fSampleFrame + (clientBufferListStart->mixedPosition.fSampleFrame - clippedPosition.fSampleFrame); 1826*/ 1827 UInt32 numSamplesToClip; //<rdar://problem/5994776> 1828 1829 if (clientBufferListStart->mixedPosition.fLoopCount == clippedPosition.fLoopCount) { 1830 if (clientBufferListStart->mixedPosition.fSampleFrame > clippedPosition.fSampleFrame) { 1831 numSamplesToClip = clientBufferListStart->mixedPosition.fSampleFrame - clippedPosition.fSampleFrame; 1832 if (!format.fIsMixable) { 1833 if ( numSamplesToClip <= kMixBufferMaxSize ) { // <rdar://problem/5994776> 1834 clipOutputSamples(clippedPosition.fSampleFrame, numSamplesToClip ); 1835 } else { 1836 reserved->mClipOutputStatus = kIOReturnOverrun; 1837#ifdef DEBUG 1838 audioDebugIOLog(6,"IOAudioStream[%p]::clipIfNecessary() clipOutputSamples clip too large for source buffer numSamplesToClip=%lu clientBufferListStart->numSampleFrames %lu\n", 1839 this, (long unsigned int)numSamplesToClip, (long unsigned int)clientBufferListStart->numSampleFrames ); 1840 IOLog("IOAudioStream[%p]::clipIfNecessary() clipOutputSamples clip too large for source buffer numSamplesToClip=%lu clientBufferListStart->numSampleFrames %lu\n", 1841 this, (long unsigned int)numSamplesToClip, (long unsigned int)clientBufferListStart->numSampleFrames ); 1842#endif 1843 } 1844 } else { 1845 clipOutputSamples(clippedPosition.fSampleFrame, numSamplesToClip); 1846 } 1847 clippedPosition.fSampleFrame = clientBufferListStart->mixedPosition.fSampleFrame; 1848 } else if (clientBufferListStart->mixedPosition.fSampleFrame < clippedPosition.fSampleFrame) { 1849 IOLog("IOAudioStream[%p]::clipIfNecessary() - Error: already clipped to a position (0x%lx,0x%lx) past data to be clipped (0x%lx, 0x%lx) - data ignored.\n", this,(long unsigned int) clippedPosition.fLoopCount,(long unsigned int) clippedPosition.fSampleFrame,(long unsigned int) clientBufferListStart->mixedPosition.fLoopCount,(long unsigned int) clientBufferListStart->mixedPosition.fSampleFrame); 1850 //clippedPosition.fSampleFrame = clientBufferListStart->mixedPosition.fSampleFrame; 1851 } 1852 } else { // Clip wraps around 1853 UInt32 numSampleFramesPerBuffer; 1854 1855 assert(audioEngine); 1856 1857 numSampleFramesPerBuffer = audioEngine->getNumSampleFramesPerBuffer(); 1858 numSamplesToClip = numSampleFramesPerBuffer - clippedPosition.fSampleFrame; 1859 1860 if (!format.fIsMixable) { 1861 if ( numSamplesToClip <= kMixBufferMaxSize) { // <rdar://problem/5994776> 1862 clipOutputSamples(clippedPosition.fSampleFrame, numSamplesToClip ); 1863 } else { 1864 reserved->mClipOutputStatus = kIOReturnOverrun; 1865#ifdef DEBUG 1866 audioDebugIOLog(6,"IOAudioStream[%p]::clipIfNecessary() clipOutputSamples wrap clip too large for source buffer numSamplesToClip=%lu clientBufferListStart->numSampleFrames %lu\n", 1867 this, (long unsigned int)numSamplesToClip, (long unsigned int)clientBufferListStart->numSampleFrames ); 1868 IOLog("IOAudioStream[%p]::clipIfNecessary() clipOutputSamples wrap clip too large for source buffer numSamplesToClip=%lu clientBufferListStart->numSampleFrames %lu\n", 1869 this, (long unsigned int)numSamplesToClip, (long unsigned int)clientBufferListStart->numSampleFrames ); 1870#endif 1871 } 1872 } else { 1873 clipOutputSamples(clippedPosition.fSampleFrame, numSamplesToClip ); 1874 } 1875 1876 if (!format.fIsMixable) { 1877 UInt32 remainingSamplesToClip = (numSampleFramesPerBuffer - clippedPosition.fSampleFrame); //<rdar://problem/5994776> 1878 1879 // Move the mix buffer to where we left off because the clip routine always starts at the beginning of the source buffer, 1880 // but that's not the right place when we don't have a source buffer and are using the mixbuffer as a pseduo-source buffer. 1881 audioDebugIOLog(6,"IOAudioStream[%p]::clipIfNecessary() clipOutputSamples wrap mixBuffer=%p remainingSamplesToClip=0x%lu clientBufferListStart->mixedPosition.fSampleFrame=%lu\n", 1882 this, mixBuffer, (long unsigned int)remainingSamplesToClip, (long unsigned int)clientBufferListStart->mixedPosition.fSampleFrame ); 1883 if ( remainingSamplesToClip + clientBufferListStart->mixedPosition.fSampleFrame <= kMixBufferMaxSize) // <rdar://problem/5994776> 1884 { 1885 mixBuffer = (char *)mixBuffer + (remainingSamplesToClip * format.fNumChannels * (format.fBitWidth / 8)); 1886 clipOutputSamples(0, clientBufferListStart->mixedPosition.fSampleFrame); 1887 } else { 1888 reserved->mClipOutputStatus = kIOReturnOverrun; 1889#ifdef DEBUG 1890 audioDebugIOLog(6,"IOAudioStream[%p]::clipIfNecessary() clipOutputSamples mixBufferOffset too large for source buffer numSamplesToClip=%lu clientBufferListStart->numSampleFrames=%lu remainingSamplesToClip=%lu\n", 1891 this, (long unsigned int)clientBufferListStart->mixedPosition.fSampleFrame, (long unsigned int)clientBufferListStart->numSampleFrames, (long unsigned int)remainingSamplesToClip ); 1892 IOLog("IOAudioStream[%p]::clipIfNecessary() clipOutputSamples mixBufferOffset too large for source buffer numSamplesToClip=%lu clientBufferListStart->numSampleFrames=%lu remainingSamplesToClip=%lu\n", 1893 this, (long unsigned int)clientBufferListStart->mixedPosition.fSampleFrame, (long unsigned int)clientBufferListStart->numSampleFrames, (long unsigned int)remainingSamplesToClip ); 1894#endif 1895 } 1896 } else { 1897 clipOutputSamples(0, clientBufferListStart->mixedPosition.fSampleFrame); 1898 } 1899 clippedPosition = clientBufferListStart->mixedPosition; 1900 } 1901 } 1902 } 1903} 1904 1905void IOAudioStream::clipOutputSamples(UInt32 firstSampleFrame, UInt32 numSampleFrames) 1906{ 1907 IOReturn result = kIOReturnSuccess; 1908 1909 //audioDebugIOLog(6, "IOAudioStream[%p]::clipOutputSamples(0x%lx, 0x%lx)\n", this, firstSampleFrame, numSampleFrames); 1910 //audioDebugIOLog(6, "c(%lx,%lx) %lx\n", firstSampleFrame, numSampleFrames, audioEngine->getCurrentSampleFrame()); 1911 1912 assert(direction == kIOAudioStreamDirectionOutput); 1913 assert(audioEngine); 1914 assert(reserved); 1915 1916 if (!mixBuffer || !sampleBuffer) { 1917 IOLog("IOAudioStream[%p]::clipOutputSamples(0x%lx, 0x%lx) - Internal Error: mixBuffer = %p - sampleBuffer = %p\n", this ,(long unsigned int) firstSampleFrame,(long unsigned int) numSampleFrames, mixBuffer, sampleBuffer); 1918 return; 1919 } 1920 1921/* 1922#ifdef DEBUG 1923 UInt32 currentSampleFrame = audioEngine->getCurrentSampleFrame(); 1924 1925 if (currentSampleFrame > firstSampleFrame) { 1926 if ((firstSampleFrame + numSampleFrames) > currentSampleFrame) { 1927 //audioDebugIOLog(6, "IOAudioStream[%p]::clipOutputSamples(0x%lx, 0x%lx) - too late for some samples - current position = 0x%lx.\n", this, firstSampleFrame, numSampleFrames, currentSampleFrame); 1928 audioDebugIOLog(6, "clip(%lx,%lx) missed curr=%lx.\n", firstSampleFrame, numSampleFrames, currentSampleFrame); 1929 } 1930 } else { 1931 if ((numSampleFrames + firstSampleFrame) > (currentSampleFrame + audioEngine->getNumSampleFramesPerBuffer())) { 1932 //audioDebugIOLog(6, "IOAudioStream[%p]::clipOutputSamples(0x%lx, 0x%lx) - too late for some samples - current position = 0x%lx.\n", this, firstSampleFrame, numSampleFrames, currentSampleFrame); 1933 audioDebugIOLog(6, "clip(%lx,%lx) missed curr=%lx.\n", firstSampleFrame, numSampleFrames, currentSampleFrame); 1934 } 1935 } 1936#endif 1937*/ 1938 1939 if (audioIOFunctions && (numIOFunctions != 0)) { 1940 UInt32 functionNum; 1941 1942 for (functionNum = 0; functionNum < numIOFunctions; functionNum++) { 1943 if (audioIOFunctions[functionNum]) { 1944 result = audioIOFunctions[functionNum](mixBuffer, sampleBuffer, firstSampleFrame, numSampleFrames, &format, this); 1945 if (result != kIOReturnSuccess) { 1946 break; 1947 } 1948 } 1949 } 1950 } else { 1951 result = audioEngine->clipOutputSamples(mixBuffer, sampleBuffer, firstSampleFrame, numSampleFrames, &format, this); 1952 } 1953 1954 if (result != kIOReturnSuccess) { 1955 IOLog("IOAudioStream[%p]::clipOutputSamples(0x%lx, 0x%lx) - clipping function returned error: 0x%x\n", this,(long unsigned int) firstSampleFrame,(long unsigned int) numSampleFrames, result); 1956 } 1957 reserved->mClipOutputStatus = result; 1958} 1959 1960void IOAudioStream::lockStreamForIO() 1961{ 1962 assert(streamIOLock); 1963 1964 IORecursiveLockLock(streamIOLock); 1965} 1966 1967void IOAudioStream::unlockStreamForIO() 1968{ 1969 assert(streamIOLock); 1970 1971 IORecursiveLockUnlock(streamIOLock); 1972} 1973 1974void IOAudioStream::setStreamAvailable(bool available) 1975{ 1976 if (streamAvailable != available) { 1977 streamAvailable = available; 1978 setProperty(kIOAudioStreamAvailableKey, available ? 1 : 0, sizeof(UInt8)*8); 1979 1980 assert(audioEngine); 1981 audioEngine->updateChannelNumbers(); 1982 } 1983} 1984 1985bool IOAudioStream::getStreamAvailable() 1986{ 1987 return streamAvailable; 1988} 1989 1990IOReturn IOAudioStream::addDefaultAudioControl(IOAudioControl *defaultAudioControl) 1991{ 1992 IOReturn result = kIOReturnBadArgument; 1993 1994 if (defaultAudioControl) { 1995 UInt32 controlChannelID; 1996 1997 if (defaultAudioControl->getChannelID() == kIOAudioControlChannelIDAll) { 1998 if (((getDirection() == kIOAudioStreamDirectionOutput) && (defaultAudioControl->getUsage() == kIOAudioControlUsageInput)) || 1999 ((getDirection() == kIOAudioStreamDirectionInput) && (defaultAudioControl->getUsage() == kIOAudioControlUsageOutput))) { 2000 result = kIOReturnError; 2001 IOLog("IOAudioStream[%p]::addDefaultAudioControl() - Error: invalid audio control - stream direction is opposite of control usage.\n", this); 2002 goto Done; 2003 } 2004 2005 controlChannelID = defaultAudioControl->getChannelID(); 2006 2007 if ((controlChannelID != 0) && ((controlChannelID < startingChannelID) || (controlChannelID >= (startingChannelID + maxNumChannels)))) { 2008 result = kIOReturnError; 2009 IOLog("IOAudioStream[%p]::addDefaultAudioControl() - Error: audio control channel is not in this stream.\n", this); 2010 goto Done; 2011 } 2012 2013 if (defaultAudioControl->attachAndStart(this)) { 2014 if (!defaultAudioControls) { 2015 defaultAudioControls = OSSet::withObjects((const OSObject **)&defaultAudioControl, 1, 1); 2016 } else { 2017 defaultAudioControls->setObject(defaultAudioControl); 2018 } 2019 } else { 2020 result = kIOReturnError; 2021 } 2022 } else { // Control for an individual channel - attach to audio engine instead 2023 assert(audioEngine); 2024 result = audioEngine->addDefaultAudioControl(defaultAudioControl); 2025 } 2026 } 2027 2028Done: 2029 2030 return result; 2031} 2032 2033void IOAudioStream::removeDefaultAudioControls() 2034{ 2035 if (defaultAudioControls) { 2036 if (!isInactive()) { 2037 OSCollectionIterator *controlIterator; 2038 2039 controlIterator = OSCollectionIterator::withCollection(defaultAudioControls); 2040 2041 if (controlIterator) { 2042 IOAudioControl *control; 2043 2044 while ( (control = (IOAudioControl *)controlIterator->getNextObject()) ) { 2045 if (control->getProvider() == this) { 2046 control->terminate(); 2047 } else { 2048 control->detach(this); 2049 } 2050 } 2051 2052 controlIterator->release(); 2053 } 2054 } 2055 2056 defaultAudioControls->flushCollection(); 2057 } 2058} 2059