![]() |
pl-nk v0.4.5
Plonk|Plink|Plank are a set of cross-platform C/C++ frameworks for audio software development
|
00001 /* 00002 ------------------------------------------------------------------------------- 00003 This file is part of the Plink, Plonk, Plank libraries 00004 by Martin Robinson 00005 00006 http://code.google.com/p/pl-nk/ 00007 00008 Copyright University of the West of England, Bristol 2011-14 00009 All rights reserved. 00010 00011 Redistribution and use in source and binary forms, with or without 00012 modification, are permitted provided that the following conditions are met: 00013 00014 * Redistributions of source code must retain the above copyright 00015 notice, this list of conditions and the following disclaimer. 00016 * Redistributions in binary form must reproduce the above copyright 00017 notice, this list of conditions and the following disclaimer in the 00018 documentation and/or other materials provided with the distribution. 00019 * Neither the name of University of the West of England, Bristol nor 00020 the names of its contributors may be used to endorse or promote products 00021 derived from this software without specific prior written permission. 00022 00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 00024 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00025 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00026 DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF THE WEST OF ENGLAND, BRISTOL BE 00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 00034 This software makes use of third party libraries. For more information see: 00035 doc/license.txt included in the distribution. 00036 ------------------------------------------------------------------------------- 00037 */ 00038 00039 #if PLANK_INLINING_FUNCTIONS 00040 00041 BEGIN_PLONK_NAMESPACE 00042 00045 // #pragma clang diagnostic push 00046 // #pragma clang diagnostic ignored "-Wno-deprecated" 00047 // 00048 #pragma GCC diagnostic push 00049 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 00050 // 00053 00054 template<class SampleType> 00055 static OSStatus Render (void *inRefCon, 00056 AudioUnitRenderActionFlags *ioActionFlags, 00057 const AudioTimeStamp *inTimeStamp, 00058 UInt32 inBusNumber, 00059 UInt32 inNumberFrames, 00060 AudioBufferList *ioData) 00061 { 00062 IOSAudioHostBase<SampleType> *host = (IOSAudioHostBase<SampleType> *)inRefCon; 00063 const OSStatus result = host->renderCallback (inNumberFrames, 00064 ioActionFlags, 00065 inTimeStamp, 00066 ioData); 00067 return result; 00068 } 00069 00070 template<class SampleType> 00071 static void PropertyListener (void * inClientData, 00072 AudioSessionPropertyID inID, 00073 UInt32 inDataSize, 00074 const void * inPropertyValue) 00075 { 00076 IOSAudioHostBase<SampleType> *host = (IOSAudioHostBase<SampleType> *)inClientData; 00077 host->propertyCallback (inID, inDataSize, inPropertyValue); 00078 } 00079 00080 template<class SampleType> 00081 static void InterruptionListener (void *inClientData, 00082 UInt32 inInterruption) 00083 { 00084 IOSAudioHostBase<SampleType> *host = (IOSAudioHostBase<SampleType> *)inClientData; 00085 host->interruptionCallback (inInterruption); 00086 } 00087 00088 00089 template<class SrcType> 00090 static inline void audioTypeToShort (const SrcType * const src, short* const dst, const unsigned int length) throw() 00091 { 00092 NumericalArrayConverter<short,SrcType>::convertScaled (dst, src, length); 00093 } 00094 00095 template<class SrcType> 00096 static inline void audioTypeToShortChannels (SrcType * const src[], AudioBufferList* const dst, const unsigned int length, const unsigned int numChannels) throw() 00097 { 00098 for (UInt32 channel = 0; channel < numChannels; ++channel) 00099 { 00100 AudioBuffer* const audioBuffer = dst->mBuffers + channel; 00101 AudioSampleType* const audioUnitSamples = (AudioSampleType*)audioBuffer->mData; 00102 audioTypeToShort (src[channel], audioUnitSamples, length); 00103 } 00104 } 00105 00106 template<class DstType> 00107 static inline void audioShortToType (const short * const src, DstType* const dst, const unsigned int length) throw() 00108 { 00109 NumericalArrayConverter<DstType,short>::convertScaled (dst, src, length); 00110 } 00111 00112 template<class DstType> 00113 static inline void audioShortToTypeChannels (AudioBufferList* src, DstType* const dst[], const unsigned int length, const unsigned int numChannels) throw() 00114 { 00115 for (UInt32 channel = 0; channel < numChannels; ++channel) 00116 { 00117 AudioSampleType* const audioUnitSamples = (AudioSampleType*)src->mBuffers[0].mData; // need this other than 0?... 00118 audioShortToType (audioUnitSamples, dst[channel], length); 00119 } 00120 } 00121 00122 //------------------------------------------------------------------------------ 00123 00124 template<class SampleType> 00125 IOSAudioHostBase<SampleType>::IOSAudioHostBase() throw() 00126 : hwSampleRate (0.0), // let the hardware choose 00127 rioUnit (NULL), 00128 cpuUsage (0.0), 00129 interrupted (NO), 00130 customRenderCallbackRef (NULL), 00131 customPreRender (NULL), 00132 customPostRender (NULL) 00133 { 00134 this->setPreferredGraphBlockSize (256); 00135 } 00136 00137 template<class SampleType> 00138 IOSAudioHostBase<SampleType>::~IOSAudioHostBase() 00139 { 00140 this->stopHost(); 00141 this->killRemoteIO(); 00142 } 00143 00144 template<class SampleType> 00145 Text IOSAudioHostBase<SampleType>::getHostName() const throw() 00146 { 00147 return "iOS (" + TypeUtility<SampleType>::getTypeName() + ")"; 00148 } 00149 00150 template<class SampleType> 00151 Text IOSAudioHostBase<SampleType>::getNativeHostName() const throw() 00152 { 00153 return "RemoteIO (Short)"; 00154 } 00155 00156 template<class SampleType> 00157 Text IOSAudioHostBase<SampleType>::getInputName() const throw() 00158 { 00159 Text result = "Default Input"; 00160 00161 CFDictionaryRef dictionary = 0; 00162 UInt32 propertySize = sizeof (dictionary); 00163 00164 if (AudioSessionGetProperty (kAudioSessionProperty_AudioRouteDescription, &propertySize, &dictionary) == noErr) 00165 { 00166 CFArrayRef array = (CFArrayRef)CFDictionaryGetValue (dictionary, kAudioSession_AudioRouteKey_Inputs); 00167 00168 if (array != nil) 00169 { 00170 for (int i = 0; i < CFArrayGetCount (array); ++i) 00171 { 00172 CFDictionaryRef dictionary2 = (CFDictionaryRef)CFArrayGetValueAtIndex (array, i); 00173 NSString* name = (NSString*)CFDictionaryGetValue (dictionary2, kAudioSession_AudioRouteKey_Type); 00174 result = [name UTF8String]; 00175 } 00176 } 00177 00178 CFRelease (dictionary); 00179 } 00180 00181 return result; 00182 } 00183 00184 template<class SampleType> 00185 Text IOSAudioHostBase<SampleType>::getOutputName() const throw() 00186 { 00187 Text result = "Default Output"; 00188 00189 CFDictionaryRef dictionary = 0; 00190 UInt32 propertySize = sizeof (dictionary); 00191 00192 if (AudioSessionGetProperty (kAudioSessionProperty_AudioRouteDescription, &propertySize, &dictionary) == noErr) 00193 { 00194 CFArrayRef array = (CFArrayRef)CFDictionaryGetValue (dictionary, kAudioSession_AudioRouteKey_Outputs); 00195 00196 if (array != nil) 00197 { 00198 for (int i = 0; i < CFArrayGetCount (array); ++i) 00199 { 00200 CFDictionaryRef dictionary2 = (CFDictionaryRef)CFArrayGetValueAtIndex (array, i); 00201 NSString* name = (NSString*)CFDictionaryGetValue (dictionary2, kAudioSession_AudioRouteKey_Type); 00202 result = [name UTF8String]; 00203 } 00204 } 00205 00206 CFRelease (dictionary); 00207 } 00208 00209 return result; 00210 } 00211 00212 template<class SampleType> 00213 void IOSAudioHostBase<SampleType>::stopHost() throw() 00214 { 00215 if (this->getIsRunning()) 00216 { 00217 AudioOutputUnitStop (rioUnit); 00218 this->setIsRunning (false); 00219 this->hostStopped(); 00220 } 00221 } 00222 00223 template<class SampleType> 00224 void IOSAudioHostBase<SampleType>::startHost() throw() 00225 { 00226 if (rioUnit != NULL) 00227 { 00228 this->setIsRunning (true); 00229 AudioSessionSetActive (true); 00230 this->hostStarting(); 00231 restart(); 00232 } 00233 else 00234 { 00235 UInt32 size; 00236 00237 // render proc 00238 inputProc.inputProc = Render<SampleType>; 00239 inputProc.inputProcRefCon = this; 00240 00241 // session 00242 AudioSessionInitialize (NULL, NULL, InterruptionListener<SampleType>, this); 00243 AudioSessionSetActive (true); 00244 00245 AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, PropertyListener<SampleType>, this); 00246 AudioSessionAddPropertyListener (kAudioSessionProperty_InterruptionType, PropertyListener<SampleType>, this); 00247 AudioSessionAddPropertyListener (kAudioSessionProperty_AudioCategory, PropertyListener<SampleType>, this); 00248 00249 fixAudioRouteIfSetToReceiver(); 00250 00251 const double preferredHostSampleRate = this->getPreferredHostSampleRate(); 00252 size = sizeof (preferredHostSampleRate); 00253 00254 if (preferredHostSampleRate > 0.0) 00255 AudioSessionSetProperty (kAudioSessionProperty_PreferredHardwareSampleRate, size, &preferredHostSampleRate); 00256 00257 resetIO(); 00258 00259 this->setPreferredHostSampleRate (hwSampleRate); 00260 this->setPreferredHostBlockSize (bufferSize); 00261 this->startHostInternal(); 00262 00263 restart(); 00264 00265 size = sizeof (format); 00266 AudioUnitGetProperty (rioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &size); 00267 } 00268 } 00269 00270 template<class SampleType> 00271 void IOSAudioHostBase<SampleType>::pauseHost() throw() 00272 { 00273 if (this->getIsRunning()) 00274 { 00275 AudioOutputUnitStop (rioUnit); 00276 } 00277 00278 this->setIsPaused (true); 00279 this->hostPaused(); 00280 } 00281 00282 00283 template<class SampleType> 00284 void IOSAudioHostBase<SampleType>::resumeHost() throw() 00285 { 00286 this->hostResuming(); 00287 this->setIsPaused (false); 00288 00289 if (this->getIsRunning()) 00290 { 00291 AudioSessionSetActive (true); 00292 AudioOutputUnitStart (rioUnit); 00293 } 00294 } 00295 00296 template<class SampleType> 00297 void IOSAudioHostBase<SampleType>::setFormat() throw() 00298 { 00299 memset (&format, 0, sizeof (AudioStreamBasicDescription)); 00300 format.mSampleRate = hwSampleRate; 00301 format.mFormatID = kAudioFormatLinearPCM; 00302 const int sampleSize = sizeof (AudioSampleType); 00303 format.mFormatFlags = kAudioFormatFlagsCanonical; 00304 format.mBitsPerChannel = 8 * sampleSize; 00305 format.mChannelsPerFrame = this->getNumOutputs(); 00306 format.mFramesPerPacket = 1; 00307 format.mBytesPerPacket = format.mBytesPerFrame = sampleSize; 00308 format.mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 00309 } 00310 00311 template<class SampleType> 00312 int IOSAudioHostBase<SampleType>::setupRemoteIO() throw() 00313 { 00314 // Open the output unit 00315 AudioComponentDescription desc; 00316 desc.componentType = kAudioUnitType_Output; 00317 desc.componentSubType = kAudioUnitSubType_RemoteIO; 00318 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 00319 desc.componentFlags = 0; 00320 desc.componentFlagsMask = 0; 00321 00322 AudioComponent comp = AudioComponentFindNext (NULL, &desc); 00323 AudioComponentInstanceNew (comp, &rioUnit); 00324 00325 OSStatus status; 00326 const UInt32 one = 1; 00327 00328 status = AudioUnitSetProperty (rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &one, sizeof (one)); 00329 status = AudioUnitSetProperty (rioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format)); 00330 00331 if (this->getNumInputs() > 0) 00332 { 00333 status = AudioUnitSetProperty (rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof (one)); 00334 status = AudioUnitSetProperty (rioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &format, sizeof (format)); 00335 } 00336 00337 status = AudioUnitSetProperty (rioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &inputProc, sizeof (inputProc)); 00338 00339 AudioUnitInitialize (rioUnit); 00340 00341 // NSLog(@"setupRemoteIO complete"); 00342 00343 return 0; 00344 } 00345 00346 template<class SampleType> 00347 void IOSAudioHostBase<SampleType>::killRemoteIO() throw() 00348 { 00349 if (rioUnit) 00350 { 00351 AudioOutputUnitStop (rioUnit); 00352 AudioComponentInstanceDispose (rioUnit); 00353 rioUnit = NULL; 00354 } 00355 } 00356 00357 template<class SampleType> 00358 void IOSAudioHostBase<SampleType>::restart() throw() 00359 { 00360 resetIO(); 00361 00362 this->setPreferredHostSampleRate (hwSampleRate); 00363 this->setPreferredHostBlockSize (bufferSize); 00364 00365 if (!rioUnit) 00366 { 00367 setFormat(); 00368 setupRemoteIO(); 00369 } 00370 00371 AudioOutputUnitStart (rioUnit); 00372 } 00373 00374 template<class SampleType> 00375 void IOSAudioHostBase<SampleType>::fixAudioRouteIfSetToReceiver() throw() 00376 { 00377 CFStringRef audioRoute = 0; 00378 UInt32 propertySize = sizeof (audioRoute); 00379 00380 if (AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &propertySize, &audioRoute) == noErr) 00381 { 00382 NSString* route = (PLANK_OBJC_BRIDGE NSString*)audioRoute; 00383 00384 if ([route hasPrefix: @"Receiver"]) 00385 { 00386 UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; 00387 AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof (audioRouteOverride), &audioRouteOverride); 00388 } 00389 00390 CFRelease (audioRoute); 00391 } 00392 } 00393 00394 template<class SampleType> 00395 OSStatus IOSAudioHostBase<SampleType>::renderCallback (UInt32 inNumberFrames, 00396 AudioUnitRenderActionFlags *ioActionFlags, 00397 const AudioTimeStamp *inTimeStamp, 00398 AudioBufferList *ioData) throw() 00399 { 00400 OSStatus err = 0; 00401 int i; 00402 00403 double renderTime = CFAbsoluteTimeGetCurrent(); 00404 00405 if (inNumberFrames > bufferSize) 00406 { 00407 bufferSize = inNumberFrames; 00408 convertBuffer.setSize (inNumberFrames * plonk::max (this->getNumInputs(), 00409 this->getNumOutputs()), 00410 false); 00411 } 00412 00413 this->setPreferredHostBlockSize (inNumberFrames); 00414 00415 SampleType *convertBufferData[2]; 00416 convertBufferData[0] = convertBuffer.getArray(); 00417 convertBufferData[1] = convertBufferData[0] + inNumberFrames; 00418 00419 ConstBufferArray& inputs = this->getInputs(); 00420 BufferArray& outputs = this->getOutputs(); 00421 00422 if (audioInputIsAvailable && (inputs.length() > 0)) 00423 { 00424 err = AudioUnitRender (rioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData); 00425 00426 if (err == noErr) 00427 audioShortToTypeChannels (ioData, convertBufferData, inNumberFrames, numInputChannels); 00428 } 00429 else convertBuffer.clear(); 00430 00431 for (i = 0; i < inputs.length(); ++i) 00432 inputs.atUnchecked (i) = convertBufferData[i]; 00433 00434 for (i = 0; i < outputs.length(); ++i) 00435 outputs.atUnchecked (i) = convertBufferData[i]; 00436 00437 if (customPreRender) 00438 customPreRender (customRenderCallbackRef, inNumberFrames, ioActionFlags, inTimeStamp, ioData); 00439 00440 this->process(); 00441 00442 audioTypeToShortChannels (convertBufferData, ioData, inNumberFrames, ioData->mNumberBuffers); 00443 00444 if (customPostRender) 00445 customPostRender (customRenderCallbackRef, inNumberFrames, ioActionFlags, inTimeStamp, ioData); 00446 00447 renderTime = CFAbsoluteTimeGetCurrent() - renderTime; 00448 00449 const double timeRatio = renderTime * reciprocalBufferDuration; 00450 cpuUsage += 0.2 * (timeRatio - cpuUsage); 00451 00452 return err; 00453 } 00454 00455 template<class SampleType> 00456 void IOSAudioHostBase<SampleType>::propertyCallback (AudioSessionPropertyID inID, 00457 UInt32 inDataSize, 00458 const void * inPropertyValue) throw() 00459 { 00460 00461 if (inID == kAudioSessionProperty_AudioRouteChange) 00462 { 00463 if (inPropertyValue) 00464 { 00465 CFDictionaryRef routeChangeDictionary = (CFDictionaryRef)inPropertyValue; 00466 CFNumberRef routeChangeReasonRef = 00467 (CFNumberRef)CFDictionaryGetValue (routeChangeDictionary, CFSTR (kAudioSession_AudioRouteChangeKey_Reason)); 00468 00469 SInt32 routeChangeReason; 00470 CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason); 00471 00472 static const char* reasons[] = { 00473 "Unknown", 00474 "NewDeviceAvailable", "OldDeviceUnavailable", "CategoryChange", "Override", 00475 "(Invalid)", 00476 "WakeFromSleep", "NoSuitableRouteForCategory" 00477 }; 00478 00479 CFStringRef newAudioRoute; 00480 UInt32 propertySize = sizeof (CFStringRef); 00481 AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &propertySize, &newAudioRoute); 00482 NSLog (@"route: %s reason = '%s' (%d)\n", 00483 CFStringGetCStringPtr (newAudioRoute,CFStringGetSystemEncoding()), 00484 reasons[routeChangeReason], (int)routeChangeReason); 00485 CFRelease(newAudioRoute); 00486 00487 this->fixAudioRouteIfSetToReceiver(); 00488 } 00489 } 00490 else if (inID == kAudioSessionProperty_AudioCategory) 00491 { 00492 resetIO(); 00493 } 00494 } 00495 00496 template<class SampleType> 00497 void IOSAudioHostBase<SampleType>::resetIO() throw() 00498 { 00499 OSStatus err; 00500 UInt32 size; 00501 00502 size = sizeof (hwSampleRate); 00503 AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareSampleRate, &size, &hwSampleRate); 00504 00505 if (this->getPreferredHostBlockSize() > 0) 00506 { 00507 bufferDuration = (double (this->getPreferredHostBlockSize()) + 0.5) / hwSampleRate; 00508 AudioSessionSetProperty (kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof (bufferDuration), &bufferDuration); 00509 } 00510 00511 size = sizeof(UInt32); 00512 err = AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareInputNumberChannels, &size, &numInputChannels); 00513 00514 if (err != noErr) 00515 numInputChannels = 0; 00516 00517 err = AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareOutputNumberChannels, &size, &numOutputChannels); 00518 err = AudioSessionGetProperty (kAudioSessionProperty_AudioInputAvailable, &size, &audioInputIsAvailable); 00519 00520 if (audioInputIsAvailable && this->getNumInputs() > 0) 00521 this->setNumInputs (numInputChannels); 00522 00523 this->setNumOutputs (numOutputChannels); 00524 00525 size = sizeof (bufferDuration); 00526 AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, &size, &bufferDuration); 00527 00528 reciprocalBufferDuration = 1.0 / double (bufferDuration); 00529 00530 bufferSize = (int)(hwSampleRate * bufferDuration + 0.5); 00531 convertBuffer.setSize (bufferSize * plonk::max (this->getNumOutputs(), 00532 this->getNumInputs()), 00533 false); 00534 } 00535 00536 template<class SampleType> 00537 void IOSAudioHostBase<SampleType>::interruptionCallback (UInt32 inInterruption) throw() 00538 { 00539 NSLog (@"...Interruption..."); 00540 00541 if (inInterruption == kAudioSessionBeginInterruption) 00542 { 00543 interrupted = true; 00544 this->hostInterruption (true); 00545 this->stopHost(); 00546 00547 NSLog (@"Interruption %s", inInterruption ? "begin" : "end"); 00548 } 00549 else if (inInterruption == kAudioSessionEndInterruption) 00550 { 00551 this->hostInterruption (false); 00552 this->startHost(); 00553 interrupted = false; 00554 00555 NSLog (@"Interruption %s", inInterruption ? "begin" : "end"); 00556 } 00557 } 00558 00559 template<class SampleType> 00560 void IOSAudioHostBase<SampleType>::setCustomRenderCallbacks (void* refCon, 00561 RenderCallbackFunction preRender, 00562 RenderCallbackFunction postRender) throw() 00563 { 00564 customRenderCallbackRef = refCon; 00565 customPreRender = preRender; 00566 customPostRender = postRender; 00567 } 00568 00569 //#ifdef __llvm__ 00570 // #ifdef __clang__ 00571 // #pragma clang diagnostic pop 00572 // #endif 00573 //#endif 00574 00575 #pragma GCC diagnostic pop 00576 00577 00578 END_PLONK_NAMESPACE 00579 00580 #endif 00581 00582 #undef kOutputBus 00583 #undef kInputBus