/*=============================================================================
	CAAudioFileRecorder.cpp
	
	$Log: CAAudioFileRecorder.cpp,v $
	Revision 1.5  2004/10/15 17:10:34  dwyatt
	CAAudioFile rewrite to use ExtAudioFile (changeable via compile flag in CAAudioFile.h)
	
	Revision 1.4  2004/05/27 19:51:54  dwyatt
	reinstate SetFile methods which allowed wrapping an existing CAAudioFile
	
	Revision 1.3  2004/05/26 00:36:02  dwyatt
	track change to CABufferQueue; writer/recorder now create their own CAAudioFile's from scratch instead of wrapping the client's [3666988]
	
	Revision 1.2  2004/01/27 01:42:42  dwyatt
	remove cruft
	
	Revision 1.1  2004/01/14 00:08:09  dwyatt
	moved from Source/Tests/AudioFileUtility/Utility
	
	Revision 1.3  2003/10/21 23:39:31  dwyatt
	fix order of input unit initialization, use default input device
	
	Revision 1.2  2003/10/15 00:27:30  dwyatt
	refactor on top of CABufferQueue
	
	Revision 1.1  2003/10/09 23:19:37  dwyatt
	initial checkin
	
	created Tue Sep 30 2003, Doug Wyatt
	Copyright (c) 2003 Apple Computer, Inc.  All Rights Reserved

	$NoKeywords: $
=============================================================================*/

#include "CAAudioFileRecorder.h"
#include "CAXException.h"

CAAudioFileRecorder::CAAudioFileRecorder(int nBuffers, UInt32 bufferSizeFrames) :
	CAAudioFileWriter(nBuffers, bufferSizeFrames),
	mInputUnit(NULL),
	mAudioInputPtrs(NULL)
{
	// open input unit
	Component comp;
	ComponentDescription desc;
	
	desc.componentType = kAudioUnitType_Output;
	desc.componentSubType = kAudioUnitSubType_HALOutput;
	desc.componentManufacturer = kAudioUnitManufacturer_Apple;
	desc.componentFlags = 0;
	desc.componentFlagsMask = 0;
	comp = FindNextComponent(NULL, &desc);
	XThrowIf(comp == NULL, -1, "find audio input unit");
	XThrowIfError(OpenAComponent(comp, &mInputUnit), "open audio input unit");

	UInt32 enableIO;
	UInt32 propSize;
	
	enableIO = 0;
	XThrowIfError(AudioUnitSetProperty(mInputUnit,
		kAudioOutputUnitProperty_EnableIO,
		kAudioUnitScope_Output,
		0,
		&enableIO,
		sizeof(enableIO)), "failed to disable output");

	enableIO = 1;
	XThrowIfError(AudioUnitSetProperty(mInputUnit,
		kAudioOutputUnitProperty_EnableIO,
		kAudioUnitScope_Input,
		1,
		&enableIO,
		sizeof(enableIO)), "failed to enable input");
	
	// select the default input device
	propSize = sizeof(AudioDeviceID);
	AudioDeviceID inputDevice;
	XThrowIfError(
		AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propSize, &inputDevice),
		"failed to get default input device");
	
	XThrowIfError(
		AudioUnitSetProperty(mInputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(inputDevice)),
		"failed to select input device");
	
	// set render callback
	AURenderCallbackStruct input;
	input.inputProc = InputProc;
	input.inputProcRefCon = this;
	XThrowIfError(AudioUnitSetProperty(
								mInputUnit, 
								kAudioOutputUnitProperty_SetInputCallback, 
								kAudioUnitScope_Global,
								0,
								&input, 
								sizeof(input)), "connect input proc to output unit");

	XThrowIfError(AudioUnitInitialize(mInputUnit), "initialize audio input unit");
	
	// get the hardware format
	propSize = sizeof(mInputDataFormat);
	XThrowIfError(AudioUnitGetProperty(mInputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &mInputDataFormat, &propSize), "couldn't get input unit's stream format");
}

CAAudioFileRecorder::~CAAudioFileRecorder()
{
	if (mInputUnit)
		CloseComponent(mInputUnit);
	delete mAudioInputPtrs;
}

void	CAAudioFileRecorder::SetFile(const FSRef &parentFSRef, CFStringRef filename, AudioFileTypeID filetype, const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout)
{
	delete mAudioInputPtrs; mAudioInputPtrs = NULL;
	
	CAStreamBasicDescription fileDataFormat = dataFormat;
	if (fileDataFormat.mSampleRate == 0.)
		fileDataFormat.mSampleRate = mInputDataFormat.mSampleRate;
	
	CAAudioFileWriter::SetFile(parentFSRef, filename, filetype, fileDataFormat, layout);

	const CAStreamBasicDescription &fmt = GetFile().GetClientDataFormat();
	XThrowIfError(AudioUnitSetProperty(
							mInputUnit,
							kAudioUnitProperty_StreamFormat,
							kAudioUnitScope_Output,
							1,
							(void *)&fmt,
							sizeof(AudioStreamBasicDescription)), "set audio input format");

	GetFile().SetIOBufferSizeBytes(GetBufferSizeFrames() * fmt.mBytesPerFrame);

	mAudioInputPtrs = CABufferList::New("audio input ptrs", fmt);
}

/*
void	CAAudioFileRecorder::SetFile(AudioFileID fileID)
{
	delete mAudioInputPtrs; mAudioInputPtrs = NULL;
	
	CAAudioFileWriter::SetFile(fileID, mInputDataFormat.mSampleRate);

	const CAStreamBasicDescription &fmt = GetFile().GetClientDataFormat();
	XThrowIfError(AudioUnitSetProperty(
							mInputUnit,
							kAudioUnitProperty_StreamFormat,
							kAudioUnitScope_Output,
							1,
							(void *)&fmt,
							sizeof(AudioStreamBasicDescription)), "set audio input format");

	GetFile().SetIOBufferSize(GetBufferSizeFrames() * fmt.mBytesPerFrame);

	mAudioInputPtrs = CABufferList::New("audio input ptrs", fmt);
}
*/
	
void	CAAudioFileRecorder::Start()
{
	CAAudioFileWriter::Start();
	XThrowIfError(AudioOutputUnitStart(mInputUnit), "start audio input unit");
}

void	CAAudioFileRecorder::Stop()
{
	XThrowIfError(AudioOutputUnitStop(mInputUnit), "stop audio input unit");
	CAAudioFileWriter::Stop();
}

OSStatus	CAAudioFileRecorder::InputProc(
							void *						inRefCon,
							AudioUnitRenderActionFlags *ioActionFlags,
							const AudioTimeStamp *		inTimeStamp,
							UInt32 						inBusNumber,
							UInt32 						inNumberFrames,
							AudioBufferList *			ioData)
{
	CAAudioFileRecorder *This = static_cast<CAAudioFileRecorder *>(inRefCon);
	AudioUnitRenderActionFlags flags = 0;
	AudioBufferList *abl = &This->mAudioInputPtrs->GetModifiableBufferList();
	OSStatus err = AudioUnitRender(This->mInputUnit, &flags, inTimeStamp, 1, inNumberFrames, abl);
	
	if (err)
		return err;
	
	This->PushBuffer(inNumberFrames, abl);
	return noErr;
}
