--- /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