bool SoundSource::processID3v2Tag(TagLib::ID3v2::Tag* id3v2) { // Print every frame in the file. if (s_bDebugMetadata) { TagLib::ID3v2::FrameList::ConstIterator it = id3v2->frameList().begin(); for(; it != id3v2->frameList().end(); it++) { qDebug() << "ID3V2" << (*it)->frameID().data() << "-" << TStringToQString((*it)->toString()); } } TagLib::ID3v2::FrameList bpmFrame = id3v2->frameListMap()["TBPM"]; if (!bpmFrame.isEmpty()) { QString sBpm = TStringToQString(bpmFrame.front()->toString()); processBpmString("ID3v2", sBpm); } TagLib::ID3v2::FrameList keyFrame = id3v2->frameListMap()["TKEY"]; if (!keyFrame.isEmpty()) { QString sKey = TStringToQString(keyFrame.front()->toString()); setKey(sKey); } // Foobar2000-style ID3v2.3.0 tags // TODO: Check if everything is ok. TagLib::ID3v2::FrameList frames = id3v2->frameListMap()["TXXX"]; for ( TagLib::ID3v2::FrameList::Iterator it = frames.begin(); it != frames.end(); ++it ) { TagLib::ID3v2::UserTextIdentificationFrame* ReplayGainframe = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>( *it ); if ( ReplayGainframe && ReplayGainframe->fieldList().size() >= 2 ) { QString desc = TStringToQString( ReplayGainframe->description() ).toLower(); if ( desc == "replaygain_album_gain" ){ QString sReplayGain = TStringToQString( ReplayGainframe->fieldList()[1]); parseReplayGainString(sReplayGain); } if ( desc == "replaygain_track_gain" ){ QString sReplayGain = TStringToQString( ReplayGainframe->fieldList()[1]); parseReplayGainString(sReplayGain); } } } TagLib::ID3v2::FrameList composerFrame = id3v2->frameListMap()["TCOM"]; if (!composerFrame.isEmpty()) { QString sComposer = TStringToQString(composerFrame.front()->toString()); setComposer(sComposer); } return true; }
void ReplayGainReader::readID3v2(TagLib::ID3v2::Tag *tag) { TagLib::ID3v2::UserTextIdentificationFrame* frame = 0; TagLib::ID3v2::FrameList frames = tag->frameList("TXXX"); for(TagLib::ID3v2::FrameList::Iterator it = frames.begin(); it != frames.end(); ++it) { frame = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>(*it); if(frame && frame->fieldList().size() >= 2) { TagLib::String desc = frame->description().upper(); if (desc == "REPLAYGAIN_TRACK_GAIN") setValue(Qmmp::REPLAYGAIN_TRACK_GAIN, TStringToQString(frame->fieldList()[1])); else if (desc == "REPLAYGAIN_TRACK_PEAK") setValue(Qmmp::REPLAYGAIN_TRACK_PEAK, TStringToQString(frame->fieldList()[1])); else if (desc == "REPLAYGAIN_ALBUM_GAIN") setValue(Qmmp::REPLAYGAIN_ALBUM_GAIN, TStringToQString(frame->fieldList()[1])); else if (desc == "REPLAYGAIN_ALBUM_PEAK") setValue(Qmmp::REPLAYGAIN_ALBUM_PEAK, TStringToQString(frame->fieldList()[1])); } } }
static Meta::ReplayGainTagMap readID3v2Tags( TagLib::ID3v2::Tag *tag ) { Meta::ReplayGainTagMap map; { // ID3v2.4.0 native replay gain tag support (as written by Quod Libet, for example). TagLib::ID3v2::FrameList frames = tag->frameListMap()["RVA2"]; frames.append(tag->frameListMap()["XRVA"]); if ( !frames.isEmpty() ) { for ( unsigned int i = 0; i < frames.size(); ++i ) { // we have to parse this frame ourselves // ID3v2 frame header is 10 bytes, so skip that TagLib::ByteVector data = frames[i]->render().mid( 10 ); unsigned int offset = 0; QString desc( data.data() ); offset += desc.count() + 1; unsigned int channel = data.mid( offset, 1 ).toUInt( true ); // channel 1 is the main volume - the only one we care about if ( channel == 1 ) { ++offset; qint16 adjustment512 = data.mid( offset, 2 ).toShort( true ); qreal adjustment = ( (qreal)adjustment512 ) / 512.0; offset += 2; unsigned int peakBits = data.mid( offset, 1 ).toUInt( true ); ++offset; bool ok = false; qreal peak = readRVA2PeakValue( data.mid( offset ), peakBits, &ok ); if ( ok ) { if ( desc.toLower() == "album" ) { map[Meta::ReplayGain_Album_Gain] = adjustment; map[Meta::ReplayGain_Album_Peak] = peakToDecibels( peak ); } else if ( desc.toLower() == "track" || !map.contains( Meta::ReplayGain_Track_Gain ) ) { map[Meta::ReplayGain_Track_Gain] = adjustment; map[Meta::ReplayGain_Track_Peak] = peakToDecibels( peak ); } } } } if ( !map.isEmpty() ) return map; } } { // Foobar2000-style ID3v2.3.0 tags TagLib::ID3v2::FrameList frames = tag->frameListMap()["TXXX"]; for ( TagLib::ID3v2::FrameList::Iterator it = frames.begin(); it != frames.end(); ++it ) { TagLib::ID3v2::UserTextIdentificationFrame* frame = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>( *it ); if ( frame && frame->fieldList().size() >= 2 ) { QString desc = TStringToQString( frame->description() ).toLower(); if ( desc == "replaygain_album_gain" ) maybeAddGain( frame->fieldList()[1], Meta::ReplayGain_Album_Gain, &map ); if ( desc == "replaygain_album_peak" ) maybeAddPeak( frame->fieldList()[1], Meta::ReplayGain_Album_Peak, &map ); if ( desc == "replaygain_track_gain" ) maybeAddGain( frame->fieldList()[1], Meta::ReplayGain_Track_Gain, &map ); if ( desc == "replaygain_track_peak" ) maybeAddPeak( frame->fieldList()[1], Meta::ReplayGain_Track_Peak, &map ); } } } return map; }
Meta::FieldHash ID3v2TagHelper::tags() const { Meta::FieldHash data = TagHelper::tags(); TagLib::ID3v2::FrameList list = m_tag->frameList(); for( TagLib::ID3v2::FrameList::ConstIterator it = list.begin(); it != list.end(); ++it ) { qint64 field; TagLib::String frameName = TagLib::String( ( *it )->frameID() ); if( ( field = fieldName( frameName ) ) ) { if( field == Meta::valUniqueId ) { TagLib::ID3v2::UniqueFileIdentifierFrame *frame = dynamic_cast< TagLib::ID3v2::UniqueFileIdentifierFrame * >( *it ); if( !frame ) continue; QString identifier = TStringToQString( TagLib::String( frame->identifier() ) ); if( identifier.isEmpty() ) continue; if( frame->owner() == uidFieldName( UIDAFT ) && isValidUID( identifier, UIDAFT ) ) data.insert( Meta::valUniqueId, identifier ); continue; } else if( field == Meta::valHasCover ) { TagLib::ID3v2::AttachedPictureFrame *frame = dynamic_cast< TagLib::ID3v2::AttachedPictureFrame * >( *it ); if( !frame ) continue; if( ( frame->type() == TagLib::ID3v2::AttachedPictureFrame::FrontCover || frame->type() == TagLib::ID3v2::AttachedPictureFrame::Other ) && frame->picture().size() > MIN_COVER_SIZE ) // must be at least 1kb { data.insert( Meta::valHasCover, true ); } continue; } TagLib::ID3v2::TextIdentificationFrame *frame = dynamic_cast< TagLib::ID3v2::TextIdentificationFrame * >( *it ); if( !frame ) continue; QString value = TStringToQString( frame->fieldList().toString( '\n' ) ); if( field == Meta::valDiscNr ) { int disc; if( ( disc = splitDiscNr( value ).first ) ) data.insert( field, disc ); } else if( field == Meta::valBpm ) data.insert( field, value.toFloat() ); else data.insert( field, value ); } else if( frameName == POPM_Frame ) { TagLib::ID3v2::PopularimeterFrame *frame = dynamic_cast< TagLib::ID3v2::PopularimeterFrame * >( *it ); if( !frame ) continue; if( TStringToQString( frame->email() ).isEmpty() ) // only read anonymous ratings { // FMPS tags have precedence if( !data.contains( Meta::valRating ) && frame->rating() != 0 ) data.insert( Meta::valRating, qRound( frame->rating() / 256.0 * 10.0 ) ); if( !data.contains( Meta::valPlaycount ) && frame->counter() < 10000 ) data.insert( Meta::valPlaycount, frame->counter() ); } } else if( frameName == TXXX_Frame ) { TagLib::ID3v2::UserTextIdentificationFrame *frame = dynamic_cast< TagLib::ID3v2::UserTextIdentificationFrame * >( *it ); if( !frame ) continue; // the value of the user text frame is stored in the // second and following fields. TagLib::StringList fields = frame->fieldList(); if( fields.size() >= 2 ) { QString value = TStringToQString( fields[1] ); if( fields[0] == fmpsFieldName( FMPSRating ) ) data.insert( Meta::valRating, qRound( value.toFloat() * 10.0 ) ); else if( fields[0] == fmpsFieldName( FMPSScore ) ) data.insert( Meta::valScore, value.toFloat() * 100.0 ); else if( fields[0] == fmpsFieldName( FMPSPlayCount ) ) data.insert( Meta::valPlaycount, value.toFloat() ); } } } return data; }