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}