aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2012-08-19 22:37:14 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2012-08-19 22:37:14 +0200
commit4d73bd5c0a0aaededa70d59b2b862c3d7fa4b13f (patch)
treef3aac202e0d2af45c951b284650a1b2df9654f7e
downloadmusic-file-organizer-4d73bd5c0a0aaededa70d59b2b862c3d7fa4b13f.tar.xz
music-file-organizer-4d73bd5c0a0aaededa70d59b2b862c3d7fa4b13f.zip
Initial commit of tag extractor and test main.
-rw-r--r--AudioFile.cpp189
-rw-r--r--AudioFile.h39
-rw-r--r--test-main.cpp30
3 files changed, 258 insertions, 0 deletions
diff --git a/AudioFile.cpp b/AudioFile.cpp
new file mode 100644
index 0000000..542d0c6
--- /dev/null
+++ b/AudioFile.cpp
@@ -0,0 +1,189 @@
+#include "AudioFile.h"
+
+#include <sstream>
+#include <algorithm>
+
+#include <taglib.h>
+#include <fileref.h>
+
+#include <mpegfile.h>
+#include <id3v2tag.h>
+
+#include <vorbisfile.h>
+#include <flacfile.h>
+#include <xiphcomment.h>
+
+#include <mp4file.h>
+#include <mp4tag.h>
+
+template <typename T, typename M>
+inline T extractTag(M &map, const char *key)
+{
+ T ret;
+ std::stringstream stream(extractTag<std::string>(map, key));
+ stream >> ret;
+ return ret;
+}
+template <typename M>
+inline bool extractTag(M &map, const char *key)
+{
+ std::string str(extractTag<std::string>(map, key));
+ std::transform(str.begin(), str.end(), str.begin(), ::tolower);
+ return (str == "1" || str == "true");
+}
+template <>
+inline std::string extractTag(const TagLib::ID3v2::FrameListMap &map, const char *key)
+{
+ if (map[key].isEmpty())
+ return std::string();
+ return map[key].front()->toString().to8Bit(true);
+}
+template <>
+inline std::string extractTag(const TagLib::Ogg::FieldListMap &map, const char *key)
+{
+ if (map[key].isEmpty())
+ return std::string();
+ return map[key].front().to8Bit(true);
+}
+template <>
+inline unsigned int extractTag(const TagLib::MP4::ItemListMap &map, const char *key)
+{
+ if (!map[key].isValid())
+ return 0;
+ return map[key].toInt();
+}
+template <>
+inline std::string extractTag(const TagLib::MP4::ItemListMap &map, const char *key)
+{
+ if (!map[key].isValid())
+ return std::string();
+ return map[key].toStringList().toString().to8Bit(true);
+}
+template <>
+inline bool extractTag(const TagLib::MP4::ItemListMap &map, const char *key)
+{
+ if (!map[key].isValid())
+ return false;
+ return map[key].toBool();
+}
+
+AudioFile::AudioFile(const std::string &filename) :
+ m_isValid(false),
+ m_filename(filename),
+ m_track(0),
+ m_disc(0),
+ m_bpm(0),
+ m_year(0),
+ m_compilation(false)
+{
+ TagLib::FileRef fileRef(filename.c_str());
+ if (fileRef.isNull() || !fileRef.tag())
+ return;
+
+ /* First we copy out everything TagLib actually supports. */
+ const TagLib::Tag *tag = fileRef.tag();
+ m_title = tag->title().to8Bit(true);
+ m_artist = tag->artist().to8Bit(true);
+ m_album = tag->album().to8Bit(true);
+ m_comment = tag->comment().to8Bit(true);
+ m_genre = tag->genre().to8Bit(true);
+ m_year = tag->year();
+ m_track = tag->track();
+
+ /* Now we look at format specific tags. */
+ if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File*>(fileRef.file())) {
+ if (file->ID3v2Tag()) {
+ const TagLib::ID3v2::FrameListMap &map = file->ID3v2Tag()->frameListMap();
+ m_disc = extractTag<unsigned int>(map, "TPOS");
+ m_bpm = extractTag<unsigned int>(map, "TBPM");
+ m_composer = extractTag<std::string>(map, "TCOM");
+ m_albumArtist = extractTag<std::string>(map, "TPE2");
+ m_compilation = extractTag<bool>(map, "TCMP");
+ }
+
+ } else if (TagLib::Ogg::Vorbis::File *file = dynamic_cast<TagLib::Ogg::Vorbis::File*>(fileRef.file())) {
+ if (file->tag()) {
+ const TagLib::Ogg::FieldListMap &map = file->tag()->fieldListMap();
+ m_disc = extractTag<unsigned int>(map, "DISCNUMBER");
+ m_bpm = extractTag<unsigned int>(map, "BPM");
+ m_composer = extractTag<std::string>(map, "COMPOSER");
+ m_compilation = extractTag<bool>(map, "COMPILATION");
+ }
+ } else if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File*>(fileRef.file())) {
+ if (file->xiphComment()) {
+ const TagLib::Ogg::FieldListMap &map = file->xiphComment()->fieldListMap();
+ m_disc = extractTag<unsigned int>(map, "DISCNUMBER");
+ m_bpm = extractTag<unsigned int>(map, "BPM");
+ m_composer = extractTag<std::string>(map, "COMPOSER");
+ m_compilation = extractTag<bool>(map, "COMPILATION");
+ }
+ } else if (TagLib::MP4::File *file = dynamic_cast<TagLib::MP4::File*>(fileRef.file())) {
+ if (file->tag()) {
+ const TagLib::MP4::ItemListMap &map = file->tag()->itemListMap();
+ m_disc = extractTag<unsigned int>(map, "disk");
+ m_bpm = extractTag<unsigned int>(map, "tmpo");
+ m_albumArtist = extractTag<std::string>(map, "aART");
+ m_composer = extractTag<std::string>(map, "\xa9wrt");
+ m_compilation = extractTag<bool>(map, "cpil");
+ }
+ }
+
+ m_isValid = true;
+}
+
+bool AudioFile::isValid() const
+{
+ return m_isValid;
+}
+std::string AudioFile::filename() const
+{
+ return m_filename;
+}
+std::string AudioFile::artist() const
+{
+ return m_artist;
+}
+std::string AudioFile::composer() const
+{
+ return m_composer;
+}
+std::string AudioFile::album() const
+{
+ return m_album;
+}
+std::string AudioFile::albumArtist() const
+{
+ return m_albumArtist;
+}
+std::string AudioFile::title() const
+{
+ return m_title;
+}
+std::string AudioFile::genre() const
+{
+ return m_genre;
+}
+std::string AudioFile::comment() const
+{
+ return m_comment;
+}
+unsigned int AudioFile::track() const
+{
+ return m_track;
+}
+unsigned int AudioFile::disc() const
+{
+ return m_disc;
+}
+unsigned int AudioFile::bpm() const
+{
+ return m_bpm;
+}
+unsigned int AudioFile::year() const
+{
+ return m_year;
+}
+bool AudioFile::compilation() const
+{
+ return m_compilation;
+}
diff --git a/AudioFile.h b/AudioFile.h
new file mode 100644
index 0000000..73618ba
--- /dev/null
+++ b/AudioFile.h
@@ -0,0 +1,39 @@
+#include <string>
+
+class AudioFile
+{
+public:
+ AudioFile(const std::string &filename);
+ bool isValid() const;
+
+ std::string filename() const;
+ std::string artist() const;
+ std::string composer() const;
+ std::string album() const;
+ std::string albumArtist() const;
+ std::string title() const;
+ std::string genre() const;
+ std::string comment() const;
+ unsigned int track() const;
+ unsigned int disc() const;
+ unsigned int bpm() const;
+ unsigned int year() const;
+ bool compilation() const;
+
+private:
+ bool m_isValid;
+
+ std::string m_filename;
+ std::string m_artist;
+ std::string m_composer;
+ std::string m_album;
+ std::string m_albumArtist;
+ std::string m_title;
+ std::string m_genre;
+ std::string m_comment;
+ unsigned int m_track;
+ unsigned int m_disc;
+ unsigned int m_bpm;
+ unsigned int m_year;
+ bool m_compilation;
+};
diff --git a/test-main.cpp b/test-main.cpp
new file mode 100644
index 0000000..b5a3ef4
--- /dev/null
+++ b/test-main.cpp
@@ -0,0 +1,30 @@
+#include "AudioFile.h"
+
+#include <iostream>
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ for (int i = 1; i < argc; ++i) {
+ AudioFile f(argv[i]);
+ if (!f.isValid()) {
+ cout << argv[i] << " is not valid." << endl;
+ continue;
+ }
+ cout << "Filename: " << f.filename() << endl;
+ cout << "Artist: " << f.artist() << endl;
+ cout << "Composer: " << f.composer() << endl;
+ cout << "Album: " << f.album() << endl;
+ cout << "Album Artist: " << f.albumArtist() << endl;
+ cout << "Title: " << f.title() << endl;
+ cout << "Genre: " << f.genre() << endl;
+ cout << "Comment: " << f.comment() << endl;
+ cout << "Track: " << f.track() << endl;
+ cout << "Disc: " << f.disc() << endl;
+ cout << "Bpm: " << f.bpm() << endl;
+ cout << "Year: " << f.year() << endl;
+ cout << "Compilation: " << f.compilation() << endl;
+ cout << endl;
+ }
+}