![]() |
Mixxx
|
00001 /*************************************************************************** 00002 soundsource.cpp - description 00003 ------------------- 00004 begin : Wed Feb 20 2002 00005 copyright : (C) 2002 by Tue and Ken Haste Andersen 00006 email : 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * * 00011 * This program is free software; you can redistribute it and/or modify * 00012 * it under the terms of the GNU General Public License as published by * 00013 * the Free Software Foundation; either version 2 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 ***************************************************************************/ 00017 00018 #include <QtDebug> 00019 00020 #include <taglib/tag.h> 00021 #include <taglib/audioproperties.h> 00022 #include <taglib/vorbisfile.h> 00023 #include <taglib/id3v2frame.h> 00024 #include <taglib/id3v2header.h> 00025 #include <taglib/id3v1tag.h> 00026 #include <taglib/tmap.h> 00027 #include <taglib/tstringlist.h> 00028 #include <taglib/textidentificationframe.h> 00029 #include <taglib/wavpackfile.h> 00030 00031 #include "soundsource.h" 00032 00033 namespace Mixxx 00034 { 00035 00036 // static 00037 const bool SoundSource::s_bDebugMetadata = false; 00038 00039 /* 00040 SoundSource is an Uber-class for the reading and decoding of audio-files. 00041 Each class must have the following member functions: 00042 initializer with a filename 00043 seek() 00044 read() 00045 length() 00046 In addition there must be a static member: 00047 int ParseHeader(TrackInfoObject *Track) 00048 which is used for parsing header information, like trackname,length etc. The 00049 return type is int: 0 for OK, -1 for an error. 00050 */ 00051 SoundSource::SoundSource(QString qFilename) 00052 { 00053 m_qFilename = qFilename; 00054 m_iSampleRate = 0; 00055 m_fBPM = 0.0f; 00056 m_fReplayGain = 0.0f; 00057 m_iDuration = 0; 00058 m_iBitrate = 0; 00059 m_iChannels = 0; 00060 m_sKey = ""; 00061 } 00062 00063 SoundSource::~SoundSource() 00064 { 00065 } 00066 00067 00068 QList<long> *SoundSource::getCuePoints() 00069 { 00070 return 0; 00071 } 00072 00073 QString SoundSource::getFilename() 00074 { 00075 return m_qFilename; 00076 } 00077 00078 float SoundSource::str2bpm( QString sBpm ) { 00079 float bpm = sBpm.toFloat(); 00080 if(bpm < 60) bpm = 0; 00081 while( bpm > 300 ) bpm = bpm / 10.; 00082 return bpm; 00083 } 00084 00085 QString SoundSource::getArtist() 00086 { 00087 return m_sArtist; 00088 } 00089 QString SoundSource::getTitle() 00090 { 00091 return m_sTitle; 00092 } 00093 QString SoundSource::getAlbum() 00094 { 00095 return m_sAlbum; 00096 } 00097 QString SoundSource::getType() 00098 { 00099 return m_sType; 00100 } 00101 QString SoundSource::getComment() 00102 { 00103 return m_sComment; 00104 } 00105 QString SoundSource::getYear() 00106 { 00107 return m_sYear; 00108 } 00109 QString SoundSource::getGenre() 00110 { 00111 return m_sGenre; 00112 } 00113 QString SoundSource::getTrackNumber() 00114 { 00115 return m_sTrackNumber; 00116 } 00117 float SoundSource::getReplayGain() 00118 { 00119 return m_fReplayGain; 00120 } 00121 float SoundSource::getBPM() 00122 { 00123 return m_fBPM; 00124 } 00125 int SoundSource::getDuration() 00126 { 00127 return m_iDuration; 00128 } 00129 int SoundSource::getBitrate() 00130 { 00131 return m_iBitrate; 00132 } 00133 unsigned int SoundSource::getSampleRate() 00134 { 00135 return m_iSampleRate; 00136 } 00137 int SoundSource::getChannels() 00138 { 00139 return m_iChannels; 00140 } 00141 00142 void SoundSource::setArtist(QString artist) 00143 { 00144 m_sArtist = artist; 00145 } 00146 void SoundSource::setTitle(QString title) 00147 { 00148 m_sTitle = title; 00149 } 00150 void SoundSource::setAlbum(QString album) 00151 { 00152 m_sAlbum = album; 00153 } 00154 void SoundSource::setComment(QString comment) 00155 { 00156 m_sComment = comment; 00157 } 00158 void SoundSource::setType(QString type) 00159 { 00160 m_sType = type; 00161 } 00162 void SoundSource::setYear(QString year) 00163 { 00164 m_sYear = year; 00165 } 00166 void SoundSource::setGenre(QString genre) 00167 { 00168 m_sGenre = genre; 00169 } 00170 void SoundSource::setTrackNumber(QString trackNumber) 00171 { 00172 m_sTrackNumber = trackNumber; 00173 } 00174 void SoundSource::setReplayGain(float replaygain) 00175 { 00176 m_fReplayGain = replaygain; 00177 } 00178 void SoundSource::setBPM(float bpm) 00179 { 00180 m_fBPM = bpm; 00181 } 00182 void SoundSource::setDuration(int duration) 00183 { 00184 m_iDuration = duration; 00185 } 00186 void SoundSource::setBitrate(int bitrate) 00187 { 00188 m_iBitrate = bitrate; 00189 } 00190 void SoundSource::setSampleRate(unsigned int samplerate) 00191 { 00192 m_iSampleRate = samplerate; 00193 } 00194 void SoundSource::setChannels(int channels) 00195 { 00196 m_iChannels = channels; 00197 } 00198 QString SoundSource::getKey(){ 00199 return m_sKey; 00200 } 00201 void SoundSource::setKey(QString key){ 00202 m_sKey = key; 00203 } 00204 00205 bool SoundSource::processTaglibFile(TagLib::File& f) { 00206 if (s_bDebugMetadata) 00207 qDebug() << "Parsing" << getFilename(); 00208 00209 if (f.isValid()) { 00210 TagLib::Tag *tag = f.tag(); 00211 if (tag) { 00212 QString title = TStringToQString(tag->title()); 00213 setTitle(title); 00214 00215 QString artist = TStringToQString(tag->artist()); 00216 setArtist(artist); 00217 00218 QString album = TStringToQString(tag->album()); 00219 setAlbum(album); 00220 00221 QString comment = TStringToQString(tag->comment()); 00222 setComment(comment); 00223 00224 QString genre = TStringToQString(tag->genre()); 00225 setGenre(genre); 00226 00227 int iYear = tag->year(); 00228 QString year = ""; 00229 if (iYear > 0) { 00230 year = QString("%1").arg(iYear); 00231 setYear(year); 00232 } 00233 00234 int iTrack = tag->track(); 00235 QString trackNumber = ""; 00236 if (iTrack > 0) { 00237 trackNumber = QString("%1").arg(tag->track()); 00238 setTrackNumber(trackNumber); 00239 } 00240 00241 if (s_bDebugMetadata) 00242 qDebug() << "TagLib" << "title" << title << "artist" << artist << "album" << album << "comment" << comment << "genre" << genre << "year" << year << "trackNumber" << trackNumber; 00243 } 00244 00245 TagLib::AudioProperties *properties = f.audioProperties(); 00246 if (properties) { 00247 int lengthSeconds = properties->length(); 00248 int bitrate = properties->bitrate(); 00249 int sampleRate = properties->sampleRate(); 00250 int channels = properties->channels(); 00251 00252 if (s_bDebugMetadata) 00253 qDebug() << "TagLib" << "length" << lengthSeconds << "bitrate" << bitrate << "sampleRate" << sampleRate << "channels" << channels; 00254 00255 setDuration(lengthSeconds); 00256 setBitrate(bitrate); 00257 setSampleRate(sampleRate); 00258 setChannels(channels); 00259 } 00260 00261 // If we didn't get any audio properties, this was a failure. 00262 return properties; 00263 } 00264 return false; 00265 } 00266 00267 void SoundSource::parseReplayGainString (QString sReplayGain) { 00268 QString ReplayGainstring = sReplayGain.remove( " dB" ); 00269 float fReplayGain = pow(10,(ReplayGainstring.toFloat())/20); 00270 //I found some mp3s of mine with replaygain tag set to 0dB even if not normalized. 00271 //This is because of Rapid Evolution 3, I suppose. I prefer to rescan them by setting value to 0 (i.e. rescan via analyserrg) 00272 if(fReplayGain==1.0f){ 00273 fReplayGain= 0.0f; 00274 } 00275 setReplayGain(fReplayGain); 00276 } 00277 00278 void SoundSource::processBpmString(QString tagName, QString sBpm) { 00279 if (s_bDebugMetadata) 00280 qDebug() << tagName << "BPM" << sBpm; 00281 if (sBpm.length() > 0) { 00282 float fBpm = str2bpm(sBpm); 00283 if (fBpm > 0) 00284 setBPM(fBpm); 00285 } 00286 } 00287 00288 bool SoundSource::processID3v2Tag(TagLib::ID3v2::Tag* id3v2) { 00289 00290 // Print every frame in the file. 00291 if (s_bDebugMetadata) { 00292 TagLib::ID3v2::FrameList::ConstIterator it = id3v2->frameList().begin(); 00293 for(; it != id3v2->frameList().end(); it++) { 00294 qDebug() << "ID3V2" << (*it)->frameID().data() << "-" 00295 << TStringToQString((*it)->toString()); 00296 } 00297 } 00298 00299 TagLib::ID3v2::FrameList bpmFrame = id3v2->frameListMap()["TBPM"]; 00300 if (!bpmFrame.isEmpty()) { 00301 QString sBpm = TStringToQString(bpmFrame.front()->toString()); 00302 processBpmString("ID3v2", sBpm); 00303 } 00304 00305 TagLib::ID3v2::FrameList keyFrame = id3v2->frameListMap()["TKEY"]; 00306 if (!keyFrame.isEmpty()) { 00307 QString sKey = TStringToQString(keyFrame.front()->toString()); 00308 setKey(sKey); 00309 } 00310 // Foobar2000-style ID3v2.3.0 tags 00311 // TODO: Check if everything is ok. 00312 TagLib::ID3v2::FrameList frames = id3v2->frameListMap()["TXXX"]; 00313 for ( TagLib::ID3v2::FrameList::Iterator it = frames.begin(); it != frames.end(); ++it ) { 00314 TagLib::ID3v2::UserTextIdentificationFrame* ReplayGainframe = 00315 dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>( *it ); 00316 if ( ReplayGainframe && ReplayGainframe->fieldList().size() >= 2 ) 00317 { 00318 QString desc = TStringToQString( ReplayGainframe->description() ).toLower(); 00319 if ( desc == "replaygain_album_gain" ){ 00320 QString sReplayGain = TStringToQString( ReplayGainframe->fieldList()[1]); 00321 parseReplayGainString(sReplayGain); 00322 } 00323 if ( desc == "replaygain_track_gain" ){ 00324 QString sReplayGain = TStringToQString( ReplayGainframe->fieldList()[1]); 00325 parseReplayGainString(sReplayGain); 00326 } 00327 } 00328 } 00329 return true; 00330 } 00331 00332 bool SoundSource::processAPETag(TagLib::APE::Tag* ape) { 00333 if (s_bDebugMetadata) { 00334 for(TagLib::APE::ItemListMap::ConstIterator it = ape->itemListMap().begin(); 00335 it != ape->itemListMap().end(); ++it) { 00336 qDebug() << "APE" << TStringToQString((*it).first) << "-" << TStringToQString((*it).second.toString()); 00337 } 00338 } 00339 00340 if (ape->itemListMap().contains("BPM")) { 00341 QString sBpm = TStringToQString(ape->itemListMap()["BPM"].toString()); 00342 processBpmString("APE", sBpm); 00343 } 00344 00345 if (ape->itemListMap().contains("REPLAYGAIN_ALBUM_GAIN")) { 00346 QString sReplayGain = TStringToQString(ape->itemListMap()["REPLAYGAIN_ALBUM_GAIN"].toString()); 00347 parseReplayGainString(sReplayGain); 00348 } 00349 00350 //Prefer track gain over album gain. 00351 if (ape->itemListMap().contains("REPLAYGAIN_TRACK_GAIN")) { 00352 QString sReplayGain = TStringToQString(ape->itemListMap()["REPLAYGAIN_TRACK_GAIN"].toString()); 00353 parseReplayGainString(sReplayGain); 00354 } 00355 return true; 00356 } 00357 00358 bool SoundSource::processXiphComment(TagLib::Ogg::XiphComment* xiph) { 00359 if (s_bDebugMetadata) { 00360 for (TagLib::Ogg::FieldListMap::ConstIterator it = xiph->fieldListMap().begin(); 00361 it != xiph->fieldListMap().end(); ++it) { 00362 qDebug() << "XIPH" << TStringToQString((*it).first) << "-" << TStringToQString((*it).second.toString()); 00363 } 00364 } 00365 00366 // Some tags use "BPM" so check for that. 00367 if (xiph->fieldListMap().contains("BPM")) { 00368 TagLib::StringList bpmString = xiph->fieldListMap()["BPM"]; 00369 QString sBpm = TStringToQString(bpmString.toString()); 00370 processBpmString("XIPH-BPM", sBpm); 00371 } 00372 00373 // Give preference to the "TEMPO" tag which seems to be more standard 00374 if (xiph->fieldListMap().contains("TEMPO")) { 00375 TagLib::StringList bpmString = xiph->fieldListMap()["TEMPO"]; 00376 QString sBpm = TStringToQString(bpmString.toString()); 00377 processBpmString("XIPH-TEMPO", sBpm); 00378 } 00379 00380 if (xiph->fieldListMap().contains("REPLAYGAIN_ALBUM_GAIN")) { 00381 TagLib::StringList rgainString = xiph->fieldListMap()["REPLAYGAIN_ALBUM_GAIN"]; 00382 QString sReplayGain = TStringToQString(rgainString.toString()); 00383 parseReplayGainString(sReplayGain); 00384 } 00385 00386 if (xiph->fieldListMap().contains("REPLAYGAIN_TRACK_GAIN")) { 00387 TagLib::StringList rgainString = xiph->fieldListMap()["REPLAYGAIN_TRACK_GAIN"]; 00388 QString sReplayGain = TStringToQString(rgainString.toString()); 00389 parseReplayGainString(sReplayGain); 00390 } 00391 00392 /* 00393 * Reading key code information 00394 * Unlike, ID3 tags, there's no standard or recommendation on how to store 'key' code 00395 * 00396 * Luckily, there are only a few tools for that, e.g., Rapid Evolution (RE). 00397 * Assuming no distinction between start and end key, RE uses a "INITIALKEY" 00398 * or a "KEY" vorbis comment. 00399 */ 00400 if (xiph->fieldListMap().contains("KEY")) { 00401 TagLib::StringList keyStr = xiph->fieldListMap()["KEY"]; 00402 QString key = TStringToQString(keyStr.toString()); 00403 setKey(key); 00404 } 00405 00406 if (getKey() == "" && xiph->fieldListMap().contains("INITIALKEY")) { 00407 TagLib::StringList keyStr = xiph->fieldListMap()["INITIALKEY"]; 00408 QString key = TStringToQString(keyStr.toString()); 00409 setKey(key); 00410 } 00411 return true; 00412 } 00413 00414 bool SoundSource::processMP4Tag(TagLib::MP4::Tag* mp4) { 00415 if (s_bDebugMetadata) { 00416 for(TagLib::MP4::ItemListMap::ConstIterator it = mp4->itemListMap().begin(); 00417 it != mp4->itemListMap().end(); ++it) { 00418 qDebug() << "MP4" << TStringToQString((*it).first) << "-" << TStringToQString((*it).second.toStringList().toString()); 00419 } 00420 } 00421 00422 // Get BPM 00423 if (mp4->itemListMap().contains("tmpo")) { 00424 QString sBpm = TStringToQString(mp4->itemListMap()["tmpo"].toStringList().toString()); 00425 processBpmString("MP4", sBpm); 00426 } 00427 // Get KEY (conforms to Rapid Evolution) 00428 if (mp4->itemListMap().contains("----:com.apple.iTunes:KEY")) { 00429 QString key = TStringToQString(mp4->itemListMap()["----:com.apple.iTunes:KEY"].toStringList().toString()); 00430 setKey(key); 00431 } 00432 00433 return true; 00434 } 00435 00436 } //namespace Mixxx