From 03a97d87ea0b813951348d3cb03f0f82abcca12e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 8 Feb 2017 08:26:58 +0100 Subject: tag/Tag*: rename several source files --- src/tag/ApeTag.cxx | 4 +- src/tag/ApeTag.hxx | 2 +- src/tag/Builder.cxx | 263 +++++++++++++++++++++++++++++++++++ src/tag/Builder.hxx | 176 ++++++++++++++++++++++++ src/tag/Config.cxx | 72 ++++++++++ src/tag/Config.hxx | 26 ++++ src/tag/FixString.cxx | 137 +++++++++++++++++++ src/tag/FixString.hxx | 33 +++++ src/tag/Generic.cxx | 2 +- src/tag/Handler.cxx | 74 ++++++++++ src/tag/Handler.hxx | 98 +++++++++++++ src/tag/Id3Scan.cxx | 363 +++++++++++++++++++++++++++++++++++++++++++++++++ src/tag/Id3Scan.hxx | 45 ++++++ src/tag/Item.hxx | 47 +++++++ src/tag/Names.c | 46 +++++++ src/tag/Pool.cxx | 171 +++++++++++++++++++++++ src/tag/Pool.hxx | 40 ++++++ src/tag/Rva2.cxx | 150 ++++++++++++++++++++ src/tag/Rva2.hxx | 37 +++++ src/tag/Set.cxx | 2 +- src/tag/Settings.hxx | 2 +- src/tag/Table.cxx | 63 +++++++++ src/tag/Table.hxx | 59 ++++++++ src/tag/Tag.cxx | 4 +- src/tag/Tag.hxx | 4 +- src/tag/TagBuilder.cxx | 263 ----------------------------------- src/tag/TagBuilder.hxx | 176 ------------------------ src/tag/TagConfig.cxx | 72 ---------- src/tag/TagConfig.hxx | 26 ---- src/tag/TagHandler.cxx | 74 ---------- src/tag/TagHandler.hxx | 98 ------------- src/tag/TagId3.cxx | 363 ------------------------------------------------- src/tag/TagId3.hxx | 45 ------ src/tag/TagItem.hxx | 47 ------- src/tag/TagNames.c | 46 ------- src/tag/TagPool.cxx | 171 ----------------------- src/tag/TagPool.hxx | 40 ------ src/tag/TagRva2.cxx | 150 -------------------- src/tag/TagRva2.hxx | 37 ----- src/tag/TagString.cxx | 137 ------------------- src/tag/TagString.hxx | 33 ----- src/tag/TagTable.cxx | 63 --------- src/tag/TagTable.hxx | 59 -------- src/tag/TagType.h | 69 ---------- src/tag/Type.h | 69 ++++++++++ 45 files changed, 1979 insertions(+), 1979 deletions(-) create mode 100644 src/tag/Builder.cxx create mode 100644 src/tag/Builder.hxx create mode 100644 src/tag/Config.cxx create mode 100644 src/tag/Config.hxx create mode 100644 src/tag/FixString.cxx create mode 100644 src/tag/FixString.hxx create mode 100644 src/tag/Handler.cxx create mode 100644 src/tag/Handler.hxx create mode 100644 src/tag/Id3Scan.cxx create mode 100644 src/tag/Id3Scan.hxx create mode 100644 src/tag/Item.hxx create mode 100644 src/tag/Names.c create mode 100644 src/tag/Pool.cxx create mode 100644 src/tag/Pool.hxx create mode 100644 src/tag/Rva2.cxx create mode 100644 src/tag/Rva2.hxx create mode 100644 src/tag/Table.cxx create mode 100644 src/tag/Table.hxx delete mode 100644 src/tag/TagBuilder.cxx delete mode 100644 src/tag/TagBuilder.hxx delete mode 100644 src/tag/TagConfig.cxx delete mode 100644 src/tag/TagConfig.hxx delete mode 100644 src/tag/TagHandler.cxx delete mode 100644 src/tag/TagHandler.hxx delete mode 100644 src/tag/TagId3.cxx delete mode 100644 src/tag/TagId3.hxx delete mode 100644 src/tag/TagItem.hxx delete mode 100644 src/tag/TagNames.c delete mode 100644 src/tag/TagPool.cxx delete mode 100644 src/tag/TagPool.hxx delete mode 100644 src/tag/TagRva2.cxx delete mode 100644 src/tag/TagRva2.hxx delete mode 100644 src/tag/TagString.cxx delete mode 100644 src/tag/TagString.hxx delete mode 100644 src/tag/TagTable.cxx delete mode 100644 src/tag/TagTable.hxx delete mode 100644 src/tag/TagType.h create mode 100644 src/tag/Type.h (limited to 'src/tag') diff --git a/src/tag/ApeTag.cxx b/src/tag/ApeTag.cxx index ebf988ee5..6e5a88a8f 100644 --- a/src/tag/ApeTag.cxx +++ b/src/tag/ApeTag.cxx @@ -21,8 +21,8 @@ #include "ApeTag.hxx" #include "ApeLoader.hxx" #include "Tag.hxx" -#include "TagTable.hxx" -#include "TagHandler.hxx" +#include "Table.hxx" +#include "Handler.hxx" #include "util/StringView.hxx" #include diff --git a/src/tag/ApeTag.hxx b/src/tag/ApeTag.hxx index 0586e530d..93b8ba126 100644 --- a/src/tag/ApeTag.hxx +++ b/src/tag/ApeTag.hxx @@ -20,7 +20,7 @@ #ifndef MPD_APE_TAG_HXX #define MPD_APE_TAG_HXX -#include "TagTable.hxx" +#include "Table.hxx" class InputStream; struct TagHandler; diff --git a/src/tag/Builder.cxx b/src/tag/Builder.cxx new file mode 100644 index 000000000..57f176869 --- /dev/null +++ b/src/tag/Builder.cxx @@ -0,0 +1,263 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Builder.hxx" +#include "Settings.hxx" +#include "Pool.hxx" +#include "FixString.hxx" +#include "Tag.hxx" +#include "util/WritableBuffer.hxx" +#include "util/StringView.hxx" + +#include + +#include +#include + +TagBuilder::TagBuilder(const Tag &other) + :duration(other.duration), has_playlist(other.has_playlist) +{ + items.reserve(other.num_items); + + tag_pool_lock.lock(); + for (unsigned i = 0, n = other.num_items; i != n; ++i) + items.push_back(tag_pool_dup_item(other.items[i])); + tag_pool_lock.unlock(); +} + +TagBuilder::TagBuilder(Tag &&other) + :duration(other.duration), has_playlist(other.has_playlist) +{ + /* move all TagItem pointers from the Tag object; we don't + need to contact the tag pool, because all we do is move + references */ + items.reserve(other.num_items); + std::copy_n(other.items, other.num_items, std::back_inserter(items)); + + /* discard the pointers from the Tag object */ + other.num_items = 0; + delete[] other.items; + other.items = nullptr; +} + +TagBuilder & +TagBuilder::operator=(const TagBuilder &other) +{ + /* copy all attributes */ + duration = other.duration; + has_playlist = other.has_playlist; + items = other.items; + + /* increment the tag pool refcounters */ + tag_pool_lock.lock(); + for (auto i : items) + tag_pool_dup_item(i); + tag_pool_lock.unlock(); + + return *this; +} + +TagBuilder & +TagBuilder::operator=(TagBuilder &&other) +{ + duration = other.duration; + has_playlist = other.has_playlist; + items = std::move(other.items); + + return *this; +} + +TagBuilder & +TagBuilder::operator=(Tag &&other) +{ + duration = other.duration; + has_playlist = other.has_playlist; + + /* move all TagItem pointers from the Tag object; we don't + need to contact the tag pool, because all we do is move + references */ + items.clear(); + items.reserve(other.num_items); + std::copy_n(other.items, other.num_items, std::back_inserter(items)); + + /* discard the pointers from the Tag object */ + other.num_items = 0; + delete[] other.items; + other.items = nullptr; + + return *this; +} + +void +TagBuilder::Clear() +{ + duration = SignedSongTime::Negative(); + has_playlist = false; + RemoveAll(); +} + +void +TagBuilder::Commit(Tag &tag) +{ + tag.Clear(); + + tag.duration = duration; + tag.has_playlist = has_playlist; + + /* move all TagItem pointers to the new Tag object without + touching the TagPool reference counters; the + vector::clear() call is important to detach them from this + object */ + const unsigned n_items = items.size(); + tag.num_items = n_items; + tag.items = new TagItem *[n_items]; + std::copy_n(items.begin(), n_items, tag.items); + items.clear(); + + /* now ensure that this object is fresh (will not delete any + items because we've already moved them out) */ + Clear(); +} + +Tag +TagBuilder::Commit() +{ + Tag tag; + Commit(tag); + return tag; +} + +Tag * +TagBuilder::CommitNew() +{ + Tag *tag = new Tag(); + Commit(*tag); + return tag; +} + +bool +TagBuilder::HasType(TagType type) const +{ + for (auto i : items) + if (i->type == type) + return true; + + return false; +} + +void +TagBuilder::Complement(const Tag &other) +{ + if (duration.IsNegative()) + duration = other.duration; + + has_playlist |= other.has_playlist; + + /* build a table of tag types that were already present in + this object, which will not be copied from #other */ + std::array present; + present.fill(false); + for (const TagItem *i : items) + present[i->type] = true; + + items.reserve(items.size() + other.num_items); + + tag_pool_lock.lock(); + for (unsigned i = 0, n = other.num_items; i != n; ++i) { + TagItem *item = other.items[i]; + if (!present[item->type]) + items.push_back(tag_pool_dup_item(item)); + } + tag_pool_lock.unlock(); +} + +inline void +TagBuilder::AddItemInternal(TagType type, StringView value) +{ + assert(!value.IsEmpty()); + + auto f = FixTagString(value); + if (!f.IsNull()) + value = { f.data, f.size }; + + tag_pool_lock.lock(); + auto i = tag_pool_get_item(type, value); + tag_pool_lock.unlock(); + + free(f.data); + + items.push_back(i); +} + +void +TagBuilder::AddItem(TagType type, StringView value) +{ + if (value.IsEmpty() || !IsTagEnabled(type)) + return; + + AddItemInternal(type, value); +} + +void +TagBuilder::AddItem(TagType type, const char *value) +{ +#if !CLANG_CHECK_VERSION(3,6) + /* disabled on clang due to -Wtautological-pointer-compare */ + assert(value != nullptr); +#endif + + AddItem(type, StringView(value)); +} + +void +TagBuilder::AddEmptyItem(TagType type) +{ + tag_pool_lock.lock(); + auto i = tag_pool_get_item(type, StringView::Empty()); + tag_pool_lock.unlock(); + + items.push_back(i); +} + +void +TagBuilder::RemoveAll() +{ + tag_pool_lock.lock(); + for (auto i : items) + tag_pool_put_item(i); + tag_pool_lock.unlock(); + + items.clear(); +} + +void +TagBuilder::RemoveType(TagType type) +{ + const auto begin = items.begin(), end = items.end(); + + items.erase(std::remove_if(begin, end, + [type](TagItem *item) { + if (item->type != type) + return false; + tag_pool_put_item(item); + return true; + }), + end); +} diff --git a/src/tag/Builder.hxx b/src/tag/Builder.hxx new file mode 100644 index 000000000..b940515c6 --- /dev/null +++ b/src/tag/Builder.hxx @@ -0,0 +1,176 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_BUILDER_HXX +#define MPD_TAG_BUILDER_HXX + +#include "Type.h" +#include "Chrono.hxx" +#include "Compiler.h" + +#include + +struct StringView; +struct TagItem; +struct Tag; + +/** + * A class that constructs #Tag objects. + */ +class TagBuilder { + /** + * The duration of the song. A negative value means that the + * length is unknown. + */ + SignedSongTime duration; + + /** + * Does this file have an embedded playlist (e.g. embedded CUE + * sheet)? + */ + bool has_playlist; + + /** an array of tag items */ + std::vector items; + +public: + /** + * Create an empty tag. + */ + TagBuilder() + :duration(SignedSongTime::Negative()), has_playlist(false) {} + + ~TagBuilder() { + Clear(); + } + + TagBuilder(const TagBuilder &other) = delete; + + explicit TagBuilder(const Tag &other); + explicit TagBuilder(Tag &&other); + + TagBuilder &operator=(const TagBuilder &other); + TagBuilder &operator=(TagBuilder &&other); + + TagBuilder &operator=(Tag &&other); + + /** + * Returns true if the tag contains no items. This ignores + * the "duration" attribute. + */ + bool IsEmpty() const { + return items.empty(); + } + + /** + * Returns true if the object contains any information. + */ + gcc_pure + bool IsDefined() const { + return !duration.IsNegative() || has_playlist || !IsEmpty(); + } + + void Clear(); + + /** + * Move this object to the given #Tag instance. This object + * is empty afterwards. + */ + void Commit(Tag &tag); + + /** + * Create a new #Tag instance from data in this object. This + * object is empty afterwards. + */ + Tag Commit(); + + /** + * Create a new #Tag instance from data in this object. The + * returned object is owned by the caller. This object is + * empty afterwards. + */ + Tag *CommitNew(); + + void SetDuration(SignedSongTime _duration) { + duration = _duration; + } + + void SetHasPlaylist(bool _has_playlist) { + has_playlist = _has_playlist; + } + + void Reserve(unsigned n) { + items.reserve(n); + } + + /** + * Checks whether the tag contains one or more items with + * the specified type. + */ + gcc_pure + bool HasType(TagType type) const; + + /** + * Copy attributes and items from the other object that do not + * exist in this object. + */ + void Complement(const Tag &other); + + /** + * Appends a new tag item. + * + * @param type the type of the new tag item + * @param value the value of the tag item (not null-terminated) + * @param length the length of #value + */ + gcc_nonnull_all + void AddItem(TagType type, StringView value); + + /** + * Appends a new tag item. + * + * @param type the type of the new tag item + * @param value the value of the tag item (null-terminated) + */ + gcc_nonnull_all + void AddItem(TagType type, const char *value); + + /** + * Appends a new tag item with an empty value. Do not use + * this unless you know what you're doing - because usually, + * empty values are discarded. + */ + void AddEmptyItem(TagType type); + + /** + * Removes all tag items. + */ + void RemoveAll(); + + /** + * Removes all tag items of the specified type. + */ + void RemoveType(TagType type); + +private: + gcc_nonnull_all + void AddItemInternal(TagType type, StringView value); +}; + +#endif diff --git a/src/tag/Config.cxx b/src/tag/Config.cxx new file mode 100644 index 000000000..8a691efd0 --- /dev/null +++ b/src/tag/Config.cxx @@ -0,0 +1,72 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Config.hxx" +#include "Settings.hxx" +#include "Tag.hxx" +#include "config/ConfigGlobal.hxx" +#include "config/ConfigOption.hxx" +#include "system/FatalError.hxx" +#include "util/Alloc.hxx" +#include "util/ASCII.hxx" +#include "util/StringUtil.hxx" + +#include + +void +TagLoadConfig() +{ + const char *value = config_get_string(ConfigOption::METADATA_TO_USE); + if (value == nullptr) + return; + + global_tag_mask = 0; + + if (StringEqualsCaseASCII(value, "none")) + return; + + bool quit = false; + char *temp, *c, *s; + temp = c = s = xstrdup(value); + do { + if (*s == ',' || *s == '\0') { + if (*s == '\0') + quit = true; + *s = '\0'; + + c = Strip(c); + if (*c == 0) + continue; + + const auto type = tag_name_parse_i(c); + if (type == TAG_NUM_OF_ITEM_TYPES) + FormatFatalError("error parsing metadata item \"%s\"", + c); + + global_tag_mask |= tag_mask_t(1) << unsigned(type); + + s++; + c = s; + } + s++; + } while (!quit); + + free(temp); +} diff --git a/src/tag/Config.hxx b/src/tag/Config.hxx new file mode 100644 index 000000000..47d1eb835 --- /dev/null +++ b/src/tag/Config.hxx @@ -0,0 +1,26 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_CONFIG_HXX +#define MPD_TAG_CONFIG_HXX + +void +TagLoadConfig(); + +#endif diff --git a/src/tag/FixString.cxx b/src/tag/FixString.cxx new file mode 100644 index 000000000..b70118dc4 --- /dev/null +++ b/src/tag/FixString.cxx @@ -0,0 +1,137 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "FixString.hxx" +#include "util/Alloc.hxx" +#include "util/WritableBuffer.hxx" +#include "util/StringView.hxx" +#include "util/UTF8.hxx" + +#include +#include + +gcc_pure +static const char * +FindInvalidUTF8(const char *p, const char *const end) +{ + while (p < end) { + const size_t s = SequenceLengthUTF8(*p); + if (p + s > end) + /* partial sequence at end of string */ + return p; + + /* now call the other SequenceLengthUTF8() overload + which also validates the continuations */ + const size_t t = SequenceLengthUTF8(p); + if (t == 0) + return p; + assert(s == t); + + p += s; + } + + return nullptr; +} + +/** + * Replace invalid sequences with the question mark. + */ +static WritableBuffer +patch_utf8(StringView src, const char *_invalid) +{ + /* duplicate the string, and replace invalid bytes in that + buffer */ + char *dest = (char *)xmemdup(src.data, src.size); + char *const end = dest + src.size; + + char *invalid = dest + (_invalid - src.data); + do { + *invalid = '?'; + + const char *__invalid = FindInvalidUTF8(invalid + 1, end); + invalid = const_cast(__invalid); + } while (invalid != nullptr); + + return { dest, src.size }; +} + +static WritableBuffer +fix_utf8(StringView p) +{ + /* check if the string is already valid UTF-8 */ + const char *invalid = FindInvalidUTF8(p.begin(), p.end()); + if (invalid == nullptr) + return nullptr; + + /* no, broken - patch invalid sequences */ + return patch_utf8(p, invalid); +} + +static bool +char_is_non_printable(unsigned char ch) +{ + return ch < 0x20; +} + +static const char * +find_non_printable(StringView p) +{ + for (const char &ch : p) + if (char_is_non_printable(ch)) + return &ch; + + return nullptr; +} + +/** + * Clears all non-printable characters, convert them to space. + * Returns nullptr if nothing needs to be cleared. + */ +static WritableBuffer +clear_non_printable(StringView src) +{ + const char *first = find_non_printable(src); + if (first == nullptr) + return nullptr; + + char *dest = (char *)xmemdup(src.data, src.size); + + for (size_t i = first - src.data; i < src.size; ++i) + if (char_is_non_printable(dest[i])) + dest[i] = ' '; + + return { dest, src.size }; +} + +WritableBuffer +FixTagString(StringView p) +{ + auto utf8 = fix_utf8(p); + if (!utf8.IsNull()) + p = {utf8.data, utf8.size}; + + WritableBuffer cleared = clear_non_printable(p); + if (cleared.IsNull()) + cleared = utf8; + else + free(utf8.data); + + return cleared; +} diff --git a/src/tag/FixString.hxx b/src/tag/FixString.hxx new file mode 100644 index 000000000..618d9640d --- /dev/null +++ b/src/tag/FixString.hxx @@ -0,0 +1,33 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_STRING_HXX +#define MPD_TAG_STRING_HXX + +#include "check.h" +#include "Compiler.h" + +struct StringView; +template struct WritableBuffer; + +gcc_nonnull_all +WritableBuffer +FixTagString(StringView p); + +#endif diff --git a/src/tag/Generic.cxx b/src/tag/Generic.cxx index 852065d97..859f4382b 100644 --- a/src/tag/Generic.cxx +++ b/src/tag/Generic.cxx @@ -19,7 +19,7 @@ #include "config.h" #include "Generic.hxx" -#include "TagId3.hxx" +#include "Id3Scan.hxx" #include "ApeTag.hxx" #include "fs/Path.hxx" #include "thread/Mutex.hxx" diff --git a/src/tag/Handler.cxx b/src/tag/Handler.cxx new file mode 100644 index 000000000..c04bddb72 --- /dev/null +++ b/src/tag/Handler.cxx @@ -0,0 +1,74 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Handler.hxx" +#include "Builder.hxx" +#include "util/ASCII.hxx" + +#include +#include + +static void +add_tag_duration(SongTime duration, void *ctx) +{ + TagBuilder &tag = *(TagBuilder *)ctx; + + tag.SetDuration(duration); +} + +static void +add_tag_tag(TagType type, const char *value, void *ctx) +{ + TagBuilder &tag = *(TagBuilder *)ctx; + + if (type == TAG_TRACK || type == TAG_DISC) { + /* filter out this extra data and leading zeroes */ + char *end; + unsigned n = strtoul(value, &end, 10); + if (value != end) { + char s[21]; + if (snprintf(s, 21, "%u", n) >= 0) + tag.AddItem(type, s); + } + } else + tag.AddItem(type, value); +} + +const TagHandler add_tag_handler = { + add_tag_duration, + add_tag_tag, + nullptr, +}; + +static void +full_tag_pair(const char *name, gcc_unused const char *value, void *ctx) +{ + TagBuilder &tag = *(TagBuilder *)ctx; + + if (StringEqualsCaseASCII(name, "cuesheet")) + tag.SetHasPlaylist(true); +} + +const TagHandler full_tag_handler = { + add_tag_duration, + add_tag_tag, + full_tag_pair, +}; + diff --git a/src/tag/Handler.hxx b/src/tag/Handler.hxx new file mode 100644 index 000000000..8611cb5ba --- /dev/null +++ b/src/tag/Handler.hxx @@ -0,0 +1,98 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_HANDLER_HXX +#define MPD_TAG_HANDLER_HXX + +#include "check.h" +#include "Type.h" +#include "Chrono.hxx" + +#include + +/** + * A callback table for receiving metadata of a song. + */ +struct TagHandler { + /** + * Declare the duration of a song. Do not call + * this when the duration could not be determined, because + * there is no magic value for "unknown duration". + */ + void (*duration)(SongTime duration, void *ctx); + + /** + * A tag has been read. + * + * @param the value of the tag; the pointer will become + * invalid after returning + */ + void (*tag)(TagType type, const char *value, void *ctx); + + /** + * A name-value pair has been read. It is the codec specific + * representation of tags. + */ + void (*pair)(const char *key, const char *value, void *ctx); +}; + +static inline void +tag_handler_invoke_duration(const TagHandler &handler, void *ctx, + SongTime duration) +{ + if (handler.duration != nullptr) + handler.duration(duration, ctx); +} + +static inline void +tag_handler_invoke_tag(const TagHandler &handler, void *ctx, + TagType type, const char *value) +{ + assert((unsigned)type < TAG_NUM_OF_ITEM_TYPES); + assert(value != nullptr); + + if (handler.tag != nullptr) + handler.tag(type, value, ctx); +} + +static inline void +tag_handler_invoke_pair(const TagHandler &handler, void *ctx, + const char *name, const char *value) +{ + assert(name != nullptr); + assert(value != nullptr); + + if (handler.pair != nullptr) + handler.pair(name, value, ctx); +} + +/** + * This #TagHandler implementation adds tag values to a #TagBuilder object + * (casted from the context pointer). + */ +extern const TagHandler add_tag_handler; + +/** + * This #TagHandler implementation adds tag values to a #TagBuilder object + * (casted from the context pointer), and supports the has_playlist + * attribute. + */ +extern const TagHandler full_tag_handler; + +#endif diff --git a/src/tag/Id3Scan.cxx b/src/tag/Id3Scan.cxx new file mode 100644 index 000000000..74433fa47 --- /dev/null +++ b/src/tag/Id3Scan.cxx @@ -0,0 +1,363 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Id3Scan.hxx" +#include "Id3Load.hxx" +#include "Handler.hxx" +#include "Table.hxx" +#include "Builder.hxx" +#include "util/Alloc.hxx" +#include "util/ScopeExit.hxx" +#include "util/StringUtil.hxx" +#include "Log.hxx" + +#include + +#include +#include + +#include +#include + +# ifndef ID3_FRAME_COMPOSER +# define ID3_FRAME_COMPOSER "TCOM" +# endif +# ifndef ID3_FRAME_DISC +# define ID3_FRAME_DISC "TPOS" +# endif + +#ifndef ID3_FRAME_ARTIST_SORT +#define ID3_FRAME_ARTIST_SORT "TSOP" +#endif + +#ifndef ID3_FRAME_ALBUM_ARTIST_SORT +#define ID3_FRAME_ALBUM_ARTIST_SORT "TSO2" /* this one is unofficial, introduced by Itunes */ +#endif + +#ifndef ID3_FRAME_ALBUM_ARTIST +#define ID3_FRAME_ALBUM_ARTIST "TPE2" +#endif + +gcc_pure +static id3_utf8_t * +tag_id3_getstring(const struct id3_frame *frame, unsigned i) +{ + id3_field *field = id3_frame_field(frame, i); + if (field == nullptr) + return nullptr; + + const id3_ucs4_t *ucs4 = id3_field_getstring(field); + if (ucs4 == nullptr) + return nullptr; + + return id3_ucs4_utf8duplicate(ucs4); +} + +/* This will try to convert a string to utf-8, + */ +static id3_utf8_t * +import_id3_string(const id3_ucs4_t *ucs4) +{ + id3_utf8_t *utf8 = id3_ucs4_utf8duplicate(ucs4); + if (gcc_unlikely(utf8 == nullptr)) + return nullptr; + + AtScopeExit(utf8) { free(utf8); }; + + return (id3_utf8_t *)xstrdup(Strip((char *)utf8)); +} + +/** + * Import a "Text information frame" (ID3v2.4.0 section 4.2). It + * contains 2 fields: + * + * - encoding + * - string list + */ +static void +tag_id3_import_text_frame(const struct id3_frame *frame, + TagType type, + const TagHandler &handler, void *handler_ctx) +{ + if (frame->nfields != 2) + return; + + /* check the encoding field */ + + const id3_field *field = id3_frame_field(frame, 0); + if (field == nullptr || field->type != ID3_FIELD_TYPE_TEXTENCODING) + return; + + /* process the value(s) */ + + field = id3_frame_field(frame, 1); + if (field == nullptr || field->type != ID3_FIELD_TYPE_STRINGLIST) + return; + + /* Get the number of strings available */ + const unsigned nstrings = id3_field_getnstrings(field); + for (unsigned i = 0; i < nstrings; i++) { + const id3_ucs4_t *ucs4 = id3_field_getstrings(field, i); + if (ucs4 == nullptr) + continue; + + if (type == TAG_GENRE) + ucs4 = id3_genre_name(ucs4); + + id3_utf8_t *utf8 = import_id3_string(ucs4); + if (utf8 == nullptr) + continue; + + AtScopeExit(utf8) { free(utf8); }; + + tag_handler_invoke_tag(handler, handler_ctx, + type, (const char *)utf8); + } +} + +/** + * Import all text frames with the specified id (ID3v2.4.0 section + * 4.2). This is a wrapper for tag_id3_import_text_frame(). + */ +static void +tag_id3_import_text(struct id3_tag *tag, const char *id, TagType type, + const TagHandler &handler, void *handler_ctx) +{ + const struct id3_frame *frame; + for (unsigned i = 0; + (frame = id3_tag_findframe(tag, id, i)) != nullptr; ++i) + tag_id3_import_text_frame(frame, type, + handler, handler_ctx); +} + +/** + * Import a "Comment frame" (ID3v2.4.0 section 4.10). It + * contains 4 fields: + * + * - encoding + * - language + * - string + * - full string (we use this one) + */ +static void +tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type, + const TagHandler &handler, + void *handler_ctx) +{ + if (frame->nfields != 4) + return; + + /* for now I only read the 4th field, with the fullstring */ + const id3_field *field = id3_frame_field(frame, 3); + if (field == nullptr) + return; + + const id3_ucs4_t *ucs4 = id3_field_getfullstring(field); + if (ucs4 == nullptr) + return; + + id3_utf8_t *utf8 = import_id3_string(ucs4); + if (utf8 == nullptr) + return; + + AtScopeExit(utf8) { free(utf8); }; + + tag_handler_invoke_tag(handler, handler_ctx, type, (const char *)utf8); +} + +/** + * Import all comment frames (ID3v2.4.0 section 4.10). This is a + * wrapper for tag_id3_import_comment_frame(). + */ +static void +tag_id3_import_comment(struct id3_tag *tag, const char *id, TagType type, + const TagHandler &handler, void *handler_ctx) +{ + const struct id3_frame *frame; + for (unsigned i = 0; + (frame = id3_tag_findframe(tag, id, i)) != nullptr; ++i) + tag_id3_import_comment_frame(frame, type, + handler, handler_ctx); +} + +/** + * Parse a TXXX name, and convert it to a TagType enum value. + * Returns TAG_NUM_OF_ITEM_TYPES if the TXXX name is not understood. + */ +gcc_pure +static TagType +tag_id3_parse_txxx_name(const char *name) +{ + static constexpr struct tag_table txxx_tags[] = { + { "ALBUMARTISTSORT", TAG_ALBUM_ARTIST_SORT }, + { "MusicBrainz Artist Id", TAG_MUSICBRAINZ_ARTISTID }, + { "MusicBrainz Album Id", TAG_MUSICBRAINZ_ALBUMID }, + { "MusicBrainz Album Artist Id", + TAG_MUSICBRAINZ_ALBUMARTISTID }, + { "MusicBrainz Track Id", TAG_MUSICBRAINZ_TRACKID }, + { "MusicBrainz Release Track Id", + TAG_MUSICBRAINZ_RELEASETRACKID }, + { nullptr, TAG_NUM_OF_ITEM_TYPES } + }; + + return tag_table_lookup(txxx_tags, name); +} + +/** + * Import all known MusicBrainz tags from TXXX frames. + */ +static void +tag_id3_import_musicbrainz(struct id3_tag *id3_tag, + const TagHandler &handler, + void *handler_ctx) +{ + for (unsigned i = 0;; ++i) { + const id3_frame *frame = id3_tag_findframe(id3_tag, "TXXX", i); + if (frame == nullptr) + break; + + id3_utf8_t *name = tag_id3_getstring(frame, 1); + if (name == nullptr) + continue; + + AtScopeExit(name) { free(name); }; + + id3_utf8_t *value = tag_id3_getstring(frame, 2); + if (value == nullptr) + continue; + + AtScopeExit(value) { free(value); }; + + tag_handler_invoke_pair(handler, handler_ctx, + (const char *)name, + (const char *)value); + + TagType type = tag_id3_parse_txxx_name((const char*)name); + + if (type != TAG_NUM_OF_ITEM_TYPES) + tag_handler_invoke_tag(handler, handler_ctx, + type, (const char*)value); + } +} + +/** + * Imports the MusicBrainz TrackId from the UFID tag. + */ +static void +tag_id3_import_ufid(struct id3_tag *id3_tag, + const TagHandler &handler, void *handler_ctx) +{ + for (unsigned i = 0;; ++i) { + const id3_frame *frame = id3_tag_findframe(id3_tag, "UFID", i); + if (frame == nullptr) + break; + + id3_field *field = id3_frame_field(frame, 0); + if (field == nullptr) + continue; + + const id3_latin1_t *name = id3_field_getlatin1(field); + if (name == nullptr || + strcmp((const char *)name, "http://musicbrainz.org") != 0) + continue; + + field = id3_frame_field(frame, 1); + if (field == nullptr) + continue; + + id3_length_t length; + const id3_byte_t *value = + id3_field_getbinarydata(field, &length); + if (value == nullptr || length == 0) + continue; + + std::string p((const char *)value, length); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_MUSICBRAINZ_TRACKID, p.c_str()); + } +} + +void +scan_id3_tag(struct id3_tag *tag, + const TagHandler &handler, void *handler_ctx) +{ + tag_id3_import_text(tag, ID3_FRAME_ARTIST, TAG_ARTIST, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST, + TAG_ALBUM_ARTIST, handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_ARTIST_SORT, + TAG_ARTIST_SORT, handler, handler_ctx); + + tag_id3_import_text(tag, "TSOA", TAG_ALBUM_SORT, handler, handler_ctx); + + tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST_SORT, + TAG_ALBUM_ARTIST_SORT, handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_TITLE, TAG_TITLE, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_ALBUM, TAG_ALBUM, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_TRACK, TAG_TRACK, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_YEAR, TAG_DATE, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_GENRE, TAG_GENRE, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_COMPOSER, TAG_COMPOSER, + handler, handler_ctx); + tag_id3_import_text(tag, "TPE3", TAG_PERFORMER, + handler, handler_ctx); + tag_id3_import_text(tag, "TPE4", TAG_PERFORMER, handler, handler_ctx); + tag_id3_import_comment(tag, ID3_FRAME_COMMENT, TAG_COMMENT, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_DISC, TAG_DISC, + handler, handler_ctx); + + tag_id3_import_musicbrainz(tag, handler, handler_ctx); + tag_id3_import_ufid(tag, handler, handler_ctx); +} + +Tag * +tag_id3_import(struct id3_tag *tag) +{ + TagBuilder tag_builder; + scan_id3_tag(tag, add_tag_handler, &tag_builder); + return tag_builder.IsEmpty() + ? nullptr + : tag_builder.CommitNew(); +} + +bool +tag_id3_scan(InputStream &is, + const TagHandler &handler, void *handler_ctx) +{ + UniqueId3Tag tag; + + try { + tag = tag_id3_load(is); + if (!tag) + return false; + } catch (const std::runtime_error &e) { + LogError(e); + return false; + } + + scan_id3_tag(tag.get(), handler, handler_ctx); + return true; +} diff --git a/src/tag/Id3Scan.hxx b/src/tag/Id3Scan.hxx new file mode 100644 index 000000000..21d86346c --- /dev/null +++ b/src/tag/Id3Scan.hxx @@ -0,0 +1,45 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_ID3_SCAN_HXX +#define MPD_TAG_ID3_SCAN_HXX + +#include "check.h" + +class InputStream; +struct TagHandler; +struct Tag; +struct id3_tag; + +bool +tag_id3_scan(InputStream &is, + const TagHandler &handler, void *handler_ctx); + +Tag * +tag_id3_import(id3_tag *); + +/** + * Import all tags from the provided id3_tag *tag + * + */ +void +scan_id3_tag(id3_tag *tag, + const TagHandler &handler, void *handler_ctx); + +#endif diff --git a/src/tag/Item.hxx b/src/tag/Item.hxx new file mode 100644 index 000000000..e3ff7b6a5 --- /dev/null +++ b/src/tag/Item.hxx @@ -0,0 +1,47 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_ITEM_HXX +#define MPD_TAG_ITEM_HXX + +#include "Type.h" + +/** + * One tag value. It is a mapping of #TagType to am arbitrary string + * value. Each tag can have multiple items of one tag type (although + * few clients support that). + */ +struct TagItem { + /** the type of this item */ + TagType type; + + /** + * the value of this tag; this is a variable length string + */ + char value[1]; + + TagItem() = default; + TagItem(const TagItem &other) = delete; + TagItem &operator=(const TagItem &other) = delete; +}; + +static_assert(sizeof(TagItem) == 2, "Unexpected size"); +static_assert(alignof(TagItem) == 1, "Unexpected alignment"); + +#endif diff --git a/src/tag/Names.c b/src/tag/Names.c new file mode 100644 index 000000000..6e5ebc2bc --- /dev/null +++ b/src/tag/Names.c @@ -0,0 +1,46 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Type.h" + +const char *const tag_item_names[TAG_NUM_OF_ITEM_TYPES] = { + [TAG_ARTIST] = "Artist", + [TAG_ARTIST_SORT] = "ArtistSort", + [TAG_ALBUM] = "Album", + [TAG_ALBUM_SORT] = "AlbumSort", + [TAG_ALBUM_ARTIST] = "AlbumArtist", + [TAG_ALBUM_ARTIST_SORT] = "AlbumArtistSort", + [TAG_TITLE] = "Title", + [TAG_TRACK] = "Track", + [TAG_NAME] = "Name", + [TAG_GENRE] = "Genre", + [TAG_DATE] = "Date", + [TAG_COMPOSER] = "Composer", + [TAG_PERFORMER] = "Performer", + [TAG_COMMENT] = "Comment", + [TAG_DISC] = "Disc", + + /* MusicBrainz tags from http://musicbrainz.org/doc/MusicBrainzTag */ + [TAG_MUSICBRAINZ_ARTISTID] = "MUSICBRAINZ_ARTISTID", + [TAG_MUSICBRAINZ_ALBUMID] = "MUSICBRAINZ_ALBUMID", + [TAG_MUSICBRAINZ_ALBUMARTISTID] = "MUSICBRAINZ_ALBUMARTISTID", + [TAG_MUSICBRAINZ_TRACKID] = "MUSICBRAINZ_TRACKID", + [TAG_MUSICBRAINZ_RELEASETRACKID] = "MUSICBRAINZ_RELEASETRACKID", +}; diff --git a/src/tag/Pool.cxx b/src/tag/Pool.cxx new file mode 100644 index 000000000..fa38d6526 --- /dev/null +++ b/src/tag/Pool.cxx @@ -0,0 +1,171 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Pool.hxx" +#include "Item.hxx" +#include "util/Cast.hxx" +#include "util/VarSize.hxx" +#include "util/StringView.hxx" + +#include + +#include +#include +#include + +Mutex tag_pool_lock; + +static constexpr size_t NUM_SLOTS = 4093; + +struct TagPoolSlot { + TagPoolSlot *next; + unsigned char ref; + TagItem item; + + static constexpr unsigned MAX_REF = std::numeric_limits::max(); + + TagPoolSlot(TagPoolSlot *_next, TagType type, + StringView value) + :next(_next), ref(1) { + item.type = type; + memcpy(item.value, value.data, value.size); + item.value[value.size] = 0; + } + + static TagPoolSlot *Create(TagPoolSlot *_next, TagType type, + StringView value); +}; + +TagPoolSlot * +TagPoolSlot::Create(TagPoolSlot *_next, TagType type, + StringView value) +{ + TagPoolSlot *dummy; + return NewVarSize(sizeof(dummy->item.value), + value.size + 1, + _next, type, + value); +} + +static TagPoolSlot *slots[NUM_SLOTS]; + +static inline unsigned +calc_hash(TagType type, StringView p) +{ + unsigned hash = 5381; + + for (auto ch : p) + hash = (hash << 5) + hash + ch; + + return hash ^ type; +} + +static inline unsigned +calc_hash(TagType type, const char *p) +{ + unsigned hash = 5381; + + assert(p != nullptr); + + while (*p != 0) + hash = (hash << 5) + hash + *p++; + + return hash ^ type; +} + +#if CLANG_OR_GCC_VERSION(4,7) + constexpr +#endif +static inline TagPoolSlot * +tag_item_to_slot(TagItem *item) +{ + return &ContainerCast(*item, &TagPoolSlot::item); +} + +static inline TagPoolSlot ** +tag_value_slot_p(TagType type, StringView value) +{ + return &slots[calc_hash(type, value) % NUM_SLOTS]; +} + +static inline TagPoolSlot ** +tag_value_slot_p(TagType type, const char *value) +{ + return &slots[calc_hash(type, value) % NUM_SLOTS]; +} + +TagItem * +tag_pool_get_item(TagType type, StringView value) +{ + auto slot_p = tag_value_slot_p(type, value); + for (auto slot = *slot_p; slot != nullptr; slot = slot->next) { + if (slot->item.type == type && + value.Equals(slot->item.value) && + slot->ref < TagPoolSlot::MAX_REF) { + assert(slot->ref > 0); + ++slot->ref; + return &slot->item; + } + } + + auto slot = TagPoolSlot::Create(*slot_p, type, value); + *slot_p = slot; + return &slot->item; +} + +TagItem * +tag_pool_dup_item(TagItem *item) +{ + TagPoolSlot *slot = tag_item_to_slot(item); + + assert(slot->ref > 0); + + if (slot->ref < TagPoolSlot::MAX_REF) { + ++slot->ref; + return item; + } else { + /* the reference counter overflows above MAX_REF; + obtain a reference to a different TagPoolSlot which + isn't yet "full" */ + return tag_pool_get_item(item->type, item->value); + } +} + +void +tag_pool_put_item(TagItem *item) +{ + TagPoolSlot **slot_p, *slot; + + slot = tag_item_to_slot(item); + assert(slot->ref > 0); + --slot->ref; + + if (slot->ref > 0) + return; + + for (slot_p = tag_value_slot_p(item->type, item->value); + *slot_p != slot; + slot_p = &(*slot_p)->next) { + assert(*slot_p != nullptr); + } + + *slot_p = slot->next; + DeleteVarSize(slot); +} diff --git a/src/tag/Pool.hxx b/src/tag/Pool.hxx new file mode 100644 index 000000000..927774d4e --- /dev/null +++ b/src/tag/Pool.hxx @@ -0,0 +1,40 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_POOL_HXX +#define MPD_TAG_POOL_HXX + +#include "Type.h" +#include "thread/Mutex.hxx" + +extern Mutex tag_pool_lock; + +struct TagItem; +struct StringView; + +TagItem * +tag_pool_get_item(TagType type, StringView value); + +TagItem * +tag_pool_dup_item(TagItem *item); + +void +tag_pool_put_item(TagItem *item); + +#endif diff --git a/src/tag/Rva2.cxx b/src/tag/Rva2.cxx new file mode 100644 index 000000000..2291aa34d --- /dev/null +++ b/src/tag/Rva2.cxx @@ -0,0 +1,150 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Rva2.hxx" +#include "ReplayGainInfo.hxx" + +#include + +#include +#include + +enum class Rva2Channel : uint8_t { + OTHER = 0x00, + MASTER_VOLUME = 0x01, + FRONT_RIGHT = 0x02, + FRONT_LEFT = 0x03, + BACK_RIGHT = 0x04, + BACK_LEFT = 0x05, + FRONT_CENTRE = 0x06, + BACK_CENTRE = 0x07, + SUBWOOFER = 0x08 +}; + +struct Rva2Data { + Rva2Channel type; + uint8_t volume_adjustment[2]; + uint8_t peak_bits; +}; + +static inline id3_length_t +rva2_peak_bytes(const Rva2Data &data) +{ + return (data.peak_bits + 7) / 8; +} + +static inline int +rva2_fixed_volume_adjustment(const Rva2Data &data) +{ + signed int voladj_fixed; + voladj_fixed = (data.volume_adjustment[0] << 8) | + data.volume_adjustment[1]; + voladj_fixed |= -(voladj_fixed & 0x8000); + return voladj_fixed; +} + +static inline float +rva2_float_volume_adjustment(const Rva2Data &data) +{ + /* + * "The volume adjustment is encoded as a fixed point decibel + * value, 16 bit signed integer representing (adjustment*512), + * giving +/- 64 dB with a precision of 0.001953125 dB." + */ + + return (float)rva2_fixed_volume_adjustment(data) / (float)512; +} + +static inline bool +rva2_apply_data(ReplayGainInfo &rgi, + const Rva2Data &data, const id3_latin1_t *id) +{ + if (data.type != Rva2Channel::MASTER_VOLUME) + return false; + + float volume_adjustment = rva2_float_volume_adjustment(data); + + if (strcmp((const char *)id, "album") == 0) { + rgi.album.gain = volume_adjustment; + } else if (strcmp((const char *)id, "track") == 0) { + rgi.track.gain = volume_adjustment; + } else { + rgi.album.gain = volume_adjustment; + rgi.track.gain = volume_adjustment; + } + + return true; +} + +static bool +rva2_apply_frame(ReplayGainInfo &replay_gain_info, + const struct id3_frame *frame) +{ + const id3_latin1_t *id = id3_field_getlatin1(id3_frame_field(frame, 0)); + id3_length_t length; + const id3_byte_t *data = + id3_field_getbinarydata(id3_frame_field(frame, 1), &length); + + if (id == nullptr || data == nullptr) + return false; + + /* + * "The 'identification' string is used to identify the + * situation and/or device where this adjustment should apply. + * The following is then repeated for every channel + * + * Type of channel $xx + * Volume adjustment $xx xx + * Bits representing peak $xx + * Peak volume $xx (xx ...)" + */ + + while (length >= 4) { + const Rva2Data &d = *(const Rva2Data *)data; + unsigned int peak_bytes = rva2_peak_bytes(d); + if (4 + peak_bytes > length) + break; + + if (rva2_apply_data(replay_gain_info, d, id)) + return true; + + data += 4 + peak_bytes; + length -= 4 + peak_bytes; + } + + return false; +} + +bool +tag_rva2_parse(struct id3_tag *tag, ReplayGainInfo &replay_gain_info) +{ + bool found = false; + + /* Loop through all RVA2 frames as some programs (e.g. mp3gain) store + track and album gain in separate tags */ + const struct id3_frame *frame; + for (unsigned i = 0; + (frame = id3_tag_findframe(tag, "RVA2", i)) != nullptr; + ++i) + if (rva2_apply_frame(replay_gain_info, frame)) + found = true; + + return found; +} diff --git a/src/tag/Rva2.hxx b/src/tag/Rva2.hxx new file mode 100644 index 000000000..d30d2ad0f --- /dev/null +++ b/src/tag/Rva2.hxx @@ -0,0 +1,37 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_RVA2_HXX +#define MPD_TAG_RVA2_HXX + +#include "check.h" + +struct id3_tag; +struct ReplayGainInfo; + +/** + * Parse the RVA2 tag, and fill the #ReplayGainInfo struct. This is + * used by decoder plugins with ID3 support. + * + * @return true on success + */ +bool +tag_rva2_parse(id3_tag *tag, ReplayGainInfo &replay_gain_info); + +#endif diff --git a/src/tag/Set.cxx b/src/tag/Set.cxx index 8becc4cf8..97aabef08 100644 --- a/src/tag/Set.cxx +++ b/src/tag/Set.cxx @@ -18,7 +18,7 @@ */ #include "Set.hxx" -#include "TagBuilder.hxx" +#include "Builder.hxx" #include "Settings.hxx" #include diff --git a/src/tag/Settings.hxx b/src/tag/Settings.hxx index a6e573cee..01cd6af4e 100644 --- a/src/tag/Settings.hxx +++ b/src/tag/Settings.hxx @@ -21,7 +21,7 @@ #define MPD_TAG_SETTINGS_HXX #include "Mask.hxx" -#include "TagType.h" +#include "Type.h" #include "Compiler.h" extern tag_mask_t global_tag_mask; diff --git a/src/tag/Table.cxx b/src/tag/Table.cxx new file mode 100644 index 000000000..d8ca1798b --- /dev/null +++ b/src/tag/Table.cxx @@ -0,0 +1,63 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "Table.hxx" +#include "util/ASCII.hxx" + +#include + +/** + * Looks up a string in a tag translation table (case sensitive). + * Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found + * in the table. + */ +TagType +tag_table_lookup(const struct tag_table *table, const char *name) +{ + for (; table->name != nullptr; ++table) + if (strcmp(name, table->name) == 0) + return table->type; + + return TAG_NUM_OF_ITEM_TYPES; +} + +/** + * Looks up a string in a tag translation table (case insensitive). + * Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found + * in the table. + */ +TagType +tag_table_lookup_i(const struct tag_table *table, const char *name) +{ + for (; table->name != nullptr; ++table) + if (StringEqualsCaseASCII(name, table->name)) + return table->type; + + return TAG_NUM_OF_ITEM_TYPES; +} + +const char * +tag_table_lookup(const tag_table *table, TagType type) +{ + for (; table->name != nullptr; ++table) + if (table->type == type) + return table->name; + + return nullptr; +} diff --git a/src/tag/Table.hxx b/src/tag/Table.hxx new file mode 100644 index 000000000..3bda0f21f --- /dev/null +++ b/src/tag/Table.hxx @@ -0,0 +1,59 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_TABLE_HXX +#define MPD_TAG_TABLE_HXX + +#include "Type.h" +#include "Compiler.h" + +struct tag_table { + const char *name; + + TagType type; +}; + +/** + * Looks up a string in a tag translation table (case sensitive). + * Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found + * in the table. + */ +gcc_pure +TagType +tag_table_lookup(const tag_table *table, const char *name); + +/** + * Looks up a string in a tag translation table (case insensitive). + * Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found + * in the table. + */ +gcc_pure +TagType +tag_table_lookup_i(const tag_table *table, const char *name); + +/** + * Looks up a #TagType in a tag translation table and returns its + * string representation. Returns nullptr if the specified type was + * not found in the table. + */ +gcc_pure +const char * +tag_table_lookup(const tag_table *table, TagType type); + +#endif diff --git a/src/tag/Tag.cxx b/src/tag/Tag.cxx index d2623793b..85732d2e8 100644 --- a/src/tag/Tag.cxx +++ b/src/tag/Tag.cxx @@ -19,8 +19,8 @@ #include "config.h" #include "Tag.hxx" -#include "TagPool.hxx" -#include "TagBuilder.hxx" +#include "Pool.hxx" +#include "Builder.hxx" #include "util/ASCII.hxx" #include diff --git a/src/tag/Tag.hxx b/src/tag/Tag.hxx index 236793649..9320e767e 100644 --- a/src/tag/Tag.hxx +++ b/src/tag/Tag.hxx @@ -20,8 +20,8 @@ #ifndef MPD_TAG_HXX #define MPD_TAG_HXX -#include "TagType.h" // IWYU pragma: export -#include "TagItem.hxx" // IWYU pragma: export +#include "Type.h" // IWYU pragma: export +#include "Item.hxx" // IWYU pragma: export #include "Chrono.hxx" #include "Compiler.h" diff --git a/src/tag/TagBuilder.cxx b/src/tag/TagBuilder.cxx deleted file mode 100644 index 6d259dc54..000000000 --- a/src/tag/TagBuilder.cxx +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "TagBuilder.hxx" -#include "Settings.hxx" -#include "TagPool.hxx" -#include "TagString.hxx" -#include "Tag.hxx" -#include "util/WritableBuffer.hxx" -#include "util/StringView.hxx" - -#include - -#include -#include - -TagBuilder::TagBuilder(const Tag &other) - :duration(other.duration), has_playlist(other.has_playlist) -{ - items.reserve(other.num_items); - - tag_pool_lock.lock(); - for (unsigned i = 0, n = other.num_items; i != n; ++i) - items.push_back(tag_pool_dup_item(other.items[i])); - tag_pool_lock.unlock(); -} - -TagBuilder::TagBuilder(Tag &&other) - :duration(other.duration), has_playlist(other.has_playlist) -{ - /* move all TagItem pointers from the Tag object; we don't - need to contact the tag pool, because all we do is move - references */ - items.reserve(other.num_items); - std::copy_n(other.items, other.num_items, std::back_inserter(items)); - - /* discard the pointers from the Tag object */ - other.num_items = 0; - delete[] other.items; - other.items = nullptr; -} - -TagBuilder & -TagBuilder::operator=(const TagBuilder &other) -{ - /* copy all attributes */ - duration = other.duration; - has_playlist = other.has_playlist; - items = other.items; - - /* increment the tag pool refcounters */ - tag_pool_lock.lock(); - for (auto i : items) - tag_pool_dup_item(i); - tag_pool_lock.unlock(); - - return *this; -} - -TagBuilder & -TagBuilder::operator=(TagBuilder &&other) -{ - duration = other.duration; - has_playlist = other.has_playlist; - items = std::move(other.items); - - return *this; -} - -TagBuilder & -TagBuilder::operator=(Tag &&other) -{ - duration = other.duration; - has_playlist = other.has_playlist; - - /* move all TagItem pointers from the Tag object; we don't - need to contact the tag pool, because all we do is move - references */ - items.clear(); - items.reserve(other.num_items); - std::copy_n(other.items, other.num_items, std::back_inserter(items)); - - /* discard the pointers from the Tag object */ - other.num_items = 0; - delete[] other.items; - other.items = nullptr; - - return *this; -} - -void -TagBuilder::Clear() -{ - duration = SignedSongTime::Negative(); - has_playlist = false; - RemoveAll(); -} - -void -TagBuilder::Commit(Tag &tag) -{ - tag.Clear(); - - tag.duration = duration; - tag.has_playlist = has_playlist; - - /* move all TagItem pointers to the new Tag object without - touching the TagPool reference counters; the - vector::clear() call is important to detach them from this - object */ - const unsigned n_items = items.size(); - tag.num_items = n_items; - tag.items = new TagItem *[n_items]; - std::copy_n(items.begin(), n_items, tag.items); - items.clear(); - - /* now ensure that this object is fresh (will not delete any - items because we've already moved them out) */ - Clear(); -} - -Tag -TagBuilder::Commit() -{ - Tag tag; - Commit(tag); - return tag; -} - -Tag * -TagBuilder::CommitNew() -{ - Tag *tag = new Tag(); - Commit(*tag); - return tag; -} - -bool -TagBuilder::HasType(TagType type) const -{ - for (auto i : items) - if (i->type == type) - return true; - - return false; -} - -void -TagBuilder::Complement(const Tag &other) -{ - if (duration.IsNegative()) - duration = other.duration; - - has_playlist |= other.has_playlist; - - /* build a table of tag types that were already present in - this object, which will not be copied from #other */ - std::array present; - present.fill(false); - for (const TagItem *i : items) - present[i->type] = true; - - items.reserve(items.size() + other.num_items); - - tag_pool_lock.lock(); - for (unsigned i = 0, n = other.num_items; i != n; ++i) { - TagItem *item = other.items[i]; - if (!present[item->type]) - items.push_back(tag_pool_dup_item(item)); - } - tag_pool_lock.unlock(); -} - -inline void -TagBuilder::AddItemInternal(TagType type, StringView value) -{ - assert(!value.IsEmpty()); - - auto f = FixTagString(value); - if (!f.IsNull()) - value = { f.data, f.size }; - - tag_pool_lock.lock(); - auto i = tag_pool_get_item(type, value); - tag_pool_lock.unlock(); - - free(f.data); - - items.push_back(i); -} - -void -TagBuilder::AddItem(TagType type, StringView value) -{ - if (value.IsEmpty() || !IsTagEnabled(type)) - return; - - AddItemInternal(type, value); -} - -void -TagBuilder::AddItem(TagType type, const char *value) -{ -#if !CLANG_CHECK_VERSION(3,6) - /* disabled on clang due to -Wtautological-pointer-compare */ - assert(value != nullptr); -#endif - - AddItem(type, StringView(value)); -} - -void -TagBuilder::AddEmptyItem(TagType type) -{ - tag_pool_lock.lock(); - auto i = tag_pool_get_item(type, StringView::Empty()); - tag_pool_lock.unlock(); - - items.push_back(i); -} - -void -TagBuilder::RemoveAll() -{ - tag_pool_lock.lock(); - for (auto i : items) - tag_pool_put_item(i); - tag_pool_lock.unlock(); - - items.clear(); -} - -void -TagBuilder::RemoveType(TagType type) -{ - const auto begin = items.begin(), end = items.end(); - - items.erase(std::remove_if(begin, end, - [type](TagItem *item) { - if (item->type != type) - return false; - tag_pool_put_item(item); - return true; - }), - end); -} diff --git a/src/tag/TagBuilder.hxx b/src/tag/TagBuilder.hxx deleted file mode 100644 index 43b4d71e7..000000000 --- a/src/tag/TagBuilder.hxx +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_TAG_BUILDER_HXX -#define MPD_TAG_BUILDER_HXX - -#include "TagType.h" -#include "Chrono.hxx" -#include "Compiler.h" - -#include - -struct StringView; -struct TagItem; -struct Tag; - -/** - * A class that constructs #Tag objects. - */ -class TagBuilder { - /** - * The duration of the song. A negative value means that the - * length is unknown. - */ - SignedSongTime duration; - - /** - * Does this file have an embedded playlist (e.g. embedded CUE - * sheet)? - */ - bool has_playlist; - - /** an array of tag items */ - std::vector items; - -public: - /** - * Create an empty tag. - */ - TagBuilder() - :duration(SignedSongTime::Negative()), has_playlist(false) {} - - ~TagBuilder() { - Clear(); - } - - TagBuilder(const TagBuilder &other) = delete; - - explicit TagBuilder(const Tag &other); - explicit TagBuilder(Tag &&other); - - TagBuilder &operator=(const TagBuilder &other); - TagBuilder &operator=(TagBuilder &&other); - - TagBuilder &operator=(Tag &&other); - - /** - * Returns true if the tag contains no items. This ignores - * the "duration" attribute. - */ - bool IsEmpty() const { - return items.empty(); - } - - /** - * Returns true if the object contains any information. - */ - gcc_pure - bool IsDefined() const { - return !duration.IsNegative() || has_playlist || !IsEmpty(); - } - - void Clear(); - - /** - * Move this object to the given #Tag instance. This object - * is empty afterwards. - */ - void Commit(Tag &tag); - - /** - * Create a new #Tag instance from data in this object. This - * object is empty afterwards. - */ - Tag Commit(); - - /** - * Create a new #Tag instance from data in this object. The - * returned object is owned by the caller. This object is - * empty afterwards. - */ - Tag *CommitNew(); - - void SetDuration(SignedSongTime _duration) { - duration = _duration; - } - - void SetHasPlaylist(bool _has_playlist) { - has_playlist = _has_playlist; - } - - void Reserve(unsigned n) { - items.reserve(n); - } - - /** - * Checks whether the tag contains one or more items with - * the specified type. - */ - gcc_pure - bool HasType(TagType type) const; - - /** - * Copy attributes and items from the other object that do not - * exist in this object. - */ - void Complement(const Tag &other); - - /** - * Appends a new tag item. - * - * @param type the type of the new tag item - * @param value the value of the tag item (not null-terminated) - * @param length the length of #value - */ - gcc_nonnull_all - void AddItem(TagType type, StringView value); - - /** - * Appends a new tag item. - * - * @param type the type of the new tag item - * @param value the value of the tag item (null-terminated) - */ - gcc_nonnull_all - void AddItem(TagType type, const char *value); - - /** - * Appends a new tag item with an empty value. Do not use - * this unless you know what you're doing - because usually, - * empty values are discarded. - */ - void AddEmptyItem(TagType type); - - /** - * Removes all tag items. - */ - void RemoveAll(); - - /** - * Removes all tag items of the specified type. - */ - void RemoveType(TagType type); - -private: - gcc_nonnull_all - void AddItemInternal(TagType type, StringView value); -}; - -#endif diff --git a/src/tag/TagConfig.cxx b/src/tag/TagConfig.cxx deleted file mode 100644 index 93f8675e5..000000000 --- a/src/tag/TagConfig.cxx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "TagConfig.hxx" -#include "Settings.hxx" -#include "Tag.hxx" -#include "config/ConfigGlobal.hxx" -#include "config/ConfigOption.hxx" -#include "system/FatalError.hxx" -#include "util/Alloc.hxx" -#include "util/ASCII.hxx" -#include "util/StringUtil.hxx" - -#include - -void -TagLoadConfig() -{ - const char *value = config_get_string(ConfigOption::METADATA_TO_USE); - if (value == nullptr) - return; - - global_tag_mask = 0; - - if (StringEqualsCaseASCII(value, "none")) - return; - - bool quit = false; - char *temp, *c, *s; - temp = c = s = xstrdup(value); - do { - if (*s == ',' || *s == '\0') { - if (*s == '\0') - quit = true; - *s = '\0'; - - c = Strip(c); - if (*c == 0) - continue; - - const auto type = tag_name_parse_i(c); - if (type == TAG_NUM_OF_ITEM_TYPES) - FormatFatalError("error parsing metadata item \"%s\"", - c); - - global_tag_mask |= tag_mask_t(1) << unsigned(type); - - s++; - c = s; - } - s++; - } while (!quit); - - free(temp); -} diff --git a/src/tag/TagConfig.hxx b/src/tag/TagConfig.hxx deleted file mode 100644 index 47d1eb835..000000000 --- a/src/tag/TagConfig.hxx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_TAG_CONFIG_HXX -#define MPD_TAG_CONFIG_HXX - -void -TagLoadConfig(); - -#endif diff --git a/src/tag/TagHandler.cxx b/src/tag/TagHandler.cxx deleted file mode 100644 index 46e3e79b4..000000000 --- a/src/tag/TagHandler.cxx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "TagHandler.hxx" -#include "TagBuilder.hxx" -#include "util/ASCII.hxx" - -#include -#include - -static void -add_tag_duration(SongTime duration, void *ctx) -{ - TagBuilder &tag = *(TagBuilder *)ctx; - - tag.SetDuration(duration); -} - -static void -add_tag_tag(TagType type, const char *value, void *ctx) -{ - TagBuilder &tag = *(TagBuilder *)ctx; - - if (type == TAG_TRACK || type == TAG_DISC) { - /* filter out this extra data and leading zeroes */ - char *end; - unsigned n = strtoul(value, &end, 10); - if (value != end) { - char s[21]; - if (snprintf(s, 21, "%u", n) >= 0) - tag.AddItem(type, s); - } - } else - tag.AddItem(type, value); -} - -const TagHandler add_tag_handler = { - add_tag_duration, - add_tag_tag, - nullptr, -}; - -static void -full_tag_pair(const char *name, gcc_unused const char *value, void *ctx) -{ - TagBuilder &tag = *(TagBuilder *)ctx; - - if (StringEqualsCaseASCII(name, "cuesheet")) - tag.SetHasPlaylist(true); -} - -const TagHandler full_tag_handler = { - add_tag_duration, - add_tag_tag, - full_tag_pair, -}; - diff --git a/src/tag/TagHandler.hxx b/src/tag/TagHandler.hxx deleted file mode 100644 index a66d4d41c..000000000 --- a/src/tag/TagHandler.hxx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_TAG_HANDLER_HXX -#define MPD_TAG_HANDLER_HXX - -#include "check.h" -#include "TagType.h" -#include "Chrono.hxx" - -#include - -/** - * A callback table for receiving metadata of a song. - */ -struct TagHandler { - /** - * Declare the duration of a song. Do not call - * this when the duration could not be determined, because - * there is no magic value for "unknown duration". - */ - void (*duration)(SongTime duration, void *ctx); - - /** - * A tag has been read. - * - * @param the value of the tag; the pointer will become - * invalid after returning - */ - void (*tag)(TagType type, const char *value, void *ctx); - - /** - * A name-value pair has been read. It is the codec specific - * representation of tags. - */ - void (*pair)(const char *key, const char *value, void *ctx); -}; - -static inline void -tag_handler_invoke_duration(const TagHandler &handler, void *ctx, - SongTime duration) -{ - if (handler.duration != nullptr) - handler.duration(duration, ctx); -} - -static inline void -tag_handler_invoke_tag(const TagHandler &handler, void *ctx, - TagType type, const char *value) -{ - assert((unsigned)type < TAG_NUM_OF_ITEM_TYPES); - assert(value != nullptr); - - if (handler.tag != nullptr) - handler.tag(type, value, ctx); -} - -static inline void -tag_handler_invoke_pair(const TagHandler &handler, void *ctx, - const char *name, const char *value) -{ - assert(name != nullptr); - assert(value != nullptr); - - if (handler.pair != nullptr) - handler.pair(name, value, ctx); -} - -/** - * This #TagHandler implementation adds tag values to a #TagBuilder object - * (casted from the context pointer). - */ -extern const TagHandler add_tag_handler; - -/** - * This #TagHandler implementation adds tag values to a #TagBuilder object - * (casted from the context pointer), and supports the has_playlist - * attribute. - */ -extern const TagHandler full_tag_handler; - -#endif diff --git a/src/tag/TagId3.cxx b/src/tag/TagId3.cxx deleted file mode 100644 index f9bf7e9b2..000000000 --- a/src/tag/TagId3.cxx +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "TagId3.hxx" -#include "Id3Load.hxx" -#include "TagHandler.hxx" -#include "TagTable.hxx" -#include "TagBuilder.hxx" -#include "util/Alloc.hxx" -#include "util/ScopeExit.hxx" -#include "util/StringUtil.hxx" -#include "Log.hxx" - -#include - -#include -#include - -#include -#include - -# ifndef ID3_FRAME_COMPOSER -# define ID3_FRAME_COMPOSER "TCOM" -# endif -# ifndef ID3_FRAME_DISC -# define ID3_FRAME_DISC "TPOS" -# endif - -#ifndef ID3_FRAME_ARTIST_SORT -#define ID3_FRAME_ARTIST_SORT "TSOP" -#endif - -#ifndef ID3_FRAME_ALBUM_ARTIST_SORT -#define ID3_FRAME_ALBUM_ARTIST_SORT "TSO2" /* this one is unofficial, introduced by Itunes */ -#endif - -#ifndef ID3_FRAME_ALBUM_ARTIST -#define ID3_FRAME_ALBUM_ARTIST "TPE2" -#endif - -gcc_pure -static id3_utf8_t * -tag_id3_getstring(const struct id3_frame *frame, unsigned i) -{ - id3_field *field = id3_frame_field(frame, i); - if (field == nullptr) - return nullptr; - - const id3_ucs4_t *ucs4 = id3_field_getstring(field); - if (ucs4 == nullptr) - return nullptr; - - return id3_ucs4_utf8duplicate(ucs4); -} - -/* This will try to convert a string to utf-8, - */ -static id3_utf8_t * -import_id3_string(const id3_ucs4_t *ucs4) -{ - id3_utf8_t *utf8 = id3_ucs4_utf8duplicate(ucs4); - if (gcc_unlikely(utf8 == nullptr)) - return nullptr; - - AtScopeExit(utf8) { free(utf8); }; - - return (id3_utf8_t *)xstrdup(Strip((char *)utf8)); -} - -/** - * Import a "Text information frame" (ID3v2.4.0 section 4.2). It - * contains 2 fields: - * - * - encoding - * - string list - */ -static void -tag_id3_import_text_frame(const struct id3_frame *frame, - TagType type, - const TagHandler &handler, void *handler_ctx) -{ - if (frame->nfields != 2) - return; - - /* check the encoding field */ - - const id3_field *field = id3_frame_field(frame, 0); - if (field == nullptr || field->type != ID3_FIELD_TYPE_TEXTENCODING) - return; - - /* process the value(s) */ - - field = id3_frame_field(frame, 1); - if (field == nullptr || field->type != ID3_FIELD_TYPE_STRINGLIST) - return; - - /* Get the number of strings available */ - const unsigned nstrings = id3_field_getnstrings(field); - for (unsigned i = 0; i < nstrings; i++) { - const id3_ucs4_t *ucs4 = id3_field_getstrings(field, i); - if (ucs4 == nullptr) - continue; - - if (type == TAG_GENRE) - ucs4 = id3_genre_name(ucs4); - - id3_utf8_t *utf8 = import_id3_string(ucs4); - if (utf8 == nullptr) - continue; - - AtScopeExit(utf8) { free(utf8); }; - - tag_handler_invoke_tag(handler, handler_ctx, - type, (const char *)utf8); - } -} - -/** - * Import all text frames with the specified id (ID3v2.4.0 section - * 4.2). This is a wrapper for tag_id3_import_text_frame(). - */ -static void -tag_id3_import_text(struct id3_tag *tag, const char *id, TagType type, - const TagHandler &handler, void *handler_ctx) -{ - const struct id3_frame *frame; - for (unsigned i = 0; - (frame = id3_tag_findframe(tag, id, i)) != nullptr; ++i) - tag_id3_import_text_frame(frame, type, - handler, handler_ctx); -} - -/** - * Import a "Comment frame" (ID3v2.4.0 section 4.10). It - * contains 4 fields: - * - * - encoding - * - language - * - string - * - full string (we use this one) - */ -static void -tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type, - const TagHandler &handler, - void *handler_ctx) -{ - if (frame->nfields != 4) - return; - - /* for now I only read the 4th field, with the fullstring */ - const id3_field *field = id3_frame_field(frame, 3); - if (field == nullptr) - return; - - const id3_ucs4_t *ucs4 = id3_field_getfullstring(field); - if (ucs4 == nullptr) - return; - - id3_utf8_t *utf8 = import_id3_string(ucs4); - if (utf8 == nullptr) - return; - - AtScopeExit(utf8) { free(utf8); }; - - tag_handler_invoke_tag(handler, handler_ctx, type, (const char *)utf8); -} - -/** - * Import all comment frames (ID3v2.4.0 section 4.10). This is a - * wrapper for tag_id3_import_comment_frame(). - */ -static void -tag_id3_import_comment(struct id3_tag *tag, const char *id, TagType type, - const TagHandler &handler, void *handler_ctx) -{ - const struct id3_frame *frame; - for (unsigned i = 0; - (frame = id3_tag_findframe(tag, id, i)) != nullptr; ++i) - tag_id3_import_comment_frame(frame, type, - handler, handler_ctx); -} - -/** - * Parse a TXXX name, and convert it to a TagType enum value. - * Returns TAG_NUM_OF_ITEM_TYPES if the TXXX name is not understood. - */ -gcc_pure -static TagType -tag_id3_parse_txxx_name(const char *name) -{ - static constexpr struct tag_table txxx_tags[] = { - { "ALBUMARTISTSORT", TAG_ALBUM_ARTIST_SORT }, - { "MusicBrainz Artist Id", TAG_MUSICBRAINZ_ARTISTID }, - { "MusicBrainz Album Id", TAG_MUSICBRAINZ_ALBUMID }, - { "MusicBrainz Album Artist Id", - TAG_MUSICBRAINZ_ALBUMARTISTID }, - { "MusicBrainz Track Id", TAG_MUSICBRAINZ_TRACKID }, - { "MusicBrainz Release Track Id", - TAG_MUSICBRAINZ_RELEASETRACKID }, - { nullptr, TAG_NUM_OF_ITEM_TYPES } - }; - - return tag_table_lookup(txxx_tags, name); -} - -/** - * Import all known MusicBrainz tags from TXXX frames. - */ -static void -tag_id3_import_musicbrainz(struct id3_tag *id3_tag, - const TagHandler &handler, - void *handler_ctx) -{ - for (unsigned i = 0;; ++i) { - const id3_frame *frame = id3_tag_findframe(id3_tag, "TXXX", i); - if (frame == nullptr) - break; - - id3_utf8_t *name = tag_id3_getstring(frame, 1); - if (name == nullptr) - continue; - - AtScopeExit(name) { free(name); }; - - id3_utf8_t *value = tag_id3_getstring(frame, 2); - if (value == nullptr) - continue; - - AtScopeExit(value) { free(value); }; - - tag_handler_invoke_pair(handler, handler_ctx, - (const char *)name, - (const char *)value); - - TagType type = tag_id3_parse_txxx_name((const char*)name); - - if (type != TAG_NUM_OF_ITEM_TYPES) - tag_handler_invoke_tag(handler, handler_ctx, - type, (const char*)value); - } -} - -/** - * Imports the MusicBrainz TrackId from the UFID tag. - */ -static void -tag_id3_import_ufid(struct id3_tag *id3_tag, - const TagHandler &handler, void *handler_ctx) -{ - for (unsigned i = 0;; ++i) { - const id3_frame *frame = id3_tag_findframe(id3_tag, "UFID", i); - if (frame == nullptr) - break; - - id3_field *field = id3_frame_field(frame, 0); - if (field == nullptr) - continue; - - const id3_latin1_t *name = id3_field_getlatin1(field); - if (name == nullptr || - strcmp((const char *)name, "http://musicbrainz.org") != 0) - continue; - - field = id3_frame_field(frame, 1); - if (field == nullptr) - continue; - - id3_length_t length; - const id3_byte_t *value = - id3_field_getbinarydata(field, &length); - if (value == nullptr || length == 0) - continue; - - std::string p((const char *)value, length); - tag_handler_invoke_tag(handler, handler_ctx, - TAG_MUSICBRAINZ_TRACKID, p.c_str()); - } -} - -void -scan_id3_tag(struct id3_tag *tag, - const TagHandler &handler, void *handler_ctx) -{ - tag_id3_import_text(tag, ID3_FRAME_ARTIST, TAG_ARTIST, - handler, handler_ctx); - tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST, - TAG_ALBUM_ARTIST, handler, handler_ctx); - tag_id3_import_text(tag, ID3_FRAME_ARTIST_SORT, - TAG_ARTIST_SORT, handler, handler_ctx); - - tag_id3_import_text(tag, "TSOA", TAG_ALBUM_SORT, handler, handler_ctx); - - tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST_SORT, - TAG_ALBUM_ARTIST_SORT, handler, handler_ctx); - tag_id3_import_text(tag, ID3_FRAME_TITLE, TAG_TITLE, - handler, handler_ctx); - tag_id3_import_text(tag, ID3_FRAME_ALBUM, TAG_ALBUM, - handler, handler_ctx); - tag_id3_import_text(tag, ID3_FRAME_TRACK, TAG_TRACK, - handler, handler_ctx); - tag_id3_import_text(tag, ID3_FRAME_YEAR, TAG_DATE, - handler, handler_ctx); - tag_id3_import_text(tag, ID3_FRAME_GENRE, TAG_GENRE, - handler, handler_ctx); - tag_id3_import_text(tag, ID3_FRAME_COMPOSER, TAG_COMPOSER, - handler, handler_ctx); - tag_id3_import_text(tag, "TPE3", TAG_PERFORMER, - handler, handler_ctx); - tag_id3_import_text(tag, "TPE4", TAG_PERFORMER, handler, handler_ctx); - tag_id3_import_comment(tag, ID3_FRAME_COMMENT, TAG_COMMENT, - handler, handler_ctx); - tag_id3_import_text(tag, ID3_FRAME_DISC, TAG_DISC, - handler, handler_ctx); - - tag_id3_import_musicbrainz(tag, handler, handler_ctx); - tag_id3_import_ufid(tag, handler, handler_ctx); -} - -Tag * -tag_id3_import(struct id3_tag *tag) -{ - TagBuilder tag_builder; - scan_id3_tag(tag, add_tag_handler, &tag_builder); - return tag_builder.IsEmpty() - ? nullptr - : tag_builder.CommitNew(); -} - -bool -tag_id3_scan(InputStream &is, - const TagHandler &handler, void *handler_ctx) -{ - UniqueId3Tag tag; - - try { - tag = tag_id3_load(is); - if (!tag) - return false; - } catch (const std::runtime_error &e) { - LogError(e); - return false; - } - - scan_id3_tag(tag.get(), handler, handler_ctx); - return true; -} diff --git a/src/tag/TagId3.hxx b/src/tag/TagId3.hxx deleted file mode 100644 index 13300c0a2..000000000 --- a/src/tag/TagId3.hxx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_TAG_ID3_HXX -#define MPD_TAG_ID3_HXX - -#include "check.h" - -class InputStream; -struct TagHandler; -struct Tag; -struct id3_tag; - -bool -tag_id3_scan(InputStream &is, - const TagHandler &handler, void *handler_ctx); - -Tag * -tag_id3_import(id3_tag *); - -/** - * Import all tags from the provided id3_tag *tag - * - */ -void -scan_id3_tag(id3_tag *tag, - const TagHandler &handler, void *handler_ctx); - -#endif diff --git a/src/tag/TagItem.hxx b/src/tag/TagItem.hxx deleted file mode 100644 index f92269ad1..000000000 --- a/src/tag/TagItem.hxx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_TAG_ITEM_HXX -#define MPD_TAG_ITEM_HXX - -#include "TagType.h" - -/** - * One tag value. It is a mapping of #TagType to am arbitrary string - * value. Each tag can have multiple items of one tag type (although - * few clients support that). - */ -struct TagItem { - /** the type of this item */ - TagType type; - - /** - * the value of this tag; this is a variable length string - */ - char value[1]; - - TagItem() = default; - TagItem(const TagItem &other) = delete; - TagItem &operator=(const TagItem &other) = delete; -}; - -static_assert(sizeof(TagItem) == 2, "Unexpected size"); -static_assert(alignof(TagItem) == 1, "Unexpected alignment"); - -#endif diff --git a/src/tag/TagNames.c b/src/tag/TagNames.c deleted file mode 100644 index 9f470b094..000000000 --- a/src/tag/TagNames.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "TagType.h" - -const char *const tag_item_names[TAG_NUM_OF_ITEM_TYPES] = { - [TAG_ARTIST] = "Artist", - [TAG_ARTIST_SORT] = "ArtistSort", - [TAG_ALBUM] = "Album", - [TAG_ALBUM_SORT] = "AlbumSort", - [TAG_ALBUM_ARTIST] = "AlbumArtist", - [TAG_ALBUM_ARTIST_SORT] = "AlbumArtistSort", - [TAG_TITLE] = "Title", - [TAG_TRACK] = "Track", - [TAG_NAME] = "Name", - [TAG_GENRE] = "Genre", - [TAG_DATE] = "Date", - [TAG_COMPOSER] = "Composer", - [TAG_PERFORMER] = "Performer", - [TAG_COMMENT] = "Comment", - [TAG_DISC] = "Disc", - - /* MusicBrainz tags from http://musicbrainz.org/doc/MusicBrainzTag */ - [TAG_MUSICBRAINZ_ARTISTID] = "MUSICBRAINZ_ARTISTID", - [TAG_MUSICBRAINZ_ALBUMID] = "MUSICBRAINZ_ALBUMID", - [TAG_MUSICBRAINZ_ALBUMARTISTID] = "MUSICBRAINZ_ALBUMARTISTID", - [TAG_MUSICBRAINZ_TRACKID] = "MUSICBRAINZ_TRACKID", - [TAG_MUSICBRAINZ_RELEASETRACKID] = "MUSICBRAINZ_RELEASETRACKID", -}; diff --git a/src/tag/TagPool.cxx b/src/tag/TagPool.cxx deleted file mode 100644 index fc5cd06aa..000000000 --- a/src/tag/TagPool.cxx +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "TagPool.hxx" -#include "TagItem.hxx" -#include "util/Cast.hxx" -#include "util/VarSize.hxx" -#include "util/StringView.hxx" - -#include - -#include -#include -#include - -Mutex tag_pool_lock; - -static constexpr size_t NUM_SLOTS = 4093; - -struct TagPoolSlot { - TagPoolSlot *next; - unsigned char ref; - TagItem item; - - static constexpr unsigned MAX_REF = std::numeric_limits::max(); - - TagPoolSlot(TagPoolSlot *_next, TagType type, - StringView value) - :next(_next), ref(1) { - item.type = type; - memcpy(item.value, value.data, value.size); - item.value[value.size] = 0; - } - - static TagPoolSlot *Create(TagPoolSlot *_next, TagType type, - StringView value); -}; - -TagPoolSlot * -TagPoolSlot::Create(TagPoolSlot *_next, TagType type, - StringView value) -{ - TagPoolSlot *dummy; - return NewVarSize(sizeof(dummy->item.value), - value.size + 1, - _next, type, - value); -} - -static TagPoolSlot *slots[NUM_SLOTS]; - -static inline unsigned -calc_hash(TagType type, StringView p) -{ - unsigned hash = 5381; - - for (auto ch : p) - hash = (hash << 5) + hash + ch; - - return hash ^ type; -} - -static inline unsigned -calc_hash(TagType type, const char *p) -{ - unsigned hash = 5381; - - assert(p != nullptr); - - while (*p != 0) - hash = (hash << 5) + hash + *p++; - - return hash ^ type; -} - -#if CLANG_OR_GCC_VERSION(4,7) - constexpr -#endif -static inline TagPoolSlot * -tag_item_to_slot(TagItem *item) -{ - return &ContainerCast(*item, &TagPoolSlot::item); -} - -static inline TagPoolSlot ** -tag_value_slot_p(TagType type, StringView value) -{ - return &slots[calc_hash(type, value) % NUM_SLOTS]; -} - -static inline TagPoolSlot ** -tag_value_slot_p(TagType type, const char *value) -{ - return &slots[calc_hash(type, value) % NUM_SLOTS]; -} - -TagItem * -tag_pool_get_item(TagType type, StringView value) -{ - auto slot_p = tag_value_slot_p(type, value); - for (auto slot = *slot_p; slot != nullptr; slot = slot->next) { - if (slot->item.type == type && - value.Equals(slot->item.value) && - slot->ref < TagPoolSlot::MAX_REF) { - assert(slot->ref > 0); - ++slot->ref; - return &slot->item; - } - } - - auto slot = TagPoolSlot::Create(*slot_p, type, value); - *slot_p = slot; - return &slot->item; -} - -TagItem * -tag_pool_dup_item(TagItem *item) -{ - TagPoolSlot *slot = tag_item_to_slot(item); - - assert(slot->ref > 0); - - if (slot->ref < TagPoolSlot::MAX_REF) { - ++slot->ref; - return item; - } else { - /* the reference counter overflows above MAX_REF; - obtain a reference to a different TagPoolSlot which - isn't yet "full" */ - return tag_pool_get_item(item->type, item->value); - } -} - -void -tag_pool_put_item(TagItem *item) -{ - TagPoolSlot **slot_p, *slot; - - slot = tag_item_to_slot(item); - assert(slot->ref > 0); - --slot->ref; - - if (slot->ref > 0) - return; - - for (slot_p = tag_value_slot_p(item->type, item->value); - *slot_p != slot; - slot_p = &(*slot_p)->next) { - assert(*slot_p != nullptr); - } - - *slot_p = slot->next; - DeleteVarSize(slot); -} diff --git a/src/tag/TagPool.hxx b/src/tag/TagPool.hxx deleted file mode 100644 index 3c4496ec6..000000000 --- a/src/tag/TagPool.hxx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_TAG_POOL_HXX -#define MPD_TAG_POOL_HXX - -#include "TagType.h" -#include "thread/Mutex.hxx" - -extern Mutex tag_pool_lock; - -struct TagItem; -struct StringView; - -TagItem * -tag_pool_get_item(TagType type, StringView value); - -TagItem * -tag_pool_dup_item(TagItem *item); - -void -tag_pool_put_item(TagItem *item); - -#endif diff --git a/src/tag/TagRva2.cxx b/src/tag/TagRva2.cxx deleted file mode 100644 index d51a5cb2e..000000000 --- a/src/tag/TagRva2.cxx +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "TagRva2.hxx" -#include "ReplayGainInfo.hxx" - -#include - -#include -#include - -enum class Rva2Channel : uint8_t { - OTHER = 0x00, - MASTER_VOLUME = 0x01, - FRONT_RIGHT = 0x02, - FRONT_LEFT = 0x03, - BACK_RIGHT = 0x04, - BACK_LEFT = 0x05, - FRONT_CENTRE = 0x06, - BACK_CENTRE = 0x07, - SUBWOOFER = 0x08 -}; - -struct Rva2Data { - Rva2Channel type; - uint8_t volume_adjustment[2]; - uint8_t peak_bits; -}; - -static inline id3_length_t -rva2_peak_bytes(const Rva2Data &data) -{ - return (data.peak_bits + 7) / 8; -} - -static inline int -rva2_fixed_volume_adjustment(const Rva2Data &data) -{ - signed int voladj_fixed; - voladj_fixed = (data.volume_adjustment[0] << 8) | - data.volume_adjustment[1]; - voladj_fixed |= -(voladj_fixed & 0x8000); - return voladj_fixed; -} - -static inline float -rva2_float_volume_adjustment(const Rva2Data &data) -{ - /* - * "The volume adjustment is encoded as a fixed point decibel - * value, 16 bit signed integer representing (adjustment*512), - * giving +/- 64 dB with a precision of 0.001953125 dB." - */ - - return (float)rva2_fixed_volume_adjustment(data) / (float)512; -} - -static inline bool -rva2_apply_data(ReplayGainInfo &rgi, - const Rva2Data &data, const id3_latin1_t *id) -{ - if (data.type != Rva2Channel::MASTER_VOLUME) - return false; - - float volume_adjustment = rva2_float_volume_adjustment(data); - - if (strcmp((const char *)id, "album") == 0) { - rgi.album.gain = volume_adjustment; - } else if (strcmp((const char *)id, "track") == 0) { - rgi.track.gain = volume_adjustment; - } else { - rgi.album.gain = volume_adjustment; - rgi.track.gain = volume_adjustment; - } - - return true; -} - -static bool -rva2_apply_frame(ReplayGainInfo &replay_gain_info, - const struct id3_frame *frame) -{ - const id3_latin1_t *id = id3_field_getlatin1(id3_frame_field(frame, 0)); - id3_length_t length; - const id3_byte_t *data = - id3_field_getbinarydata(id3_frame_field(frame, 1), &length); - - if (id == nullptr || data == nullptr) - return false; - - /* - * "The 'identification' string is used to identify the - * situation and/or device where this adjustment should apply. - * The following is then repeated for every channel - * - * Type of channel $xx - * Volume adjustment $xx xx - * Bits representing peak $xx - * Peak volume $xx (xx ...)" - */ - - while (length >= 4) { - const Rva2Data &d = *(const Rva2Data *)data; - unsigned int peak_bytes = rva2_peak_bytes(d); - if (4 + peak_bytes > length) - break; - - if (rva2_apply_data(replay_gain_info, d, id)) - return true; - - data += 4 + peak_bytes; - length -= 4 + peak_bytes; - } - - return false; -} - -bool -tag_rva2_parse(struct id3_tag *tag, ReplayGainInfo &replay_gain_info) -{ - bool found = false; - - /* Loop through all RVA2 frames as some programs (e.g. mp3gain) store - track and album gain in separate tags */ - const struct id3_frame *frame; - for (unsigned i = 0; - (frame = id3_tag_findframe(tag, "RVA2", i)) != nullptr; - ++i) - if (rva2_apply_frame(replay_gain_info, frame)) - found = true; - - return found; -} diff --git a/src/tag/TagRva2.hxx b/src/tag/TagRva2.hxx deleted file mode 100644 index d30d2ad0f..000000000 --- a/src/tag/TagRva2.hxx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_TAG_RVA2_HXX -#define MPD_TAG_RVA2_HXX - -#include "check.h" - -struct id3_tag; -struct ReplayGainInfo; - -/** - * Parse the RVA2 tag, and fill the #ReplayGainInfo struct. This is - * used by decoder plugins with ID3 support. - * - * @return true on success - */ -bool -tag_rva2_parse(id3_tag *tag, ReplayGainInfo &replay_gain_info); - -#endif diff --git a/src/tag/TagString.cxx b/src/tag/TagString.cxx deleted file mode 100644 index 8462c1fa1..000000000 --- a/src/tag/TagString.cxx +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "TagString.hxx" -#include "util/Alloc.hxx" -#include "util/WritableBuffer.hxx" -#include "util/StringView.hxx" -#include "util/UTF8.hxx" - -#include -#include - -gcc_pure -static const char * -FindInvalidUTF8(const char *p, const char *const end) -{ - while (p < end) { - const size_t s = SequenceLengthUTF8(*p); - if (p + s > end) - /* partial sequence at end of string */ - return p; - - /* now call the other SequenceLengthUTF8() overload - which also validates the continuations */ - const size_t t = SequenceLengthUTF8(p); - if (t == 0) - return p; - assert(s == t); - - p += s; - } - - return nullptr; -} - -/** - * Replace invalid sequences with the question mark. - */ -static WritableBuffer -patch_utf8(StringView src, const char *_invalid) -{ - /* duplicate the string, and replace invalid bytes in that - buffer */ - char *dest = (char *)xmemdup(src.data, src.size); - char *const end = dest + src.size; - - char *invalid = dest + (_invalid - src.data); - do { - *invalid = '?'; - - const char *__invalid = FindInvalidUTF8(invalid + 1, end); - invalid = const_cast(__invalid); - } while (invalid != nullptr); - - return { dest, src.size }; -} - -static WritableBuffer -fix_utf8(StringView p) -{ - /* check if the string is already valid UTF-8 */ - const char *invalid = FindInvalidUTF8(p.begin(), p.end()); - if (invalid == nullptr) - return nullptr; - - /* no, broken - patch invalid sequences */ - return patch_utf8(p, invalid); -} - -static bool -char_is_non_printable(unsigned char ch) -{ - return ch < 0x20; -} - -static const char * -find_non_printable(StringView p) -{ - for (const char &ch : p) - if (char_is_non_printable(ch)) - return &ch; - - return nullptr; -} - -/** - * Clears all non-printable characters, convert them to space. - * Returns nullptr if nothing needs to be cleared. - */ -static WritableBuffer -clear_non_printable(StringView src) -{ - const char *first = find_non_printable(src); - if (first == nullptr) - return nullptr; - - char *dest = (char *)xmemdup(src.data, src.size); - - for (size_t i = first - src.data; i < src.size; ++i) - if (char_is_non_printable(dest[i])) - dest[i] = ' '; - - return { dest, src.size }; -} - -WritableBuffer -FixTagString(StringView p) -{ - auto utf8 = fix_utf8(p); - if (!utf8.IsNull()) - p = {utf8.data, utf8.size}; - - WritableBuffer cleared = clear_non_printable(p); - if (cleared.IsNull()) - cleared = utf8; - else - free(utf8.data); - - return cleared; -} diff --git a/src/tag/TagString.hxx b/src/tag/TagString.hxx deleted file mode 100644 index 618d9640d..000000000 --- a/src/tag/TagString.hxx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_TAG_STRING_HXX -#define MPD_TAG_STRING_HXX - -#include "check.h" -#include "Compiler.h" - -struct StringView; -template struct WritableBuffer; - -gcc_nonnull_all -WritableBuffer -FixTagString(StringView p); - -#endif diff --git a/src/tag/TagTable.cxx b/src/tag/TagTable.cxx deleted file mode 100644 index 74ef03521..000000000 --- a/src/tag/TagTable.cxx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "TagTable.hxx" -#include "util/ASCII.hxx" - -#include - -/** - * Looks up a string in a tag translation table (case sensitive). - * Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found - * in the table. - */ -TagType -tag_table_lookup(const struct tag_table *table, const char *name) -{ - for (; table->name != nullptr; ++table) - if (strcmp(name, table->name) == 0) - return table->type; - - return TAG_NUM_OF_ITEM_TYPES; -} - -/** - * Looks up a string in a tag translation table (case insensitive). - * Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found - * in the table. - */ -TagType -tag_table_lookup_i(const struct tag_table *table, const char *name) -{ - for (; table->name != nullptr; ++table) - if (StringEqualsCaseASCII(name, table->name)) - return table->type; - - return TAG_NUM_OF_ITEM_TYPES; -} - -const char * -tag_table_lookup(const tag_table *table, TagType type) -{ - for (; table->name != nullptr; ++table) - if (table->type == type) - return table->name; - - return nullptr; -} diff --git a/src/tag/TagTable.hxx b/src/tag/TagTable.hxx deleted file mode 100644 index 3591fbb1d..000000000 --- a/src/tag/TagTable.hxx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_TAG_TABLE_HXX -#define MPD_TAG_TABLE_HXX - -#include "TagType.h" -#include "Compiler.h" - -struct tag_table { - const char *name; - - TagType type; -}; - -/** - * Looks up a string in a tag translation table (case sensitive). - * Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found - * in the table. - */ -gcc_pure -TagType -tag_table_lookup(const tag_table *table, const char *name); - -/** - * Looks up a string in a tag translation table (case insensitive). - * Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found - * in the table. - */ -gcc_pure -TagType -tag_table_lookup_i(const tag_table *table, const char *name); - -/** - * Looks up a #TagType in a tag translation table and returns its - * string representation. Returns nullptr if the specified type was - * not found in the table. - */ -gcc_pure -const char * -tag_table_lookup(const tag_table *table, TagType type); - -#endif diff --git a/src/tag/TagType.h b/src/tag/TagType.h deleted file mode 100644 index e5a4ef1f7..000000000 --- a/src/tag/TagType.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2003-2017 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_TAG_TYPE_H -#define MPD_TAG_TYPE_H - -#ifdef __cplusplus -#include -#endif - -/** - * Codes for the type of a tag item. - */ -enum TagType -#ifdef __cplusplus -/* the size of this enum is 1 byte; this is only relevant for C++ - code; the only C sources including this header don't use instances - of this enum, they only refer to the integer values */ -: uint8_t -#endif - { - TAG_ARTIST, - TAG_ARTIST_SORT, - TAG_ALBUM, - TAG_ALBUM_SORT, - TAG_ALBUM_ARTIST, - TAG_ALBUM_ARTIST_SORT, - TAG_TITLE, - TAG_TRACK, - TAG_NAME, - TAG_GENRE, - TAG_DATE, - TAG_COMPOSER, - TAG_PERFORMER, - TAG_COMMENT, - TAG_DISC, - - TAG_MUSICBRAINZ_ARTISTID, - TAG_MUSICBRAINZ_ALBUMID, - TAG_MUSICBRAINZ_ALBUMARTISTID, - TAG_MUSICBRAINZ_TRACKID, - TAG_MUSICBRAINZ_RELEASETRACKID, - - TAG_NUM_OF_ITEM_TYPES -}; - -/** - * An array of strings, which map the #TagType to its machine - * readable name (specific to the MPD protocol). - */ -extern const char *const tag_item_names[]; - -#endif diff --git a/src/tag/Type.h b/src/tag/Type.h new file mode 100644 index 000000000..e5a4ef1f7 --- /dev/null +++ b/src/tag/Type.h @@ -0,0 +1,69 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_TYPE_H +#define MPD_TAG_TYPE_H + +#ifdef __cplusplus +#include +#endif + +/** + * Codes for the type of a tag item. + */ +enum TagType +#ifdef __cplusplus +/* the size of this enum is 1 byte; this is only relevant for C++ + code; the only C sources including this header don't use instances + of this enum, they only refer to the integer values */ +: uint8_t +#endif + { + TAG_ARTIST, + TAG_ARTIST_SORT, + TAG_ALBUM, + TAG_ALBUM_SORT, + TAG_ALBUM_ARTIST, + TAG_ALBUM_ARTIST_SORT, + TAG_TITLE, + TAG_TRACK, + TAG_NAME, + TAG_GENRE, + TAG_DATE, + TAG_COMPOSER, + TAG_PERFORMER, + TAG_COMMENT, + TAG_DISC, + + TAG_MUSICBRAINZ_ARTISTID, + TAG_MUSICBRAINZ_ALBUMID, + TAG_MUSICBRAINZ_ALBUMARTISTID, + TAG_MUSICBRAINZ_TRACKID, + TAG_MUSICBRAINZ_RELEASETRACKID, + + TAG_NUM_OF_ITEM_TYPES +}; + +/** + * An array of strings, which map the #TagType to its machine + * readable name (specific to the MPD protocol). + */ +extern const char *const tag_item_names[]; + +#endif -- cgit v1.2.3