1/********************************************************************************************************************************** 2* 3* OpenAL cross platform audio library 4* Copyright (c) 2005, Apple Computer, Inc. All rights reserved. 5* 6* Redistribution and use in source and binary forms, with or without modification, are permitted provided 7* that the following conditions are met: 8* 9* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 11* disclaimer in the documentation and/or other materials provided with the distribution. 12* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its contributors may be used to endorse or promote 13* products derived from this software without specific prior written permission. 14* 15* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 17* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 18* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 19* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 20* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21* 22**********************************************************************************************************************************/ 23 24#include "OALCaptureDevice.h" 25 26#define LOG_CAPTURE 0 27 28// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 29// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 30#pragma mark ***** OALCaptureDevices ***** 31// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 33 34OALCaptureDevice::OALCaptureDevice (const char* inDeviceName, uintptr_t inSelfToken, UInt32 inSampleRate, UInt32 inFormat, UInt32 inBufferSize) 35 : 36#if LOG_CAPTUREDEVICE_VERBOSE 37 mSelfToken (inSelfToken), 38#endif 39 mCurrentError(ALC_NO_ERROR), 40 mCaptureOn(false), 41 mStoreSampleTime(0), 42 mFetchSampleTime(0), 43 mInputUnit(0), 44 mRingBuffer(NULL), 45 mBufferData(NULL), 46 mSampleRateRatio(1.0), 47 mRequestedRingFrames(inBufferSize), 48 mAudioInputPtrs(NULL), 49 mInUseFlag(0) 50{ 51 char *useThisDevice = (char *) inDeviceName; 52 53 try { 54 // translate the sample rate and data format parameters into an ASBD - this method throws 55 FillInASBD(mRequestedFormat, inFormat, inSampleRate); 56 57 // inBufferSize must be at least as big as one packet of the requested output formnat 58 if (inBufferSize < mRequestedFormat.mBytesPerPacket) 59 throw ((OSStatus) AL_INVALID_VALUE); 60 61 // until the ALC_ENUMERATION_EXT extension is supported only use the default input device 62 useThisDevice = NULL; 63 64 InitializeAU(useThisDevice); 65 66 if(mRequestedFormat.mSampleRate != mNativeFormat.mSampleRate) 67 { 68#if LOG_CAPTURE 69 DebugMessageN2("OALCaptureDevice::OALCaptureDevice - Hardware Sample Rate: %5.1f Requested Sample Rate: %5.1f", mNativeFormat.mSampleRate, mRequestedFormat.mSampleRate); 70#endif 71 mSampleRateRatio = mNativeFormat.mSampleRate / mRequestedFormat.mSampleRate; 72 mBufferData = (UInt8*)malloc(mRequestedFormat.mBytesPerFrame*mRequestedRingFrames*mSampleRateRatio); 73 OSStatus result = AudioConverterNew(&mOutputFormat, &mRequestedFormat, &mAudioConverter); 74 THROW_RESULT 75 } 76 77 mAudioInputPtrs = CABufferList::New("WriteBufferList", mRequestedFormat); 78 79 mRingBuffer = new OALRingBuffer(); 80 mRingBuffer->Allocate(mRequestedFormat.mBytesPerFrame, mRequestedRingFrames*mSampleRateRatio); 81 82 } 83 catch (OSStatus result) { 84 if (mRingBuffer) delete (mRingBuffer); 85 if (mBufferData) delete mBufferData; 86 if (mAudioInputPtrs) delete (mAudioInputPtrs); 87 throw result; 88 } 89 catch (...) { 90 if (mRingBuffer) delete (mRingBuffer); 91 if (mBufferData) delete mBufferData; 92 if (mAudioInputPtrs) delete (mAudioInputPtrs); 93 throw -1; 94 } 95} 96 97// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 98OALCaptureDevice::~OALCaptureDevice() 99{ 100#if LOG_CAPTUREDEVICE_VERBOSE 101 DebugMessageN1("OALCaptureDevice::~OALCaptureDevice() - OALCaptureDevice = %ld", (long int) mSelfToken); 102#endif 103 if (mInputUnit) 104 CloseComponent(mInputUnit); 105 if (mBufferData) 106 delete mBufferData; 107 delete mRingBuffer; 108 delete mAudioInputPtrs; 109} 110 111// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 112void OALCaptureDevice::SetError(ALenum errorCode) 113{ 114#if LOG_CAPTUREDEVICE_VERBOSE 115 DebugMessageN2("OALCaptureDevice::SetError() - OALCaptureDevice:errorCode = %ld:%d", (long int) mSelfToken, errorCode); 116#endif 117 if (mCurrentError == ALC_NO_ERROR) 118 return; 119 120 mCurrentError = errorCode; 121} 122 123// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 124ALenum OALCaptureDevice::GetError() 125{ 126#if LOG_CAPTUREDEVICE_VERBOSE 127 DebugMessageN1("OALCaptureDevice::~OALCaptureDevice() - OALCaptureDevice = %ld", (long int) mSelfToken); 128#endif 129 ALenum latestError = mCurrentError; 130 mCurrentError = ALC_NO_ERROR; 131 132 return latestError; 133} 134 135// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 136void OALCaptureDevice::InitializeAU(const char* inDeviceName) 137{ 138#if LOG_CAPTUREDEVICE_VERBOSE 139 DebugMessageN2("OALCaptureDevice::InitializeAU() - OALCaptureDevice = %ld:%s", (long int) mSelfToken, inDeviceName); 140#endif 141 // open input unit 142 OSStatus result = noErr; 143 Component comp; 144 ComponentDescription desc; 145 146 try { 147 desc.componentType = kAudioUnitType_Output; 148 desc.componentSubType = kAudioUnitSubType_HALOutput; 149 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 150 desc.componentFlags = 0; 151 desc.componentFlagsMask = 0; 152 comp = FindNextComponent(NULL, &desc); 153 if (comp == NULL) 154 throw -1; 155 156 result = OpenAComponent(comp, &mInputUnit); 157 THROW_RESULT 158 159 UInt32 enableIO; 160 UInt32 propSize; 161 162 // turn off output 163 enableIO = 0; 164 result = AudioUnitSetProperty(mInputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); 165 THROW_RESULT 166 167 // turn on input 168 enableIO = 1; 169 result = AudioUnitSetProperty(mInputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); 170 THROW_RESULT 171 172 // get the default input device 173 propSize = sizeof(AudioDeviceID); 174 AudioDeviceID inputDevice; 175 result = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propSize, &inputDevice); 176 THROW_RESULT 177 178 if (inputDevice == kAudioDeviceUnknown) 179 throw -1; // there is no input device 180 181 // track the default input device with our AUHal 182 result = AudioUnitSetProperty(mInputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(inputDevice)); 183 THROW_RESULT 184 185 // set render callback 186 AURenderCallbackStruct input; 187 input.inputProc = InputProc; 188 input.inputProcRefCon = this; 189 190 result = AudioUnitSetProperty(mInputUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(input)); 191 THROW_RESULT 192 193 result = AudioUnitInitialize(mInputUnit); 194 THROW_RESULT 195 196 // get the hardware format 197 propSize = sizeof(mNativeFormat); 198 result = AudioUnitGetProperty(mInputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &mNativeFormat, &propSize); 199 THROW_RESULT 200 201 mOutputFormat = mRequestedFormat; 202 mOutputFormat.mSampleRate = mNativeFormat.mSampleRate; 203 204 // the output format should be the requested format, but using the native hardware sample rate, i.e. the output format 205 result = AudioUnitSetProperty(mInputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&mOutputFormat, sizeof(mOutputFormat)); 206 THROW_RESULT 207 208 } 209 catch (OSStatus result) { 210 if (mInputUnit) CloseComponent(mInputUnit); 211 throw result; 212 } 213 catch (...) { 214 if (mInputUnit) CloseComponent(mInputUnit); 215 throw - 1; 216 } 217} 218 219// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 220OSStatus OALCaptureDevice::InputProc(void * inRefCon, 221 AudioUnitRenderActionFlags *ioActionFlags, 222 const AudioTimeStamp * inTimeStamp, 223 UInt32 inBusNumber, 224 UInt32 inNumberFrames, 225 AudioBufferList * ioData) 226{ 227#if LOG_CAPTUREDEVICE_VERBOSE 228 DebugMessage("OALCaptureDevice::InputProc() - OALCaptureDevice"); 229#endif 230 OALCaptureDevice *This = static_cast<OALCaptureDevice *>(inRefCon); 231 AudioUnitRenderActionFlags flags = 0; 232 233 AudioBufferList *abl = &This->mAudioInputPtrs->GetModifiableBufferList(); 234 for (UInt32 i = 0; i < abl->mNumberBuffers; ++i) 235 abl->mBuffers[i].mData = NULL; 236 237 OSStatus err = AudioUnitRender(This->mInputUnit, &flags, inTimeStamp, 1, inNumberFrames, abl); 238 if (err) 239 return err; 240 241 if(This->mRingBuffer->Store((const Byte*)abl->mBuffers[0].mData, inNumberFrames, This->mStoreSampleTime)) 242 This->mStoreSampleTime += inNumberFrames; 243 244 return noErr; 245} 246 247// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 248OSStatus OALCaptureDevice::ACComplexInputDataProc ( AudioConverterRef inAudioConverter, 249 UInt32 *ioNumberDataPackets, 250 AudioBufferList *ioData, 251 AudioStreamPacketDescription **outDataPacketDescription, 252 void* inUserData) 253{ 254 OALCaptureDevice* THIS = (OALCaptureDevice*)inUserData; 255 UInt32 nFramesRemaining; 256 257 ioData->mNumberBuffers = 1; 258 ioData->mBuffers[0].mNumberChannels = THIS->mRequestedFormat.NumberChannels(); 259 ioData->mBuffers[0].mData = THIS->mRingBuffer->GetFramePtr(THIS->mFetchSampleTime, nFramesRemaining); 260 261 *ioNumberDataPackets = ((*ioNumberDataPackets) > nFramesRemaining) ? nFramesRemaining : (*ioNumberDataPackets); 262 263 ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * THIS->mRequestedFormat.mBytesPerFrame; 264 265 if (nFramesRemaining == 0) 266 { 267#if LOG_CAPTURE 268 DebugMessage("OALCaptureDevice::ACComplexInputDataProc - buffer is empty" ); 269#endif 270 return -1; 271 } 272 273 THIS->mFetchSampleTime += *ioNumberDataPackets; 274 275 return noErr; 276} 277 278// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 279// PUBLIC METHODS 280// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 281void OALCaptureDevice::StartCapture() 282{ 283#if LOG_CAPTUREDEVICE_VERBOSE 284 DebugMessageN1("OALCaptureDevice::StartCapture() - OALCaptureDevice = %ld", (long int) mSelfToken); 285#endif 286 OSStatus result = AudioOutputUnitStart(mInputUnit); 287 THROW_RESULT 288 mCaptureOn = true; 289 mRingBuffer->Clear(); 290#if LOG_CAPTURE 291 DebugMessage("OALCaptureDevice::StartCapture"); 292#endif 293} 294 295// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 296void OALCaptureDevice::StopCapture() 297{ 298#if LOG_CAPTUREDEVICE_VERBOSE 299 DebugMessageN1("OALCaptureDevice::StopCapture() - OALCaptureDevice = %ld", (long int) mSelfToken); 300#endif 301 OSStatus result = AudioOutputUnitStop(mInputUnit); 302 THROW_RESULT 303 mCaptureOn = false; 304 mRingBuffer->Clear(); 305#if LOG_CAPTURE 306 DebugMessage("OALCaptureDevice::StopCapture"); 307#endif 308} 309 310// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 311OSStatus OALCaptureDevice::GetFrames(UInt32 inFrameCount, UInt8* inBuffer) 312{ 313#if LOG_CAPTUREDEVICE_VERBOSE 314 DebugMessageN1("OALCaptureDevice::GetFrames() - OALCaptureDevice = %ld", (long int) mSelfToken); 315#endif 316 OSStatus result = noErr; 317 318 if (!mCaptureOn) 319 throw ((OSStatus) AL_INVALID_OPERATION); // error condition, device not currently capturing 320 321 322#if LOG_CAPTURE 323 DebugMessageN1("OALCaptureDevice::GetFrames - requested frames: %ld", inFrameCount); 324#endif 325 326 if (inFrameCount > AvailableFrames()) 327 { 328#if LOG_CAPTURE 329 DebugMessage("OALCaptureDevice::GetFrames - Not enough frames available"); 330#endif 331 return -1; // error condition, there aren't enough valid frames to satisfy request 332 } 333 334 if (mSampleRateRatio != 1.0) 335 { 336 UInt32 theFramesToCopy = inFrameCount; 337 AudioBufferList abl; 338 abl.mNumberBuffers = 1; 339 abl.mBuffers[0].mNumberChannels = mRequestedFormat.NumberChannels(); 340 abl.mBuffers[0].mDataByteSize = theFramesToCopy * mRequestedFormat.mBytesPerFrame; 341 abl.mBuffers[0].mData = inBuffer; 342 343 result = AudioConverterFillComplexBuffer(mAudioConverter, ACComplexInputDataProc, this, &theFramesToCopy, &abl, NULL); 344 345 if (result) 346 { 347#if LOG_CAPTURE 348 DebugMessageN1("OALCaptureDevice::GetFrames - AudioConverterFillComplexBuffer Failed result = %ld", result); 349#endif 350 return result; 351 } 352 353 if (theFramesToCopy != inFrameCount) 354 { 355#if LOG_CAPTURE 356 DebugMessageN1("OALCaptureDevice::GetFrames - AudioConverterFillComplexBuffer returned invalid number of frames = %ld", theFramesToCopy); 357#endif 358 return -1; 359 } 360 } 361 362 else 363 { 364 result = mRingBuffer->Fetch((Byte*)inBuffer, inFrameCount, mFetchSampleTime); 365 366 if (result) 367 { 368#if LOG_CAPTURE 369 DebugMessageN1("OALCaptureDevice::GetFrames - mRingBuffer->Fetch Failed result = %ld", result); 370#endif 371 return result; 372 } 373 374 if (result == noErr) 375 mFetchSampleTime += inFrameCount; 376 } 377 378#if LOG_CAPTURE 379 DebugMessageN1("OALCaptureDevice::GetFrames - new mFetchSampleTime = %qd", mFetchSampleTime); 380#endif 381 382 return result; 383} 384 385// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 386UInt32 OALCaptureDevice::AvailableFrames() 387{ 388#if LOG_CAPTUREDEVICE_VERBOSE 389 DebugMessageN1("OALCaptureDevice::AvailableFrames() - OALCaptureDevice = %ld", (long int) mSelfToken); 390#endif 391 SInt64 start, end; 392 mRingBuffer->GetTimeBounds(start, end); 393 394 if (mFetchSampleTime < start) 395 mFetchSampleTime = start; // move up our fetch starting point, we have fallen too far behind 396 397 UInt32 availableFrames = end - mFetchSampleTime; 398 399 if (availableFrames > mRequestedRingFrames*mSampleRateRatio) 400 availableFrames = mRequestedRingFrames*mSampleRateRatio; 401 402#if LOG_CAPTURE 403 DebugMessageN2("OALCaptureDevice::AvailableFrames - buffer: %ld actual: %ld", availableFrames, (UInt32)(availableFrames / mSampleRateRatio)); 404#endif 405 return availableFrames / mSampleRateRatio; 406}