mmserv/metadatautility/Src/MetaDataParserID3v1.cpp
changeset 0 71ca22bcf22a
child 53 eabc8c503852
equal deleted inserted replaced
-1:000000000000 0:71ca22bcf22a
       
     1 /*
       
     2 * Copyright (c) 2004 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  This class implements an ID3v1 and v1.1 parser as specified in
       
    15 *                www.id3.org.
       
    16 *
       
    17 */
       
    18 
       
    19 
       
    20 
       
    21 // INCLUDE FILES
       
    22 #include	"MetaDataParserID3v1.h"
       
    23 #include <centralrepository.h>
       
    24 #include "MetadataUtilityCRKeys.h"
       
    25 #include    <barsc.h>
       
    26 #include    <barsread.h>
       
    27 #include 	<data_caging_path_literals.hrh>
       
    28 #include	<TopCharacterSet.rsg>
       
    29 #ifdef _DEBUG
       
    30 #include	<e32svr.h>
       
    31 #endif
       
    32 
       
    33 // CONSTANTS
       
    34 // (ID3v1 specification found in www.id3.org)
       
    35 _LIT8( KID3v1TagHeader, "TAG" );
       
    36 const TInt KID3v1TagHeaderLength = 3;
       
    37 
       
    38 const TInt KOffsetTitle 		= 3;
       
    39 const TInt KOffsetArtist		= 33;
       
    40 const TInt KOffsetAlbum 		= 63;
       
    41 const TInt KOffsetYear 			= 93;
       
    42 const TInt KOffsetComment 		= 97;
       
    43 const TInt KOffsetAlbumTrack 	= 125;
       
    44 const TInt KOffsetGenre 		= 127;
       
    45 
       
    46 const TInt KLengthTitle 		= 30;
       
    47 const TInt KLengthArtist		= 30;
       
    48 const TInt KLengthAlbum 		= 30;
       
    49 const TInt KLengthYear 			= 4;
       
    50 const TInt KLengthComment 		= 30;
       
    51 const TInt KLengthAlbumTrack 	= 1;
       
    52 const TInt KLengthGenre 		= 1;
       
    53 
       
    54 const TInt KMinimumConfidenceRequired = 90;
       
    55 
       
    56 
       
    57 // ============================ MEMBER FUNCTIONS ===============================
       
    58 
       
    59 // -----------------------------------------------------------------------------
       
    60 // CMetaDataParserID3v1::CMetaDataParserID3v1
       
    61 // C++ default constructor can NOT contain any code, that
       
    62 // might leave.
       
    63 // -----------------------------------------------------------------------------
       
    64 //
       
    65 CMetaDataParserID3v1::CMetaDataParserID3v1(
       
    66 	CMetaDataSource& aSource )
       
    67 	:	iSource(aSource),
       
    68 		iCharacterSetId(0)
       
    69 	{
       
    70     }
       
    71 
       
    72 // -----------------------------------------------------------------------------
       
    73 // CMetaDataParserID3v1::ConstructL
       
    74 // Symbian 2nd phase constructor can leave.
       
    75 // -----------------------------------------------------------------------------
       
    76 //
       
    77 void CMetaDataParserID3v1::ConstructL()
       
    78     {
       
    79 	iTagData.FillZ( KID3v1TagLength );
       
    80 	if ( ValidateL() )
       
    81 		{
       
    82 		User::LeaveIfError(iFs.Connect());
       
    83 		// Get list of charconv supported character sets
       
    84 		iCharacterSet = CCnvCharacterSetConverter::CreateArrayOfCharacterSetsAvailableL(iFs);
       
    85 		iTopCharacterSet = new (ELeave) CArrayFixFlat<CCnvCharacterSetConverter::SCharacterSet>(12);
       
    86 		GenerateTopCharacterSetsL();
       
    87     	}
       
    88 #ifdef _DEBUG
       
    89 	RDebug::Print(_L("CMetaDataParserID3v1::ConstructL - Done"));
       
    90 #endif
       
    91     }
       
    92 
       
    93 // -----------------------------------------------------------------------------
       
    94 // CMetaDataParserID3v1::NewL
       
    95 // Two-phased constructor.
       
    96 // -----------------------------------------------------------------------------
       
    97 //
       
    98 CMetaDataParserID3v1* CMetaDataParserID3v1::NewL(
       
    99 	CMetaDataSource& aSource )
       
   100     {
       
   101 #ifdef _DEBUG
       
   102 	RDebug::Print(_L("CMetaDataParserID3v1::NewL"));
       
   103 #endif
       
   104 	CMetaDataParserID3v1* self = new( ELeave ) CMetaDataParserID3v1(aSource);
       
   105     CleanupStack::PushL( self );
       
   106     self->ConstructL();
       
   107     CleanupStack::Pop();
       
   108     return self;
       
   109     }
       
   110 
       
   111 // Destructor
       
   112 CMetaDataParserID3v1::~CMetaDataParserID3v1()
       
   113 	{
       
   114 	if ( iExists )
       
   115 		{
       
   116 		delete iCharacterSet;
       
   117 		delete iTopCharacterSet;
       
   118 		iFs.Close();
       
   119 		}
       
   120 	}
       
   121 
       
   122 // -----------------------------------------------------------------------------
       
   123 // CMetaDataParserID3v1::ParseL
       
   124 // -----------------------------------------------------------------------------
       
   125 //
       
   126 void CMetaDataParserID3v1::ParseL(
       
   127 	const RArray<TMetaDataFieldId>& aWantedFields,
       
   128 	CMetaDataFieldContainer& aContainer )
       
   129     {
       
   130 #ifdef _DEBUG
       
   131 	RDebug::Print(_L("CMetaDataParserID3v1::ParseL"));
       
   132 #endif
       
   133 	iContainer = &aContainer;
       
   134 	TInt err = KErrNone;
       
   135 	if ( aWantedFields.Count() == 0 )
       
   136 		{
       
   137 		TRAP(err, GetArtistL()); // ignore errors, coz other entries may be 
       
   138 		TRAP(err, GetTitleL()); // extracted wihout exceptions.
       
   139 		TRAP(err, GetAlbumL());
       
   140 		TRAP(err, GetYearL());
       
   141 		TRAP(err, GetCommentL());
       
   142 		TRAP(err, GetAlbumTrackL());
       
   143 		TRAP(err, GetGenreL());
       
   144 		}
       
   145 	else
       
   146 		{
       
   147 		// Look for it in the wanted field array
       
   148 		TInt count( aWantedFields.Count() );
       
   149 		for ( TInt i = 0; i < count; i++ )
       
   150 			{
       
   151 			switch ( aWantedFields[ i ] )
       
   152 				{
       
   153 				case EMetaDataSongTitle:
       
   154 					TRAP(err, GetTitleL());
       
   155 					break;
       
   156 				case EMetaDataArtist:
       
   157 					TRAP(err, GetArtistL());
       
   158 					break;
       
   159 				case EMetaDataAlbum:
       
   160 					TRAP(err, GetAlbumL());
       
   161 					break;
       
   162 				case EMetaDataYear:
       
   163 					TRAP(err, GetYearL());
       
   164 					break;
       
   165 				case EMetaDataComment:
       
   166 					TRAP(err, GetCommentL());
       
   167 					break;
       
   168 				case EMetaDataAlbumTrack:
       
   169 					TRAP(err, GetAlbumTrackL());
       
   170 					break;
       
   171 				case EMetaDataGenre:
       
   172 					TRAP(err, GetGenreL());
       
   173 					break;
       
   174 				default:
       
   175 					break;
       
   176 				}
       
   177 			}
       
   178 		}
       
   179     }
       
   180 
       
   181 // -----------------------------------------------------------------------------
       
   182 // CMetaDataParserID3v1::ValidateL
       
   183 // -----------------------------------------------------------------------------
       
   184 //
       
   185 TBool CMetaDataParserID3v1::ValidateL()
       
   186 	{
       
   187 	// ID3v1 tags are always 128 bytes long
       
   188 	TInt size;
       
   189 	User::LeaveIfError( iSource.Size( size ) );
       
   190 	if ( size < KID3v1TagLength )
       
   191 		{
       
   192 		// This isn't ID3v1
       
   193 		return EFalse;
       
   194 		}
       
   195 
       
   196 	iSource.ReadL( size - KID3v1TagLength, iTagData );
       
   197 	// Search for the ID3v1 header in the tag data
       
   198 	if ( iTagData.Left( KID3v1TagHeaderLength ).Compare( KID3v1TagHeader ) != 0 )
       
   199 		{
       
   200 		return EFalse;
       
   201 		}
       
   202 
       
   203 	iExists = ETrue;
       
   204 	return ETrue;
       
   205 	}
       
   206 
       
   207 // -----------------------------------------------------------------------------
       
   208 // CMetaDataParserID3v1::GetTitleL
       
   209 // -----------------------------------------------------------------------------
       
   210 //
       
   211 void CMetaDataParserID3v1::GetTitleL()
       
   212 	{
       
   213 	TPtrC8 title = StripTrailingZeroes( iTagData.Mid( KOffsetTitle, KLengthTitle ) );
       
   214 	if ( title.Length() )
       
   215 		{
       
   216 		TBuf<KLengthTitle> titleUnicode;
       
   217 		if ( ConvertToUnicodeL(title, titleUnicode) == KErrNone )
       
   218 			{
       
   219 			iContainer->AppendL( EMetaDataSongTitle, titleUnicode );
       
   220 			}
       
   221 		}
       
   222 	}
       
   223 
       
   224 // -----------------------------------------------------------------------------
       
   225 // CMetaDataParserID3v1::GetArtistL
       
   226 // -----------------------------------------------------------------------------
       
   227 //
       
   228 void CMetaDataParserID3v1::GetArtistL()
       
   229 	{
       
   230 	TPtrC8 artist = StripTrailingZeroes( iTagData.Mid( KOffsetArtist, KLengthArtist ) );
       
   231 	if ( artist.Length() )
       
   232 		{
       
   233 		TBuf<KLengthArtist> artistUnicode;
       
   234 		if ( ConvertToUnicodeL(artist, artistUnicode) == KErrNone )
       
   235 			{
       
   236 			iContainer->AppendL( EMetaDataArtist, artistUnicode );
       
   237 			}
       
   238 		}
       
   239 	}
       
   240 
       
   241 // -----------------------------------------------------------------------------
       
   242 // CMetaDataParserID3v1::GetAlbumL
       
   243 // -----------------------------------------------------------------------------
       
   244 //
       
   245 void CMetaDataParserID3v1::GetAlbumL()
       
   246 	{
       
   247 	TPtrC8 album = StripTrailingZeroes( iTagData.Mid( KOffsetAlbum, KLengthAlbum ) );
       
   248 	if ( album.Length() )
       
   249 		{
       
   250 		TBuf<KLengthAlbum> albumUnicode;
       
   251 		if ( ConvertToUnicodeL(album, albumUnicode) == KErrNone )
       
   252 			{
       
   253 			iContainer->AppendL( EMetaDataAlbum, albumUnicode );
       
   254 			}
       
   255 		}
       
   256 	}
       
   257 
       
   258 // -----------------------------------------------------------------------------
       
   259 // CMetaDataParserID3v1::GetYearL
       
   260 // -----------------------------------------------------------------------------
       
   261 //
       
   262 void CMetaDataParserID3v1::GetYearL()
       
   263 	{
       
   264 	TBuf<KLengthYear> year;
       
   265 	year.Copy( StripTrailingZeroes( iTagData.Mid( KOffsetYear, KLengthYear ) ) );
       
   266 	if ( year.Length() )
       
   267 		{
       
   268 		iContainer->AppendL( EMetaDataYear, year );
       
   269 		}
       
   270 	}
       
   271 
       
   272 // -----------------------------------------------------------------------------
       
   273 // CMetaDataParserID3v1::GetCommentL
       
   274 // -----------------------------------------------------------------------------
       
   275 //
       
   276 void CMetaDataParserID3v1::GetCommentL()
       
   277 	{
       
   278 	TInt commentLength = 0;
       
   279 	// To be compliant with ID3 v1.1
       
   280 	if ( iTagData[KOffsetAlbumTrack] == 0 && iTagData[KOffsetAlbumTrack + 1] != 0 )
       
   281 		{
       
   282 		commentLength = 28;
       
   283 		}
       
   284 	else
       
   285 		{
       
   286 		commentLength = KLengthComment;
       
   287 		}
       
   288 	TPtrC8 comment = StripTrailingZeroes( iTagData.Mid( KOffsetComment, commentLength ) );
       
   289 	if ( comment.Length() )
       
   290 		{
       
   291 		TBuf<KLengthComment> commentUnicode;
       
   292 		if ( ConvertToUnicodeL(comment, commentUnicode) == KErrNone )
       
   293 			{
       
   294 			iContainer->AppendL( EMetaDataComment, commentUnicode );
       
   295 			}
       
   296 		}
       
   297 	}
       
   298 
       
   299 // -----------------------------------------------------------------------------
       
   300 // CMetaDataParserID3v1::GetAlbumTrackL
       
   301 // -----------------------------------------------------------------------------
       
   302 //
       
   303 void CMetaDataParserID3v1::GetAlbumTrackL()
       
   304 	{
       
   305 	// To be compliant with ID3 v1.1
       
   306 	if ( iTagData[KOffsetAlbumTrack] == 0 && iTagData[KOffsetAlbumTrack + 1] != 0 )
       
   307 		{
       
   308 		TBuf<3> track;	// 1 byte, but can be upto 3 bytes in when converted to ascii
       
   309 		TUint num = iTagData.Mid( KOffsetAlbumTrack + 1, KLengthAlbumTrack )[0];
       
   310 		track.NumUC( num );
       
   311 		iContainer->AppendL( EMetaDataAlbumTrack, track );
       
   312 		}
       
   313 	}
       
   314 
       
   315 // -----------------------------------------------------------------------------
       
   316 // CMetaDataParserID3v1::GetGenreL
       
   317 // -----------------------------------------------------------------------------
       
   318 //
       
   319 void CMetaDataParserID3v1::GetGenreL()
       
   320 	{
       
   321 		TUint num = iTagData.Mid( KOffsetGenre, KLengthGenre )[0];
       
   322 		TBufC<KMaxGenreSize> genre;
       
   323 		TPtr genrePtr = genre.Des();
       
   324 		if(num <= 125 || num == 199)
       
   325 			{
       
   326 			MapID3GenreToStringL(num, genrePtr);
       
   327 			iContainer->AppendL( EMetaDataGenre, genre );
       
   328 		}
       
   329 	}
       
   330 
       
   331 // -----------------------------------------------------------------------------
       
   332 // CMetaDataParserID3v1::ConvertToUnicodeL
       
   333 // -----------------------------------------------------------------------------
       
   334 //
       
   335 TInt CMetaDataParserID3v1::ConvertToUnicodeL(
       
   336 	const TDesC8& aDesc,
       
   337 	TDes16& aUnicode )
       
   338 	{
       
   339 	if ( iCharacterSetId == 0 )
       
   340 		{
       
   341 		if ( DetectCharacterSetL(aDesc) == KErrNotFound )		
       
   342 			{
       
   343 			return KErrNotFound;
       
   344 			}
       
   345 		}
       
   346 
       
   347 	CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC();
       
   348 	TInt state = CCnvCharacterSetConverter::KStateDefault;
       
   349 	TInt numOfUnconvertibleChars = 0;
       
   350 
       
   351 	charSetConv->PrepareToConvertToOrFromL(iCharacterSetId, *iCharacterSet, iFs);
       
   352 	TInt retVal = charSetConv->ConvertToUnicode(aUnicode, aDesc, state, numOfUnconvertibleChars);
       
   353 #ifdef _DEBUG
       
   354 	RDebug::Print(_L("CMetaDataParserID3v1::ConvertToUnicode :-> Tag Size[%d] Unicode Tag Size[%d]Bytes Unconverted[%d] retVal[%d]"),
       
   355 		aDesc.Length(), aUnicode.Length(), numOfUnconvertibleChars, retVal);
       
   356 #endif
       
   357 	if ( retVal < 0 )
       
   358 		{
       
   359 		CleanupStack::PopAndDestroy();	// charSetConv
       
   360 		return retVal;
       
   361 		//return KErrGeneral;
       
   362 		}
       
   363 
       
   364 	if ( retVal > 0 || numOfUnconvertibleChars > 0 )
       
   365 		{
       
   366 		// This is different character set. Need to auto detect again
       
   367 		if ( DetectCharacterSetL(aDesc) == KErrNotFound )
       
   368 			{
       
   369 			CleanupStack::PopAndDestroy();	// charSetConv
       
   370 			return KErrNotFound;
       
   371 			}
       
   372 		state = CCnvCharacterSetConverter::KStateDefault;
       
   373 		numOfUnconvertibleChars = 0;
       
   374 		charSetConv->PrepareToConvertToOrFromL(iCharacterSetId, *iCharacterSet, iFs);
       
   375 		retVal = charSetConv->ConvertToUnicode(aUnicode, aDesc, state, numOfUnconvertibleChars);
       
   376 #ifdef _DEBUG
       
   377 	RDebug::Print(_L("CMetaDataParserID3v1::ConvertToUnicode :-> Tag Size[%d] Unicode Tag Size[%d]Bytes Unconverted[%d] retVal[%d]"),
       
   378 		aDesc.Length(), aUnicode.Length(), numOfUnconvertibleChars, retVal);
       
   379 #endif
       
   380         if ( retVal != 0 || numOfUnconvertibleChars > 0)
       
   381 			{
       
   382 	        CleanupStack::PopAndDestroy();	// charSetConv
       
   383 	        return KErrGeneral;
       
   384 			}
       
   385 		}
       
   386 
       
   387 	CleanupStack::PopAndDestroy();	// charSetConv
       
   388 	return KErrNone;
       
   389 	}
       
   390 
       
   391 
       
   392 // -----------------------------------------------------------------------------
       
   393 // CMetaDataParserID3v1::DetectCharacterSetL
       
   394 // -----------------------------------------------------------------------------
       
   395 //
       
   396 TInt CMetaDataParserID3v1::DetectCharacterSetL(
       
   397 	const TDesC8& aDesc)
       
   398 	{
       
   399 	TInt confidence = 0;
       
   400 	TInt highestConfidence = 0;
       
   401 	TUint charSetId;
       
   402 	TUint highestConfidencecharSetId = 0;
       
   403 
       
   404 	CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC();
       
   405 	TInt count = iTopCharacterSet->Count();
       
   406 	for ( TInt i=0; i < iTopCharacterSet->Count(); i++)
       
   407 		{
       
   408 		charSetId = iTopCharacterSet->At(i).Identifier();
       
   409 		charSetConv->ConvertibleToCharSetL(confidence, charSetId, *iTopCharacterSet, aDesc);
       
   410 		if ( confidence > highestConfidence )
       
   411 			{
       
   412 			highestConfidence = confidence;
       
   413 			highestConfidencecharSetId = charSetId;
       
   414 			}
       
   415 		}
       
   416 	CleanupStack::PopAndDestroy(charSetConv);
       
   417 #ifdef _DEBUG
       
   418 	RDebug::Print(_L("CMetaDataParserID3v1::DetectCharacterSetL :-> Confidence[%d] CharSetId[%x]"),
       
   419    		confidence, iCharacterSetId);
       
   420 #endif
       
   421 	if ( highestConfidence < KMinimumConfidenceRequired )
       
   422 		{
       
   423 		return KErrNotFound;
       
   424 		}
       
   425 	else
       
   426 		{
       
   427 		iCharacterSetId = highestConfidencecharSetId;
       
   428 		return KErrNone;
       
   429 		}
       
   430 	}
       
   431 	
       
   432 // -----------------------------------------------------------------------------
       
   433 // CMetaDataParserID3v1::Version()
       
   434 // -----------------------------------------------------------------------------
       
   435 //
       
   436 TID3Version CMetaDataParserID3v1::ID3Version()
       
   437 {
       
   438 	return EID3Version1;
       
   439 }
       
   440 
       
   441 //  End of File