From 617679e00162800b3e2baf66052d8dda2faac375 Mon Sep 17 00:00:00 2001 From: Martchus Date: Tue, 23 Feb 2016 20:33:00 +0100 Subject: [PATCH] improved parsing ID3 genres --- aac/aacframe.cpp | 5 +-- id3/id3genres.cpp | 17 ++++++---- id3/id3genres.h | 18 +++++----- id3/id3v2frame.cpp | 82 ++++++++++++++++++++++++++++++++++------------ id3/id3v2frame.h | 2 +- 5 files changed, 85 insertions(+), 39 deletions(-) diff --git a/aac/aacframe.cpp b/aac/aacframe.cpp index 58d8ede..fa83581 100644 --- a/aac/aacframe.cpp +++ b/aac/aacframe.cpp @@ -16,8 +16,9 @@ using namespace std; using namespace IoUtilities; /*! - * \remarks Nothing of this code has been tested yet. These classes are - * not used by the rest of the library (currently). + * \file aacframe.cpp + * \remarks Nothing of this code has been tested yet. It is currently not used + * in the rest of the library. */ namespace Media { diff --git a/id3/id3genres.cpp b/id3/id3genres.cpp index fe6b753..0c91c91 100644 --- a/id3/id3genres.cpp +++ b/id3/id3genres.cpp @@ -32,7 +32,16 @@ const char **Id3Genres::genreNames() "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", - "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall" + "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", + "Goa", "Drum & Bass", "Club-House", "Hardcore Techno", "Terror", "Indie", "BritPop", + "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal", + "Crossover", "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", + "Anime", "Jpop", "Synthpop", "Abstract", "Art Rock", "Baroque", "Bhangra", "Big Beat", "Breakbeat", + "Chillout", "Downtempo", "Dub", "EBM", "Eclectic", "Electro", "Electroclash", "Emo", "Experimental", + "Garage", "Global", "IDM", "Illbient", "Industro-Goth", "Jam Band", "Krautrock", "Leftfield", "Lounge", + "Math Rock", "New Romantic", "Nu-Breakz", "Post-Punk", "Post-Rock", "Psytrance", "Shoegaze", "Space Rock", + "Trop Rock", "World Music", "Neoclassical", "Audiobook", "Audio Theatre", "Neue Deutsche Welle", "Podcast", + "Indie Rock", "G-Funk", "Dubstep", "Garage Rock", "Psybient" }; return names; } @@ -43,13 +52,9 @@ const char **Id3Genres::genreNames() int Id3Genres::indexFromString(const string &genre) { const char **ptr = genreNames(); - int index = 0; - while(index < genreCount()) { + for(int index = 0; index < genreCount(); ++ptr, ++index) { if(genre == *ptr) { return index; - } else { - ++ptr; - ++index; } } return 0; diff --git a/id3/id3genres.h b/id3/id3genres.h index e59e163..c0afbdf 100644 --- a/id3/id3genres.h +++ b/id3/id3genres.h @@ -14,8 +14,8 @@ class LIB_EXPORT Id3Genres public: static const char *stringFromIndex(int index); static int indexFromString(const std::string &genre); - static bool isIndexSupported(int index); - static int genreCount(); + static constexpr int genreCount(); + static constexpr bool isIndexSupported(int index); private: static const char **genreNames(); @@ -30,20 +30,20 @@ inline const char *Id3Genres::stringFromIndex(int index) } /*! - * \brief Returns an indication whether the specified numerical denotation is - * supported by this class. + * \brief Returns the number of supported genres. */ -inline bool Id3Genres::isIndexSupported(int index) +constexpr int Id3Genres::genreCount() { - return index >= 0 && index < genreCount(); + return 192; } /*! - * \brief Returns the number of supported genres. + * \brief Returns an indication whether the specified numerical denotation is + * supported by this class. */ -inline int Id3Genres::genreCount() +constexpr bool Id3Genres::isIndexSupported(int index) { - return 126; + return index >= 0 && index < genreCount(); } } diff --git a/id3/id3v2frame.cpp b/id3/id3v2frame.cpp index 1eda70d..7755979 100644 --- a/id3/id3v2frame.cpp +++ b/id3/id3v2frame.cpp @@ -49,6 +49,52 @@ Id3v2Frame::Id3v2Frame(const identifierType &id, const TagValue &value, const by m_padding(false) {} +/*! + * \brief Helper function to parse the genre index. + * \returns Returns the genre index or -1 if the specified string does not denote a genre index. + */ +template +int parseGenreIndex(const stringtype &denotation, bool isBigEndian = false) +{ + int index = -1; + for(auto c : denotation) { + if(sizeof(typename stringtype::value_type) == 2 && isBigEndian != CONVERSION_UTILITIES_IS_BYTE_ORDER_BIG_ENDIAN) { + c = swapOrder(static_cast(c)); + } + if(index == -1) { + switch(c) { + case ' ': + break; + case '(': + index = 0; + break; + case '\0': + return -1; + default: + if(c >= '0' && c <= '9') { + index = c - '0'; + } else { + return -1; + } + } + } else { + switch(c) { + case ')': + return index; + case '\0': + return index; + default: + if(c >= '0' && c <= '9') { + index = index * 10 + c - '0'; + } else { + return -1; + } + } + } + } + return index; +} + /*! * \brief Parses a frame from the stream read using the specified \a reader. * @@ -195,8 +241,7 @@ void Id3v2Frame::parse(BinaryReader &reader, const uint32 version, const uint32 double milliseconds; try { if(characterSize(dataEncoding) > 1) { - wstring millisecondsStr = parseWideString(buffer.get() + 1, m_dataSize - 1, dataEncoding); - milliseconds = ConversionUtilities::stringToNumber(millisecondsStr, 10); + milliseconds = ConversionUtilities::stringToNumber(parseWideString(buffer.get() + 1, m_dataSize - 1, dataEncoding), 10); } else { milliseconds = ConversionUtilities::stringToNumber(parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding), 10); } @@ -208,22 +253,17 @@ void Id3v2Frame::parse(BinaryReader &reader, const uint32 version, const uint32 } else if((version >= 3 && id() == Id3v2FrameIds::lGenre) || (version < 3 && id() == Id3v2FrameIds::sGenre)) { // genre/content type int genreIndex; - try { - if(characterSize(dataEncoding) > 1) { - wstring indexStr = parseWideString(buffer.get() + 1, m_dataSize - 1, dataEncoding); - if(indexStr.front() == L'(' && indexStr.back() == L')') { - indexStr = indexStr.substr(1, indexStr.length() - 2); - } - genreIndex = ConversionUtilities::stringToNumber(indexStr, 10); - } else { - string indexStr = parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding); - if(indexStr.front() == '(' && indexStr.back() == ')') { - indexStr = indexStr.substr(1, indexStr.length() - 2); - } - genreIndex = ConversionUtilities::stringToNumber(indexStr, 10); - } - value().assignStandardGenreIndex(genreIndex); // genre is specified as ID3 genre number - } catch(ConversionException &) { + if(characterSize(dataEncoding) > 1) { + auto genreDenotation = parseWideString(buffer.get() + 1, m_dataSize - 1, dataEncoding); + genreIndex = parseGenreIndex(genreDenotation, dataEncoding == TagTextEncoding::Utf16BigEndian); + } else { + auto genreDenotation = parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding); + genreIndex = parseGenreIndex(genreDenotation); + } + if(genreIndex != -1) { + // genre is specified as ID3 genre number + value().assignStandardGenreIndex(genreIndex); + } else { // genre is specified as string // string might be null terminated auto substr = parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding); @@ -600,12 +640,12 @@ string Id3v2Frame::parseString(const char *buffer, size_t dataSize, TagTextEncod /*! * \brief Parses a substring in the specified \a buffer. * - * Same as Id3v2Frame::parseSubstring() but returns the substring as wstring object. + * Same as Id3v2Frame::parseSubstring() but returns the substring as u16string object. */ -wstring Id3v2Frame::parseWideString(const char *buffer, size_t dataSize, TagTextEncoding &encoding, bool addWarnings) +u16string Id3v2Frame::parseWideString(const char *buffer, size_t dataSize, TagTextEncoding &encoding, bool addWarnings) { auto substr = parseSubstring(buffer, dataSize, encoding, addWarnings); - return wstring(reinterpret_cast(get<0>(substr)), get<1>(substr) / 2); + return u16string(reinterpret_cast(get<0>(substr)), get<1>(substr) / 2); } /*! diff --git a/id3/id3v2frame.h b/id3/id3v2frame.h index a96a0eb..53fca66 100644 --- a/id3/id3v2frame.h +++ b/id3/id3v2frame.h @@ -137,7 +137,7 @@ public: TagTextEncoding parseTextEncodingByte(byte textEncodingByte); std::tuple parseSubstring(const char *buffer, std::size_t maxSize, TagTextEncoding &encoding, bool addWarnings = false); std::string parseString(const char *buffer, std::size_t maxSize, TagTextEncoding &encoding, bool addWarnings = false); - std::wstring parseWideString(const char *buffer, std::size_t dataSize, TagTextEncoding &encoding, bool addWarnings = false); + std::u16string parseWideString(const char *buffer, std::size_t dataSize, TagTextEncoding &encoding, bool addWarnings = false); void parseLegacyPicture(const char *buffer, size_t maxSize, TagValue &tagValue, byte &typeInfo); void parsePicture(const char *buffer, size_t maxSize, TagValue &tagValue, byte &typeInfo); void parseComment(const char *buffer, size_t maxSize, TagValue &tagValue);