sl@0: // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: // All rights reserved.
sl@0: // This component and the accompanying materials are made available
sl@0: // under the terms of "Eclipse Public License v1.0"
sl@0: // which accompanies this distribution, and is available
sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: //
sl@0: // Initial Contributors:
sl@0: // Nokia Corporation - initial contribution.
sl@0: //
sl@0: // Contributors:
sl@0: //
sl@0: // Description:
sl@0: //
sl@0: 
sl@0: #include "mmfwavformat.h"
sl@0: 
sl@0: #include "WavDecodeUtility.h" 
sl@0: 
sl@0: 
sl@0: CWavDecodeUtility::CWavDecodeUtility()
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: void CWavDecodeUtility::ConstructL(TDesC8& aBuffer)
sl@0: 	{
sl@0: 	iBuffer = &aBuffer;
sl@0: 	FindRiffChunksL();
sl@0: 	ProcessFormatHeaderL();
sl@0: 	}
sl@0: 
sl@0: CWavDecodeUtility* CWavDecodeUtility::NewL(TDesC8& aBuffer)
sl@0: 	{
sl@0: 	CWavDecodeUtility* self = new (ELeave) CWavDecodeUtility();
sl@0: 	CleanupStack::PushL(self);
sl@0: 	self->ConstructL(aBuffer);
sl@0: 	CleanupStack::Pop();
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: 
sl@0: CWavDecodeUtility::~CWavDecodeUtility()
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: TUint16 CWavDecodeUtility::Read16(const TUint8* aPtr)
sl@0: 
sl@0:     {
sl@0: 	TUint16 ret = *(REINTERPRET_CAST(const TUint16*, aPtr));
sl@0: 	return ret;
sl@0:     }
sl@0: 
sl@0: TUint32 CWavDecodeUtility::Read32(const TUint8* aPtr)
sl@0:     {
sl@0:     TUint32 x = *aPtr++;
sl@0:     x |= *aPtr++ << 8;
sl@0:     x |= *aPtr++ << 16;
sl@0:     x |= *aPtr++ << 24;
sl@0:     return x;
sl@0:     }
sl@0: void CWavDecodeUtility::AssignChunkTo(TMdaRiffChunk* aAssignedChunk)
sl@0: 	{
sl@0: 	Mem::Copy(REINTERPRET_CAST(TUint8*, aAssignedChunk), REINTERPRET_CAST(TUint8*, &iCurrent),sizeof(TMdaRiffChunk));
sl@0: 	aAssignedChunk->iFound=ETrue;
sl@0: 	}
sl@0: 
sl@0: void CWavDecodeUtility::ReadChunk(TMdaRiffChunk* aChunk)
sl@0: 	{
sl@0: 	Mem::FillZ(REINTERPRET_CAST(TUint8*, aChunk),sizeof(TMdaRiffChunk)); // Zero data
sl@0: 	aChunk->iPosition=iPos + KRiffChunkHeaderLength;
sl@0: 	aChunk->iName = Read32(iStartPtr + iPos - iLastReadPosition);
sl@0: 	aChunk->iLength = Read32(iStartPtr + iPos - iLastReadPosition + KRiffChunkDataLength);
sl@0: 	}
sl@0: void CWavDecodeUtility::FindRiffChunksL()
sl@0: 	{
sl@0: 	if (!iFoundChunks)
sl@0: 		{
sl@0: 		iFoundChunks=ETrue;
sl@0: //		iStartPtr=iBuffer->Data().Ptr();
sl@0: 		iStartPtr=iBuffer->Ptr();
sl@0: 		iPos=0;
sl@0: 		iLastReadPosition=0;//Set by CBase, but what the heck
sl@0: 		iDone=EFalse;
sl@0: 		}
sl@0: 	else
sl@0: 		{//We've done another read. If there's < chunk in the buffer then something's wrong
sl@0: 		if (iBuffer->Length() < STATIC_CAST(TInt, KRiffChunkHeaderLength))
sl@0: 			{
sl@0: 			if ((iFormatChunk.iFound) && (iDataChunk.iFound)) 
sl@0: 				{
sl@0: 				iDone = ETrue; //it should be ok to exit loop
sl@0: 				return;
sl@0: 				}
sl@0: 			else
sl@0: 				{
sl@0: 				User::Leave(KErrCorrupt);
sl@0: 				}
sl@0: 			}
sl@0: 		}
sl@0: 	
sl@0: 	while (!iDone)
sl@0: 		{
sl@0: 		TInt advance=0;
sl@0: 		
sl@0: 		ReadChunk(&iCurrent);
sl@0: 		
sl@0: 		if (iCurrent.iName == KRiffChunkNameRiff)//we need to look INSIDE the RIFF chunk
sl@0: 			{
sl@0: 			if(iBuffer->Length() < STATIC_CAST(TInt, KRiffContainerChunkHeaderLength))
sl@0: 				User::Leave(KErrCorrupt);
sl@0: 			iRiffChunkLength=iCurrent.iLength + KRiffChunkHeaderLength;
sl@0: 			advance=KRiffContainerChunkHeaderLength;
sl@0: 			}
sl@0: 		else
sl@0: 			{
sl@0: 			advance=iCurrent.iLength + KRiffChunkHeaderLength;		//... and skip all others
sl@0: 			}
sl@0: 		
sl@0: 		if (iCurrent.iName == KRiffChunkNameFmt_)
sl@0: 			AssignChunkTo(&iFormatChunk);
sl@0: 		
sl@0: 		else if (iCurrent.iName == KRiffChunkNameFact)
sl@0: 			AssignChunkTo(&iFactChunk);
sl@0: 		
sl@0: 		else if (iCurrent.iName == KRiffChunkNameData)
sl@0: 			AssignChunkTo(&iDataChunk);
sl@0: 		
sl@0: 		if (iDataChunk.iFound && iFormatChunk.iFound && iFactChunk.iFound)
sl@0: 			{
sl@0: 			iDone=ETrue;
sl@0: 			}
sl@0: 		else
sl@0: 			{//still have chunks to look for
sl@0: 			iPos+=advance;
sl@0: 			if (iPos & 1)
sl@0: 				iPos++;
sl@0: 			
sl@0: 			if ((TUint)iPos>=(TUint)iRiffChunkLength)
sl@0: 				{
sl@0: 				iDone=ETrue;//end of file
sl@0: 				iClipLength = iRiffChunkLength;
sl@0: 				}
sl@0: 			else
sl@0: 				{//make sure we have at least a chunk's worth left in the buffer
sl@0: 				if ((TUint)(iPos-iLastReadPosition) > 
sl@0: 					(TUint)(iBuffer->Length() -KRiffChunkHeaderLength))
sl@0: 					{
sl@0: 					iLastReadPosition=iPos;
sl@0: 					//DoReadL(iLastReadPosition);
sl@0: 					return;
sl@0: 					}	
sl@0: 				}
sl@0: 			}
sl@0: 		}
sl@0: 	
sl@0: 	iClipLength = iRiffChunkLength;
sl@0: 	if (iClipLength == 0) User::Leave(KErrNotFound);
sl@0: 	else if (!(iDataChunk.iFound && iFormatChunk.iFound))
sl@0: 		User::Leave(KErrCorrupt);
sl@0: 
sl@0: 	}
sl@0: 
sl@0: void CWavDecodeUtility::ProcessFormatHeaderL()
sl@0: 	{
sl@0: 	TMdaRiffChunk* chunk = &iFormatChunk;
sl@0: 	
sl@0: 	if (!chunk)
sl@0: 		User::Leave(KErrCorrupt);
sl@0: 	
sl@0: 	iLastReadPosition = chunk->iPosition; // Should be beginning of fmt block
sl@0: 	//DoReadL(iLastReadPosition);
sl@0: 	
sl@0: 	// Set the real format
sl@0: 	const TUint8* rawform = iBuffer->Ptr() + iLastReadPosition;	//skip _fmt & length
sl@0: 	iCodecId = Read16(rawform); rawform+=2;
sl@0: 	iChannels = Read16(rawform); rawform+=2;
sl@0: 	if ((iChannels != 1)&&(iChannels != 2))		//only 1 or 2 channels allowed
sl@0: 		User::Leave(KErrCorrupt); 
sl@0: 	
sl@0: 	iSampleRate = Read32(rawform); rawform+=4; // Skip bytes per second estimate
sl@0: 	if (!iSampleRate) 	User::Leave(KErrCorrupt);
sl@0: 	
sl@0: 	iAverageBytesPerSecond = Read32(rawform); rawform+=4;
sl@0: 	iBlockAlign = Read16(rawform); rawform+=2;
sl@0: 	
sl@0: 	iBitsPerSample = Read16(rawform);
sl@0: 	rawform+=2;
sl@0: 	
sl@0: 	switch (iCodecId)
sl@0: 		{
sl@0: 		case KMMFWavFormatTypePcm:
sl@0: 			{
sl@0: 			}
sl@0: 			break;
sl@0: 		case KMMFWavFormatTypeImaAdpcm:
sl@0: 			{
sl@0: 			}
sl@0: 			break;
sl@0: 		case KMMFWavFormatTypeAlaw:
sl@0: 			{
sl@0: 			}
sl@0: 			break;
sl@0: 		case KMMFWavFormatTypeMulaw:
sl@0: 			{
sl@0: 			}
sl@0: 			break;
sl@0: 		case KMMFWavFormatTypeGSM610:
sl@0: 			{
sl@0: 			}
sl@0: 			break;
sl@0: 		default:
sl@0: 			User::Leave(KErrNotSupported);
sl@0: 		}
sl@0: 	
sl@0: 	if (iCodecId == KMMFWavFormatTypeImaAdpcm)
sl@0: 		{
sl@0: 		TUint16 extraData = Read16(rawform);
sl@0: 		if (extraData == 2)
sl@0: 			{
sl@0: 			rawform+=2;
sl@0: 			iSamplesPerBlock = Read16(rawform);
sl@0: 			rawform+=2;
sl@0: 			}
sl@0: 		}
sl@0: 	
sl@0: 	// Is there a fact chunk?
sl@0: 	if (iFactChunk.iFound)			
sl@0: 		iHasFactChunk = ETrue;
sl@0: 	
sl@0: 	// Find the data block
sl@0: 	chunk=&iDataChunk;
sl@0: 	iStartPosition = chunk->iPosition; 
sl@0: 	iDataLength = chunk->iLength;
sl@0: 	}