diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/apetag.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/apetag.h index 804b130f1..8f2aa96d9 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/apetag.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/apetag.h @@ -56,6 +56,8 @@ namespace TagLib { class TAGLIB_EXPORT Tag : public TagLib::Tag { public: + using TagLib::Tag::ReplayGain; + /*! * Create an APE tag with default values. */ @@ -90,20 +92,34 @@ namespace TagLib { // Reimplementations. String title() const override; + String albumartist() const override; String artist() const override; + String composer() const override; String album() const override; + String unsyncedlyrics() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; + unsigned int disc() const override; + String cuesheet() const override; + ReplayGain replaygain() const override; + String soundcheck() const override; void setTitle(const String &s) override; + void setAlbumArtist(const String &s) override; void setArtist(const String &s) override; + void setComposer(const String &s) override; void setAlbum(const String &s) override; + void setUnsyncedLyrics(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; + void setDisc(unsigned int i) override; + void setCuesheet(const String &s) override; + void setReplaygain(ReplayGain r) override; + void setSoundcheck(const String &s) override; /*! * Implements the unified tag dictionary interface -- export function. diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/asftag.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/asftag.h index 752bad2c7..a461422e6 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/asftag.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/asftag.h @@ -46,6 +46,7 @@ namespace TagLib { friend class File; public: + using TagLib::Tag::ReplayGain; Tag(); @@ -59,17 +60,33 @@ namespace TagLib { */ String title() const override; + /*! + * Returns the album artist name. + */ + String albumartist() const override; + /*! * Returns the artist name. */ String artist() const override; + /*! + * Returns the composer name. + */ + String composer() const override; + /*! * Returns the album name; if no album name is present in the tag * an empty string will be returned. */ String album() const override; + /*! + * Returns the unsynchronized lyrics; if no unsynced lyrics are + * present in the tag an empty string will be returned. + */ + String unsyncedlyrics() const override; + /*! * Returns the track comment. */ @@ -103,22 +120,62 @@ namespace TagLib { */ unsigned int track() const override; + /*! + * Returns the disc number; if there is no disc number set, this will + * return 0. + */ + unsigned int disc() const override; + + /*! + * Returns the embedded cuesheet; currently unimplemented, this will + * always return an empty string. + */ + String cuesheet() const override; + + /*! + * Returns the ReplayGain tag; currently unimplemented, will alway + * always return an empty tag. + */ + ReplayGain replaygain() const override; + + /*! + * Returns the SoundCheck tag; currently unimplemented, this will + * always return an empty string. + */ + String soundcheck() const override; + /*! * Sets the title to \a value. */ void setTitle(const String &value) override; + /*! + * Sets the album artist to \a value. + */ + void setAlbumArtist(const String &value) override; + /*! * Sets the artist to \a value. */ void setArtist(const String &value) override; + /*! + * Sets the composer to \a value. + */ + void setComposer(const String &value) override; + /*! * Sets the album to \a value. If \a value is an empty string then this value will be * cleared. */ void setAlbum(const String &value) override; + /*! + * Sets the unsynchronized lyrics to \a value. If \a value is an empty string then + * this value will be cleared. + */ + void setUnsyncedLyrics(const String &value) override; + /*! * Sets the comment to \a value. */ @@ -150,6 +207,29 @@ namespace TagLib { void setTrack(unsigned int value) override; /*! + * Sets the disc to \a value. If \a value is 0 then this value will be cleared. + */ + void setDisc(unsigned int value) override; + + /*! + * Sets the embedded cuesheet to \a value. Currently unimplemented and does + * nothing. + */ + void setCuesheet(const String &value) override; + + /*! + * Sets the ReplayGain tag to \a value. Currently unimplemented and does + * nothing. + */ + void setReplaygain(ReplayGain value) override; + + /*! + * Sets the SoundCheck tag to \a value. Currently unimplemented and does + * nothing. + */ + void setSoundcheck(const String &value) override; + + /*! * Returns \c true if the tag does not contain any data. This should be * reimplemented in subclasses that provide more than the basic tagging * abilities in this class. diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/dsdiffdiintag.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/dsdiffdiintag.h index 62a7b7355..2870d9e41 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/dsdiffdiintag.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/dsdiffdiintag.h @@ -42,6 +42,7 @@ namespace TagLib { class TAGLIB_EXPORT Tag : public TagLib::Tag { public: + using TagLib::Tag::ReplayGain; Tag(); ~Tag() override; @@ -52,16 +53,31 @@ namespace TagLib { String title() const override; /*! + * Not supported. Therefore always returns String(). + */ + String albumartist() const override; + + /*! * Returns the artist name; if no artist name is present in the tag * String() will be returned. */ String artist() const override; + /*! + * Not supported. Therefore always returns String(). + */ + String composer() const override; + /*! * Not supported. Therefore always returns String(). */ String album() const override; + /*! + * Not supported. Therefore always returns String(). + */ + String unsyncedlyrics() const override; + /*! * Not supported. Therefore always returns String(). */ @@ -82,23 +98,58 @@ namespace TagLib { */ unsigned int track() const override; + /*! + * Not supported. Therefore always returns 0. + */ + unsigned int disc() const override; + + /*! + * Not supported. Therefore always returns String(). + */ + String cuesheet() const override; + + /*! + * Not supported. Therefore always returns ReplayGain(). + */ + ReplayGain replaygain() const override; + + /*! + * Not supported. Therefore always returns String(). + */ + String soundcheck() const override; + /*! * Sets the title to \a title. If \a title is String() then this * value will be cleared. */ void setTitle(const String &title) override; + /*! + * Not supported and therefore ignored. + */ + void setAlbumArtist(const String &albumartist) override; + /*! * Sets the artist to \a artist. If \a artist is String() then this * value will be cleared. */ void setArtist(const String &artist) override; + /*! + * Not supported and therefore ignored. + */ + void setComposer(const String &composer) override; + /*! * Not supported and therefore ignored. */ void setAlbum(const String &album) override; + /*! + * Not supported and therefore ignored. + */ + void setUnsyncedLyrics(const String &unsyncedlyrics) override; + /*! * Not supported and therefore ignored. */ @@ -119,6 +170,26 @@ namespace TagLib { */ void setTrack(unsigned int track) override; + /*! + * Not supported and therefore ignored. + */ + void setDisc(unsigned int disc) override; + + /*! + * Not supported and therefore ignored. + */ + void setCuesheet(const String &cuesheet) override; + + /*! + * Not supported and therefore ignored. + */ + void setReplaygain(ReplayGain replaygain) override; + + /*! + * Not supported and therefore ignored. + */ + void setSoundcheck(const String &soundcheck) override; + /*! * Implements the unified property interface -- export function. * Since the DIIN tag is very limited, the exported map is as well. diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/id3v1tag.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/id3v1tag.h index e85023ef3..641282272 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/id3v1tag.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/id3v1tag.h @@ -114,6 +114,7 @@ namespace TagLib { class TAGLIB_EXPORT Tag : public TagLib::Tag { public: + using TagLib::Tag::ReplayGain; /*! * Create an ID3v1 tag with default values. */ @@ -148,20 +149,34 @@ namespace TagLib { // Reimplementations. String title() const override; + String albumartist() const override; String artist() const override; + String composer() const override; String album() const override; + String unsyncedlyrics() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; + unsigned int disc() const override; + String cuesheet() const override; + ReplayGain replaygain() const override; + String soundcheck() const override; void setTitle(const String &s) override; + void setAlbumArtist(const String &s) override; void setArtist(const String &s) override; + void setComposer(const String &s) override; void setAlbum(const String &s) override; + void setUnsyncedLyrics(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; + void setDisc(unsigned int i) override; + void setCuesheet(const String &s) override; + void setReplaygain(ReplayGain r) override; + void setSoundcheck(const String &s) override; /*! * Returns the genre in number. diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/id3v2tag.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/id3v2tag.h index 20fa0b7a1..a01b4853f 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/id3v2tag.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/id3v2tag.h @@ -133,6 +133,7 @@ namespace TagLib { class TAGLIB_EXPORT Tag : public TagLib::Tag { public: + using TagLib::Tag::ReplayGain; /*! * Constructs an empty ID3v2 tag. * @@ -166,20 +167,34 @@ namespace TagLib { // Reimplementations. String title() const override; + String albumartist() const override; String artist() const override; + String composer() const override; String album() const override; + String unsyncedlyrics() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; + unsigned int disc() const override; + String cuesheet() const override; + ReplayGain replaygain() const override; + String soundcheck() const override; void setTitle(const String &s) override; + void setAlbumArtist(const String &s) override; void setArtist(const String &s) override; + void setComposer(const String &s) override; void setAlbum(const String &s) override; + void setUnsyncedLyrics(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; + void setDisc(unsigned int i) override; + void setCuesheet(const String &s) override; + void setReplaygain(ReplayGain r) override; + void setSoundcheck(const String &s) override; bool isEmpty() const override; diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/infotag.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/infotag.h index b9ab43528..9b6a055fa 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/infotag.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/infotag.h @@ -95,6 +95,7 @@ namespace TagLib { class TAGLIB_EXPORT Tag : public TagLib::Tag { public: + using TagLib::Tag::ReplayGain; /*! * Constructs an empty INFO tag. */ @@ -113,20 +114,34 @@ namespace TagLib { // Reimplementations String title() const override; + String albumartist() const override; String artist() const override; + String composer() const override; String album() const override; + String unsyncedlyrics() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; + unsigned int disc() const override; + String cuesheet() const override; + ReplayGain replaygain() const override; + String soundcheck() const override; void setTitle(const String &s) override; + void setAlbumArtist(const String &s) override; void setArtist(const String &s) override; + void setComposer(const String &s) override; void setAlbum(const String &s) override; + void setUnsyncedLyrics(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; + void setDisc(unsigned int i) override; + void setCuesheet(const String &s) override; + void setReplaygain(ReplayGain r) override; + void setSoundcheck(const String &s) override; bool isEmpty() const override; diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/modtag.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/modtag.h index 336d75ee1..18b16f3c9 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/modtag.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/modtag.h @@ -48,6 +48,8 @@ namespace TagLib { class TAGLIB_EXPORT Tag : public TagLib::Tag { public: + using TagLib::Tag::ReplayGain; + Tag(); ~Tag() override; @@ -60,6 +62,11 @@ namespace TagLib { */ String title() const override; + /*! + * Not supported by module files. Therefore always returns an empty string. + */ + String albumartist() const override; + /*! * Not supported by module files. Therefore always returns an empty string. */ @@ -68,9 +75,19 @@ namespace TagLib { /*! * Not supported by module files. Therefore always returns an empty string. */ + String composer() const override; + + /*! + * Not supported by module files. Therefore always returns an empty string. + */ String album() const override; /*! + * Not supported by module files. Therefore always returns an empty string. + */ + String unsyncedlyrics() const override; + + /*! * Returns the track comment derived from the instrument/sample/pattern * names; if no comment is present in the tag an empty string will be * returned. @@ -93,6 +110,26 @@ namespace TagLib { unsigned int track() const override; /*! + * Not supported by module files. Therefore always returns 0. + */ + unsigned int disc() const override; + + /*! + * Not supported by module files. Therefore always returns an empty string. + */ + String cuesheet() const override; + + /*! + * Not supported by module files. Therefore always returns an empty tag. + */ + ReplayGain replaygain() const override; + + /*! + * Not supported by module files. Therefore always returns an empty string. + */ + String soundcheck() const override; + + /*! * Returns the name of the tracker used to create/edit the module file. * Only XM files store this tag to the file as such, for other formats * (Mod, S3M, IT) this is derived from the file type or the flavour of @@ -111,16 +148,31 @@ namespace TagLib { */ void setTitle(const String &title) override; + /*! + * Not supported by module files and therefore ignored. + */ + void setAlbumArtist(const String &albumartist) override; + /*! * Not supported by module files and therefore ignored. */ void setArtist(const String &artist) override; + /*! + * Not supported by module files and therefore ignored. + */ + void setComposer(const String &composer) override; + /*! * Not supported by module files and therefore ignored. */ void setAlbum(const String &album) override; + /*! + * Not supported by module files and therefore ignored. + */ + void setUnsyncedLyrics(const String &unsyncedlyrics) override; + /*! * Sets the comment to \a comment. If \a comment is an empty string then * this value will be cleared. @@ -156,6 +208,26 @@ namespace TagLib { void setTrack(unsigned int track) override; /*! + * Not supported by module files and therefore ignored. + */ + void setDisc(unsigned int disc) override; + + /*! + * Not supported by module files and therefore ignored. + */ + void setCuesheet(const String &cuesheet) override; + + /*! + * Not supported by module files and therefore ignored. + */ + void setReplaygain(ReplayGain r) override; + + /*! + * Not supported by module files and therefore ignored. + */ + void setSoundcheck(const String &soundcheck) override; + + /*! * Sets the tracker name to \a trackerName. If \a trackerName is * an empty string then this value will be cleared. * diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/mp4tag.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/mp4tag.h index bae7d232f..59ee14f1d 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/mp4tag.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/mp4tag.h @@ -43,6 +43,7 @@ namespace TagLib { class TAGLIB_EXPORT Tag: public TagLib::Tag { public: + using TagLib::Tag::ReplayGain; Tag(); Tag(TagLib::File *file, Atoms *atoms, const ItemFactory *factory = nullptr); @@ -52,20 +53,34 @@ namespace TagLib { bool save(); String title() const override; + String albumartist() const override; String artist() const override; + String composer() const override; String album() const override; + String unsyncedlyrics() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; + unsigned int disc() const override; + String cuesheet() const override; + ReplayGain replaygain() const override; + String soundcheck() const override; void setTitle(const String &value) override; + void setAlbumArtist(const String &value) override; void setArtist(const String &value) override; + void setComposer(const String &value) override; void setAlbum(const String &value) override; + void setUnsyncedLyrics(const String &value) override; void setComment(const String &value) override; void setGenre(const String &value) override; void setYear(unsigned int value) override; void setTrack(unsigned int value) override; + void setDisc(unsigned int value) override; + void setCuesheet(const String &value) override; + void setReplaygain(ReplayGain value) override; + void setSoundcheck(const String &value) override; bool isEmpty() const override; diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/tag.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/tag.h index cfd159179..ae79b340f 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/tag.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/tag.h @@ -48,6 +48,50 @@ namespace TagLib { class TAGLIB_EXPORT Tag { public: + class ReplayGain { + protected: + bool _equals(const ReplayGain &) const; + + public: + ReplayGain(); + + ReplayGain &operator=(const ReplayGain &); + bool operator==(const ReplayGain &) const; + bool operator!=(const ReplayGain &) const; + + bool isEmpty() const; + bool albumGainSet() const; + bool albumPeakSet() const; + bool trackGainSet() const; + bool trackPeakSet() const; + + float albumGain() const; + float albumPeak() const; + float trackGain() const; + float trackPeak() const; + + void setAlbumGain(float f); + void setAlbumPeak(float f); + void setTrackGain(float f); + void setTrackPeak(float f); + + void clear(); + void clearAlbumGain(); + void clearAlbumPeak(); + void clearTrackGain(); + void clearTrackPeak(); + + private: + bool albumgainSet; + bool albumpeakSet; + bool trackgainSet; + bool trackpeakSet; + + float albumgain; + float albumpeak; + float trackgain; + float trackpeak; + }; /*! * Destroys this Tag instance. @@ -129,18 +173,36 @@ namespace TagLib { */ virtual String title() const = 0; + /*! + * Returns the album artist name; if no album artist name is present + * in the tag an empty string will be returned. + */ + virtual String albumartist() const = 0; + /*! * Returns the artist name; if no artist name is present in the tag * an empty string will be returned. */ virtual String artist() const = 0; + /*! + * Returns the composer name; if no composer name is present in the + * tag an empty string will be returned. + */ + virtual String composer() const = 0; + /*! * Returns the album name; if no album name is present in the tag * an empty string will be returned. */ virtual String album() const = 0; + /*! + * Returns the unsynchronized lyrics; if no unsynced lyrics are + * present in the tag an empty string will be returned. + */ + virtual String unsyncedlyrics() const = 0; + /*! * Returns the track comment; if no comment is present in the tag * an empty string will be returned. @@ -164,24 +226,65 @@ namespace TagLib { */ virtual unsigned int track() const = 0; + /*! + * Returns the disc number; if there is no disc number set, this will + * return 0. + */ + virtual unsigned int disc() const = 0; + + /*! + * Returns the embedded cue sheet; if there is no embedded cue sheet set, + * this will return an empty string. + */ + virtual String cuesheet() const = 0; + + /*! + * Returns the ReplayGain tags; if there are no tags set then it will be + * marked empty. + */ + virtual ReplayGain replaygain() const = 0; + + /*! + * Returns the SoundCheck tag; if there is no SoundCheck tag set then this + * will return an empty string. + */ + virtual String soundcheck() const = 0; + /*! * Sets the title to \a s. If \a s is an empty string then this value will be * cleared. */ virtual void setTitle(const String &s) = 0; + /*! + * Sets the album artist to \a s. If \a s is an empty string then this value + * will be cleared. */ + virtual void setAlbumArtist(const String &s) = 0; + /*! * Sets the artist to \a s. If \a s is an empty string then this value will be * cleared. */ virtual void setArtist(const String &s) = 0; + /*! + * Sets the composer to \a s. If \a s is an empty string then this value will + * be cleared. + */ + virtual void setComposer(const String &s) = 0; + /*! * Sets the album to \a s. If \a s is an empty string then this value will be * cleared. */ virtual void setAlbum(const String &s) = 0; + /*! + * Sets the unsynchronized lyrics to \a s. If \a s is an empty string then this + * value will be cleared. + */ + virtual void setUnsyncedLyrics(const String &s) = 0; + /*! * Sets the comment to \a s. If \a s is an empty string then this value will be * cleared. @@ -198,15 +301,38 @@ namespace TagLib { virtual void setGenre(const String &s) = 0; /*! - * Sets the year to \a i. If \a s is 0 then this value will be cleared. + * Sets the year to \a i. If \a i is 0 then this value will be cleared. */ virtual void setYear(unsigned int i) = 0; /*! - * Sets the track to \a i. If \a s is 0 then this value will be cleared. + * Sets the track to \a i. If \a i is 0 then this value will be cleared. */ virtual void setTrack(unsigned int i) = 0; + /*! + * Sets the disc to \a i. If \a i is 0 then this value will be cleared. + */ + virtual void setDisc(unsigned int i) = 0; + + /*! + * Sets the embedded cue sheet to \a s. If \a s is an empty string then + * this value will be cleared. + */ + virtual void setCuesheet(const String &s) = 0; + + /*! + * Sets the ReplayGain tag to \a t. If any fields are marked as empty + * then they will be cleared. + */ + virtual void setReplaygain(ReplayGain replaygain) = 0; + + /*! + * Sets the SoundCheck tag to \a s. If \a s is an empty string then this + * value will be cleared. + */ + virtual void setSoundcheck(const String &s) = 0; + /*! * Returns \c true if the tag does not contain any data. This should be * reimplemented in subclasses that provide more than the basic tagging @@ -242,6 +368,12 @@ namespace TagLib { */ Tag(); + /*! + * Internal ReplayGain tag state. This is protected since subclasses should + * be able to manipulate it freely. + */ + ReplayGain rg; + private: class TagPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/tstring.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/tstring.h index f7e408151..060c084a9 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/tstring.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/tstring.h @@ -357,7 +357,16 @@ namespace TagLib { * \c true and returns the integer. Otherwise it sets \a *ok to \c false * and the result is undefined. */ - int toInt(bool *ok = nullptr) const; + int toInt(bool *ok = nullptr, int base = 10) const; + + /*! + * Convert the string to a float. + * + * If the conversion was successful, it sets the value of \a *ok to + * \c true and returns the float. Otherwise it sets \a *ok to \c false + * and the result is undefined. + */ + float toFloat(bool *ok = nullptr) const; /*! * Returns a string with the leading and trailing whitespace stripped. diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/xiphcomment.h b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/xiphcomment.h index b6b8d3945..12a0ad61d 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/xiphcomment.h +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Headers/xiphcomment.h @@ -70,6 +70,7 @@ namespace TagLib { class TAGLIB_EXPORT XiphComment : public TagLib::Tag { public: + using TagLib::Tag::ReplayGain; /*! * Constructs an empty Vorbis comment. */ @@ -89,20 +90,34 @@ namespace TagLib { XiphComment &operator=(const XiphComment &) = delete; String title() const override; + String albumartist() const override; String artist() const override; + String composer() const override; String album() const override; + String unsyncedlyrics() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; + unsigned int disc() const override; + String cuesheet() const override; + ReplayGain replaygain() const override; + String soundcheck() const override; void setTitle(const String &s) override; + void setAlbumArtist(const String &s) override; void setArtist(const String &s) override; + void setComposer(const String &s) override; void setAlbum(const String &s) override; + void setUnsyncedLyrics(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; + void setDisc(unsigned int i) override; + void setCuesheet(const String &s) override; + void setReplaygain(ReplayGain r) override; + void setSoundcheck(const String &s) override; bool isEmpty() const override; diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/Resources/Info.plist b/ThirdParty/Frameworks/tag.framework/Versions/A/Resources/Info.plist index 0c0e3c1ab..821ac486b 100644 --- a/ThirdParty/Frameworks/tag.framework/Versions/A/Resources/Info.plist +++ b/ThirdParty/Frameworks/tag.framework/Versions/A/Resources/Info.plist @@ -9,11 +9,11 @@ CFBundleIconFile CFBundleIdentifier - org.taglib.tag + CFBundleInfoDictionaryVersion 6.0 CFBundleName - tag + CFBundlePackageType FMWK CFBundleSignature @@ -21,7 +21,7 @@ CFBundleVersion CFBundleShortVersionString - 2.0.2 + CSResourcesFileMapped diff --git a/ThirdParty/Frameworks/tag.framework/Versions/A/tag b/ThirdParty/Frameworks/tag.framework/Versions/A/tag index 8f49ca61c..43ff8f2b5 100755 Binary files a/ThirdParty/Frameworks/tag.framework/Versions/A/tag and b/ThirdParty/Frameworks/tag.framework/Versions/A/tag differ diff --git a/ThirdParty/Frameworks/taglib.patch b/ThirdParty/Frameworks/taglib.patch new file mode 100644 index 000000000..98369b829 --- /dev/null +++ b/ThirdParty/Frameworks/taglib.patch @@ -0,0 +1,3162 @@ +diff -ur taglib-2.0.2.orig/taglib/ape/apetag.cpp taglib-2.0.2/taglib/ape/apetag.cpp +--- taglib-2.0.2.orig/taglib/ape/apetag.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/ape/apetag.cpp 2025-02-17 02:18:36 +@@ -73,6 +73,9 @@ + + Footer footer; + ItemListMap itemListMap; ++ ++ String albumartistField; ++ String unsyncedlyricsField; + }; + + //////////////////////////////////////////////////////////////////////////////// +@@ -106,18 +109,59 @@ + return val.isEmpty() ? String() : joinTagValues(val.values()); + } + ++String APE::Tag::albumartist() const ++{ ++ Item val = d->itemListMap.value("ALBUMARTIST"); ++ if(!val.isEmpty()) { ++ d->albumartistField = "ALBUMARTIST"; ++ return joinTagValues(val.values()); ++ } ++ val = d->itemListMap.value("ALBUM ARTIST"); ++ if(!val.isEmpty()) { ++ d->albumartistField = "ALBUM ARTIST"; ++ return joinTagValues(val.values()); ++ } ++ return String(); ++} ++ + String APE::Tag::artist() const + { + Item val = d->itemListMap.value("ARTIST"); + return val.isEmpty() ? String() : joinTagValues(val.values()); + } + ++String APE::Tag::composer() const ++{ ++ Item val = d->itemListMap.value("COMPOSER"); ++ return val.isEmpty() ? String() : joinTagValues(val.values()); ++} ++ + String APE::Tag::album() const + { + Item val = d->itemListMap.value("ALBUM"); + return val.isEmpty() ? String() : joinTagValues(val.values()); + } + ++String APE::Tag::unsyncedlyrics() const ++{ ++ Item val = d->itemListMap.value("UNSYNCEDLYRICS"); ++ if(!val.isEmpty()) { ++ d->unsyncedlyricsField = "UNSYNCEDLYRICS"; ++ return joinTagValues(val.values()); ++ } ++ val = d->itemListMap.value("UNSYNCED LYRICS"); ++ if(!val.isEmpty()) { ++ d->unsyncedlyricsField = "UNSYNCED LYRICS"; ++ return joinTagValues(val.values()); ++ } ++ val = d->itemListMap.value("LYRICS"); ++ if(!val.isEmpty()) { ++ d->unsyncedlyricsField = "LYRICS"; ++ return joinTagValues(val.values()); ++ } ++ return String(); ++} ++ + String APE::Tag::comment() const + { + Item val = d->itemListMap.value("COMMENT"); +@@ -142,21 +186,70 @@ + return val.isEmpty() ? 0 : val.toString().toInt(); + } + ++unsigned int APE::Tag::disc() const ++{ ++ Item val = d->itemListMap.value("DISC"); ++ return val.isEmpty() ? 0 : val.toString().toInt(); ++} ++ ++String APE::Tag::cuesheet() const ++{ ++ Item val = d->itemListMap.value("CUESHEET"); ++ return val.isEmpty() ? String() : val.toString(); ++} ++ ++TagLib::Tag::ReplayGain APE::Tag::replaygain() const ++{ ++ return rg; ++} ++ ++String APE::Tag::soundcheck() const ++{ ++ return String(); ++} ++ + void APE::Tag::setTitle(const String &s) + { + addValue("TITLE", s, true); + } + ++void APE::Tag::setAlbumArtist(const String &s) ++{ ++ if(d->albumartistField.isEmpty()) { ++ if(d->itemListMap.contains("ALBUMARTIST")) ++ d->albumartistField = "ALBUMARTIST"; ++ else ++ d->albumartistField = "ALBUM ARTIST"; ++ } ++ addValue(d->albumartistField, s, true); ++} ++ + void APE::Tag::setArtist(const String &s) + { + addValue("ARTIST", s, true); + } + ++void APE::Tag::setComposer(const String &s) ++{ ++ addValue("COMPOSER", s, true); ++} ++ + void APE::Tag::setAlbum(const String &s) + { + addValue("ALBUM", s, true); + } + ++void APE::Tag::setUnsyncedLyrics(const String &s) ++{ ++ if(d->unsyncedlyricsField.isEmpty()) { ++ if(d->itemListMap.contains("UNSYNCEDLYRICS")) ++ d->unsyncedlyricsField = "UNSYNCEDLYRICS"; ++ else ++ d->unsyncedlyricsField = "UNSYNCED LYRICS"; ++ } ++ addValue(d->unsyncedlyricsField, s); ++} ++ + void APE::Tag::setComment(const String &s) + { + addValue("COMMENT", s, true); +@@ -183,6 +276,44 @@ + addValue("TRACK", String::number(i), true); + } + ++void APE::Tag::setDisc(unsigned int i) ++{ ++ if(i == 0) ++ removeItem("DISC"); ++ else ++ addValue("DISC", String::number(i), true); ++} ++ ++void APE::Tag::setCuesheet(const String &s) ++{ ++ addValue("CUESHEET", s); ++} ++ ++void APE::Tag::setReplaygain(TagLib::Tag::ReplayGain r) ++{ ++ rg = r; ++ if(!rg.albumGainSet()) ++ removeItem("REPLAYGAIN_ALBUM_GAIN"); ++ else ++ addValue("REPLAYGAIN_ALBUM_GAIN", String::number(rg.albumGain()) + " dB", true); ++ if(!rg.albumPeakSet()) ++ removeItem("REPLAYGAIN_ALBUM_PEAK"); ++ else ++ addValue("REPLAYGAIN_ALBUM_PEAK", String::number(rg.albumPeak()), true); ++ if(!rg.trackGainSet()) ++ removeItem("REPLAYGAIN_TRACK_GAIN"); ++ else ++ addValue("REPLAYGAIN_TRACK_GAIN", String::number(rg.trackGain()) + " dB", true); ++ if(!rg.trackPeakSet()) ++ removeItem("REPLAYGAIN_TRACK_PEAK"); ++ else ++ addValue("REPLAYGAIN_TRACK_PEAK", String::number(rg.trackPeak()), true); ++} ++ ++void APE::Tag::setSoundcheck(const String &) ++{ ++} ++ + namespace + { + // conversions of tag keys between what we use in PropertyMap and what's usual +@@ -500,4 +631,25 @@ + + pos += keyLength + valLength + 9; + } ++ ++ Item val = d->itemListMap.value("REPLAYGAIN_ALBUM_GAIN"); ++ if(!val.isEmpty()) ++ rg.setAlbumGain(val.toString().toFloat()); ++ else ++ rg.clearAlbumGain(); ++ val = d->itemListMap.value("REPLAYGAIN_ALBUM_PEAK"); ++ if(!val.isEmpty()) ++ rg.setAlbumPeak(val.toString().toFloat()); ++ else ++ rg.clearAlbumPeak(); ++ val = d->itemListMap.value("REPLAYGAIN_TRACK_GAIN"); ++ if(!val.isEmpty()) ++ rg.setTrackGain(val.toString().toFloat()); ++ else ++ rg.clearTrackGain(); ++ val = d->itemListMap.value("REPLAYGAIN_TRACK_PEAK"); ++ if(!val.isEmpty()) ++ rg.setTrackPeak(val.toString().toFloat()); ++ else ++ rg.clearTrackPeak(); + } +diff -ur taglib-2.0.2.orig/taglib/ape/apetag.h taglib-2.0.2/taglib/ape/apetag.h +--- taglib-2.0.2.orig/taglib/ape/apetag.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/ape/apetag.h 2025-02-17 01:22:05 +@@ -56,6 +56,8 @@ + class TAGLIB_EXPORT Tag : public TagLib::Tag + { + public: ++ using TagLib::Tag::ReplayGain; ++ + /*! + * Create an APE tag with default values. + */ +@@ -90,20 +92,34 @@ + // Reimplementations. + + String title() const override; ++ String albumartist() const override; + String artist() const override; ++ String composer() const override; + String album() const override; ++ String unsyncedlyrics() const override; + String comment() const override; + String genre() const override; + unsigned int year() const override; + unsigned int track() const override; ++ unsigned int disc() const override; ++ String cuesheet() const override; ++ ReplayGain replaygain() const override; ++ String soundcheck() const override; + + void setTitle(const String &s) override; ++ void setAlbumArtist(const String &s) override; + void setArtist(const String &s) override; ++ void setComposer(const String &s) override; + void setAlbum(const String &s) override; ++ void setUnsyncedLyrics(const String &s) override; + void setComment(const String &s) override; + void setGenre(const String &s) override; + void setYear(unsigned int i) override; + void setTrack(unsigned int i) override; ++ void setDisc(unsigned int i) override; ++ void setCuesheet(const String &s) override; ++ void setReplaygain(ReplayGain r) override; ++ void setSoundcheck(const String &s) override; + + /*! + * Implements the unified tag dictionary interface -- export function. +diff -ur taglib-2.0.2.orig/taglib/asf/asftag.cpp taglib-2.0.2/taglib/asf/asftag.cpp +--- taglib-2.0.2.orig/taglib/asf/asftag.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/asf/asftag.cpp 2025-02-17 01:37:32 +@@ -50,10 +50,13 @@ + { + public: + String title; ++ String albumartist; + String artist; ++ String composer; + String copyright; + String comment; + String rating; ++ String unsyncedlyrics; + AttributeListMap attributeListMap; + }; + +@@ -69,11 +72,21 @@ + return d->title; + } + ++String ASF::Tag::albumartist() const ++{ ++ return d->albumartist; ++} ++ + String ASF::Tag::artist() const + { + return d->artist; + } + ++String ASF::Tag::composer() const ++{ ++ return d->composer; ++} ++ + String ASF::Tag::album() const + { + if(d->attributeListMap.contains("WM/AlbumTitle")) +@@ -97,6 +110,11 @@ + return d->rating; + } + ++String ASF::Tag::unsyncedlyrics() const ++{ ++ return d->unsyncedlyrics; ++} ++ + unsigned int ASF::Tag::year() const + { + if(d->attributeListMap.contains("WM/Year")) +@@ -117,6 +135,25 @@ + return 0; + } + ++unsigned int ASF::Tag::disc() const ++{ ++ if(d->attributeListMap.contains("WM/PartOfSet")) { ++ const ASF::Attribute attr = d->attributeListMap["WM/PartOfSet"][0]; ++ if(attr.type() == ASF::Attribute::DWordType) ++ return attr.toUInt(); ++ return attr.toString().toInt(); ++ } ++ if(d->attributeListMap.contains("WM/DiscNumber")) { ++ const ASF::Attribute attr = d->attributeListMap["WM/DiscNumber"][0]; ++ if(attr.type() == ASF::Attribute::DWordType) ++ return attr.toUInt(); ++ return attr.toString().toInt(); ++ } ++ if(d->attributeListMap.contains("WM/Disc")) ++ return d->attributeListMap["WM/Disc"][0].toUInt(); ++ return 0; ++} ++ + String ASF::Tag::genre() const + { + if(d->attributeListMap.contains("WM/Genre")) +@@ -125,16 +162,41 @@ + return String(); + } + ++String ASF::Tag::cuesheet() const ++{ ++ return String(); ++} ++ ++TagLib::Tag::ReplayGain ASF::Tag::replaygain() const ++{ ++ return TagLib::Tag::ReplayGain(); ++} ++ ++String ASF::Tag::soundcheck() const ++{ ++ return String(); ++} ++ + void ASF::Tag::setTitle(const String &value) + { + d->title = value; + } + ++void ASF::Tag::setAlbumArtist(const String &value) ++{ ++ d->albumartist = value; ++} ++ + void ASF::Tag::setArtist(const String &value) + { + d->artist = value; + } + ++void ASF::Tag::setComposer(const String &value) ++{ ++ d->composer = value; ++} ++ + void ASF::Tag::setCopyright(const String &value) + { + d->copyright = value; +@@ -150,6 +212,11 @@ + d->rating = value; + } + ++void ASF::Tag::setUnsyncedLyrics(const String &value) ++{ ++ d->unsyncedlyrics = value; ++} ++ + void ASF::Tag::setAlbum(const String &value) + { + setAttribute("WM/AlbumTitle", value); +@@ -167,9 +234,32 @@ + + void ASF::Tag::setTrack(unsigned int value) + { ++ if(d->attributeListMap.contains("WM/Track")) ++ d->attributeListMap.erase("WM/Track"); + setAttribute("WM/TrackNumber", String::number(value)); + } + ++void ASF::Tag::setDisc(unsigned int value) ++{ ++ if(d->attributeListMap.contains("WM/DiscNumber")) ++ d->attributeListMap.erase("WM/DiscNumber"); ++ if(d->attributeListMap.contains("WM/Disc")) ++ d->attributeListMap.erase("WM/Disc"); ++ setAttribute("WM/PartOfSet", String::number(value)); ++} ++ ++void ASF::Tag::setCuesheet(const String &value) ++{ ++} ++ ++void ASF::Tag::setReplaygain(TagLib::Tag::ReplayGain) ++{ ++} ++ ++void ASF::Tag::setSoundcheck(const String &) ++{ ++} ++ + ASF::AttributeListMap& ASF::Tag::attributeListMap() + { + return d->attributeListMap; +@@ -302,15 +392,24 @@ + if(!d->title.isEmpty()) { + props["TITLE"] = d->title; + } ++ if(!d->albumartist.isEmpty()) { ++ props["ALBUMARTIST"] = d->albumartist; ++ } + if(!d->artist.isEmpty()) { + props["ARTIST"] = d->artist; + } ++ if(!d->composer.isEmpty()) { ++ props["COMPOSER"] = d->composer; ++ } + if(!d->copyright.isEmpty()) { + props["COPYRIGHT"] = d->copyright; + } + if(!d->comment.isEmpty()) { + props["COMMENT"] = d->comment; + } ++ if(!d->unsyncedlyrics.isEmpty()) { ++ props["LYRICS"] = d->unsyncedlyrics; ++ } + + for(const auto &[k, attributes] : std::as_const(d->attributeListMap)) { + if(const String key = translateKey(k); !key.isEmpty()) { +@@ -354,15 +453,24 @@ + if(prop == "TITLE") { + d->title.clear(); + } ++ else if(prop == "ALBUMARTIST") { ++ d->albumartist.clear(); ++ } + else if(prop == "ARTIST") { + d->artist.clear(); + } ++ else if(prop == "COMPOSER") { ++ d->composer.clear(); ++ } + else if(prop == "COMMENT") { + d->comment.clear(); + } + else if(prop == "COPYRIGHT") { + d->copyright.clear(); + } ++ else if(prop == "LYRICS") { ++ d->unsyncedlyrics.clear(); ++ } + else { + d->attributeListMap.erase(reverseKeyMap[prop]); + } +@@ -381,14 +489,23 @@ + else if(prop == "TITLE") { + d->title = attributes.toString(); + } ++ else if(prop == "ALBUMARTIST") { ++ d->albumartist = attributes.toString(); ++ } + else if(prop == "ARTIST") { + d->artist = attributes.toString(); + } ++ else if(prop == "COMPOSER") { ++ d->composer = attributes.toString(); ++ } + else if(prop == "COMMENT") { + d->comment = attributes.toString(); + } + else if(prop == "COPYRIGHT") { + d->copyright = attributes.toString(); ++ } ++ else if(prop == "LYRICS") { ++ d->unsyncedlyrics = attributes.toString(); + } + else { + ignoredProps.insert(prop, attributes); +diff -ur taglib-2.0.2.orig/taglib/asf/asftag.h taglib-2.0.2/taglib/asf/asftag.h +--- taglib-2.0.2.orig/taglib/asf/asftag.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/asf/asftag.h 2025-02-17 01:35:57 +@@ -46,6 +46,7 @@ + friend class File; + + public: ++ using TagLib::Tag::ReplayGain; + + Tag(); + +@@ -60,17 +61,33 @@ + String title() const override; + + /*! ++ * Returns the album artist name. ++ */ ++ String albumartist() const override; ++ ++ /*! + * Returns the artist name. + */ + String artist() const override; + + /*! ++ * Returns the composer name. ++ */ ++ String composer() const override; ++ ++ /*! + * Returns the album name; if no album name is present in the tag + * an empty string will be returned. + */ + String album() const override; + + /*! ++ * Returns the unsynchronized lyrics; if no unsynced lyrics are ++ * present in the tag an empty string will be returned. ++ */ ++ String unsyncedlyrics() const override; ++ ++ /*! + * Returns the track comment. + */ + String comment() const override; +@@ -104,22 +121,62 @@ + unsigned int track() const override; + + /*! ++ * Returns the disc number; if there is no disc number set, this will ++ * return 0. ++ */ ++ unsigned int disc() const override; ++ ++ /*! ++ * Returns the embedded cuesheet; currently unimplemented, this will ++ * always return an empty string. ++ */ ++ String cuesheet() const override; ++ ++ /*! ++ * Returns the ReplayGain tag; currently unimplemented, will alway ++ * always return an empty tag. ++ */ ++ ReplayGain replaygain() const override; ++ ++ /*! ++ * Returns the SoundCheck tag; currently unimplemented, this will ++ * always return an empty string. ++ */ ++ String soundcheck() const override; ++ ++ /*! + * Sets the title to \a value. + */ + void setTitle(const String &value) override; + + /*! ++ * Sets the album artist to \a value. ++ */ ++ void setAlbumArtist(const String &value) override; ++ ++ /*! + * Sets the artist to \a value. + */ + void setArtist(const String &value) override; + + /*! ++ * Sets the composer to \a value. ++ */ ++ void setComposer(const String &value) override; ++ ++ /*! + * Sets the album to \a value. If \a value is an empty string then this value will be + * cleared. + */ + void setAlbum(const String &value) override; + + /*! ++ * Sets the unsynchronized lyrics to \a value. If \a value is an empty string then ++ * this value will be cleared. ++ */ ++ void setUnsyncedLyrics(const String &value) override; ++ ++ /*! + * Sets the comment to \a value. + */ + void setComment(const String &value) override; +@@ -150,6 +207,29 @@ + void setTrack(unsigned int value) override; + + /*! ++ * Sets the disc to \a value. If \a value is 0 then this value will be cleared. ++ */ ++ void setDisc(unsigned int value) override; ++ ++ /*! ++ * Sets the embedded cuesheet to \a value. Currently unimplemented and does ++ * nothing. ++ */ ++ void setCuesheet(const String &value) override; ++ ++ /*! ++ * Sets the ReplayGain tag to \a value. Currently unimplemented and does ++ * nothing. ++ */ ++ void setReplaygain(ReplayGain value) override; ++ ++ /*! ++ * Sets the SoundCheck tag to \a value. Currently unimplemented and does ++ * nothing. ++ */ ++ void setSoundcheck(const String &value) override; ++ ++ /*! + * Returns \c true if the tag does not contain any data. This should be + * reimplemented in subclasses that provide more than the basic tagging + * abilities in this class. +diff -ur taglib-2.0.2.orig/taglib/dsdiff/dsdiffdiintag.cpp taglib-2.0.2/taglib/dsdiff/dsdiffdiintag.cpp +--- taglib-2.0.2.orig/taglib/dsdiff/dsdiffdiintag.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/dsdiff/dsdiffdiintag.cpp 2025-02-17 01:58:54 +@@ -53,16 +53,31 @@ + return d->title; + } + ++String DSDIFF::DIIN::Tag::albumartist() const ++{ ++ return String(); ++} ++ + String DSDIFF::DIIN::Tag::artist() const + { + return d->artist; + } + ++String DSDIFF::DIIN::Tag::composer() const ++{ ++ return String(); ++} ++ + String DSDIFF::DIIN::Tag::album() const + { + return String(); + } + ++String DSDIFF::DIIN::Tag::unsyncedlyrics() const ++{ ++ return String(); ++} ++ + String DSDIFF::DIIN::Tag::comment() const + { + return String(); +@@ -83,21 +98,56 @@ + return 0; + } + ++unsigned int DSDIFF::DIIN::Tag::disc() const ++{ ++ return 0; ++} ++ ++String DSDIFF::DIIN::Tag::cuesheet() const ++{ ++ return String(); ++} ++ ++TagLib::Tag::ReplayGain DSDIFF::DIIN::Tag::replaygain() const ++{ ++ return TagLib::Tag::ReplayGain(); ++} ++ ++String DSDIFF::DIIN::Tag::soundcheck() const ++{ ++ return String(); ++} ++ + void DSDIFF::DIIN::Tag::setTitle(const String &title) + { + d->title = title; + } + ++void DSDIFF::DIIN::Tag::setAlbumArtist(const String &) ++{ ++ debug("DSDIFF::DIIN::Tag::setAlbumArtist() -- Ignoring unsupported tag."); ++} ++ + void DSDIFF::DIIN::Tag::setArtist(const String &artist) + { + d->artist = artist; + } + ++void DSDIFF::DIIN::Tag::setComposer(const String &) ++{ ++ debug("DSDIFF::DIIN::Tag::setComposer() -- Ignoring unsupported tag."); ++} ++ + void DSDIFF::DIIN::Tag::setAlbum(const String &) + { + debug("DSDIFF::DIIN::Tag::setAlbum() -- Ignoring unsupported tag."); + } + ++void DSDIFF::DIIN::Tag::setUnsyncedLyrics(const String &) ++{ ++ debug("DSDIFF::DIIN::Tag::setUnsyncedLyrics() -- Ignoring unsupported tag."); ++} ++ + void DSDIFF::DIIN::Tag::setComment(const String &) + { + debug("DSDIFF::DIIN::Tag::setComment() -- Ignoring unsupported tag."); +@@ -116,6 +166,26 @@ + void DSDIFF::DIIN::Tag::setTrack(unsigned int) + { + debug("DSDIFF::DIIN::Tag::setTrack() -- Ignoring unsupported tag."); ++} ++ ++void DSDIFF::DIIN::Tag::setDisc(unsigned int) ++{ ++ debug("DSDIFF::DIIN::Tag::setDisc() -- Ignoring unsupported tag."); ++} ++ ++void DSDIFF::DIIN::Tag::setCuesheet(const String &) ++{ ++ debug("DSDIFF::DIIN::Tag::setCuesheet() -- Ignoring unsupported tag."); ++} ++ ++void DSDIFF::DIIN::Tag::setReplaygain(TagLib::Tag::ReplayGain) ++{ ++ debug("DSDIFF::DIIN::Tag::setReplaygain() -- Ignoring unsupported tag."); ++} ++ ++void DSDIFF::DIIN::Tag::setSoundcheck(const String &) ++{ ++ debug("DSDIFF::DIIN::Tag::setSoundcheck() -- Ignoring unsupported tag."); + } + + PropertyMap DSDIFF::DIIN::Tag::properties() const +diff -ur taglib-2.0.2.orig/taglib/dsdiff/dsdiffdiintag.h taglib-2.0.2/taglib/dsdiff/dsdiffdiintag.h +--- taglib-2.0.2.orig/taglib/dsdiff/dsdiffdiintag.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/dsdiff/dsdiffdiintag.h 2025-02-17 01:53:14 +@@ -42,6 +42,7 @@ + class TAGLIB_EXPORT Tag : public TagLib::Tag + { + public: ++ using TagLib::Tag::ReplayGain; + Tag(); + ~Tag() override; + +@@ -52,6 +53,11 @@ + String title() const override; + + /*! ++ * Not supported. Therefore always returns String(). ++ */ ++ String albumartist() const override; ++ ++ /*! + * Returns the artist name; if no artist name is present in the tag + * String() will be returned. + */ +@@ -60,11 +66,21 @@ + /*! + * Not supported. Therefore always returns String(). + */ ++ String composer() const override; ++ ++ /*! ++ * Not supported. Therefore always returns String(). ++ */ + String album() const override; + + /*! + * Not supported. Therefore always returns String(). + */ ++ String unsyncedlyrics() const override; ++ ++ /*! ++ * Not supported. Therefore always returns String(). ++ */ + String comment() const override; + + /*! +@@ -83,12 +99,37 @@ + unsigned int track() const override; + + /*! ++ * Not supported. Therefore always returns 0. ++ */ ++ unsigned int disc() const override; ++ ++ /*! ++ * Not supported. Therefore always returns String(). ++ */ ++ String cuesheet() const override; ++ ++ /*! ++ * Not supported. Therefore always returns ReplayGain(). ++ */ ++ ReplayGain replaygain() const override; ++ ++ /*! ++ * Not supported. Therefore always returns String(). ++ */ ++ String soundcheck() const override; ++ ++ /*! + * Sets the title to \a title. If \a title is String() then this + * value will be cleared. + */ + void setTitle(const String &title) override; + + /*! ++ * Not supported and therefore ignored. ++ */ ++ void setAlbumArtist(const String &albumartist) override; ++ ++ /*! + * Sets the artist to \a artist. If \a artist is String() then this + * value will be cleared. + */ +@@ -97,11 +138,21 @@ + /*! + * Not supported and therefore ignored. + */ ++ void setComposer(const String &composer) override; ++ ++ /*! ++ * Not supported and therefore ignored. ++ */ + void setAlbum(const String &album) override; + + /*! + * Not supported and therefore ignored. + */ ++ void setUnsyncedLyrics(const String &unsyncedlyrics) override; ++ ++ /*! ++ * Not supported and therefore ignored. ++ */ + void setComment(const String &comment) override; + + /*! +@@ -118,6 +169,26 @@ + * Not supported and therefore ignored. + */ + void setTrack(unsigned int track) override; ++ ++ /*! ++ * Not supported and therefore ignored. ++ */ ++ void setDisc(unsigned int disc) override; ++ ++ /*! ++ * Not supported and therefore ignored. ++ */ ++ void setCuesheet(const String &cuesheet) override; ++ ++ /*! ++ * Not supported and therefore ignored. ++ */ ++ void setReplaygain(ReplayGain replaygain) override; ++ ++ /*! ++ * Not supported and therefore ignored. ++ */ ++ void setSoundcheck(const String &soundcheck) override; + + /*! + * Implements the unified property interface -- export function. +diff -ur taglib-2.0.2.orig/taglib/mod/modtag.cpp taglib-2.0.2/taglib/mod/modtag.cpp +--- taglib-2.0.2.orig/taglib/mod/modtag.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/mod/modtag.cpp 2025-02-17 01:39:36 +@@ -53,16 +53,31 @@ + return d->title; + } + ++String Mod::Tag::albumartist() const ++{ ++ return String(); ++} ++ + String Mod::Tag::artist() const + { + return String(); + } + ++String Mod::Tag::composer() const ++{ ++ return String(); ++} ++ + String Mod::Tag::album() const + { + return String(); + } + ++String Mod::Tag::unsyncedlyrics() const ++{ ++ return String(); ++} ++ + String Mod::Tag::comment() const + { + return d->comment; +@@ -83,6 +98,26 @@ + return 0; + } + ++unsigned int Mod::Tag::disc() const ++{ ++ return 0; ++} ++ ++String Mod::Tag::cuesheet() const ++{ ++ return String(); ++} ++ ++TagLib::Tag::ReplayGain Mod::Tag::replaygain() const ++{ ++ return TagLib::Tag::ReplayGain(); ++} ++ ++String Mod::Tag::soundcheck() const ++{ ++ return String(); ++} ++ + String Mod::Tag::trackerName() const + { + return d->trackerName; +@@ -93,14 +128,26 @@ + d->title = title; + } + ++void Mod::Tag::setAlbumArtist(const String &) ++{ ++} ++ + void Mod::Tag::setArtist(const String &) + { + } + ++void Mod::Tag::setComposer(const String &) ++{ ++} ++ + void Mod::Tag::setAlbum(const String &) + { + } + ++void Mod::Tag::setUnsyncedLyrics(const String &) ++{ ++} ++ + void Mod::Tag::setComment(const String &comment) + { + d->comment = comment; +@@ -115,6 +162,22 @@ + } + + void Mod::Tag::setTrack(unsigned int) ++{ ++} ++ ++void Mod::Tag::setDisc(unsigned int) ++{ ++} ++ ++void Mod::Tag::setCuesheet(const String &) ++{ ++} ++ ++void Mod::Tag::setReplaygain(TagLib::Tag::ReplayGain) ++{ ++} ++ ++void Mod::Tag::setSoundcheck(const String &) + { + } + +diff -ur taglib-2.0.2.orig/taglib/mod/modtag.h taglib-2.0.2/taglib/mod/modtag.h +--- taglib-2.0.2.orig/taglib/mod/modtag.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/mod/modtag.h 2025-02-17 01:24:04 +@@ -48,6 +48,8 @@ + class TAGLIB_EXPORT Tag : public TagLib::Tag + { + public: ++ using TagLib::Tag::ReplayGain; ++ + Tag(); + ~Tag() override; + +@@ -63,14 +65,29 @@ + /*! + * Not supported by module files. Therefore always returns an empty string. + */ ++ String albumartist() const override; ++ ++ /*! ++ * Not supported by module files. Therefore always returns an empty string. ++ */ + String artist() const override; + + /*! + * Not supported by module files. Therefore always returns an empty string. + */ ++ String composer() const override; ++ ++ /*! ++ * Not supported by module files. Therefore always returns an empty string. ++ */ + String album() const override; + + /*! ++ * Not supported by module files. Therefore always returns an empty string. ++ */ ++ String unsyncedlyrics() const override; ++ ++ /*! + * Returns the track comment derived from the instrument/sample/pattern + * names; if no comment is present in the tag an empty string will be + * returned. +@@ -93,6 +110,26 @@ + unsigned int track() const override; + + /*! ++ * Not supported by module files. Therefore always returns 0. ++ */ ++ unsigned int disc() const override; ++ ++ /*! ++ * Not supported by module files. Therefore always returns an empty string. ++ */ ++ String cuesheet() const override; ++ ++ /*! ++ * Not supported by module files. Therefore always returns an empty tag. ++ */ ++ ReplayGain replaygain() const override; ++ ++ /*! ++ * Not supported by module files. Therefore always returns an empty string. ++ */ ++ String soundcheck() const override; ++ ++ /*! + * Returns the name of the tracker used to create/edit the module file. + * Only XM files store this tag to the file as such, for other formats + * (Mod, S3M, IT) this is derived from the file type or the flavour of +@@ -114,14 +151,29 @@ + /*! + * Not supported by module files and therefore ignored. + */ ++ void setAlbumArtist(const String &albumartist) override; ++ ++ /*! ++ * Not supported by module files and therefore ignored. ++ */ + void setArtist(const String &artist) override; + + /*! + * Not supported by module files and therefore ignored. + */ ++ void setComposer(const String &composer) override; ++ ++ /*! ++ * Not supported by module files and therefore ignored. ++ */ + void setAlbum(const String &album) override; + + /*! ++ * Not supported by module files and therefore ignored. ++ */ ++ void setUnsyncedLyrics(const String &unsyncedlyrics) override; ++ ++ /*! + * Sets the comment to \a comment. If \a comment is an empty string then + * this value will be cleared. + * +@@ -156,6 +208,26 @@ + void setTrack(unsigned int track) override; + + /*! ++ * Not supported by module files and therefore ignored. ++ */ ++ void setDisc(unsigned int disc) override; ++ ++ /*! ++ * Not supported by module files and therefore ignored. ++ */ ++ void setCuesheet(const String &cuesheet) override; ++ ++ /*! ++ * Not supported by module files and therefore ignored. ++ */ ++ void setReplaygain(ReplayGain r) override; ++ ++ /*! ++ * Not supported by module files and therefore ignored. ++ */ ++ void setSoundcheck(const String &soundcheck) override; ++ ++ /*! + * Sets the tracker name to \a trackerName. If \a trackerName is + * an empty string then this value will be cleared. + * +diff -ur taglib-2.0.2.orig/taglib/mp4/mp4tag.cpp taglib-2.0.2/taglib/mp4/mp4tag.cpp +--- taglib-2.0.2.orig/taglib/mp4/mp4tag.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/mp4/mp4tag.cpp 2025-02-17 01:39:07 +@@ -78,6 +78,34 @@ + addItem(name, itm); + } + } ++ ++ if(d->items.contains("----:com.apple.iTunes:replaygain_album_gain")) ++ rg.setAlbumGain(d->items["----:com.apple.iTunes:replaygain_album_gain"].toStringList().toString().toFloat()); ++ else if(d->items.contains("----:org.hydrogenaudio.replaygain:replaygain_album_gain")) ++ rg.setAlbumGain(d->items["----:org.hydrogenaudio.replaygain:replaygain_album_gain"].toStringList().toString().toFloat()); ++ else ++ rg.clearAlbumGain(); ++ ++ if(d->items.contains("----:com.apple.iTunes:replaygain_album_peak")) ++ rg.setAlbumPeak(d->items["----:com.apple.iTunes:replaygain_album_peak"].toStringList().toString().toFloat()); ++ else if(d->items.contains("----:org.hydrogenaudio.replaygain:replaygain_album_peak")) ++ rg.setAlbumPeak(d->items["----:org.hydrogenaudio.replaygain:replaygain_album_peak"].toStringList().toString().toFloat()); ++ else ++ rg.clearAlbumPeak(); ++ ++ if(d->items.contains("----:com.apple.iTunes:replaygain_track_gain")) ++ rg.setTrackGain(d->items["----:com.apple.iTunes:replaygain_track_gain"].toStringList().toString().toFloat()); ++ else if(d->items.contains("----:org.hydrogenaudio.replaygain:replaygain_track_gain")) ++ rg.setTrackGain(d->items["----:org.hydrogenaudio.replaygain:replaygain_track_gain"].toStringList().toString().toFloat()); ++ else ++ rg.clearTrackGain(); ++ ++ if(d->items.contains("----:com.apple.iTunes:replaygain_track_peak")) ++ rg.setTrackPeak(d->items["----:com.apple.iTunes:replaygain_track_peak"].toStringList().toString().toFloat()); ++ else if(d->items.contains("----:org.hydrogenaudio.replaygain:replaygain_track_peak")) ++ rg.setTrackPeak(d->items["----:org.hydrogenaudio.replaygain:replaygain_track_peak"].toStringList().toString().toFloat()); ++ else ++ rg.clearTrackPeak(); + } + + MP4::Tag::~Tag() = default; +@@ -321,6 +349,14 @@ + } + + String ++MP4::Tag::albumartist() const ++{ ++ if(d->items.contains("aART")) ++ return d->items["aART"].toStringList().toString(", "); ++ return String(); ++} ++ ++String + MP4::Tag::artist() const + { + if(d->items.contains("\251ART")) +@@ -329,6 +365,14 @@ + } + + String ++MP4::Tag::composer() const ++{ ++ if(d->items.contains("\251wrt")) ++ return d->items["\251wrt"].toStringList().toString(", "); ++ return String(); ++} ++ ++String + MP4::Tag::album() const + { + if(d->items.contains("\251alb")) +@@ -337,6 +381,14 @@ + } + + String ++MP4::Tag::unsyncedlyrics() const ++{ ++ if(d->items.contains("\251lyr")) ++ return d->items["\251lyr"].toStringList().toString(", "); ++ return String(); ++} ++ ++String + MP4::Tag::comment() const + { + if(d->items.contains("\251cmt")) +@@ -368,6 +420,34 @@ + return 0; + } + ++unsigned int ++MP4::Tag::disc() const ++{ ++ if(d->items.contains("disk")) ++ return d->items["disk"].toIntPair().first; ++ return 0; ++} ++ ++String ++MP4::Tag::cuesheet() const ++{ ++ return String(); ++} ++ ++TagLib::Tag::ReplayGain ++MP4::Tag::replaygain() const ++{ ++ return rg; ++} ++ ++String ++MP4::Tag::soundcheck() const ++{ ++ if(d->items.contains("----:com.apple.iTunes:iTunNORM")) ++ return d->items["----:com.apple.iTunes:iTunNORM"].toStringList().toString(); ++ return String(); ++} ++ + void + MP4::Tag::setTitle(const String &value) + { +@@ -375,18 +455,36 @@ + } + + void ++MP4::Tag::setAlbumArtist(const String &value) ++{ ++ setTextItem("aART", value); ++} ++ ++void + MP4::Tag::setArtist(const String &value) + { + setTextItem("\251ART", value); + } + + void ++MP4::Tag::setComposer(const String &value) ++{ ++ setTextItem("\251wrt", value); ++} ++ ++void + MP4::Tag::setAlbum(const String &value) + { + setTextItem("\251alb", value); + } + + void ++MP4::Tag::setUnsyncedLyrics(const String &value) ++{ ++ setTextItem("\251lyr", value); ++} ++ ++void + MP4::Tag::setComment(const String &value) + { + setTextItem("\251cmt", value); +@@ -428,6 +526,54 @@ + else { + d->items["trkn"] = MP4::Item(value, 0); + } ++} ++ ++void ++MP4::Tag::setDisc(unsigned int value) ++{ ++ if (value == 0) { ++ d->items.erase("disk"); ++ } ++ else { ++ d->items["disk"] = MP4::Item(value, 0); ++ } ++} ++ ++void ++MP4::Tag::setCuesheet(const String &) ++{ ++} ++ ++void ++MP4::Tag::setReplaygain(TagLib::Tag::ReplayGain r) ++{ ++ rg = r; ++ d->items.erase("----:org.hydrogenaudio.replaygain:replaygain_album_gain"); ++ if(!rg.albumGainSet()) ++ d->items.erase("----:com.apple.iTunes:replaygain_album_gain"); ++ else ++ setTextItem("----:com.apple.iTunes:replaygain_album_gain", String::number(rg.albumGain()) + " dB"); ++ d->items.erase("----:org.hydrogenaudio.replaygain:replaygain_album_peak"); ++ if(!rg.albumPeakSet()) ++ d->items.erase("----:com.apple.iTunes:replaygain_album_peak"); ++ else ++ setTextItem("----:com.apple.iTunes:replaygain_album_peak", String::number(rg.albumPeak())); ++ d->items.erase("----:org.hydrogenaudio.replaygain:replaygain_track_gain"); ++ if(!rg.trackGainSet()) ++ d->items.erase("----:com.apple.iTunes:replaygain_track_gain"); ++ else ++ setTextItem("----:com.apple.iTunes:replaygain_track_gain", String::number(rg.trackGain()) + " dB"); ++ d->items.erase("----:org.hydrogenaudio.replaygain:replaygain_track_peak"); ++ if(!rg.trackPeakSet()) ++ d->items.erase("----:com.apple.iTunes:replaygain_track_peak"); ++ else ++ setTextItem("----:com.apple.iTunes:replaygain_track_peak", String::number(rg.trackPeak())); ++} ++ ++void ++MP4::Tag::setSoundcheck(const String &value) ++{ ++ setTextItem("----:com.apple.iTunes:iTunNORM", value); + } + + bool MP4::Tag::isEmpty() const +diff -ur taglib-2.0.2.orig/taglib/mp4/mp4tag.h taglib-2.0.2/taglib/mp4/mp4tag.h +--- taglib-2.0.2.orig/taglib/mp4/mp4tag.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/mp4/mp4tag.h 2025-02-17 01:37:52 +@@ -43,6 +43,7 @@ + class TAGLIB_EXPORT Tag: public TagLib::Tag + { + public: ++ using TagLib::Tag::ReplayGain; + Tag(); + Tag(TagLib::File *file, Atoms *atoms, + const ItemFactory *factory = nullptr); +@@ -52,20 +53,34 @@ + bool save(); + + String title() const override; ++ String albumartist() const override; + String artist() const override; ++ String composer() const override; + String album() const override; ++ String unsyncedlyrics() const override; + String comment() const override; + String genre() const override; + unsigned int year() const override; + unsigned int track() const override; ++ unsigned int disc() const override; ++ String cuesheet() const override; ++ ReplayGain replaygain() const override; ++ String soundcheck() const override; + + void setTitle(const String &value) override; ++ void setAlbumArtist(const String &value) override; + void setArtist(const String &value) override; ++ void setComposer(const String &value) override; + void setAlbum(const String &value) override; ++ void setUnsyncedLyrics(const String &value) override; + void setComment(const String &value) override; + void setGenre(const String &value) override; + void setYear(unsigned int value) override; + void setTrack(unsigned int value) override; ++ void setDisc(unsigned int value) override; ++ void setCuesheet(const String &value) override; ++ void setReplaygain(ReplayGain value) override; ++ void setSoundcheck(const String &value) override; + + bool isEmpty() const override; + +diff -ur taglib-2.0.2.orig/taglib/mpeg/id3v1/id3v1tag.cpp taglib-2.0.2/taglib/mpeg/id3v1/id3v1tag.cpp +--- taglib-2.0.2.orig/taglib/mpeg/id3v1/id3v1tag.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/mpeg/id3v1/id3v1tag.cpp 2025-02-17 01:28:50 +@@ -124,16 +124,31 @@ + return d->title; + } + ++String ID3v1::Tag::albumartist() const ++{ ++ return String(); ++} ++ + String ID3v1::Tag::artist() const + { + return d->artist; + } + ++String ID3v1::Tag::composer() const ++{ ++ return String(); ++} ++ + String ID3v1::Tag::album() const + { + return d->album; + } + ++String ID3v1::Tag::unsyncedlyrics() const ++{ ++ return String(); ++} ++ + String ID3v1::Tag::comment() const + { + return d->comment; +@@ -154,21 +169,53 @@ + return d->track; + } + ++unsigned int ID3v1::Tag::disc() const ++{ ++ return 0; ++} ++ ++String ID3v1::Tag::cuesheet() const ++{ ++ return String(); ++} ++ ++TagLib::Tag::ReplayGain ID3v1::Tag::replaygain() const ++{ ++ return TagLib::Tag::ReplayGain(); ++} ++ ++String ID3v1::Tag::soundcheck() const ++{ ++ return String(); ++} ++ + void ID3v1::Tag::setTitle(const String &s) + { + d->title = s; + } + ++void ID3v1::Tag::setAlbumArtist(const String &) ++{ ++} ++ + void ID3v1::Tag::setArtist(const String &s) + { + d->artist = s; + } + ++void ID3v1::Tag::setComposer(const String &) ++{ ++} ++ + void ID3v1::Tag::setAlbum(const String &s) + { + d->album = s; + } + ++void ID3v1::Tag::setUnsyncedLyrics(const String &) ++{ ++} ++ + void ID3v1::Tag::setComment(const String &s) + { + d->comment = s; +@@ -187,6 +234,22 @@ + void ID3v1::Tag::setTrack(unsigned int i) + { + d->track = i < 256 ? i : 0; ++} ++ ++void ID3v1::Tag::setDisc(unsigned int) ++{ ++} ++ ++void ID3v1::Tag::setCuesheet(const String &) ++{ ++} ++ ++void ID3v1::Tag::setReplaygain(TagLib::Tag::ReplayGain) ++{ ++} ++ ++void ID3v1::Tag::setSoundcheck(const String &) ++{ + } + + unsigned int ID3v1::Tag::genreNumber() const +diff -ur taglib-2.0.2.orig/taglib/mpeg/id3v1/id3v1tag.h taglib-2.0.2/taglib/mpeg/id3v1/id3v1tag.h +--- taglib-2.0.2.orig/taglib/mpeg/id3v1/id3v1tag.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/mpeg/id3v1/id3v1tag.h 2025-02-17 01:25:21 +@@ -114,6 +114,7 @@ + class TAGLIB_EXPORT Tag : public TagLib::Tag + { + public: ++ using TagLib::Tag::ReplayGain; + /*! + * Create an ID3v1 tag with default values. + */ +@@ -148,20 +149,34 @@ + // Reimplementations. + + String title() const override; ++ String albumartist() const override; + String artist() const override; ++ String composer() const override; + String album() const override; ++ String unsyncedlyrics() const override; + String comment() const override; + String genre() const override; + unsigned int year() const override; + unsigned int track() const override; ++ unsigned int disc() const override; ++ String cuesheet() const override; ++ ReplayGain replaygain() const override; ++ String soundcheck() const override; + + void setTitle(const String &s) override; ++ void setAlbumArtist(const String &s) override; + void setArtist(const String &s) override; ++ void setComposer(const String &s) override; + void setAlbum(const String &s) override; ++ void setUnsyncedLyrics(const String &s) override; + void setComment(const String &s) override; + void setGenre(const String &s) override; + void setYear(unsigned int i) override; + void setTrack(unsigned int i) override; ++ void setDisc(unsigned int i) override; ++ void setCuesheet(const String &s) override; ++ void setReplaygain(ReplayGain r) override; ++ void setSoundcheck(const String &s) override; + + /*! + * Returns the genre in number. +diff -ur taglib-2.0.2.orig/taglib/mpeg/id3v2/id3v2tag.cpp taglib-2.0.2/taglib/mpeg/id3v2/id3v2tag.cpp +--- taglib-2.0.2.orig/taglib/mpeg/id3v2/id3v2tag.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/mpeg/id3v2/id3v2tag.cpp 2025-02-17 01:31:54 +@@ -125,6 +125,13 @@ + return String(); + } + ++String ID3v2::Tag::albumartist() const ++{ ++ if(!d->frameListMap["TPE2"].isEmpty()) ++ return joinTagValues(d->frameListMap["TPE2"].front()->toStringList()); ++ return String(); ++} ++ + String ID3v2::Tag::artist() const + { + if(!d->frameListMap["TPE1"].isEmpty()) +@@ -132,6 +139,13 @@ + return String(); + } + ++String ID3v2::Tag::composer() const ++{ ++ if(!d->frameListMap["TCOM"].isEmpty()) ++ return joinTagValues(d->frameListMap["TCOM"].front()->toStringList()); ++ return String(); ++} ++ + String ID3v2::Tag::album() const + { + if(!d->frameListMap["TALB"].isEmpty()) +@@ -139,6 +153,13 @@ + return String(); + } + ++String ID3v2::Tag::unsyncedlyrics() const ++{ ++ if(!d->frameListMap["USLT"].isEmpty()) ++ return d->frameListMap["USLT"].front()->toString(); ++ return String(); ++} ++ + String ID3v2::Tag::comment() const + { + const FrameList &comments = d->frameListMap["COMM"]; +@@ -209,21 +230,90 @@ + return 0; + } + ++unsigned int ID3v2::Tag::disc() const ++{ ++ if(!d->frameListMap["TPOS"].isEmpty()) ++ return d->frameListMap["TPOS"].front()->toString().toInt(); ++ return 0; ++} ++ ++String ID3v2::Tag::cuesheet() const ++{ ++ auto frame = UserTextIdentificationFrame::find(this, "cuesheet"); ++ if(frame) { ++ StringList l = frame->fieldList(); ++ l.erase(l.begin()); ++ return l.toString(); ++ } ++ return String(); ++} ++ ++TagLib::Tag::ReplayGain ID3v2::Tag::replaygain() const ++{ ++ return rg; ++} ++ ++String ID3v2::Tag::soundcheck() const ++{ ++ if(const FrameList &comments = d->frameListMap["COMM"]; !comments.isEmpty()) { ++ for(const auto &commFrame : comments) { ++ auto frame = dynamic_cast(commFrame); ++ if(frame && frame->description() == "iTunNORM") { ++ return commFrame->toString(); ++ } ++ } ++ } ++ return String(); ++} ++ + void ID3v2::Tag::setTitle(const String &s) + { + setTextFrame("TIT2", s); + } + ++void ID3v2::Tag::setAlbumArtist(const String &s) ++{ ++ setTextFrame("TPE2", s); ++} ++ + void ID3v2::Tag::setArtist(const String &s) + { + setTextFrame("TPE1", s); + } + ++void ID3v2::Tag::setComposer(const String &s) ++{ ++ setTextFrame("TCOM", s); ++} ++ + void ID3v2::Tag::setAlbum(const String &s) + { + setTextFrame("TALB", s); + } + ++void ID3v2::Tag::setUnsyncedLyrics(const String &s) ++{ ++ if(s.isEmpty()) { ++ removeFrames("USLT"); ++ return; ++ } ++ ++ if(const FrameList &unsyncedlyrics = d->frameListMap["USLT"]; !unsyncedlyrics.isEmpty()) { ++ for(const auto &lyricsFrame: unsyncedlyrics) { ++ auto frame = dynamic_cast(lyricsFrame); ++ if(frame && (frame->description().isEmpty() || frame->description() == "LYRICS")) { ++ frame->setText(s); ++ return; ++ } ++ } ++ } ++ ++ auto frame = new UnsynchronizedLyricsFrame(d->factory->defaultTextEncoding()); ++ addFrame(frame); ++ frame->setDescription("LYRICS"); ++ frame->setText(s); ++} ++ + void ID3v2::Tag::setComment(const String &s) + { + if(s.isEmpty()) { +@@ -293,6 +383,120 @@ + setTextFrame("TRCK", String::number(i)); + } + ++void ID3v2::Tag::setDisc(unsigned int i) ++{ ++ if(i == 0) { ++ removeFrames("TPOS"); ++ return; ++ } ++ setTextFrame("TPOS", String::number(i)); ++} ++ ++void ID3v2::Tag::setCuesheet(const String &s) ++{ ++ auto frame = UserTextIdentificationFrame::find(this, "cuesheet"); ++ if(s.isEmpty()) { ++ if(frame) ++ removeFrame(frame); ++ return; ++ } ++ if(frame == nullptr) { ++ frame = new UserTextIdentificationFrame(d->factory->defaultTextEncoding()); ++ frame->setDescription("cuesheet"); ++ addFrame(frame); ++ } ++ frame->setText(s); ++} ++ ++void ID3v2::Tag::setReplaygain(TagLib::Tag::ReplayGain r) ++{ ++ rg = r; ++ ++ auto frame = UserTextIdentificationFrame::find(this, "replaygain_album_gain"); ++ if(!rg.albumGainSet()) { ++ if(frame) ++ removeFrame(frame); ++ } ++ else { ++ if(frame == nullptr) { ++ frame = new UserTextIdentificationFrame(d->factory->defaultTextEncoding()); ++ frame->setDescription("replaygain_album_gain"); ++ addFrame(frame); ++ } ++ frame->setText(String::number(rg.albumGain()) + " dB"); ++ } ++ ++ frame = UserTextIdentificationFrame::find(this, "replaygain_album_peak"); ++ if(!rg.albumPeakSet()) { ++ if(frame) ++ removeFrame(frame); ++ } ++ else { ++ if(frame == nullptr) { ++ frame = new UserTextIdentificationFrame(d->factory->defaultTextEncoding()); ++ frame->setDescription("replaygain_album_peak"); ++ addFrame(frame); ++ } ++ frame->setText(String::number(rg.albumPeak())); ++ } ++ ++ frame = UserTextIdentificationFrame::find(this, "replaygain_track_gain"); ++ if(!rg.trackGainSet()) { ++ if(frame) ++ removeFrame(frame); ++ } ++ else { ++ if(frame == nullptr) { ++ frame = new UserTextIdentificationFrame(d->factory->defaultTextEncoding()); ++ frame->setDescription("replaygain_track_gain"); ++ addFrame(frame); ++ } ++ frame->setText(String::number(rg.trackGain()) + " dB"); ++ } ++ ++ frame = UserTextIdentificationFrame::find(this, "replaygain_track_peak"); ++ if(!rg.trackPeakSet()) { ++ if(frame) ++ removeFrame(frame); ++ } ++ else { ++ if(frame == nullptr) { ++ frame = new UserTextIdentificationFrame(d->factory->defaultTextEncoding()); ++ frame->setDescription("replaygain_track_peak"); ++ addFrame(frame); ++ } ++ frame->setText(String::number(rg.trackPeak())); ++ } ++} ++ ++void ID3v2::Tag::setSoundcheck(const String &s) ++{ ++ CommentsFrame *soundcheckFrame = nullptr; ++ if(const FrameList &comments = d->frameListMap["COMM"]; !comments.isEmpty()) { ++ for(const auto &commFrame : comments) { ++ auto frame = dynamic_cast(commFrame); ++ if(frame && frame->description() == "iTunNORM") { ++ soundcheckFrame = frame; ++ break; ++ } ++ } ++ } ++ ++ if(s.isEmpty()) { ++ if(soundcheckFrame) ++ removeFrame(soundcheckFrame); ++ return; ++ } ++ ++ if(soundcheckFrame == nullptr) { ++ soundcheckFrame = new CommentsFrame(d->factory->defaultTextEncoding()); ++ soundcheckFrame->setDescription("iTunNORM"); ++ addFrame(soundcheckFrame); ++ } ++ ++ soundcheckFrame->setText(s); ++} ++ + bool ID3v2::Tag::isEmpty() const + { + return d->frameList.isEmpty(); +@@ -871,6 +1075,34 @@ + } + + d->factory->rebuildAggregateFrames(this); ++ ++ auto frame = UserTextIdentificationFrame::find(this, "replaygain_album_gain"); ++ if(frame) { ++ StringList l = frame->fieldList(); ++ rg.setAlbumGain(l.back().toFloat()); ++ } else ++ rg.clearAlbumGain(); ++ ++ frame = UserTextIdentificationFrame::find(this, "replaygain_album_peak"); ++ if(frame) { ++ StringList l = frame->fieldList(); ++ rg.setAlbumPeak(l.back().toFloat()); ++ } else ++ rg.clearAlbumPeak(); ++ ++ frame = UserTextIdentificationFrame::find(this, "replaygain_track_gain"); ++ if(frame) { ++ StringList l = frame->fieldList(); ++ rg.setTrackGain(l.back().toFloat()); ++ } else ++ rg.clearTrackGain(); ++ ++ frame = UserTextIdentificationFrame::find(this, "replaygain_track_peak"); ++ if(frame) { ++ StringList l = frame->fieldList(); ++ rg.setTrackPeak(l.back().toFloat()); ++ } else ++ rg.clearTrackPeak(); + } + + void ID3v2::Tag::setTextFrame(const ByteVector &id, const String &value) +diff -ur taglib-2.0.2.orig/taglib/mpeg/id3v2/id3v2tag.h taglib-2.0.2/taglib/mpeg/id3v2/id3v2tag.h +--- taglib-2.0.2.orig/taglib/mpeg/id3v2/id3v2tag.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/mpeg/id3v2/id3v2tag.h 2025-02-17 01:25:52 +@@ -133,6 +133,7 @@ + class TAGLIB_EXPORT Tag : public TagLib::Tag + { + public: ++ using TagLib::Tag::ReplayGain; + /*! + * Constructs an empty ID3v2 tag. + * +@@ -166,20 +167,34 @@ + // Reimplementations. + + String title() const override; ++ String albumartist() const override; + String artist() const override; ++ String composer() const override; + String album() const override; ++ String unsyncedlyrics() const override; + String comment() const override; + String genre() const override; + unsigned int year() const override; + unsigned int track() const override; ++ unsigned int disc() const override; ++ String cuesheet() const override; ++ ReplayGain replaygain() const override; ++ String soundcheck() const override; + + void setTitle(const String &s) override; ++ void setAlbumArtist(const String &s) override; + void setArtist(const String &s) override; ++ void setComposer(const String &s) override; + void setAlbum(const String &s) override; ++ void setUnsyncedLyrics(const String &s) override; + void setComment(const String &s) override; + void setGenre(const String &s) override; + void setYear(unsigned int i) override; + void setTrack(unsigned int i) override; ++ void setDisc(unsigned int i) override; ++ void setCuesheet(const String &s) override; ++ void setReplaygain(ReplayGain r) override; ++ void setSoundcheck(const String &s) override; + + bool isEmpty() const override; + +diff -ur taglib-2.0.2.orig/taglib/ogg/xiphcomment.cpp taglib-2.0.2/taglib/ogg/xiphcomment.cpp +--- taglib-2.0.2.orig/taglib/ogg/xiphcomment.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/ogg/xiphcomment.cpp 2025-02-17 01:32:38 +@@ -43,6 +43,8 @@ + FieldListMap fieldListMap; + String vendorID; + String commentField; ++ String albumartistField; ++ String unsyncedlyricsField; + List pictureList; + }; + +@@ -69,18 +71,59 @@ + return val.isEmpty() ? String() : joinTagValues(val); + } + ++String Ogg::XiphComment::albumartist() const ++{ ++ StringList val = d->fieldListMap.value("ALBUMARTIST"); ++ if(!val.isEmpty()) { ++ d->albumartistField = "ALBUMARTIST"; ++ return joinTagValues(val); ++ } ++ val = d->fieldListMap.value("ALBUM ARTIST"); ++ if(!val.isEmpty()) { ++ d->albumartistField = "ALBUM ARTIST"; ++ return joinTagValues(val); ++ } ++ return String(); ++} ++ + String Ogg::XiphComment::artist() const + { + StringList val = d->fieldListMap.value("ARTIST"); + return val.isEmpty() ? String() : joinTagValues(val); + } + ++String Ogg::XiphComment::composer() const ++{ ++ StringList val = d->fieldListMap.value("COMPOSER"); ++ return val.isEmpty() ? String() : joinTagValues(val); ++} ++ + String Ogg::XiphComment::album() const + { + StringList val = d->fieldListMap.value("ALBUM"); + return val.isEmpty() ? String() : joinTagValues(val); + } + ++String Ogg::XiphComment::unsyncedlyrics() const ++{ ++ StringList val = d->fieldListMap.value("UNSYNCEDLYRICS"); ++ if(!val.isEmpty()) { ++ d->unsyncedlyricsField = "UNSYNCEDLYRICS"; ++ return joinTagValues(val); ++ } ++ val = d->fieldListMap.value("UNSYNCED LYRICS"); ++ if(!val.isEmpty()) { ++ d->unsyncedlyricsField = "UNSYNCED LYRICS"; ++ return joinTagValues(val); ++ } ++ val = d->fieldListMap.value("LYRICS"); ++ if(!val.isEmpty()) { ++ d->unsyncedlyricsField = "LYRICS"; ++ return joinTagValues(val); ++ } ++ return String(); ++} ++ + String Ogg::XiphComment::comment() const + { + StringList val = d->fieldListMap.value("DESCRIPTION"); +@@ -123,24 +166,88 @@ + val = d->fieldListMap.value("TRACKNUM"); + if(!val.isEmpty()) + return val.front().toInt(); ++ val = d->fieldListMap.value("TRACK"); ++ if(!val.isEmpty()) ++ return val.front().toInt(); + return 0; + } + ++unsigned int Ogg::XiphComment::disc() const ++{ ++ StringList val = d->fieldListMap.value("DISCNUMBER"); ++ if(!val.isEmpty()) ++ return val.front().toInt(); ++ val = d->fieldListMap.value("DISCNUM"); ++ if(!val.isEmpty()) ++ return val.front().toInt(); ++ val = d->fieldListMap.value("DISC"); ++ if(!val.isEmpty()) ++ return val.front().toInt(); ++ return 0; ++} ++ ++String Ogg::XiphComment::cuesheet() const ++{ ++ StringList val = d->fieldListMap.value("CUESHEET"); ++ return val.isEmpty() ? String() : joinTagValues(val); ++} ++ ++TagLib::Tag::ReplayGain Ogg::XiphComment::replaygain() const ++{ ++ return rg; ++} ++ ++String Ogg::XiphComment::soundcheck() const ++{ ++ return String(); ++} ++ + void Ogg::XiphComment::setTitle(const String &s) + { + addField("TITLE", s); + } + ++void Ogg::XiphComment::setAlbumArtist(const String &s) ++{ ++ if(d->commentField.isEmpty()) { ++ if(!d->fieldListMap.value("ALBUMARTIST").isEmpty()) ++ d->albumartistField = "ALBUMARTIST"; ++ else ++ d->albumartistField = "ALBUM ARTIST"; ++ } ++ ++ addField(d->albumartistField, s); ++} ++ + void Ogg::XiphComment::setArtist(const String &s) + { + addField("ARTIST", s); + } + ++void Ogg::XiphComment::setComposer(const String &s) ++{ ++ addField("COMPOSER", s); ++} ++ + void Ogg::XiphComment::setAlbum(const String &s) + { + addField("ALBUM", s); + } + ++void Ogg::XiphComment::setUnsyncedLyrics(const String &s) ++{ ++ if(d->unsyncedlyricsField.isEmpty()) { ++ if(!d->fieldListMap.value("UNSYNCEDLYRICS").isEmpty()) ++ d->unsyncedlyricsField = "UNSYNCEDLYRICS"; ++ else if(!d->fieldListMap.value("LYRICS").isEmpty()) ++ d->unsyncedlyricsField = "LYRICS"; ++ else ++ d->unsyncedlyricsField = "UNSYNCED LYRICS"; ++ } ++ ++ addField(d->unsyncedlyricsField, s); ++} ++ + void Ogg::XiphComment::setComment(const String &s) + { + if(d->commentField.isEmpty()) { +@@ -169,6 +276,7 @@ + + void Ogg::XiphComment::setTrack(unsigned int i) + { ++ removeFields("TRACK"); + removeFields("TRACKNUM"); + if(i == 0) + removeFields("TRACKNUMBER"); +@@ -176,6 +284,50 @@ + addField("TRACKNUMBER", String::number(i)); + } + ++void Ogg::XiphComment::setDisc(unsigned int i) ++{ ++ removeFields("DISC"); ++ removeFields("DISCNUM"); ++ if(i == 0) ++ removeFields("DISCNUMBER"); ++ else ++ addField("DISCNUMBER", String::number(i)); ++} ++ ++void Ogg::XiphComment::setCuesheet(const String &s) ++{ ++ if(s.isEmpty()) ++ removeFields("CUESHEET"); ++ else ++ addField("CUESHEET", s); ++} ++ ++void Ogg::XiphComment::setReplaygain(TagLib::Tag::ReplayGain r) ++{ ++ rg = r; ++ ++ if(!rg.albumGainSet()) ++ removeFields("REPLAYGAIN_ALBUM_GAIN"); ++ else ++ addField("REPLAYGAIN_ALBUM_GAIN", String::number(rg.albumGain()) + " dB"); ++ if(!rg.albumPeakSet()) ++ removeFields("REPLAYGAIN_ALBUM_PEAK"); ++ else ++ addField("REPLAYGAIN_ALBUM_PEAK", String::number(rg.albumPeak())); ++ if(!rg.trackGainSet()) ++ removeFields("REPLAYGAIN_TRACK_GAIN"); ++ else ++ addField("REPLAYGAIN_TRACK_GAIN", String::number(rg.trackGain()) + " dB"); ++ if(!rg.trackPeakSet()) ++ removeFields("REPLAYGAIN_TRACK_PEAK"); ++ else ++ addField("REPLAYGAIN_TRACK_PEAK", String::number(rg.trackPeak())); ++} ++ ++void Ogg::XiphComment::setSoundcheck(const String &) ++{ ++} ++ + bool Ogg::XiphComment::isEmpty() const + { + return std::all_of(d->fieldListMap.cbegin(), d->fieldListMap.cend(), +@@ -519,4 +671,25 @@ + addField(key, String(entry.mid(sep + 1), String::UTF8), false); + } + } ++ ++ StringList val = d->fieldListMap.value("REPLAYGAIN_ALBUM_GAIN"); ++ if(val.isEmpty()) ++ rg.clearAlbumGain(); ++ else ++ rg.setAlbumGain(val.front().toFloat()); ++ val = d->fieldListMap.value("REPLAYGAIN_ALBUM_PEAK"); ++ if(val.isEmpty()) ++ rg.clearAlbumPeak(); ++ else ++ rg.setAlbumPeak(val.front().toFloat()); ++ val = d->fieldListMap.value("REPLAYGAIN_TRACK_GAIN"); ++ if(val.isEmpty()) ++ rg.clearTrackGain(); ++ else ++ rg.setTrackGain(val.front().toFloat()); ++ val = d->fieldListMap.value("REPLAYGAIN_TRACK_PEAK"); ++ if(val.isEmpty()) ++ rg.clearTrackPeak(); ++ else ++ rg.setTrackPeak(val.front().toFloat()); + } +diff -ur taglib-2.0.2.orig/taglib/ogg/xiphcomment.h taglib-2.0.2/taglib/ogg/xiphcomment.h +--- taglib-2.0.2.orig/taglib/ogg/xiphcomment.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/ogg/xiphcomment.h 2025-02-17 01:26:37 +@@ -70,6 +70,7 @@ + class TAGLIB_EXPORT XiphComment : public TagLib::Tag + { + public: ++ using TagLib::Tag::ReplayGain; + /*! + * Constructs an empty Vorbis comment. + */ +@@ -89,20 +90,34 @@ + XiphComment &operator=(const XiphComment &) = delete; + + String title() const override; ++ String albumartist() const override; + String artist() const override; ++ String composer() const override; + String album() const override; ++ String unsyncedlyrics() const override; + String comment() const override; + String genre() const override; + unsigned int year() const override; + unsigned int track() const override; ++ unsigned int disc() const override; ++ String cuesheet() const override; ++ ReplayGain replaygain() const override; ++ String soundcheck() const override; + + void setTitle(const String &s) override; ++ void setAlbumArtist(const String &s) override; + void setArtist(const String &s) override; ++ void setComposer(const String &s) override; + void setAlbum(const String &s) override; ++ void setUnsyncedLyrics(const String &s) override; + void setComment(const String &s) override; + void setGenre(const String &s) override; + void setYear(unsigned int i) override; + void setTrack(unsigned int i) override; ++ void setDisc(unsigned int i) override; ++ void setCuesheet(const String &s) override; ++ void setReplaygain(ReplayGain r) override; ++ void setSoundcheck(const String &s) override; + + bool isEmpty() const override; + +diff -ur taglib-2.0.2.orig/taglib/riff/wav/infotag.cpp taglib-2.0.2/taglib/riff/wav/infotag.cpp +--- taglib-2.0.2.orig/taglib/riff/wav/infotag.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/riff/wav/infotag.cpp 2025-02-17 01:35:07 +@@ -90,16 +90,31 @@ + return fieldText("INAM"); + } + ++String RIFF::Info::Tag::albumartist() const ++{ ++ return String(); ++} ++ + String RIFF::Info::Tag::artist() const + { + return fieldText("IART"); + } + ++String RIFF::Info::Tag::composer() const ++{ ++ return fieldText("IMUS"); ++} ++ + String RIFF::Info::Tag::album() const + { + return fieldText("IPRD"); + } + ++String RIFF::Info::Tag::unsyncedlyrics() const ++{ ++ return String(); ++} ++ + String RIFF::Info::Tag::comment() const + { + return fieldText("ICMT"); +@@ -120,21 +135,54 @@ + return fieldText("IPRT").toInt(); + } + ++unsigned int RIFF::Info::Tag::disc() const ++{ ++ return 0; ++} ++ ++String RIFF::Info::Tag::cuesheet() const ++{ ++ return String(); ++} ++ ++TagLib::Tag::ReplayGain RIFF::Info::Tag::replaygain() const ++{ ++ return TagLib::Tag::ReplayGain(); ++} ++ ++String RIFF::Info::Tag::soundcheck() const ++{ ++ return String(); ++} ++ + void RIFF::Info::Tag::setTitle(const String &s) + { + setFieldText("INAM", s); + } + ++void RIFF::Info::Tag::setAlbumArtist(const String &) ++{ ++} ++ + void RIFF::Info::Tag::setArtist(const String &s) + { + setFieldText("IART", s); + } + ++void RIFF::Info::Tag::setComposer(const String &s) ++{ ++ setFieldText("IMUS", s); ++} ++ + void RIFF::Info::Tag::setAlbum(const String &s) + { + setFieldText("IPRD", s); + } + ++void RIFF::Info::Tag::setUnsyncedLyrics(const String &) ++{ ++} ++ + void RIFF::Info::Tag::setComment(const String &s) + { + setFieldText("ICMT", s); +@@ -159,6 +207,22 @@ + setFieldText("IPRT", String::number(i)); + else + d->fieldListMap.erase("IPRT"); ++} ++ ++void RIFF::Info::Tag::setDisc(unsigned int i) ++{ ++} ++ ++void RIFF::Info::Tag::setCuesheet(const String &) ++{ ++} ++ ++void RIFF::Info::Tag::setReplaygain(TagLib::Tag::ReplayGain) ++{ ++} ++ ++void RIFF::Info::Tag::setSoundcheck(const String &) ++{ + } + + bool RIFF::Info::Tag::isEmpty() const +diff -ur taglib-2.0.2.orig/taglib/riff/wav/infotag.h taglib-2.0.2/taglib/riff/wav/infotag.h +--- taglib-2.0.2.orig/taglib/riff/wav/infotag.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/riff/wav/infotag.h 2025-02-17 01:34:48 +@@ -95,6 +95,7 @@ + class TAGLIB_EXPORT Tag : public TagLib::Tag + { + public: ++ using TagLib::Tag::ReplayGain; + /*! + * Constructs an empty INFO tag. + */ +@@ -113,20 +114,34 @@ + // Reimplementations + + String title() const override; ++ String albumartist() const override; + String artist() const override; ++ String composer() const override; + String album() const override; ++ String unsyncedlyrics() const override; + String comment() const override; + String genre() const override; + unsigned int year() const override; + unsigned int track() const override; ++ unsigned int disc() const override; ++ String cuesheet() const override; ++ ReplayGain replaygain() const override; ++ String soundcheck() const override; + + void setTitle(const String &s) override; ++ void setAlbumArtist(const String &s) override; + void setArtist(const String &s) override; ++ void setComposer(const String &s) override; + void setAlbum(const String &s) override; ++ void setUnsyncedLyrics(const String &s) override; + void setComment(const String &s) override; + void setGenre(const String &s) override; + void setYear(unsigned int i) override; + void setTrack(unsigned int i) override; ++ void setDisc(unsigned int i) override; ++ void setCuesheet(const String &s) override; ++ void setReplaygain(ReplayGain r) override; ++ void setSoundcheck(const String &s) override; + + bool isEmpty() const override; + +diff -ur taglib-2.0.2.orig/taglib/tag.cpp taglib-2.0.2/taglib/tag.cpp +--- taglib-2.0.2.orig/taglib/tag.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/tag.cpp 2025-02-17 04:15:31 +@@ -25,6 +25,7 @@ + + #include "tag.h" + ++#include + #include + + #include "tstringlist.h" +@@ -43,12 +44,19 @@ + bool Tag::isEmpty() const + { + return title().isEmpty() && ++ albumartist().isEmpty() && + artist().isEmpty() && ++ composer().isEmpty() && + album().isEmpty() && ++ unsyncedlyrics().isEmpty() && + comment().isEmpty() && + genre().isEmpty() && + year() == 0 && +- track() == 0; ++ track() == 0 && ++ disc() == 0 && ++ cuesheet().isEmpty() && ++ rg.isEmpty() && ++ soundcheck().isEmpty(); + } + + PropertyMap Tag::properties() const +@@ -56,10 +64,16 @@ + PropertyMap map; + if(!title().isEmpty()) + map["TITLE"].append(title()); ++ if(!albumartist().isEmpty()) ++ map["ALBUMARTIST"].append(albumartist()); + if(!artist().isEmpty()) + map["ARTIST"].append(artist()); ++ if(!composer().isEmpty()) ++ map["COMPOSER"].append(composer()); + if(!album().isEmpty()) + map["ALBUM"].append(album()); ++ if(!unsyncedlyrics().isEmpty()) ++ map["UNSYNCEDLYRICS"].append(unsyncedlyrics()); + if(!comment().isEmpty()) + map["COMMENT"].append(comment()); + if(!genre().isEmpty()) +@@ -68,6 +82,22 @@ + map["DATE"].append(String::number(year())); + if(track() != 0) + map["TRACKNUMBER"].append(String::number(track())); ++ if(disc() != 0) ++ map["DISCNUMBER"].append(String::number(disc())); ++ if(!cuesheet().isEmpty()) ++ map["CUESHEET"].append(cuesheet()); ++ if(!rg.isEmpty()) { ++ if(rg.albumGainSet()) ++ map["REPLAYGAIN_ALBUM_GAIN"].append(String::number(rg.albumGain()) + " dB"); ++ if(rg.albumPeakSet()) ++ map["REPLAYGAIN_ALBUM_PEAK"].append(String::number(rg.albumPeak())); ++ if(rg.trackGainSet()) ++ map["REPLAYGAIN_TRACK_GAIN"].append(String::number(rg.trackGain()) + " dB"); ++ if(rg.trackPeakSet()) ++ map["REPLAYGAIN_TRACK_PEAK"].append(String::number(rg.trackPeak())); ++ } ++ if(!soundcheck().isEmpty()) ++ map["SOUNDCHECK"].append(soundcheck()); + return map; + } + +@@ -87,18 +117,36 @@ + } else + setTitle(String()); + ++ if(props.contains("ALBUMARTIST")) { ++ setAlbumArtist(props["ALBUMARTIST"].front()); ++ oneValueSet.append("ALBUMARTIST"); ++ } else ++ setAlbumArtist(String()); ++ + if(props.contains("ARTIST")) { + setArtist(props["ARTIST"].front()); + oneValueSet.append("ARTIST"); + } else + setArtist(String()); + ++ if(props.contains("COMPOSER")) { ++ setComposer(props["COMPOSER"].front()); ++ oneValueSet.append("COMPOSER"); ++ } else ++ setComposer(String()); ++ + if(props.contains("ALBUM")) { + setAlbum(props["ALBUM"].front()); + oneValueSet.append("ALBUM"); + } else + setAlbum(String()); + ++ if(props.contains("UNSYNCEDLYRICS")) { ++ setUnsyncedLyrics(props["UNSYNCEDLYRICS"].front()); ++ oneValueSet.append("UNSYNCEDLYRICS"); ++ } else ++ setUnsyncedLyrics(String()); ++ + if(props.contains("COMMENT")) { + setComment(props["COMMENT"].front()); + oneValueSet.append("COMMENT"); +@@ -135,6 +183,80 @@ + else + setTrack(0); + ++ if(props.contains("DISCNUMBER")) { ++ bool ok; ++ int discNumber = props["DISCNUMBER"].front().toInt(&ok); ++ if(ok) { ++ setDisc(discNumber); ++ oneValueSet.append("DISCNUMBER"); ++ } else ++ setDisc(0); ++ } ++ else ++ setDisc(0); ++ ++ if(props.contains("CUESHEET")) { ++ setCuesheet(props["CUESHEET"].front()); ++ oneValueSet.append("CUESHEET"); ++ } else ++ setCuesheet(String()); ++ ++ if(props.contains("REPLAYGAIN_ALBUM_GAIN")) { ++ bool ok; ++ float albumGain = props["REPLAYGAIN_ALBUM_GAIN"].front().toFloat(&ok); ++ if(ok) { ++ rg.setAlbumGain(albumGain); ++ oneValueSet.append("REPLAYGAIN_ALBUM_GAIN"); ++ } else ++ rg.clearAlbumGain(); ++ } ++ else ++ rg.clearAlbumGain(); ++ ++ if(props.contains("REPLAYGAIN_ALBUM_PEAK")) { ++ bool ok; ++ float albumPeak = props["REPLAYGAIN_ALBUM_PEAK"].front().toFloat(&ok); ++ if(ok) { ++ rg.setAlbumPeak(albumPeak); ++ oneValueSet.append("REPLAYGAIN_ALBUM_PEAK"); ++ } else ++ rg.clearAlbumPeak(); ++ } ++ else ++ rg.clearAlbumPeak(); ++ ++ if(props.contains("REPLAYGAIN_TRACK_GAIN")) { ++ bool ok; ++ float trackGain = props["REPLAYGAIN_TRACK_GAIN"].front().toFloat(&ok); ++ if(ok) { ++ rg.setTrackGain(trackGain); ++ oneValueSet.append("REPLAYGAIN_TRACK_GAIN"); ++ } else ++ rg.clearTrackGain(); ++ } ++ else ++ rg.clearTrackGain(); ++ ++ if(props.contains("REPLAYGAIN_TRACK_PEAK")) { ++ bool ok; ++ float trackPeak = props["REPLAYGAIN_TRACK_PEAK"].front().toFloat(&ok); ++ if(ok) { ++ rg.setTrackPeak(trackPeak); ++ oneValueSet.append("REPLAYGAIN_TRACK_PEAK"); ++ } else ++ rg.clearTrackPeak(); ++ } ++ else ++ rg.clearTrackPeak(); ++ ++ setReplaygain(rg); ++ ++ if(props.contains("SOUNDCHECK")) { ++ setSoundcheck(props["SOUNDCHECK"].front()); ++ oneValueSet.append("SOUNDCHECK"); ++ } else ++ setSoundcheck(String()); ++ + // for each tag that has been set above, remove the first entry in the corresponding + // value list. The others will be returned as unsupported by this format. + for(const auto &entry : std::as_const(oneValueSet)) { +@@ -165,20 +287,33 @@ + { + if(overwrite) { + target->setTitle(source->title()); ++ target->setAlbumArtist(source->albumartist()); + target->setArtist(source->artist()); ++ target->setComposer(source->composer()); + target->setAlbum(source->album()); ++ target->setUnsyncedLyrics(source->unsyncedlyrics()); + target->setComment(source->comment()); + target->setGenre(source->genre()); + target->setYear(source->year()); + target->setTrack(source->track()); ++ target->setDisc(source->disc()); ++ target->setCuesheet(source->cuesheet()); ++ target->setReplaygain(source->replaygain()); ++ target->setSoundcheck(source->soundcheck()); + } + else { + if(target->title().isEmpty()) + target->setTitle(source->title()); ++ if(target->albumartist().isEmpty()) ++ target->setAlbumArtist(source->albumartist()); + if(target->artist().isEmpty()) + target->setArtist(source->artist()); ++ if(target->composer().isEmpty()) ++ target->setComposer(source->composer()); + if(target->album().isEmpty()) + target->setAlbum(source->album()); ++ if(target->unsyncedlyrics().isEmpty()) ++ target->setUnsyncedLyrics(source->unsyncedlyrics()); + if(target->comment().isEmpty()) + target->setComment(source->comment()); + if(target->genre().isEmpty()) +@@ -187,10 +322,170 @@ + target->setYear(source->year()); + if(target->track() == 0) + target->setTrack(source->track()); ++ if(target->disc() == 0) ++ target->setDisc(source->disc()); ++ if(target->cuesheet().isEmpty()) ++ target->setCuesheet(source->cuesheet()); ++ if(!target->replaygain().isEmpty()) ++ target->setReplaygain(source->replaygain()); ++ if(target->soundcheck().isEmpty()) ++ target->setSoundcheck(source->soundcheck()); + } + } + + String Tag::joinTagValues(const StringList &values) + { + return values.toString(" / "); ++} ++ ++Tag::ReplayGain::ReplayGain() ++{ ++ clear(); ++} ++ ++Tag::ReplayGain &Tag::ReplayGain::operator=(const Tag::ReplayGain &source) ++{ ++ clear(); ++ if(source.albumgainSet) ++ setAlbumGain(source.albumgain); ++ if(source.albumpeakSet) ++ setAlbumPeak(source.albumpeak); ++ if(source.trackgainSet) ++ setTrackGain(source.trackgain); ++ if(source.trackpeakSet) ++ setTrackPeak(source.trackpeak); ++ ++ return *this; ++} ++ ++bool Tag::ReplayGain::_equals(const Tag::ReplayGain &source) const ++{ ++ if(this->albumgainSet != source.albumgainSet || ++ this->albumpeakSet != source.albumpeakSet || ++ this->trackgainSet != source.trackgainSet || ++ this->trackpeakSet != source.trackpeakSet) ++ return false; ++ if(this->albumgainSet && std::fabs(this->albumgain - source.albumgain) > 1e-6) ++ return false; ++ if(this->albumpeakSet && std::fabs(this->albumpeak - source.albumpeak) > 1e-6) ++ return false; ++ if(this->trackgainSet && std::fabs(this->trackgain - source.trackgain) > 1e-6) ++ return false; ++ if(this->trackpeakSet && std::fabs(this->trackpeak - source.trackpeak) > 1e-6) ++ return false; ++ return true; ++} ++ ++bool Tag::ReplayGain::operator==(const Tag::ReplayGain &source) const ++{ ++ return this->_equals(source); ++} ++ ++bool Tag::ReplayGain::operator!=(const Tag::ReplayGain &source) const ++{ ++ return !(this->_equals(source)); ++} ++ ++bool Tag::ReplayGain::isEmpty() const ++{ ++ return !albumgainSet && ++ !albumpeakSet && ++ !trackgainSet && ++ !trackpeakSet; ++} ++ ++bool Tag::ReplayGain::albumGainSet() const ++{ ++ return albumgainSet; ++} ++ ++bool Tag::ReplayGain::albumPeakSet() const ++{ ++ return albumpeakSet; ++} ++ ++bool Tag::ReplayGain::trackGainSet() const ++{ ++ return trackgainSet; ++} ++ ++bool Tag::ReplayGain::trackPeakSet() const ++{ ++ return trackpeakSet; ++} ++ ++float Tag::ReplayGain::albumGain() const ++{ ++ return albumgainSet ? albumgain : 0; ++} ++ ++float Tag::ReplayGain::albumPeak() const ++{ ++ return albumpeakSet ? albumpeak : 0; ++} ++ ++float Tag::ReplayGain::trackGain() const ++{ ++ return trackgainSet ? trackgain : 0; ++} ++ ++float Tag::ReplayGain::trackPeak() const ++{ ++ return trackpeakSet ? trackpeak : 0; ++} ++ ++void Tag::ReplayGain::setAlbumGain(float f) ++{ ++ albumgain = f; ++ albumgainSet = true; ++} ++ ++void Tag::ReplayGain::setAlbumPeak(float f) ++{ ++ albumpeak = f; ++ albumpeakSet = true; ++} ++ ++void Tag::ReplayGain::setTrackGain(float f) ++{ ++ trackgain = f; ++ trackgainSet = true; ++} ++ ++void Tag::ReplayGain::setTrackPeak(float f) ++{ ++ trackpeak = f; ++ trackpeakSet = true; ++} ++ ++void Tag::ReplayGain::clear() ++{ ++ clearAlbumGain(); ++ clearAlbumPeak(); ++ clearTrackGain(); ++ clearTrackPeak(); ++} ++ ++void Tag::ReplayGain::clearAlbumGain() ++{ ++ albumgain = 0; ++ albumgainSet = false; ++} ++ ++void Tag::ReplayGain::clearAlbumPeak() ++{ ++ albumpeak = 0; ++ albumpeakSet = false; ++} ++ ++void Tag::ReplayGain::clearTrackGain() ++{ ++ trackgain = 0; ++ trackgainSet = false; ++} ++ ++void Tag::ReplayGain::clearTrackPeak() ++{ ++ trackpeak = 0; ++ trackpeakSet = false; + } +diff -ur taglib-2.0.2.orig/taglib/tag.h taglib-2.0.2/taglib/tag.h +--- taglib-2.0.2.orig/taglib/tag.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/tag.h 2025-02-17 04:14:03 +@@ -48,7 +48,51 @@ + class TAGLIB_EXPORT Tag + { + public: ++ class ReplayGain { ++ protected: ++ bool _equals(const ReplayGain &) const; + ++ public: ++ ReplayGain(); ++ ++ ReplayGain &operator=(const ReplayGain &); ++ bool operator==(const ReplayGain &) const; ++ bool operator!=(const ReplayGain &) const; ++ ++ bool isEmpty() const; ++ bool albumGainSet() const; ++ bool albumPeakSet() const; ++ bool trackGainSet() const; ++ bool trackPeakSet() const; ++ ++ float albumGain() const; ++ float albumPeak() const; ++ float trackGain() const; ++ float trackPeak() const; ++ ++ void setAlbumGain(float f); ++ void setAlbumPeak(float f); ++ void setTrackGain(float f); ++ void setTrackPeak(float f); ++ ++ void clear(); ++ void clearAlbumGain(); ++ void clearAlbumPeak(); ++ void clearTrackGain(); ++ void clearTrackPeak(); ++ ++ private: ++ bool albumgainSet; ++ bool albumpeakSet; ++ bool trackgainSet; ++ bool trackpeakSet; ++ ++ float albumgain; ++ float albumpeak; ++ float trackgain; ++ float trackpeak; ++ }; ++ + /*! + * Destroys this Tag instance. + */ +@@ -130,18 +174,36 @@ + virtual String title() const = 0; + + /*! ++ * Returns the album artist name; if no album artist name is present ++ * in the tag an empty string will be returned. ++ */ ++ virtual String albumartist() const = 0; ++ ++ /*! + * Returns the artist name; if no artist name is present in the tag + * an empty string will be returned. + */ + virtual String artist() const = 0; + + /*! ++ * Returns the composer name; if no composer name is present in the ++ * tag an empty string will be returned. ++ */ ++ virtual String composer() const = 0; ++ ++ /*! + * Returns the album name; if no album name is present in the tag + * an empty string will be returned. + */ + virtual String album() const = 0; + + /*! ++ * Returns the unsynchronized lyrics; if no unsynced lyrics are ++ * present in the tag an empty string will be returned. ++ */ ++ virtual String unsyncedlyrics() const = 0; ++ ++ /*! + * Returns the track comment; if no comment is present in the tag + * an empty string will be returned. + */ +@@ -165,24 +227,65 @@ + virtual unsigned int track() const = 0; + + /*! ++ * Returns the disc number; if there is no disc number set, this will ++ * return 0. ++ */ ++ virtual unsigned int disc() const = 0; ++ ++ /*! ++ * Returns the embedded cue sheet; if there is no embedded cue sheet set, ++ * this will return an empty string. ++ */ ++ virtual String cuesheet() const = 0; ++ ++ /*! ++ * Returns the ReplayGain tags; if there are no tags set then it will be ++ * marked empty. ++ */ ++ virtual ReplayGain replaygain() const = 0; ++ ++ /*! ++ * Returns the SoundCheck tag; if there is no SoundCheck tag set then this ++ * will return an empty string. ++ */ ++ virtual String soundcheck() const = 0; ++ ++ /*! + * Sets the title to \a s. If \a s is an empty string then this value will be + * cleared. + */ + virtual void setTitle(const String &s) = 0; + + /*! ++ * Sets the album artist to \a s. If \a s is an empty string then this value ++ * will be cleared. */ ++ virtual void setAlbumArtist(const String &s) = 0; ++ ++ /*! + * Sets the artist to \a s. If \a s is an empty string then this value will be + * cleared. + */ + virtual void setArtist(const String &s) = 0; + + /*! ++ * Sets the composer to \a s. If \a s is an empty string then this value will ++ * be cleared. ++ */ ++ virtual void setComposer(const String &s) = 0; ++ ++ /*! + * Sets the album to \a s. If \a s is an empty string then this value will be + * cleared. + */ + virtual void setAlbum(const String &s) = 0; + + /*! ++ * Sets the unsynchronized lyrics to \a s. If \a s is an empty string then this ++ * value will be cleared. ++ */ ++ virtual void setUnsyncedLyrics(const String &s) = 0; ++ ++ /*! + * Sets the comment to \a s. If \a s is an empty string then this value will be + * cleared. + */ +@@ -198,16 +301,39 @@ + virtual void setGenre(const String &s) = 0; + + /*! +- * Sets the year to \a i. If \a s is 0 then this value will be cleared. ++ * Sets the year to \a i. If \a i is 0 then this value will be cleared. + */ + virtual void setYear(unsigned int i) = 0; + + /*! +- * Sets the track to \a i. If \a s is 0 then this value will be cleared. ++ * Sets the track to \a i. If \a i is 0 then this value will be cleared. + */ + virtual void setTrack(unsigned int i) = 0; + + /*! ++ * Sets the disc to \a i. If \a i is 0 then this value will be cleared. ++ */ ++ virtual void setDisc(unsigned int i) = 0; ++ ++ /*! ++ * Sets the embedded cue sheet to \a s. If \a s is an empty string then ++ * this value will be cleared. ++ */ ++ virtual void setCuesheet(const String &s) = 0; ++ ++ /*! ++ * Sets the ReplayGain tag to \a t. If any fields are marked as empty ++ * then they will be cleared. ++ */ ++ virtual void setReplaygain(ReplayGain replaygain) = 0; ++ ++ /*! ++ * Sets the SoundCheck tag to \a s. If \a s is an empty string then this ++ * value will be cleared. ++ */ ++ virtual void setSoundcheck(const String &s) = 0; ++ ++ /*! + * Returns \c true if the tag does not contain any data. This should be + * reimplemented in subclasses that provide more than the basic tagging + * abilities in this class. +@@ -241,6 +367,12 @@ + * through subclasses. + */ + Tag(); ++ ++ /*! ++ * Internal ReplayGain tag state. This is protected since subclasses should ++ * be able to manipulate it freely. ++ */ ++ ReplayGain rg; + + private: + class TagPrivate; +diff -ur taglib-2.0.2.orig/taglib/tagunion.cpp taglib-2.0.2/taglib/tagunion.cpp +--- taglib-2.0.2.orig/taglib/tagunion.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/tagunion.cpp 2025-02-17 01:12:48 +@@ -164,16 +164,31 @@ + stringUnion(title); + } + ++String TagUnion::albumartist() const ++{ ++ stringUnion(albumartist); ++} ++ + String TagUnion::artist() const + { + stringUnion(artist); + } + ++String TagUnion::composer() const ++{ ++ stringUnion(composer); ++} ++ + String TagUnion::album() const + { + stringUnion(album); + } + ++String TagUnion::unsyncedlyrics() const ++{ ++ stringUnion(unsyncedlyrics); ++} ++ + String TagUnion::comment() const + { + stringUnion(comment); +@@ -194,21 +209,62 @@ + numberUnion(track); + } + ++unsigned int TagUnion::disc() const ++{ ++ numberUnion(disc); ++} ++ ++String TagUnion::cuesheet() const ++{ ++ stringUnion(cuesheet); ++} ++ ++Tag::ReplayGain TagUnion::replaygain() const ++{ ++ if(tag(0)) ++ return tag(0)->replaygain(); ++ if(tag(1)) ++ return tag(1)->replaygain(); ++ if(tag(2)) ++ return tag(2)->replaygain(); ++ return Tag::ReplayGain(); ++} ++ ++String TagUnion::soundcheck() const ++{ ++ stringUnion(soundcheck); ++} ++ + void TagUnion::setTitle(const String &s) + { + setUnion(Title, s); + } + ++void TagUnion::setAlbumArtist(const String &s) ++{ ++ setUnion(AlbumArtist, s); ++} ++ + void TagUnion::setArtist(const String &s) + { + setUnion(Artist, s); + } + ++void TagUnion::setComposer(const String &s) ++{ ++ setUnion(Composer, s); ++} ++ + void TagUnion::setAlbum(const String &s) + { + setUnion(Album, s); + } + ++void TagUnion::setUnsyncedLyrics(const String &s) ++{ ++ setUnion(UnsyncedLyrics, s); ++} ++ + void TagUnion::setComment(const String &s) + { + setUnion(Comment, s); +@@ -227,6 +283,26 @@ + void TagUnion::setTrack(unsigned int i) + { + setUnion(Track, i); ++} ++ ++void TagUnion::setDisc(unsigned int i) ++{ ++ setUnion(Disc, i); ++} ++ ++void TagUnion::setCuesheet(const String &s) ++{ ++ setUnion(Cuesheet, s); ++} ++ ++void TagUnion::setReplaygain(Tag::ReplayGain r) ++{ ++ setUnion(Replaygain, r); ++} ++ ++void TagUnion::setSoundcheck(const String &s) ++{ ++ setUnion(Soundcheck, s); + } + + bool TagUnion::isEmpty() const +diff -ur taglib-2.0.2.orig/taglib/tagunion.h taglib-2.0.2/taglib/tagunion.h +--- taglib-2.0.2.orig/taglib/tagunion.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/tagunion.h 2025-02-17 01:12:11 +@@ -67,20 +67,34 @@ + bool setComplexProperties(const String &key, const List &value) override; + + String title() const override; ++ String albumartist() const override; + String artist() const override; ++ String composer() const override; + String album() const override; ++ String unsyncedlyrics() const override; + String comment() const override; + String genre() const override; + unsigned int year() const override; + unsigned int track() const override; ++ unsigned int disc() const override; ++ String cuesheet() const override; ++ Tag::ReplayGain replaygain() const override; ++ String soundcheck() const override; + + void setTitle(const String &s) override; ++ void setAlbumArtist(const String &s) override; + void setArtist(const String &s) override; ++ void setComposer(const String &s) override; + void setAlbum(const String &s) override; ++ void setUnsyncedLyrics(const String &s) override; + void setComment(const String &s) override; + void setGenre(const String &s) override; + void setYear(unsigned int i) override; + void setTrack(unsigned int i) override; ++ void setDisc(unsigned int i) override; ++ void setCuesheet(const String &s) override; ++ void setReplaygain(Tag::ReplayGain r) override; ++ void setSoundcheck(const String &s) override; + bool isEmpty() const override; + + template T *access(int index, bool create) +diff -ur taglib-2.0.2.orig/taglib/toolkit/tstring.cpp taglib-2.0.2/taglib/toolkit/tstring.cpp +--- taglib-2.0.2.orig/taglib/toolkit/tstring.cpp 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/toolkit/tstring.cpp 2025-02-17 02:38:54 +@@ -475,12 +475,12 @@ + } + } + +-int String::toInt(bool *ok) const ++int String::toInt(bool *ok, int base) const + { + const wchar_t *beginPtr = d->data.c_str(); + wchar_t *endPtr; + errno = 0; +- const long value = ::wcstol(beginPtr, &endPtr, 10); ++ const long value = ::wcstol(beginPtr, &endPtr, base); + + // Has wcstol() consumed the entire string and not overflowed? + if(ok) { +@@ -489,6 +489,21 @@ + } + + return static_cast(value); ++} ++ ++float String::toFloat(bool *ok) const ++{ ++ const wchar_t *beginPtr = d->data.c_str(); ++ wchar_t *endPtr; ++ errno = 0; ++ const float value = ::wcstof(beginPtr, &endPtr); ++ ++ // Has wcstof() consumed at least part of the string and not overflowed? ++ if(ok) { ++ *ok = (errno == 0 && endPtr > beginPtr); ++ } ++ ++ return value; + } + + String String::stripWhiteSpace() const +diff -ur taglib-2.0.2.orig/taglib/toolkit/tstring.h taglib-2.0.2/taglib/toolkit/tstring.h +--- taglib-2.0.2.orig/taglib/toolkit/tstring.h 2024-08-23 21:40:41 ++++ taglib-2.0.2/taglib/toolkit/tstring.h 2025-02-17 02:38:42 +@@ -357,7 +357,16 @@ + * \c true and returns the integer. Otherwise it sets \a *ok to \c false + * and the result is undefined. + */ +- int toInt(bool *ok = nullptr) const; ++ int toInt(bool *ok = nullptr, int base = 10) const; ++ ++ /*! ++ * Convert the string to a float. ++ * ++ * If the conversion was successful, it sets the value of \a *ok to ++ * \c true and returns the float. Otherwise it sets \a *ok to \c false ++ * and the result is undefined. ++ */ ++ float toFloat(bool *ok = nullptr) const; + + /*! + * Returns a string with the leading and trailing whitespace stripped.