/*=============================================================================
	CAAudioFilePlayer.cpp
	
	$Log: CAAudioFilePlayer.cpp,v $
	Revision 1.3  2004/10/15 17:10:34  dwyatt
	CAAudioFile rewrite to use ExtAudioFile (changeable via compile flag in CAAudioFile.h)
	
	Revision 1.2  2004/09/30 21:06:04  jcm10
	add a fallback if the default output unit can't be found
	
	Revision 1.1  2004/01/14 00:08:09  dwyatt
	moved from Source/Tests/AudioFileUtility/Utility
	
	Revision 1.11  2003/12/04 00:59:02  dwyatt
	refactoring
	
	Revision 1.10  2003/10/15 00:27:30  dwyatt
	refactor on top of CABufferQueue
	
	Revision 1.9  2003/10/09 23:23:13  dwyatt
	refactor/cleanup to support CAAudioFileRecorder
	
	Revision 1.8  2003/09/11 00:48:31  dwyatt
	Stop/Start are now virtual overrides
	
	Revision 1.7  2003/08/23 04:52:57  dwyatt
	track change to CAAudioFile (layout)
	
	Revision 1.6  2003/08/04 23:44:12  dwyatt
	refactoring for subclassing
	
	Revision 1.5  2003/07/25 23:29:12  dwyatt
	use constant strings for operations in CAXException
	
	Revision 1.4  2003/07/10 17:53:42  dwyatt
	don't apply invalid layouts, don't throw if applying a layout fails
	
	Revision 1.3  2003/07/09 19:13:28  dwyatt
	sync with base class changes
	
	Revision 1.2  2003/06/30 21:51:13  dwyatt
	trap exceptions in SeekToPacket
	
	Revision 1.1  2003/06/23 23:07:37  dwyatt
	initial checkin
	
	created Thu Jun 19 2003, Doug Wyatt
	Copyright (c) 2003 Apple Computer, Inc.  All Rights Reserved

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

#include "CAAudioFilePlayer.h"
#include "CAXException.h"

CAAudioFilePlayer::CAAudioFilePlayer(int nBuffers, UInt32 bufferSizeBytes) :
	CAAudioFileReader(nBuffers, bufferSizeBytes),
	mOutputUnit(NULL)
{
	// open output unit
	Component comp;
	ComponentDescription desc;
	
	desc.componentType = kAudioUnitType_Output;
	desc.componentSubType = kAudioUnitSubType_DefaultOutput;
	desc.componentManufacturer = kAudioUnitManufacturer_Apple;
	desc.componentFlags = 0;
	desc.componentFlagsMask = 0;
	comp = FindNextComponent(NULL, &desc);
	
	//	if we can't find the default one, look explicitly for AUHAL
	if(comp == NULL)
	{
		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 output unit");
	XThrowIfError(OpenAComponent(comp, &mOutputUnit), "open audio output unit");
	XThrowIfError(AudioUnitInitialize(mOutputUnit), "initialize audio output unit");
}

CAAudioFilePlayer::~CAAudioFilePlayer()
{
	if (mOutputUnit)
		CloseComponent(mOutputUnit);
}

void	CAAudioFilePlayer::SetFile(const FSRef &inFile)
{
	Stop();
	CAAudioFileReader::SetFile(inFile);
	SetupChannelMapping();
}

void	CAAudioFilePlayer::SetupChannelMapping()
{
	// set render callback
	AURenderCallbackStruct input;
	input.inputProc = InputProc;
	input.inputProcRefCon = this;
	XThrowIfError(AudioUnitSetProperty(
								mOutputUnit, 
								kAudioUnitProperty_SetRenderCallback, 
								kAudioUnitScope_Global,
								0,
								&input, 
								sizeof(input)), "connect input proc to output unit");

	const CAStreamBasicDescription &fmt = GetFile().GetClientDataFormat();
	XThrowIfError(AudioUnitSetProperty(
							mOutputUnit,
							kAudioUnitProperty_StreamFormat,
							kAudioUnitScope_Input,
							0,
							(void *)&fmt,
							sizeof(AudioStreamBasicDescription)), "set audio output format");
	const CAAudioChannelLayout &layoutObj = GetFile().GetFileChannelLayout();
	if (layoutObj.IsValid()) {
		const AudioChannelLayout *layout = layoutObj;
		/*OSStatus err =*/ AudioUnitSetProperty(
								mOutputUnit,
								kAudioUnitProperty_AudioChannelLayout,
								kAudioUnitScope_Input,
								0,
								layout,
								layoutObj.Size());
		// $$$ FIXME: why does this fail sometimes?
	}
}

void	CAAudioFilePlayer::Start()
{
	if (IsRunning())
		Stop();
	if (ReachedEndOfStream())
		SetCurrentPosition(0.);
	CAAudioFileReader::Start();
	XThrowIfError(AudioOutputUnitStart(mOutputUnit), "start audio output unit");
}

void	CAAudioFilePlayer::Stop()
{
	if (IsRunning()) {
		XThrowIfError(AudioOutputUnitStop(mOutputUnit), "stop audio output unit");
		CAAudioFileReader::Stop();
	}
}

void	CAAudioFilePlayer::SetVolume(double volume)
{
	AudioUnitSetParameter(mOutputUnit, kHALOutputParam_Volume,
		kAudioUnitScope_Global, 0, volume, 0);
}

OSStatus	CAAudioFilePlayer::GetPlayBuffer(
						UInt32						inNumberFrames,
						AudioBufferList *			ioData)
{
	UInt32 nFrames = inNumberFrames;
	PullBuffer(nFrames, ioData);
//	printf("read %ld frames\n", nFrames);
	if (nFrames < inNumberFrames) {
		AudioBuffer *buf = ioData->mBuffers;
		UInt32 bytesPerFrame = buf->mNumberChannels * sizeof(Float32);
		for (UInt32 i = 0; i < ioData->mNumberBuffers; ++i, ++buf) {
			memset((Byte *)buf->mData + nFrames * bytesPerFrame, 0,
				(inNumberFrames - nFrames) * bytesPerFrame);
		}
	}
	return noErr;
}

OSStatus	CAAudioFilePlayer::InputProc(
						void *						inRefCon,
						AudioUnitRenderActionFlags *ioActionFlags,
						const AudioTimeStamp *		inTimeStamp,
						UInt32 						inBusNumber,
						UInt32 						inNumberFrames,
						AudioBufferList *			ioData)
{
	CAAudioFilePlayer *This = static_cast<CAAudioFilePlayer *>(inRefCon);
	return This->GetPlayBuffer(inNumberFrames, ioData);
}


