/*=============================================================================
	CAAudioFileStreamer.cpp
	
	$Log: CAAudioFileStreamer.cpp,v $
	Revision 1.9  2004/11/18 00:25:34  dwyatt
	fix GetCurrentFrame so afplay can be told to play portions of a file
	
	Revision 1.8  2004/10/25 20:05:07  dwyatt
	remove printf
	
	Revision 1.7  2004/10/15 17:10:34  dwyatt
	CAAudioFile rewrite to use ExtAudioFile (changeable via compile flag in CAAudioFile.h)
	
	Revision 1.6  2004/06/02 20:16:01  bills
	track changes to CAAudioFile::Wrap
	
	Revision 1.5  2004/06/02 20:14:49  bills
	track changes to CAAudioFile::Wrap
	
	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/31 01:50:38  dwyatt
	track CAAudioFile changes
	
	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:47:56  dwyatt
	maintain a mRunning member so that GetPosition can be accurate when we're stopped
	
	Revision 1.7  2003/08/23 04:52:23  dwyatt
	fix GetCurrentPosition so as not to report EOF until we're really there
	
	Revision 1.6  2003/08/04 23:44:41  dwyatt
	deinterleaved
	
	Revision 1.5  2003/07/25 23:29:12  dwyatt
	use constant strings for operations in CAXException
	
	Revision 1.4  2003/07/09 19:12:41  dwyatt
	multiple I/O buffers, prepare for record
	
	Revision 1.3  2003/07/03 18:53:58  dwyatt
	add missing semicolon
	
	Revision 1.2  2003/07/02 21:45:41  dwyatt
	wasn't toggling between buffers correctly
	
	Revision 1.1  2003/06/23 23:07:37  dwyatt
	initial checkin
	
	created Wed Jun 18 2003, Doug Wyatt
	Copyright (c) 2003 Apple Computer, Inc.  All Rights Reserved

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

#include "CAAudioFileStreamer.h"

// ____________________________________________________________________________

void	CAAudioFileReader::FileReadBuffer::UpdateAfterRead(SInt64 curFrame, UInt32 nFrames)
{
	//printf("read %ld PCM packets, file packets %qd-%qd\n", nPackets, b->mStartPacket, b->mEndPacket);
	mEndFrame = nFrames;
	mEndOfStream = (nFrames == 0);
	mBufferStartFileFrame = curFrame;
}

// ____________________________________________________________________________

void	CAAudioFileReader::SetFile(const FSRef &inFile)
{
	Stop();
	CancelAndDisposeBuffers();
	
	delete mFile;   mFile = NULL;
	mFile = new CAAudioFile;
	mFile->Open(inFile);
	
	const CAStreamBasicDescription &fileFmt = mFile->GetFileDataFormat();
	CAStreamBasicDescription iofmt;
	iofmt.SetCanonical(fileFmt.mChannelsPerFrame, false);	// deinterleaved
	iofmt.mSampleRate = fileFmt.mSampleRate;
	mFile->SetClientFormat(iofmt, NULL);
	
	SetFormat(iofmt);
}

void	CAAudioFileReader::Start()
{
	mErrorCount = 0;
	mEndOfStream = false;
	Prime();
	mRunning = true;
}

void	CAAudioFileReader::Stop()
{
	mRunning = false;
}

void	CAAudioFileReader::ReadBuffer(FileReadBuffer *b)
{
	b->SetEmpty();
	CABufferList *ioMemory = b->GetBufferList();
	CABufferList *fileBuffers = GetBufferList();
	fileBuffers->SetFrom(ioMemory);
	UInt32 nFrames = GetBufferSizeFrames();
	SInt64 curFrame = mFile->Tell();
	mFile->Read(nFrames, &fileBuffers->GetModifiableBufferList());
	b->UpdateAfterRead(curFrame, nFrames);
}

double		CAAudioFileReader::GetCurrentPosition() const
{
	return double(GetCurrentFrame()) / double(GetNumberFrames());
#if 0
	double nFrames = double(GetNumberFrames());	// +1 to account for leftovers from decoder
	if (!mRunning)
		return double(GetCurrentFrame()) / nFrames;

	if (mEndOfStream) {
		//printf("EOF\n");
		return 1.0;
	}

	const FileReadBuffer *b = static_cast<const FileReadBuffer *>(GetCurrentBuffer());
		// the buffer from which we're reading
	UInt32 startFrame, endFrame;
	b->GetLocation(startFrame, endFrame);
	//printf("%qd + %ld / %.f\n", b->mBufferStartFileFrame, startFrame, nFrames);
	return double(b->mBufferStartFileFrame + startFrame) / nFrames;
	//if (endFrame > 0) {
		//double frac = (double(startFrame) / double(endFrame)) * double(endPacket - startPacket);
		//packetIndex += frac;
		//printf("frames %ld-%ld, packets %qd-%qd, frac %.3f\n",
		//	startFrame, endFrame, startPacket, endPacket, frac);
	//}
	//double pos = packetIndex / nPacketsPlus1;
	//printf("%.3f / %.0f = %.3f\n", packetIndex, nPacketsPlus1, pos);
	//return pos;

	//return double(GetCurrentFrame()) / nFrames;
#endif
}

SInt64	CAAudioFileReader::GetCurrentFrame() const
{
	if (!mRunning)
		return mFile->Tell();
	if (mEndOfStream)
		return GetNumberFrames();
	const FileReadBuffer *b = static_cast<const FileReadBuffer *>(GetCurrentBuffer());
		// the buffer from which we're reading
	UInt32 startFrame, endFrame;
	b->GetLocation(startFrame, endFrame);
	//printf("%qd + %ld / %.f\n", b->mBufferStartFileFrame, startFrame, nFrames);
	return b->mBufferStartFileFrame + startFrame;
}

void	CAAudioFileReader::SetCurrentPosition(double loc)
{
	bool wasRunning = IsRunning();
	if (wasRunning)
		Stop();
	SInt64 frameNumber = SInt64(loc * GetFile().GetNumberFrames() + 0.5);
	try {
		GetFile().Seek(frameNumber);
	}
	catch (...) {
	
	}
	if (wasRunning)
		Start();
}

// ____________________________________________________________________________

void	CAAudioFileWriter::SetFile(AudioFileID fileID)
{
	Stop();
	CancelAndDisposeBuffers();
	
	delete mFile;   mFile = NULL;
	mFile = new CAAudioFile;
	mFile->Wrap(fileID, true);
	
	const CAStreamBasicDescription &fileFmt = mFile->GetFileDataFormat();
	CAStreamBasicDescription iofmt;
	iofmt.SetCanonical(fileFmt.mChannelsPerFrame, false);	// deinterleaved
	iofmt.mSampleRate = fileFmt.mSampleRate;
	mFile->SetClientFormat(iofmt, NULL);
	
	SetFormat(iofmt);
}

void	CAAudioFileWriter::SetFile(const FSRef &parentDir, CFStringRef filename, AudioFileTypeID filetype, const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout)
{
	Stop();
	CancelAndDisposeBuffers();
	
	delete mFile;   mFile = NULL;
	mFile = new CAAudioFile;
	mFile->CreateNew(parentDir, filename, filetype, dataFormat, layout ? &layout->Layout() : NULL);
	
	const CAStreamBasicDescription &fileFmt = mFile->GetFileDataFormat();
	CAStreamBasicDescription iofmt;
	iofmt.SetCanonical(fileFmt.mChannelsPerFrame, false);	// deinterleaved
	iofmt.mSampleRate = fileFmt.mSampleRate;
	mFile->SetClientFormat(iofmt, NULL);
	
	SetFormat(iofmt);
}

void	CAAudioFileWriter::Start()
{
	mErrorCount = 0;
	mRunning = true;
}

void	CAAudioFileWriter::Stop()
{
	Flush();
	mRunning = false;
}

void	CAAudioFileWriter::WriteBuffer(CABufferQueue::Buffer *b)
{
	CABufferList *ioMemory = b->GetBufferList();
	CABufferList *fileBuffers = GetBufferList();
	UInt32 nFrames = b->FrameCount();
	fileBuffers->SetFrom(ioMemory, GetBytesPerFrame() * nFrames);
	mFile->Write(nFrames, &fileBuffers->GetModifiableBufferList());
	b->SetEmpty();
}
