mmserv/metadatautility/Src/MetaDataParserID3v1.cpp
changeset 0 71ca22bcf22a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmserv/metadatautility/Src/MetaDataParserID3v1.cpp	Tue Feb 02 01:08:46 2010 +0200
@@ -0,0 +1,441 @@
+/*
+* Copyright (c) 2004 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  This class implements an ID3v1 and v1.1 parser as specified in
+*                www.id3.org.
+*
+*/
+
+
+
+// INCLUDE FILES
+#include	"MetaDataParserID3v1.h"
+#include <centralrepository.h>
+#include "MetadataUtilityCRKeys.h"
+#include    <barsc.h>
+#include    <barsread.h>
+#include 	<data_caging_path_literals.hrh>
+#include	<TopCharacterSet.rsg>
+#ifdef _DEBUG
+#include	<e32svr.h>
+#endif
+
+// CONSTANTS
+// (ID3v1 specification found in www.id3.org)
+_LIT8( KID3v1TagHeader, "TAG" );
+const TInt KID3v1TagHeaderLength = 3;
+
+const TInt KOffsetTitle 		= 3;
+const TInt KOffsetArtist		= 33;
+const TInt KOffsetAlbum 		= 63;
+const TInt KOffsetYear 			= 93;
+const TInt KOffsetComment 		= 97;
+const TInt KOffsetAlbumTrack 	= 125;
+const TInt KOffsetGenre 		= 127;
+
+const TInt KLengthTitle 		= 30;
+const TInt KLengthArtist		= 30;
+const TInt KLengthAlbum 		= 30;
+const TInt KLengthYear 			= 4;
+const TInt KLengthComment 		= 30;
+const TInt KLengthAlbumTrack 	= 1;
+const TInt KLengthGenre 		= 1;
+
+const TInt KMinimumConfidenceRequired = 90;
+
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::CMetaDataParserID3v1
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CMetaDataParserID3v1::CMetaDataParserID3v1(
+	CMetaDataSource& aSource )
+	:	iSource(aSource),
+		iCharacterSetId(0)
+	{
+    }
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CMetaDataParserID3v1::ConstructL()
+    {
+	iTagData.FillZ( KID3v1TagLength );
+	if ( ValidateL() )
+		{
+		User::LeaveIfError(iFs.Connect());
+		// Get list of charconv supported character sets
+		iCharacterSet = CCnvCharacterSetConverter::CreateArrayOfCharacterSetsAvailableL(iFs);
+		iTopCharacterSet = new (ELeave) CArrayFixFlat<CCnvCharacterSetConverter::SCharacterSet>(12);
+		GenerateTopCharacterSetsL();
+    	}
+#ifdef _DEBUG
+	RDebug::Print(_L("CMetaDataParserID3v1::ConstructL - Done"));
+#endif
+    }
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CMetaDataParserID3v1* CMetaDataParserID3v1::NewL(
+	CMetaDataSource& aSource )
+    {
+#ifdef _DEBUG
+	RDebug::Print(_L("CMetaDataParserID3v1::NewL"));
+#endif
+	CMetaDataParserID3v1* self = new( ELeave ) CMetaDataParserID3v1(aSource);
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop();
+    return self;
+    }
+
+// Destructor
+CMetaDataParserID3v1::~CMetaDataParserID3v1()
+	{
+	if ( iExists )
+		{
+		delete iCharacterSet;
+		delete iTopCharacterSet;
+		iFs.Close();
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::ParseL
+// -----------------------------------------------------------------------------
+//
+void CMetaDataParserID3v1::ParseL(
+	const RArray<TMetaDataFieldId>& aWantedFields,
+	CMetaDataFieldContainer& aContainer )
+    {
+#ifdef _DEBUG
+	RDebug::Print(_L("CMetaDataParserID3v1::ParseL"));
+#endif
+	iContainer = &aContainer;
+	TInt err = KErrNone;
+	if ( aWantedFields.Count() == 0 )
+		{
+		TRAP(err, GetArtistL()); // ignore errors, coz other entries may be 
+		TRAP(err, GetTitleL()); // extracted wihout exceptions.
+		TRAP(err, GetAlbumL());
+		TRAP(err, GetYearL());
+		TRAP(err, GetCommentL());
+		TRAP(err, GetAlbumTrackL());
+		TRAP(err, GetGenreL());
+		}
+	else
+		{
+		// Look for it in the wanted field array
+		TInt count( aWantedFields.Count() );
+		for ( TInt i = 0; i < count; i++ )
+			{
+			switch ( aWantedFields[ i ] )
+				{
+				case EMetaDataSongTitle:
+					TRAP(err, GetTitleL());
+					break;
+				case EMetaDataArtist:
+					TRAP(err, GetArtistL());
+					break;
+				case EMetaDataAlbum:
+					TRAP(err, GetAlbumL());
+					break;
+				case EMetaDataYear:
+					TRAP(err, GetYearL());
+					break;
+				case EMetaDataComment:
+					TRAP(err, GetCommentL());
+					break;
+				case EMetaDataAlbumTrack:
+					TRAP(err, GetAlbumTrackL());
+					break;
+				case EMetaDataGenre:
+					TRAP(err, GetGenreL());
+					break;
+				default:
+					break;
+				}
+			}
+		}
+    }
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::ValidateL
+// -----------------------------------------------------------------------------
+//
+TBool CMetaDataParserID3v1::ValidateL()
+	{
+	// ID3v1 tags are always 128 bytes long
+	TInt size;
+	User::LeaveIfError( iSource.Size( size ) );
+	if ( size < KID3v1TagLength )
+		{
+		// This isn't ID3v1
+		return EFalse;
+		}
+
+	iSource.ReadL( size - KID3v1TagLength, iTagData );
+	// Search for the ID3v1 header in the tag data
+	if ( iTagData.Left( KID3v1TagHeaderLength ).Compare( KID3v1TagHeader ) != 0 )
+		{
+		return EFalse;
+		}
+
+	iExists = ETrue;
+	return ETrue;
+	}
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::GetTitleL
+// -----------------------------------------------------------------------------
+//
+void CMetaDataParserID3v1::GetTitleL()
+	{
+	TPtrC8 title = StripTrailingZeroes( iTagData.Mid( KOffsetTitle, KLengthTitle ) );
+	if ( title.Length() )
+		{
+		TBuf<KLengthTitle> titleUnicode;
+		if ( ConvertToUnicodeL(title, titleUnicode) == KErrNone )
+			{
+			iContainer->AppendL( EMetaDataSongTitle, titleUnicode );
+			}
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::GetArtistL
+// -----------------------------------------------------------------------------
+//
+void CMetaDataParserID3v1::GetArtistL()
+	{
+	TPtrC8 artist = StripTrailingZeroes( iTagData.Mid( KOffsetArtist, KLengthArtist ) );
+	if ( artist.Length() )
+		{
+		TBuf<KLengthArtist> artistUnicode;
+		if ( ConvertToUnicodeL(artist, artistUnicode) == KErrNone )
+			{
+			iContainer->AppendL( EMetaDataArtist, artistUnicode );
+			}
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::GetAlbumL
+// -----------------------------------------------------------------------------
+//
+void CMetaDataParserID3v1::GetAlbumL()
+	{
+	TPtrC8 album = StripTrailingZeroes( iTagData.Mid( KOffsetAlbum, KLengthAlbum ) );
+	if ( album.Length() )
+		{
+		TBuf<KLengthAlbum> albumUnicode;
+		if ( ConvertToUnicodeL(album, albumUnicode) == KErrNone )
+			{
+			iContainer->AppendL( EMetaDataAlbum, albumUnicode );
+			}
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::GetYearL
+// -----------------------------------------------------------------------------
+//
+void CMetaDataParserID3v1::GetYearL()
+	{
+	TBuf<KLengthYear> year;
+	year.Copy( StripTrailingZeroes( iTagData.Mid( KOffsetYear, KLengthYear ) ) );
+	if ( year.Length() )
+		{
+		iContainer->AppendL( EMetaDataYear, year );
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::GetCommentL
+// -----------------------------------------------------------------------------
+//
+void CMetaDataParserID3v1::GetCommentL()
+	{
+	TInt commentLength = 0;
+	// To be compliant with ID3 v1.1
+	if ( iTagData[KOffsetAlbumTrack] == 0 && iTagData[KOffsetAlbumTrack + 1] != 0 )
+		{
+		commentLength = 28;
+		}
+	else
+		{
+		commentLength = KLengthComment;
+		}
+	TPtrC8 comment = StripTrailingZeroes( iTagData.Mid( KOffsetComment, commentLength ) );
+	if ( comment.Length() )
+		{
+		TBuf<KLengthComment> commentUnicode;
+		if ( ConvertToUnicodeL(comment, commentUnicode) == KErrNone )
+			{
+			iContainer->AppendL( EMetaDataComment, commentUnicode );
+			}
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::GetAlbumTrackL
+// -----------------------------------------------------------------------------
+//
+void CMetaDataParserID3v1::GetAlbumTrackL()
+	{
+	// To be compliant with ID3 v1.1
+	if ( iTagData[KOffsetAlbumTrack] == 0 && iTagData[KOffsetAlbumTrack + 1] != 0 )
+		{
+		TBuf<3> track;	// 1 byte, but can be upto 3 bytes in when converted to ascii
+		TUint num = iTagData.Mid( KOffsetAlbumTrack + 1, KLengthAlbumTrack )[0];
+		track.NumUC( num );
+		iContainer->AppendL( EMetaDataAlbumTrack, track );
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::GetGenreL
+// -----------------------------------------------------------------------------
+//
+void CMetaDataParserID3v1::GetGenreL()
+	{
+		TUint num = iTagData.Mid( KOffsetGenre, KLengthGenre )[0];
+		TBufC<KMaxGenreSize> genre;
+		TPtr genrePtr = genre.Des();
+		if(num <= 125 || num == 199)
+			{
+			MapID3GenreToStringL(num, genrePtr);
+			iContainer->AppendL( EMetaDataGenre, genre );
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::ConvertToUnicodeL
+// -----------------------------------------------------------------------------
+//
+TInt CMetaDataParserID3v1::ConvertToUnicodeL(
+	const TDesC8& aDesc,
+	TDes16& aUnicode )
+	{
+	if ( iCharacterSetId == 0 )
+		{
+		if ( DetectCharacterSetL(aDesc) == KErrNotFound )		
+			{
+			return KErrNotFound;
+			}
+		}
+
+	CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC();
+	TInt state = CCnvCharacterSetConverter::KStateDefault;
+	TInt numOfUnconvertibleChars = 0;
+
+	charSetConv->PrepareToConvertToOrFromL(iCharacterSetId, *iCharacterSet, iFs);
+	TInt retVal = charSetConv->ConvertToUnicode(aUnicode, aDesc, state, numOfUnconvertibleChars);
+#ifdef _DEBUG
+	RDebug::Print(_L("CMetaDataParserID3v1::ConvertToUnicode :-> Tag Size[%d] Unicode Tag Size[%d]Bytes Unconverted[%d] retVal[%d]"),
+		aDesc.Length(), aUnicode.Length(), numOfUnconvertibleChars, retVal);
+#endif
+	if ( retVal < 0 )
+		{
+		CleanupStack::PopAndDestroy();	// charSetConv
+		return retVal;
+		//return KErrGeneral;
+		}
+
+	if ( retVal > 0 || numOfUnconvertibleChars > 0 )
+		{
+		// This is different character set. Need to auto detect again
+		if ( DetectCharacterSetL(aDesc) == KErrNotFound )
+			{
+			CleanupStack::PopAndDestroy();	// charSetConv
+			return KErrNotFound;
+			}
+		state = CCnvCharacterSetConverter::KStateDefault;
+		numOfUnconvertibleChars = 0;
+		charSetConv->PrepareToConvertToOrFromL(iCharacterSetId, *iCharacterSet, iFs);
+		retVal = charSetConv->ConvertToUnicode(aUnicode, aDesc, state, numOfUnconvertibleChars);
+#ifdef _DEBUG
+	RDebug::Print(_L("CMetaDataParserID3v1::ConvertToUnicode :-> Tag Size[%d] Unicode Tag Size[%d]Bytes Unconverted[%d] retVal[%d]"),
+		aDesc.Length(), aUnicode.Length(), numOfUnconvertibleChars, retVal);
+#endif
+        if ( retVal != 0 || numOfUnconvertibleChars > 0)
+			{
+	        CleanupStack::PopAndDestroy();	// charSetConv
+	        return KErrGeneral;
+			}
+		}
+
+	CleanupStack::PopAndDestroy();	// charSetConv
+	return KErrNone;
+	}
+
+
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::DetectCharacterSetL
+// -----------------------------------------------------------------------------
+//
+TInt CMetaDataParserID3v1::DetectCharacterSetL(
+	const TDesC8& aDesc)
+	{
+	TInt confidence = 0;
+	TInt highestConfidence = 0;
+	TUint charSetId;
+	TUint highestConfidencecharSetId = 0;
+
+	CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC();
+	TInt count = iTopCharacterSet->Count();
+	for ( TInt i=0; i < iTopCharacterSet->Count(); i++)
+		{
+		charSetId = iTopCharacterSet->At(i).Identifier();
+		charSetConv->ConvertibleToCharSetL(confidence, charSetId, *iTopCharacterSet, aDesc);
+		if ( confidence > highestConfidence )
+			{
+			highestConfidence = confidence;
+			highestConfidencecharSetId = charSetId;
+			}
+		}
+	CleanupStack::PopAndDestroy(charSetConv);
+#ifdef _DEBUG
+	RDebug::Print(_L("CMetaDataParserID3v1::DetectCharacterSetL :-> Confidence[%d] CharSetId[%x]"),
+   		confidence, iCharacterSetId);
+#endif
+	if ( highestConfidence < KMinimumConfidenceRequired )
+		{
+		return KErrNotFound;
+		}
+	else
+		{
+		iCharacterSetId = highestConfidencecharSetId;
+		return KErrNone;
+		}
+	}
+	
+// -----------------------------------------------------------------------------
+// CMetaDataParserID3v1::Version()
+// -----------------------------------------------------------------------------
+//
+TID3Version CMetaDataParserID3v1::ID3Version()
+{
+	return EID3Version1;
+}
+
+//  End of File