Additional manual formatting to taglib sources

This commit is contained in:
Jonas Kvinge
2020-06-14 17:01:05 +02:00
parent 577b7d8ec8
commit ef34dce4dc
217 changed files with 7367 additions and 8204 deletions

View File

@@ -1,5 +1,6 @@
include(CheckLibraryExists) include(CheckLibraryExists)
include(CheckTypeSize) include(CheckTypeSize)
include(CheckCXXCompilerFlag)
include(CheckCXXSourceCompiles) include(CheckCXXSourceCompiles)
# Check if the size of numeric types are suitable. # Check if the size of numeric types are suitable.

View File

@@ -56,10 +56,10 @@ class APE::File::FilePrivate {
FilePrivate() : APELocation(-1), FilePrivate() : APELocation(-1),
APESize(0), APESize(0),
ID3v1Location(-1), ID3v1Location(-1),
ID3v2Header(0), ID3v2Header(nullptr),
ID3v2Location(-1), ID3v2Location(-1),
ID3v2Size(0), ID3v2Size(0),
properties(0) {} properties(nullptr) {}
~FilePrivate() { ~FilePrivate() {
delete ID3v2Header; delete ID3v2Header;
@@ -98,16 +98,18 @@ bool APE::File::isSupported(IOStream *) {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) {
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) {
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
APE::File::~File() { APE::File::~File() {
@@ -127,10 +129,12 @@ void APE::File::removeUnsupportedProperties(const StringList &properties) {
} }
PropertyMap APE::File::setProperties(const PropertyMap &properties) { PropertyMap APE::File::setProperties(const PropertyMap &properties) {
if (ID3v1Tag()) if (ID3v1Tag())
ID3v1Tag()->setProperties(properties); ID3v1Tag()->setProperties(properties);
return APETag(true)->setProperties(properties); return APETag(true)->setProperties(properties);
} }
APE::Properties *APE::File::audioProperties() const { APE::Properties *APE::File::audioProperties() const {
@@ -138,6 +142,7 @@ APE::Properties *APE::File::audioProperties() const {
} }
bool APE::File::save() { bool APE::File::save() {
if (readOnly()) { if (readOnly()) {
debug("APE::File::save() -- File is read only."); debug("APE::File::save() -- File is read only.");
return false; return false;
@@ -206,6 +211,7 @@ bool APE::File::save() {
} }
return true; return true;
} }
ID3v1::Tag *APE::File::ID3v1Tag(bool create) { ID3v1::Tag *APE::File::ID3v1Tag(bool create) {
@@ -217,14 +223,16 @@ APE::Tag *APE::File::APETag(bool create) {
} }
void APE::File::strip(int tags) { void APE::File::strip(int tags) {
if (tags & ID3v1) if (tags & ID3v1)
d->tag.set(ApeID3v1Index, 0); d->tag.set(ApeID3v1Index, nullptr);
if (tags & APE) if (tags & APE)
d->tag.set(ApeAPEIndex, 0); d->tag.set(ApeAPEIndex, nullptr);
if (!ID3v1Tag()) if (!ID3v1Tag())
APETag(true); APETag(true);
} }
bool APE::File::hasAPETag() const { bool APE::File::hasAPETag() const {
@@ -240,6 +248,7 @@ bool APE::File::hasID3v1Tag() const {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void APE::File::read(bool readProperties) { void APE::File::read(bool readProperties) {
// Look for an ID3v2 tag // Look for an ID3v2 tag
d->ID3v2Location = Utils::findID3v2(this); d->ID3v2Location = Utils::findID3v2(this);
@@ -293,4 +302,5 @@ void APE::File::read(bool readProperties) {
d->properties = new Properties(this, streamLength); d->properties = new Properties(this, streamLength);
} }
} }

View File

@@ -55,8 +55,8 @@ class Tag;
/*! /*!
* This is implementation of APE metadata. * This is implementation of APE metadata.
* *
* This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream properties from the file.
* properties from the file. *
*/ */
namespace APE { namespace APE {
@@ -66,15 +66,14 @@ namespace APE {
/*! /*!
* This implements and provides an interface for APE files to the * This implements and provides an interface for APE files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional * the abstract TagLib::File API as well as providing some additional information specific to APE files.
* information specific to APE files. *
*/ */
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
public: public:
/*! /*!
* This set of flags is used for various operations and is suitable for * This set of flags is used for various operations and is suitable for being OR-ed together.
* being OR-ed together.
*/ */
enum TagTypes { enum TagTypes {
//! Empty set. Matches no tag types. //! Empty set. Matches no tag types.
@@ -88,8 +87,8 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
}; };
/*! /*!
* Constructs an APE file from \a file. If \a readProperties is true the * Constructs an APE file from \a file.
* file's audio properties will also be read. * If \a readProperties is true the file's audio properties will also be read.
* *
* \note In the current implementation, \a propertiesStyle is ignored. * \note In the current implementation, \a propertiesStyle is ignored.
*/ */
@@ -97,11 +96,10 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Constructs an APE file from \a stream. If \a readProperties is true the * Constructs an APE file from \a stream.
* file's audio properties will also be read. * If \a readProperties is true the file's audio properties will also be read.
* *
* \note TagLib will *not* take ownership of the stream, the caller is * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object.
* responsible for deleting it after the File object.
* *
* \note In the current implementation, \a propertiesStyle is ignored. * \note In the current implementation, \a propertiesStyle is ignored.
*/ */
@@ -114,34 +112,31 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
virtual ~File(); virtual ~File();
/*! /*!
* Returns the Tag for this file. This will be an APE tag, an ID3v1 tag * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag or a combination of the two.
* or a combination of the two.
*/ */
virtual Strawberry_TagLib::TagLib::Tag *tag() const; virtual Strawberry_TagLib::TagLib::Tag *tag() const;
/*! /*!
* Implements the unified property interface -- export function. * Implements the unified property interface -- export function.
* If the file contains both an APE and an ID3v1 tag, only APE * If the file contains both an APE and an ID3v1 tag, only APE will be converted to the PropertyMap.
* will be converted to the PropertyMap.
*/ */
PropertyMap properties() const; PropertyMap properties() const;
/*! /*!
* Removes unsupported properties. Forwards to the actual Tag's * Removes unsupported properties. Forwards to the actual Tag's removeUnsupportedProperties() function.
* removeUnsupportedProperties() function.
*/ */
void removeUnsupportedProperties(const StringList &properties); void removeUnsupportedProperties(const StringList &properties);
/*! /*!
* Implements the unified property interface -- import function. * Implements the unified property interface -- import function.
* Creates an APEv2 tag if necessary. A potentially existing ID3v1 * Creates an APEv2 tag if necessary.
* tag will be updated as well. * A potentially existing ID3v1 tag will be updated as well.
*/ */
PropertyMap setProperties(const PropertyMap &); PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the APE::Properties for this file. If no audio properties * Returns the APE::Properties for this file.
* were read then this will return a null pointer. * If no audio properties were read then this will return a null pointer.
*/ */
virtual Properties *audioProperties() const; virtual Properties *audioProperties() const;
@@ -156,17 +151,14 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
/*! /*!
* Returns a pointer to the ID3v1 tag of the file. * Returns a pointer to the ID3v1 tag of the file.
* *
* If \a create is false (the default) this may return a null pointer * If \a create is false (the default) this may return a null pointer if there is no valid ID3v1 tag.
* if there is no valid ID3v1 tag. If \a create is true it will create * If \a create is true it will create an ID3v1 tag if one does not exist and returns a valid pointer.
* an ID3v1 tag if one does not exist and returns a valid pointer.
* *
* \note This may return a valid pointer regardless of whether or not the * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v1 tag.
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * Use hasID3v1Tag() to check if the file on disk actually has an ID3v1 tag.
* on disk actually has an ID3v1 tag.
* *
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be * \note The Tag <b>is still</b> owned by the MPEG::File and should not be deleted by the user.
* deleted by the user. It will be deleted when the file (object) is * It will be deleted when the file (object) is destroyed.
* destroyed.
* *
* \see hasID3v1Tag() * \see hasID3v1Tag()
*/ */
@@ -175,28 +167,24 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
/*! /*!
* Returns a pointer to the APE tag of the file. * Returns a pointer to the APE tag of the file.
* *
* If \a create is false (the default) this may return a null pointer * If \a create is false (the default) this may return a null pointer if there is no valid APE tag.
* if there is no valid APE tag. If \a create is true it will create * If \a create is true it will create an APE tag if one does not exist and returns a valid pointer.
* an APE tag if one does not exist and returns a valid pointer.
* *
* \note This may return a valid pointer regardless of whether or not the * \note This may return a valid pointer regardless of whether or not the file on disk has an APE tag.
* file on disk has an APE tag. Use hasAPETag() to check if the file * Use hasAPETag() to check if the file on disk actually has an APE tag.
* on disk actually has an APE tag.
* *
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be * \note The Tag <b>is still</b> owned by the MPEG::File and should not be deleted by the user.
* deleted by the user. It will be deleted when the file (object) is * It will be deleted when the file (object) is destroyed.
* destroyed.
* *
* \see hasAPETag() * \see hasAPETag()
*/ */
APE::Tag *APETag(bool create = false); APE::Tag *APETag(bool create = false);
/*! /*!
* This will remove the tags that match the OR-ed together TagTypes from the * This will remove the tags that match the OR-ed together TagTypes from the file.
* file. By default it removes all tags. * By default it removes all tags.
* *
* \note This will also invalidate pointers to the tags * \note This will also invalidate pointers to the tags as their memory will be freed.
* as their memory will be freed.
* \note In order to make the removal permanent save() still needs to be called * \note In order to make the removal permanent save() still needs to be called
*/ */
void strip(int tags = AllTags); void strip(int tags = AllTags);
@@ -216,11 +204,10 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
bool hasID3v1Tag() const; bool hasID3v1Tag() const;
/*! /*!
* Returns whether or not the given \a stream can be opened as an APE * Returns whether or not the given \a stream can be opened as an APE file.
* file.
* *
* \note This method is designed to do a quick check. The result may * \note This method is designed to do a quick check.
* not necessarily be correct. * The result may not necessarily be correct.
*/ */
static bool isSupported(IOStream *stream); static bool isSupported(IOStream *stream);

View File

@@ -134,10 +134,12 @@ ByteVector APE::Footer::renderFooter() const {
} }
ByteVector APE::Footer::renderHeader() const { ByteVector APE::Footer::renderHeader() const {
if (!d->headerPresent) if (!d->headerPresent)
return ByteVector(); return ByteVector();
else else
return render(true); return render(true);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -145,6 +147,7 @@ ByteVector APE::Footer::renderHeader() const {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void APE::Footer::parse(const ByteVector &data) { void APE::Footer::parse(const ByteVector &data) {
if (data.size() < size()) if (data.size() < size())
return; return;
@@ -169,9 +172,11 @@ void APE::Footer::parse(const ByteVector &data) {
d->headerPresent = flags[31]; d->headerPresent = flags[31];
d->footerPresent = !flags[30]; d->footerPresent = !flags[30];
d->isHeader = flags[29]; d->isHeader = flags[29];
} }
ByteVector APE::Footer::render(bool isHeader) const { ByteVector APE::Footer::render(bool isHeader) const {
ByteVector v; ByteVector v;
// add the file identifier -- "APETAGEX" // add the file identifier -- "APETAGEX"
@@ -206,4 +211,5 @@ ByteVector APE::Footer::render(bool isHeader) const {
v.append(ByteVector::fromLongLong(0)); v.append(ByteVector::fromLongLong(0));
return v; return v;
} }

View File

@@ -37,10 +37,11 @@ namespace APE {
//! An implementation of APE footers //! An implementation of APE footers
/*! /*!
* This class implements APE footers (and headers). It attempts to follow, both * This class implements APE footers (and headers).
* semantically and programmatically, the structure specified in * It attempts to follow, both semantically and programmatically,
* the APE v2.0 standard. The API is based on the properties of APE footer and * the structure specified in the APE v2.0 standard.
* headers specified there. * The API is based on the properties of APE footer and headers specified there.
*
*/ */
class TAGLIB_EXPORT Footer { class TAGLIB_EXPORT Footer {
@@ -51,8 +52,7 @@ class TAGLIB_EXPORT Footer {
Footer(); Footer();
/*! /*!
* Constructs an APE footer based on \a data. parse() is called * Constructs an APE footer based on \a data. parse() is called immediately.
* immediately.
*/ */
Footer(const ByteVector &data); Footer(const ByteVector &data);
@@ -98,7 +98,8 @@ class TAGLIB_EXPORT Footer {
void setItemCount(unsigned int s); void setItemCount(unsigned int s);
/*! /*!
* Returns the tag size in bytes. This is the size of the frame content and footer. * Returns the tag size in bytes.
* This is the size of the frame content and footer.
* The size of the \e entire tag will be this plus the header size, if present. * The size of the \e entire tag will be this plus the header size, if present.
* *
* \see completeTagSize() * \see completeTagSize()
@@ -142,15 +143,15 @@ class TAGLIB_EXPORT Footer {
ByteVector renderFooter() const; ByteVector renderFooter() const;
/*! /*!
* Renders the header corresponding to the footer. If headerPresent is * Renders the header corresponding to the footer.
* set to false, it returns an empty ByteVector. * If headerPresent is set to false, it returns an empty ByteVector.
*/ */
ByteVector renderHeader() const; ByteVector renderHeader() const;
protected: protected:
/*! /*!
* Called by setData() to parse the footer data. It makes this information * Called by setData() to parse the footer data.
* available through the public API. * It makes this information available through the public API.
*/ */
void parse(const ByteVector &data); void parse(const ByteVector &data);

View File

@@ -33,8 +33,7 @@ using namespace APE;
class APE::Item::ItemPrivate { class APE::Item::ItemPrivate {
public: public:
ItemPrivate() : type(Text), ItemPrivate() : type(Text), readOnly(false) {}
readOnly(false) {}
Item::ItemTypes type; Item::ItemTypes type;
String key; String key;
@@ -47,8 +46,7 @@ class APE::Item::ItemPrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
APE::Item::Item() : d(new ItemPrivate()) { APE::Item::Item() : d(new ItemPrivate()) {}
}
APE::Item::Item(const String &key, const String &value) : d(new ItemPrivate()) { APE::Item::Item(const String &key, const String &value) : d(new ItemPrivate()) {
d->key = key; d->key = key;
@@ -61,6 +59,7 @@ APE::Item::Item(const String &key, const StringList &values) : d(new ItemPrivate
} }
APE::Item::Item(const String &key, const ByteVector &value, bool binary) : d(new ItemPrivate()) { APE::Item::Item(const String &key, const ByteVector &value, bool binary) : d(new ItemPrivate()) {
d->key = key; d->key = key;
if (binary) { if (binary) {
d->type = Binary; d->type = Binary;
@@ -69,24 +68,28 @@ APE::Item::Item(const String &key, const ByteVector &value, bool binary) : d(new
else { else {
d->text.append(value); d->text.append(value);
} }
} }
APE::Item::Item(const Item &item) : d(new ItemPrivate(*item.d)) { APE::Item::Item(const Item &item) : d(new ItemPrivate(*item.d)) {}
}
APE::Item::~Item() { APE::Item::~Item() {
delete d; delete d;
} }
Item &APE::Item::operator=(const Item &item) { Item &APE::Item::operator=(const Item &item) {
Item(item).swap(*this); Item(item).swap(*this);
return *this; return *this;
} }
void APE::Item::swap(Item &item) { void APE::Item::swap(Item &item) {
using std::swap; using std::swap;
swap(d, item.d); swap(d, item.d);
} }
void APE::Item::setReadOnly(bool readOnly) { void APE::Item::setReadOnly(bool readOnly) {
@@ -114,14 +117,16 @@ ByteVector APE::Item::binaryData() const {
} }
void APE::Item::setBinaryData(const ByteVector &value) { void APE::Item::setBinaryData(const ByteVector &value) {
d->type = Binary; d->type = Binary;
d->value = value; d->value = value;
d->text.clear(); d->text.clear();
} }
ByteVector APE::Item::value() const { ByteVector APE::Item::value() const {
// This seems incorrect as it won't be actually rendering the value to keep it
// up to date. // This seems incorrect as it won't be actually rendering the value to keep it up to date.
return d->value; return d->value;
} }
@@ -131,30 +136,39 @@ void APE::Item::setKey(const String &key) {
} }
void APE::Item::setValue(const String &value) { void APE::Item::setValue(const String &value) {
d->type = Text; d->type = Text;
d->text = value; d->text = value;
d->value.clear(); d->value.clear();
} }
void APE::Item::setValues(const StringList &value) { void APE::Item::setValues(const StringList &value) {
d->type = Text; d->type = Text;
d->text = value; d->text = value;
d->value.clear(); d->value.clear();
} }
void APE::Item::appendValue(const String &value) { void APE::Item::appendValue(const String &value) {
d->type = Text; d->type = Text;
d->text.append(value); d->text.append(value);
d->value.clear(); d->value.clear();
} }
void APE::Item::appendValues(const StringList &values) { void APE::Item::appendValues(const StringList &values) {
d->type = Text; d->type = Text;
d->text.append(values); d->text.append(values);
d->value.clear(); d->value.clear();
} }
int APE::Item::size() const { int APE::Item::size() const {
int result = 8 + d->key.size() + 1; int result = 8 + d->key.size() + 1;
switch (d->type) { switch (d->type) {
case Text: case Text:
@@ -174,6 +188,7 @@ int APE::Item::size() const {
break; break;
} }
return result; return result;
} }
StringList APE::Item::toStringList() const { StringList APE::Item::toStringList() const {
@@ -185,13 +200,16 @@ StringList APE::Item::values() const {
} }
String APE::Item::toString() const { String APE::Item::toString() const {
if (d->type == Text && !isEmpty()) if (d->type == Text && !isEmpty())
return d->text.front(); return d->text.front();
else else
return String(); return String();
} }
bool APE::Item::isEmpty() const { bool APE::Item::isEmpty() const {
switch (d->type) { switch (d->type) {
case Text: case Text:
if (d->text.isEmpty()) if (d->text.isEmpty())
@@ -205,9 +223,11 @@ bool APE::Item::isEmpty() const {
default: default:
return false; return false;
} }
} }
void APE::Item::parse(const ByteVector &data) { void APE::Item::parse(const ByteVector &data) {
// 11 bytes is the minimum size for an APE item // 11 bytes is the minimum size for an APE item
if (data.size() < 11) { if (data.size() < 11) {
@@ -232,9 +252,11 @@ void APE::Item::parse(const ByteVector &data) {
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8); d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
else else
d->value = value; d->value = value;
} }
ByteVector APE::Item::render() const { ByteVector APE::Item::render() const {
ByteVector data; ByteVector data;
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1); unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
ByteVector value; ByteVector value;
@@ -263,4 +285,5 @@ ByteVector APE::Item::render() const {
data.append(value); data.append(value);
return data; return data;
} }

View File

@@ -32,7 +32,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace APE { namespace APE {
//! An implementation of APE-items //! An implementation of APE-items
@@ -130,8 +129,7 @@ class TAGLIB_EXPORT Item {
void setValue(const String &value); void setValue(const String &value);
/*! /*!
* Sets the text value of the item to the list of values in \a value and clears * Sets the text value of the item to the list of values in \a value and clears any previous contents.
* any previous contents.
* *
* \see toStringList() * \see toStringList()
*/ */
@@ -157,20 +155,14 @@ class TAGLIB_EXPORT Item {
int size() const; int size() const;
/*! /*!
* Returns the value as a single string. In case of multiple strings, * Returns the value as a single string. In case of multiple strings, the first is returned.
* the first is returned. If the data type is not \a Text, always returns * If the data type is not \a Text, always returns an empty String.
* an empty String.
*/ */
String toString() const; String toString() const;
#ifndef DO_NOT_DOCUMENT
/* Remove in next binary incompatible release */
StringList toStringList() const; StringList toStringList() const;
#endif
/*! /*!
* Returns the list of text values. If the data type is not \a Text, always * Returns the list of text values. If the data type is not \a Text, always returns an empty StringList.
* returns an empty StringList.
*/ */
StringList values() const; StringList values() const;
@@ -216,7 +208,6 @@ class TAGLIB_EXPORT Item {
ItemPrivate *d; ItemPrivate *d;
}; };
} // namespace APE } // namespace APE
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -61,13 +61,7 @@ class APE::Properties::PropertiesPrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
APE::Properties::Properties(File *, ReadStyle style) : AudioProperties(style), APE::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style), d(new PropertiesPrivate()) {
d(new PropertiesPrivate()) {
debug("APE::Properties::Properties() -- This constructor is no longer used.");
}
APE::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style),
d(new PropertiesPrivate()) {
read(file, streamLength); read(file, streamLength);
} }
@@ -75,10 +69,6 @@ APE::Properties::~Properties() {
delete d; delete d;
} }
int APE::Properties::length() const {
return lengthInSeconds();
}
int APE::Properties::lengthInSeconds() const { int APE::Properties::lengthInSeconds() const {
return d->length / 1000; return d->length / 1000;
} }
@@ -125,6 +115,7 @@ int headerVersion(const ByteVector &header) {
} // namespace } // namespace
void APE::Properties::read(File *file, long streamLength) { void APE::Properties::read(File *file, long streamLength) {
// First, we assume that the file pointer is set at the first descriptor. // First, we assume that the file pointer is set at the first descriptor.
long offset = file->tell(); long offset = file->tell();
int version = headerVersion(file->readBlock(6)); int version = headerVersion(file->readBlock(6));
@@ -153,9 +144,11 @@ void APE::Properties::read(File *file, long streamLength) {
d->length = static_cast<int>(length + 0.5); d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
} }
} }
void APE::Properties::analyzeCurrent(File *file) { void APE::Properties::analyzeCurrent(File *file) {
// Read the descriptor // Read the descriptor
file->seek(2, File::Current); file->seek(2, File::Current);
const ByteVector descriptor = file->readBlock(44); const ByteVector descriptor = file->readBlock(44);
@@ -188,9 +181,11 @@ void APE::Properties::analyzeCurrent(File *file) {
const unsigned int blocksPerFrame = header.toUInt(4, false); const unsigned int blocksPerFrame = header.toUInt(4, false);
const unsigned int finalFrameBlocks = header.toUInt(8, false); const unsigned int finalFrameBlocks = header.toUInt(8, false);
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks; d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
} }
void APE::Properties::analyzeOld(File *file) { void APE::Properties::analyzeOld(File *file) {
const ByteVector header = file->readBlock(26); const ByteVector header = file->readBlock(26);
if (header.size() < 26) { if (header.size() < 26) {
debug("APE::Properties::analyzeOld() -- MAC header is too short."); debug("APE::Properties::analyzeOld() -- MAC header is too short.");
@@ -228,4 +223,5 @@ void APE::Properties::analyzeOld(File *file) {
} }
d->bitsPerSample = fmt.toShort(26, false); d->bitsPerSample = fmt.toShort(26, false);
} }

View File

@@ -35,7 +35,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace APE { namespace APE {
class File; class File;
@@ -43,23 +42,14 @@ class File;
//! An implementation of audio property reading for APE //! An implementation of audio property reading for APE
/*! /*!
* This reads the data from an APE stream found in the AudioProperties * This reads the data from an APE stream found in the AudioProperties API.
* API.
*/ */
class TAGLIB_EXPORT Properties : public AudioProperties { class TAGLIB_EXPORT Properties : public AudioProperties {
public: public:
/*!
* Create an instance of APE::Properties with the data read from the
* APE::File \a file.
*
* \deprecated
*/
TAGLIB_DEPRECATED Properties(File *file, ReadStyle style = Average);
/*! /*!
* Create an instance of APE::Properties with the data read from the * Create an instance of APE::Properties with the data read from the APE::File \a file.
* APE::File \a file.
*/ */
Properties(File *file, long streamLength, ReadStyle style = Average); Properties(File *file, long streamLength, ReadStyle style = Average);
@@ -69,18 +59,7 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
virtual ~Properties(); virtual ~Properties();
/*! /*!
* Returns the length of the file in seconds. The length is rounded down to * Returns the length of the file in seconds. The length is rounded down to the nearest whole second.
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
* *
* \see lengthInMilliseconds() * \see lengthInMilliseconds()
*/ */

View File

@@ -51,7 +51,7 @@ const unsigned int MaxKeyLength = 255;
bool isKeyValid(const ByteVector &key) { bool isKeyValid(const ByteVector &key) {
const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", 0 }; const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", nullptr };
// only allow printable ASCII including space (32..126) // only allow printable ASCII including space (32..126)
@@ -62,7 +62,7 @@ bool isKeyValid(const ByteVector &key) {
} }
const String upperKey = String(key).upper(); const String upperKey = String(key).upper();
for (size_t i = 0; invalidKeys[i] != 0; ++i) { for (size_t i = 0; invalidKeys[i] != nullptr; ++i) {
if (upperKey == invalidKeys[i]) if (upperKey == invalidKeys[i])
return false; return false;
} }
@@ -73,7 +73,7 @@ bool isKeyValid(const ByteVector &key) {
class APE::Tag::TagPrivate { class APE::Tag::TagPrivate {
public: public:
TagPrivate() : file(0), footerLocation(0) {} TagPrivate() : file(nullptr), footerLocation(0) {}
File *file; File *file;
long footerLocation; long footerLocation;
@@ -207,6 +207,7 @@ const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions
} // namespace } // namespace
PropertyMap APE::Tag::properties() const { PropertyMap APE::Tag::properties() const {
PropertyMap properties; PropertyMap properties;
ItemListMap::ConstIterator it = itemListMap().begin(); ItemListMap::ConstIterator it = itemListMap().begin();
for (; it != itemListMap().end(); ++it) { for (; it != itemListMap().end(); ++it) {
@@ -226,15 +227,19 @@ PropertyMap APE::Tag::properties() const {
} }
} }
return properties; return properties;
} }
void APE::Tag::removeUnsupportedProperties(const StringList &properties) { void APE::Tag::removeUnsupportedProperties(const StringList &properties) {
StringList::ConstIterator it = properties.begin(); StringList::ConstIterator it = properties.begin();
for (; it != properties.end(); ++it) for (; it != properties.end(); ++it)
removeItem(*it); removeItem(*it);
} }
PropertyMap APE::Tag::setProperties(const PropertyMap &origProps) { PropertyMap APE::Tag::setProperties(const PropertyMap &origProps) {
PropertyMap properties(origProps); // make a local copy that can be modified PropertyMap properties(origProps); // make a local copy that can be modified
// see comment in properties() // see comment in properties()
@@ -280,10 +285,12 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps) {
} }
bool APE::Tag::checkKey(const String &key) { bool APE::Tag::checkKey(const String &key) {
if (key.size() < MinKeyLength || key.size() > MaxKeyLength) if (key.size() < MinKeyLength || key.size() > MaxKeyLength)
return false; return false;
return isKeyValid(key.data(String::UTF8)); return isKeyValid(key.data(String::UTF8));
} }
APE::Footer *APE::Tag::footer() const { APE::Footer *APE::Tag::footer() const {
@@ -299,6 +306,7 @@ void APE::Tag::removeItem(const String &key) {
} }
void APE::Tag::addValue(const String &key, const String &value, bool replace) { void APE::Tag::addValue(const String &key, const String &value, bool replace) {
if (replace) if (replace)
removeItem(key); removeItem(key);
@@ -314,24 +322,29 @@ void APE::Tag::addValue(const String &key, const String &value, bool replace) {
it->second.appendValue(value); it->second.appendValue(value);
else else
setItem(key, Item(key, value)); setItem(key, Item(key, value));
} }
void APE::Tag::setData(const String &key, const ByteVector &value) { void APE::Tag::setData(const String &key, const ByteVector &value) {
removeItem(key); removeItem(key);
if (value.isEmpty()) if (value.isEmpty())
return; return;
setItem(key, Item(key, value, true)); setItem(key, Item(key, value, true));
} }
void APE::Tag::setItem(const String &key, const Item &item) { void APE::Tag::setItem(const String &key, const Item &item) {
if (!checkKey(key)) { if (!checkKey(key)) {
debug("APE::Tag::setItem() - Couldn't set an item due to an invalid key."); debug("APE::Tag::setItem() - Couldn't set an item due to an invalid key.");
return; return;
} }
d->itemListMap[key.upper()] = item; d->itemListMap[key.upper()] = item;
} }
bool APE::Tag::isEmpty() const { bool APE::Tag::isEmpty() const {
@@ -343,6 +356,7 @@ bool APE::Tag::isEmpty() const {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void APE::Tag::read() { void APE::Tag::read() {
if (d->file && d->file->isValid()) { if (d->file && d->file->isValid()) {
d->file->seek(d->footerLocation); d->file->seek(d->footerLocation);
@@ -355,9 +369,11 @@ void APE::Tag::read() {
d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize()); d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize());
parse(d->file->readBlock(d->footer.tagSize() - Footer::size())); parse(d->file->readBlock(d->footer.tagSize() - Footer::size()));
} }
} }
ByteVector APE::Tag::render() const { ByteVector APE::Tag::render() const {
ByteVector data; ByteVector data;
unsigned int itemCount = 0; unsigned int itemCount = 0;
@@ -371,9 +387,11 @@ ByteVector APE::Tag::render() const {
d->footer.setHeaderPresent(true); d->footer.setHeaderPresent(true);
return d->footer.renderHeader() + data + d->footer.renderFooter(); return d->footer.renderHeader() + data + d->footer.renderFooter();
} }
void APE::Tag::parse(const ByteVector &data) { void APE::Tag::parse(const ByteVector &data) {
// 11 bytes is the minimum size for an APE item // 11 bytes is the minimum size for an APE item
if (data.size() < 11) if (data.size() < 11)
@@ -404,4 +422,5 @@ void APE::Tag::parse(const ByteVector &data) {
pos += keyLength + valLegnth + 9; pos += keyLength + valLegnth + 9;
} }
} }

View File

@@ -74,14 +74,12 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual ~Tag(); virtual ~Tag();
/*! /*!
* Renders the in memory values to a ByteVector suitable for writing to * Renders the in memory values to a ByteVector suitable for writing to the file.
* the file.
*/ */
ByteVector render() const; ByteVector render() const;
/*! /*!
* Returns the string "APETAGEX" suitable for usage in locating the tag in a * Returns the string "APETAGEX" suitable for usage in locating the tag in a file.
* file.
*/ */
static ByteVector fileIdentifier(); static ByteVector fileIdentifier();
@@ -106,24 +104,23 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
/*! /*!
* Implements the unified tag dictionary interface -- export function. * Implements the unified tag dictionary interface -- export function.
* APE tags are perfectly compatible with the dictionary interface because they * APE tags are perfectly compatible with the dictionary interface because they
* support both arbitrary tag names and multiple values. Currently only * support both arbitrary tag names and multiple values.
* APE items of type *Text* are handled by the dictionary interface; all *Binary* * Currently only APE items of type *Text* are handled by the dictionary interface; all *Binary*
* and *Locator* items will be put into the unsupportedData list and can be * and *Locator* items will be put into the unsupportedData list and can be
* deleted on request using removeUnsupportedProperties(). The same happens * deleted on request using removeUnsupportedProperties().
* to Text items if their key is invalid for PropertyMap (which should actually * The same happens to Text items if their key is invalid for PropertyMap (which should actually never happen).
* never happen).
* *
* The only conversion done by this export function is to rename the APE tags * The only conversion done by this export function is to rename the APE tags
* TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, respectively, * TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST,
* in order to be compliant with the names used in other formats. * respectively, in order to be compliant with the names used in other formats.
*/ */
PropertyMap properties() const; PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties); void removeUnsupportedProperties(const StringList &properties);
/*! /*!
* Implements the unified tag dictionary interface -- import function. The same * Implements the unified tag dictionary interface -- import function.
* comments as for the export function apply; additionally note that the APE tag * The same comments as for the export function apply; additionally note that the APE tag
* specification requires keys to have between 2 and 16 printable ASCII characters * specification requires keys to have between 2 and 16 printable ASCII characters
* with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+". * with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+".
*/ */
@@ -140,8 +137,8 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
Footer *footer() const; Footer *footer() const;
/*! /*!
* Returns a reference to the item list map. This is an ItemListMap of * Returns a reference to the item list map.
* all of the items in the tag. * This is an ItemListMap of all of the items in the tag.
* *
* This is the most powerful structure for accessing the items of the tag. * This is the most powerful structure for accessing the items of the tag.
* *
@@ -159,22 +156,20 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
void removeItem(const String &key); void removeItem(const String &key);
/*! /*!
* Adds to the text item specified by \a key the data \a value. If \a replace * Adds to the text item specified by \a key the data \a value.
* is true, then all of the other values on the same key will be removed * If \a replace is true, then all of the other values on the same key will be removed first.
* first. If a binary item exists for \a key it will be removed first. * If a binary item exists for \a key it will be removed first.
*/ */
void addValue(const String &key, const String &value, bool replace = true); void addValue(const String &key, const String &value, bool replace = true);
/*! /*!
* Set the binary data for the key specified by \a item to \a value * Set the binary data for the key specified by \a item to \a value
* This will convert the item to type \a Binary if it isn't already and * This will convert the item to type \a Binary if it isn't already and all of the other values on the same key will be removed.
* all of the other values on the same key will be removed.
*/ */
void setData(const String &key, const ByteVector &value); void setData(const String &key, const ByteVector &value);
/*! /*!
* Sets the \a key item to the value of \a item. If an item with the \a key is already * Sets the \a key item to the value of \a item. If an item with the \a key is already present, it will be replaced.
* present, it will be replaced.
*/ */
void setItem(const String &key, const Item &item); void setItem(const String &key, const Item &item);

View File

@@ -35,10 +35,7 @@ using namespace Strawberry_TagLib::TagLib;
class ASF::Attribute::AttributePrivate : public RefCounter { class ASF::Attribute::AttributePrivate : public RefCounter {
public: public:
AttributePrivate() : pictureValue(ASF::Picture::fromInvalid()), AttributePrivate() : pictureValue(ASF::Picture::fromInvalid()), numericValue(0), stream(0), language(0) {}
numericValue(0),
stream(0),
language(0) {}
AttributeTypes type; AttributeTypes type;
String stringValue; String stringValue;
ByteVector byteVectorValue; ByteVector byteVectorValue;
@@ -146,6 +143,7 @@ ASF::Picture ASF::Attribute::toPicture() const {
} }
String ASF::Attribute::parse(ASF::File &f, int kind) { String ASF::Attribute::parse(ASF::File &f, int kind) {
unsigned int size, nameLength; unsigned int size, nameLength;
String name; String name;
d->pictureValue = Picture::fromInvalid(); d->pictureValue = Picture::fromInvalid();
@@ -214,9 +212,11 @@ String ASF::Attribute::parse(ASF::File &f, int kind) {
} }
return name; return name;
} }
int ASF::Attribute::dataSize() const { int ASF::Attribute::dataSize() const {
switch (d->type) { switch (d->type) {
case WordType: case WordType:
return 2; return 2;
@@ -236,9 +236,11 @@ int ASF::Attribute::dataSize() const {
return d->byteVectorValue.size(); return d->byteVectorValue.size();
} }
return 0; return 0;
} }
ByteVector ASF::Attribute::render(const String &name, int kind) const { ByteVector ASF::Attribute::render(const String &name, int kind) const {
ByteVector data; ByteVector data;
switch (d->type) { switch (d->type) {
@@ -296,6 +298,7 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const {
} }
return data; return data;
} }
int ASF::Attribute::language() const { int ASF::Attribute::language() const {

View File

@@ -33,7 +33,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ASF { namespace ASF {
class File; class File;
@@ -199,7 +198,6 @@ class TAGLIB_EXPORT Attribute {
AttributePrivate *d; AttributePrivate *d;
}; };
} // namespace ASF } // namespace ASF
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -50,13 +50,13 @@ class ASF::File::FilePrivate {
class MetadataLibraryObject; class MetadataLibraryObject;
FilePrivate() : headerSize(0), FilePrivate() : headerSize(0),
tag(0), tag(nullptr),
properties(0), properties(nullptr),
contentDescriptionObject(0), contentDescriptionObject(nullptr),
extendedContentDescriptionObject(0), extendedContentDescriptionObject(nullptr),
headerExtensionObject(0), headerExtensionObject(nullptr),
metadataObject(0), metadataObject(nullptr),
metadataLibraryObject(0) { metadataLibraryObject(nullptr) {
objects.setAutoDelete(true); objects.setAutoDelete(true);
} }
@@ -177,19 +177,20 @@ class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::B
}; };
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size) { void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size) {
data.clear(); data.clear();
if (size > 24 && size <= (unsigned int)(file->length())) if (size > 24 && size <= (unsigned int)(file->length()))
data = file->readBlock(size - 24); data = file->readBlock(size - 24);
else else
data = ByteVector(); data = ByteVector();
} }
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/) { ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/) {
return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data; return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
} }
ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid) { ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid) {}
}
ByteVector ASF::File::FilePrivate::UnknownObject::guid() const { ByteVector ASF::File::FilePrivate::UnknownObject::guid() const {
return myGuid; return myGuid;
@@ -200,6 +201,7 @@ ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const {
} }
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size) { void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size) {
BaseObject::parse(file, size); BaseObject::parse(file, size);
if (data.size() < 64) { if (data.size() < 64) {
debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short."); debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short.");
@@ -209,6 +211,7 @@ void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsign
const long long duration = data.toLongLong(40, false); const long long duration = data.toLongLong(40, false);
const long long preroll = data.toLongLong(56, false); const long long preroll = data.toLongLong(56, false);
file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5)); file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
} }
ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const { ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const {
@@ -216,6 +219,7 @@ ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const {
} }
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size) { void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size) {
BaseObject::parse(file, size); BaseObject::parse(file, size);
if (data.size() < 70) { if (data.size() < 70) {
debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short."); debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short.");
@@ -227,6 +231,7 @@ void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsi
file->d->properties->setSampleRate(data.toUInt(58, false)); file->d->properties->setSampleRate(data.toUInt(58, false));
file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5)); file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
file->d->properties->setBitsPerSample(data.toUShort(68, false)); file->d->properties->setBitsPerSample(data.toUShort(68, false));
} }
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const { ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const {
@@ -234,6 +239,7 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const {
} }
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/) { void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/) {
const int titleLength = readWORD(file); const int titleLength = readWORD(file);
const int artistLength = readWORD(file); const int artistLength = readWORD(file);
const int copyrightLength = readWORD(file); const int copyrightLength = readWORD(file);
@@ -244,9 +250,11 @@ void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, un
file->d->tag->setCopyright(readString(file, copyrightLength)); file->d->tag->setCopyright(readString(file, copyrightLength));
file->d->tag->setComment(readString(file, commentLength)); file->d->tag->setComment(readString(file, commentLength));
file->d->tag->setRating(readString(file, ratingLength)); file->d->tag->setRating(readString(file, ratingLength));
} }
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file) { ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file) {
const ByteVector v1 = renderString(file->d->tag->title()); const ByteVector v1 = renderString(file->d->tag->title());
const ByteVector v2 = renderString(file->d->tag->artist()); const ByteVector v2 = renderString(file->d->tag->artist());
const ByteVector v3 = renderString(file->d->tag->copyright()); const ByteVector v3 = renderString(file->d->tag->copyright());
@@ -264,6 +272,7 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *f
data.append(v4); data.append(v4);
data.append(v5); data.append(v5);
return BaseObject::render(file); return BaseObject::render(file);
} }
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const { ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const {
@@ -271,19 +280,23 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() cons
} }
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/) { void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/) {
int count = readWORD(file); int count = readWORD(file);
while (count--) { while (count--) {
ASF::Attribute attribute; ASF::Attribute attribute;
String name = attribute.parse(*file); String name = attribute.parse(*file);
file->d->tag->addAttribute(name, attribute); file->d->tag->addAttribute(name, attribute);
} }
} }
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file) { ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file) {
data.clear(); data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false)); data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector("")); data.append(attributeData.toByteVector(""));
return BaseObject::render(file); return BaseObject::render(file);
} }
ByteVector ASF::File::FilePrivate::MetadataObject::guid() const { ByteVector ASF::File::FilePrivate::MetadataObject::guid() const {
@@ -291,19 +304,23 @@ ByteVector ASF::File::FilePrivate::MetadataObject::guid() const {
} }
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/) { void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/) {
int count = readWORD(file); int count = readWORD(file);
while (count--) { while (count--) {
ASF::Attribute attribute; ASF::Attribute attribute;
String name = attribute.parse(*file, 1); String name = attribute.parse(*file, 1);
file->d->tag->addAttribute(name, attribute); file->d->tag->addAttribute(name, attribute);
} }
} }
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file) { ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file) {
data.clear(); data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false)); data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector("")); data.append(attributeData.toByteVector(""));
return BaseObject::render(file); return BaseObject::render(file);
} }
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const { ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const {
@@ -311,19 +328,23 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const {
} }
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/) { void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/) {
int count = readWORD(file); int count = readWORD(file);
while (count--) { while (count--) {
ASF::Attribute attribute; ASF::Attribute attribute;
String name = attribute.parse(*file, 2); String name = attribute.parse(*file, 2);
file->d->tag->addAttribute(name, attribute); file->d->tag->addAttribute(name, attribute);
} }
} }
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file) { ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file) {
data.clear(); data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false)); data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector("")); data.append(attributeData.toByteVector(""));
return BaseObject::render(file); return BaseObject::render(file);
} }
ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject() { ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject() {
@@ -335,6 +356,7 @@ ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const {
} }
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/) { void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/) {
file->seek(18, File::Current); file->seek(18, File::Current);
long long dataSize = readDWORD(file); long long dataSize = readDWORD(file);
long long dataPos = 0; long long dataPos = 0;
@@ -366,15 +388,18 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
objects.append(obj); objects.append(obj);
dataPos += size; dataPos += size;
} }
} }
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file) { ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file) {
data.clear(); data.clear();
for (List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) { for (List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
data.append((*it)->render(file)); data.append((*it)->render(file));
} }
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data; data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
return BaseObject::render(file); return BaseObject::render(file);
} }
ByteVector ASF::File::FilePrivate::CodecListObject::guid() const { ByteVector ASF::File::FilePrivate::CodecListObject::guid() const {
@@ -382,6 +407,7 @@ ByteVector ASF::File::FilePrivate::CodecListObject::guid() const {
} }
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size) { void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size) {
BaseObject::parse(file, size); BaseObject::parse(file, size);
if (data.size() <= 20) { if (data.size() <= 20) {
debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short."); debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short.");
@@ -428,6 +454,7 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
break; break;
} }
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -435,24 +462,24 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool ASF::File::isSupported(IOStream *stream) { bool ASF::File::isSupported(IOStream *stream) {
// An ASF file has to start with the designated GUID. // An ASF file has to start with the designated GUID.
const ByteVector id = Utils::readHeader(stream, 16, false); const ByteVector id = Utils::readHeader(stream, 16, false);
return (id == headerGuid); return (id == headerGuid);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ASF::File::File(FileName file, bool, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), ASF::File::File(FileName file, bool, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) {
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(); read();
} }
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) {
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(); read();
} }
@@ -482,6 +509,7 @@ ASF::Properties *ASF::File::audioProperties() const {
} }
bool ASF::File::save() { bool ASF::File::save() {
if (readOnly()) { if (readOnly()) {
debug("ASF::File::save() -- File is read only."); debug("ASF::File::save() -- File is read only.");
return false; return false;
@@ -562,6 +590,7 @@ bool ASF::File::save() {
d->headerSize = data.size() + 30; d->headerSize = data.size() + 30;
return true; return true;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -569,6 +598,7 @@ bool ASF::File::save() {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ASF::File::read() { void ASF::File::read() {
if (!isValid()) if (!isValid())
return; return;
@@ -594,8 +624,8 @@ void ASF::File::read() {
} }
seek(2, Current); seek(2, Current);
FilePrivate::FilePropertiesObject *filePropertiesObject = 0; FilePrivate::FilePropertiesObject *filePropertiesObject = nullptr;
FilePrivate::StreamPropertiesObject *streamPropertiesObject = 0; FilePrivate::StreamPropertiesObject *streamPropertiesObject = nullptr;
for (int i = 0; i < numObjects; i++) { for (int i = 0; i < numObjects; i++) {
const ByteVector guid = readBlock(16); const ByteVector guid = readBlock(16);
if (guid.size() != 16) { if (guid.size() != 16) {
@@ -648,4 +678,5 @@ void ASF::File::read() {
setValid(false); setValid(false);
return; return;
} }
} }

View File

@@ -66,8 +66,7 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
* \note TagLib will *not* take ownership of the stream, the caller is * \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object. * responsible for deleting it after the File object.
*/ */
File(IOStream *stream, bool readProperties = true, File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
@@ -92,8 +91,7 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
PropertyMap properties() const; PropertyMap properties() const;
/*! /*!
* Removes unsupported properties. Forwards to the actual Tag's * Removes unsupported properties. Forwards to the actual Tag's removeUnsupportedProperties() function.
* removeUnsupportedProperties() function.
*/ */
void removeUnsupportedProperties(const StringList &properties); void removeUnsupportedProperties(const StringList &properties);
@@ -115,11 +113,9 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
virtual bool save(); virtual bool save();
/*! /*!
* Returns whether or not the given \a stream can be opened as an ASF * Returns whether or not the given \a stream can be opened as an ASF file.
* file.
* *
* \note This method is designed to do a quick check. The result may * \note This method is designed to do a quick check. The result may not necessarily be correct.
* not necessarily be correct.
*/ */
static bool isSupported(IOStream *stream); static bool isSupported(IOStream *stream);

View File

@@ -76,7 +76,7 @@ ASF::Picture::Type ASF::Picture::type() const {
return d->type; return d->type;
} }
void ASF::Picture::setType(const ASF::Picture::Type& t) { void ASF::Picture::setType(const ASF::Picture::Type &t) {
d->type = t; d->type = t;
} }
@@ -92,7 +92,7 @@ ByteVector ASF::Picture::picture() const {
return d->picture; return d->picture;
} }
void ASF::Picture::setPicture(const ByteVector& p) { void ASF::Picture::setPicture(const ByteVector &p) {
d->picture = p; d->picture = p;
} }
@@ -113,6 +113,7 @@ void ASF::Picture::swap(Picture& other) {
} }
ByteVector ASF::Picture::render() const { ByteVector ASF::Picture::render() const {
if (!isValid()) if (!isValid())
return ByteVector(); return ByteVector();
@@ -121,9 +122,11 @@ ByteVector ASF::Picture::render() const {
renderString(d->mimeType) + renderString(d->mimeType) +
renderString(d->description) + renderString(d->description) +
d->picture; d->picture;
} }
void ASF::Picture::parse(const ByteVector& bytes) { void ASF::Picture::parse(const ByteVector &bytes) {
d->valid = false; d->valid = false;
if (bytes.size() < 9) if (bytes.size() < 9)
return; return;
@@ -152,11 +155,13 @@ void ASF::Picture::parse(const ByteVector& bytes) {
d->picture = bytes.mid(pos, dataLen); d->picture = bytes.mid(pos, dataLen);
d->valid = true; d->valid = true;
return;
} }
ASF::Picture ASF::Picture::fromInvalid() { ASF::Picture ASF::Picture::fromInvalid() {
Picture ret; Picture ret;
ret.d->valid = false; ret.d->valid = false;
return ret; return ret;
} }

View File

@@ -38,10 +38,9 @@ namespace ASF {
//! An ASF attached picture interface implementation //! An ASF attached picture interface implementation
/*! /*!
* This is an implementation of ASF attached pictures interface. Pictures may be * This is an implementation of ASF attached pictures interface.
* included in attributes, one per WM/Picture attribute (but there may be multiple WM/Picture * Pictures may be included in attributes, one per WM/Picture attribute (but there may be multiple WM/Picture attribute in a single tag).
* attribute in a single tag). These pictures are usually in either JPEG or * These pictures are usually in either JPEG or PNG format.
* PNG format.
* \see Attribute::toPicture() * \see Attribute::toPicture()
* \see Attribute::Attribute(const Picture& picture) * \see Attribute::Attribute(const Picture& picture)
*/ */
@@ -126,8 +125,7 @@ class TAGLIB_EXPORT Picture {
bool isValid() const; bool isValid() const;
/*! /*!
* Returns the mime type of the image. This should in most cases be * Returns the mime type of the image. This should in most cases be "image/png" or "image/jpeg".
* "image/png" or "image/jpeg".
* \see setMimeType(const String &) * \see setMimeType(const String &)
* \see picture() * \see picture()
* \see setPicture(const ByteArray&) * \see setPicture(const ByteArray&)
@@ -135,8 +133,7 @@ class TAGLIB_EXPORT Picture {
String mimeType() const; String mimeType() const;
/*! /*!
* Sets the mime type of the image. This should in most cases be * Sets the mime type of the image. This should in most cases be "image/png" or "image/jpeg".
* "image/png" or "image/jpeg".
* \see setMimeType(const String &) * \see setMimeType(const String &)
* \see picture() * \see picture()
* \see setPicture(const ByteArray&) * \see setPicture(const ByteArray&)
@@ -176,8 +173,7 @@ class TAGLIB_EXPORT Picture {
/*! /*!
* Returns the image data as a ByteVector. * Returns the image data as a ByteVector.
* *
* \note ByteVector has a data() method that returns a const char * which * \note ByteVector has a data() method that returns a const char * which should make it easy to export this data to external programs.
* should make it easy to export this data to external programs.
* *
* \see setPicture() * \see setPicture()
* \see mimeType() * \see mimeType()
@@ -185,8 +181,8 @@ class TAGLIB_EXPORT Picture {
ByteVector picture() const; ByteVector picture() const;
/*! /*!
* Sets the image data to \a p. \a p should be of the type specified in * Sets the image data to \a p.
* this frame's mime-type specification. * \a p should be of the type specified in this frame's mime-type specification.
* *
* \see picture() * \see picture()
* \see mimeType() * \see mimeType()

View File

@@ -62,10 +62,6 @@ ASF::Properties::~Properties() {
delete d; delete d;
} }
int ASF::Properties::length() const {
return lengthInSeconds();
}
int ASF::Properties::lengthInSeconds() const { int ASF::Properties::lengthInSeconds() const {
return d->length / 1000; return d->length / 1000;
} }
@@ -135,6 +131,7 @@ void ASF::Properties::setBitsPerSample(int value) {
} }
void ASF::Properties::setCodec(int value) { void ASF::Properties::setCodec(int value) {
switch (value) { switch (value) {
case 0x0160: case 0x0160:
d->codec = WMA1; d->codec = WMA1;
@@ -152,6 +149,7 @@ void ASF::Properties::setCodec(int value) {
d->codec = Unknown; d->codec = Unknown;
break; break;
} }
} }
void ASF::Properties::setCodecName(const String &value) { void ASF::Properties::setCodecName(const String &value) {

View File

@@ -32,7 +32,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ASF { namespace ASF {
//! An implementation of ASF audio properties //! An implementation of ASF audio properties
@@ -78,16 +77,6 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
*/ */
virtual ~Properties(); virtual ~Properties();
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
virtual int length() const;
/*! /*!
* Returns the length of the file in seconds. The length is rounded down to * Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second. * the nearest whole second.
@@ -134,8 +123,7 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
Codec codec() const; Codec codec() const;
/*! /*!
* Returns the concrete codec name, for example "Windows Media Audio 9.1" * Returns the concrete codec name, for example "Windows Media Audio 9.1" used in the file if available, otherwise an empty string.
* used in the file if available, otherwise an empty string.
* *
* \see codec() * \see codec()
* \see codecDescription() * \see codecDescription()
@@ -144,8 +132,7 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
/*! /*!
* Returns the codec description, typically contains the encoder settings, * Returns the codec description, typically contains the encoder settings,
* for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available, * for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available, otherwise an empty string.
* otherwise an empty string.
* *
* \see codec() * \see codec()
* \see codecName() * \see codecName()

View File

@@ -38,8 +38,7 @@ class ASF::Tag::TagPrivate {
AttributeListMap attributeListMap; AttributeListMap attributeListMap;
}; };
ASF::Tag::Tag() : Strawberry_TagLib::TagLib::Tag(), ASF::Tag::Tag() : Strawberry_TagLib::TagLib::Tag(), d(new TagPrivate()) {
d(new TagPrivate()) {
} }
ASF::Tag::~Tag() { ASF::Tag::~Tag() {
@@ -55,9 +54,11 @@ String ASF::Tag::artist() const {
} }
String ASF::Tag::album() const { String ASF::Tag::album() const {
if (d->attributeListMap.contains("WM/AlbumTitle")) if (d->attributeListMap.contains("WM/AlbumTitle"))
return d->attributeListMap["WM/AlbumTitle"][0].toString(); return d->attributeListMap["WM/AlbumTitle"][0].toString();
return String(); return String();
} }
String ASF::Tag::copyright() const { String ASF::Tag::copyright() const {
@@ -73,12 +74,15 @@ String ASF::Tag::rating() const {
} }
unsigned int ASF::Tag::year() const { unsigned int ASF::Tag::year() const {
if (d->attributeListMap.contains("WM/Year")) if (d->attributeListMap.contains("WM/Year"))
return d->attributeListMap["WM/Year"][0].toString().toInt(); return d->attributeListMap["WM/Year"][0].toString().toInt();
return 0; return 0;
} }
unsigned int ASF::Tag::track() const { unsigned int ASF::Tag::track() const {
if (d->attributeListMap.contains("WM/TrackNumber")) { if (d->attributeListMap.contains("WM/TrackNumber")) {
const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0]; const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0];
if (attr.type() == ASF::Attribute::DWordType) if (attr.type() == ASF::Attribute::DWordType)
@@ -89,12 +93,15 @@ unsigned int ASF::Tag::track() const {
if (d->attributeListMap.contains("WM/Track")) if (d->attributeListMap.contains("WM/Track"))
return d->attributeListMap["WM/Track"][0].toUInt(); return d->attributeListMap["WM/Track"][0].toUInt();
return 0; return 0;
} }
String ASF::Tag::genre() const { String ASF::Tag::genre() const {
if (d->attributeListMap.contains("WM/Genre")) if (d->attributeListMap.contains("WM/Genre"))
return d->attributeListMap["WM/Genre"][0].toString(); return d->attributeListMap["WM/Genre"][0].toString();
return String(); return String();
} }
void ASF::Tag::setTitle(const String &value) { void ASF::Tag::setTitle(const String &value) {
@@ -133,11 +140,7 @@ void ASF::Tag::setTrack(unsigned int value) {
setAttribute("WM/TrackNumber", String::number(value)); setAttribute("WM/TrackNumber", String::number(value));
} }
ASF::AttributeListMap &ASF::Tag::attributeListMap() { const ASF::AttributeListMap ASF::Tag::attributeListMap() const {
return d->attributeListMap;
}
const ASF::AttributeListMap &ASF::Tag::attributeListMap() const {
return d->attributeListMap; return d->attributeListMap;
} }
@@ -154,9 +157,11 @@ ASF::AttributeList ASF::Tag::attribute(const String &name) const {
} }
void ASF::Tag::setAttribute(const String &name, const Attribute &attribute) { void ASF::Tag::setAttribute(const String &name, const Attribute &attribute) {
AttributeList value; AttributeList value;
value.append(attribute); value.append(attribute);
d->attributeListMap.insert(name, value); d->attributeListMap.insert(name, value);
} }
void ASF::Tag::setAttribute(const String &name, const AttributeList &values) { void ASF::Tag::setAttribute(const String &name, const AttributeList &values) {
@@ -164,19 +169,23 @@ void ASF::Tag::setAttribute(const String &name, const AttributeList &values) {
} }
void ASF::Tag::addAttribute(const String &name, const Attribute &attribute) { void ASF::Tag::addAttribute(const String &name, const Attribute &attribute) {
if (d->attributeListMap.contains(name)) { if (d->attributeListMap.contains(name)) {
d->attributeListMap[name].append(attribute); d->attributeListMap[name].append(attribute);
} }
else { else {
setAttribute(name, attribute); setAttribute(name, attribute);
} }
} }
bool ASF::Tag::isEmpty() const { bool ASF::Tag::isEmpty() const {
return Strawberry_TagLib::TagLib::Tag::isEmpty() && return Strawberry_TagLib::TagLib::Tag::isEmpty() &&
copyright().isEmpty() && copyright().isEmpty() &&
rating().isEmpty() && rating().isEmpty() &&
d->attributeListMap.isEmpty(); d->attributeListMap.isEmpty();
} }
namespace { namespace {
@@ -224,16 +233,19 @@ const char *keyTranslation[][2] = {
const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]); const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
String translateKey(const String &key) { String translateKey(const String &key) {
for (size_t i = 0; i < keyTranslationSize; ++i) { for (size_t i = 0; i < keyTranslationSize; ++i) {
if (key == keyTranslation[i][0]) if (key == keyTranslation[i][0])
return keyTranslation[i][1]; return keyTranslation[i][1];
} }
return String(); return String();
} }
} // namespace } // namespace
PropertyMap ASF::Tag::properties() const { PropertyMap ASF::Tag::properties() const {
PropertyMap props; PropertyMap props;
if (!d->title.isEmpty()) { if (!d->title.isEmpty()) {
@@ -271,15 +283,19 @@ PropertyMap ASF::Tag::properties() const {
} }
} }
return props; return props;
} }
void ASF::Tag::removeUnsupportedProperties(const StringList &props) { void ASF::Tag::removeUnsupportedProperties(const StringList &props) {
StringList::ConstIterator it = props.begin(); StringList::ConstIterator it = props.begin();
for (; it != props.end(); ++it) for (; it != props.end(); ++it)
d->attributeListMap.erase(*it); d->attributeListMap.erase(*it);
} }
PropertyMap ASF::Tag::setProperties(const PropertyMap &props) { PropertyMap ASF::Tag::setProperties(const PropertyMap &props) {
static Map<String, String> reverseKeyMap; static Map<String, String> reverseKeyMap;
if (reverseKeyMap.isEmpty()) { if (reverseKeyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
@@ -339,4 +355,5 @@ PropertyMap ASF::Tag::setProperties(const PropertyMap &props) {
} }
return ignoredProps; return ignoredProps;
} }

View File

@@ -60,8 +60,7 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual String artist() const; virtual String artist() const;
/*! /*!
* Returns the album name; if no album name is present in the tag * Returns the album name; if no album name is present in the tag String::null will be returned.
* String::null will be returned.
*/ */
virtual String album() const; virtual String album() const;
@@ -71,8 +70,7 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual String comment() const; virtual String comment() const;
/*! /*!
* Returns the genre name; if no genre is present in the tag String::null * Returns the genre name; if no genre is present in the tag String::null will be returned.
* will be returned.
*/ */
virtual String genre() const; virtual String genre() const;
@@ -82,8 +80,7 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual String rating() const; virtual String rating() const;
/*! /*!
* Returns the genre name; if no genre is present in the tag String::null * Returns the genre name; if no genre is present in the tag String::null will be returned.
* will be returned.
*/ */
virtual String copyright() const; virtual String copyright() const;
@@ -93,8 +90,7 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual unsigned int year() const; virtual unsigned int year() const;
/*! /*!
* Returns the track number; if there is no track number set, this will * Returns the track number; if there is no track number set, this will return 0.
* return 0.
*/ */
virtual unsigned int track() const; virtual unsigned int track() const;
@@ -109,8 +105,7 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual void setArtist(const String &s); virtual void setArtist(const String &s);
/*! /*!
* Sets the album to \a s. If \a s is String::null then this value will be * Sets the album to \a s. If \a s is String::null then this value will be cleared.
* cleared.
*/ */
virtual void setAlbum(const String &s); virtual void setAlbum(const String &s);
@@ -145,23 +140,15 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual void setTrack(unsigned int i); virtual void setTrack(unsigned int i);
/*! /*!
* Returns true if the tag does not contain any data. This should be * Returns true if the tag does not contain any data.
* reimplemented in subclasses that provide more than the basic tagging * This should be reimplemented in subclasses that provide more than the basic tagging abilities in this class.
* abilities in this class.
*/ */
virtual bool isEmpty() const; virtual bool isEmpty() const;
/*! /*!
* \deprecated * Returns a reference to the item list map. This is an AttributeListMap of all of the items in the tag.
*/ */
AttributeListMap &attributeListMap(); const AttributeListMap attributeListMap() const;
/*!
* Returns a reference to the item list map. This is an AttributeListMap of
* all of the items in the tag.
*/
// BIC: return by value
const AttributeListMap &attributeListMap() const;
/*! /*!
* \return True if a value for \a attribute is currently set. * \return True if a value for \a attribute is currently set.
@@ -174,14 +161,12 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
void removeItem(const String &name); void removeItem(const String &name);
/*! /*!
* \return The list of values for the key \a name, or an empty list if no * \return The list of values for the key \a name, or an empty list if no values have been set.
* values have been set.
*/ */
AttributeList attribute(const String &name) const; AttributeList attribute(const String &name) const;
/*! /*!
* Sets the \a key attribute to the value of \a attribute. If an attribute * Sets the \a key attribute to the value of \a attribute. If an attribute with the \a key is already present, it will be replaced.
* with the \a key is already present, it will be replaced.
*/ */
void setAttribute(const String &name, const Attribute &attribute); void setAttribute(const String &name, const Attribute &attribute);

View File

@@ -89,8 +89,7 @@ class AudioProperties::AudioPropertiesPrivate {
// public methods // public methods
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AudioProperties::~AudioProperties() { AudioProperties::~AudioProperties() {}
}
int AudioProperties::lengthInSeconds() const { int AudioProperties::lengthInSeconds() const {
VIRTUAL_FUNCTION_WORKAROUND(lengthInSeconds, 0) VIRTUAL_FUNCTION_WORKAROUND(lengthInSeconds, 0)
@@ -104,5 +103,4 @@ int AudioProperties::lengthInMilliseconds() const {
// protected methods // protected methods
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AudioProperties::AudioProperties(ReadStyle) : d(0) { AudioProperties::AudioProperties(ReadStyle) : d(nullptr) {}
}

View File

@@ -34,9 +34,9 @@ namespace TagLib {
//! A simple, abstract interface to common audio properties //! A simple, abstract interface to common audio properties
/*! /*!
* The values here are common to most audio formats. For more specific, codec * The values here are common to most audio formats.
* dependent values, please see see the subclasses APIs. This is meant to * For more specific, codec dependent values, please see see the subclasses APIs.
* compliment the TagLib::File and TagLib::Tag APIs in providing a simple * This is meant to compliment the TagLib::File and TagLib::Tag APIs in providing a simple
* interface that is sufficient for most applications. * interface that is sufficient for most applications.
*/ */
@@ -44,10 +44,9 @@ class TAGLIB_EXPORT AudioProperties {
public: public:
/*! /*!
* Reading audio properties from a file can sometimes be very time consuming * Reading audio properties from a file can sometimes be very time consuming
* and for the most accurate results can often involve reading the entire * and for the most accurate results can often involve reading the entire file.
* file. Because in many situations speed is critical or the accuracy of the * Because in many situations speed is critical or the accuracy of the values
* values is not particularly important this allows the level of desired * is not particularly important this allows the level of desired accuracy to be set.
* accuracy to be set.
*/ */
enum ReadStyle { enum ReadStyle {
//! Read as little of the file as possible //! Read as little of the file as possible
@@ -66,11 +65,10 @@ class TAGLIB_EXPORT AudioProperties {
/*! /*!
* Returns the length of the file in seconds. * Returns the length of the file in seconds.
*/ */
virtual int length() const = 0; //virtual int length() const = 0;
/*! /*!
* Returns the length of the file in seconds. The length is rounded down to * Returns the length of the file in seconds. The length is rounded down to the nearest whole second.
* the nearest whole second.
* *
* \see lengthInMilliseconds() * \see lengthInMilliseconds()
*/ */
@@ -86,9 +84,8 @@ class TAGLIB_EXPORT AudioProperties {
int lengthInMilliseconds() const; int lengthInMilliseconds() const;
/*! /*!
* Returns the most appropriate bit rate for the file in kb/s. For constant * Returns the most appropriate bit rate for the file in kb/s. For constant bitrate formats this is simply the bitrate of the file.
* bitrate formats this is simply the bitrate of the file. For variable * For variable bitrate formats this is either the average or nominal bitrate.
* bitrate formats this is either the average or nominal bitrate.
*/ */
virtual int bitrate() const = 0; virtual int bitrate() const = 0;
@@ -104,9 +101,9 @@ class TAGLIB_EXPORT AudioProperties {
protected: protected:
/*! /*!
* Construct an audio properties instance. This is protected as this class * Construct an audio properties instance.
* should not be instantiated directly, but should be instantiated via its * This is protected as this class should not be instantiated directly,
* subclasses and can be fetched from the FileRef or File APIs. * but should be instantiated via its subclasses and can be fetched from the FileRef or File APIs.
* *
* \see ReadStyle * \see ReadStyle
*/ */

View File

@@ -76,42 +76,44 @@ unsigned int DSDIFF::DIIN::Tag::track() const {
} }
void DSDIFF::DIIN::Tag::setTitle(const String &title) { void DSDIFF::DIIN::Tag::setTitle(const String &title) {
if (title.isNull() || title.isEmpty()) if (title.isNull() || title.isEmpty())
d->title = String(); d->title = String();
else else
d->title = title; d->title = title;
} }
void DSDIFF::DIIN::Tag::setArtist(const String &artist) { void DSDIFF::DIIN::Tag::setArtist(const String &artist) {
if (artist.isNull() || artist.isEmpty()) if (artist.isNull() || artist.isEmpty())
d->artist = String(); d->artist = String();
else else
d->artist = artist; d->artist = artist;
} }
void DSDIFF::DIIN::Tag::setAlbum(const String &) { void DSDIFF::DIIN::Tag::setAlbum(const String &) {}
}
void DSDIFF::DIIN::Tag::setComment(const String &) { void DSDIFF::DIIN::Tag::setComment(const String &) {}
}
void DSDIFF::DIIN::Tag::setGenre(const String &) { void DSDIFF::DIIN::Tag::setGenre(const String &) {}
}
void DSDIFF::DIIN::Tag::setYear(unsigned int) { void DSDIFF::DIIN::Tag::setYear(unsigned int) {}
}
void DSDIFF::DIIN::Tag::setTrack(unsigned int) { void DSDIFF::DIIN::Tag::setTrack(unsigned int) {}
}
PropertyMap DSDIFF::DIIN::Tag::properties() const { PropertyMap DSDIFF::DIIN::Tag::properties() const {
PropertyMap properties; PropertyMap properties;
properties["TITLE"] = d->title; properties["TITLE"] = d->title;
properties["ARTIST"] = d->artist; properties["ARTIST"] = d->artist;
return properties; return properties;
} }
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps) { PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps) {
PropertyMap properties(origProps); PropertyMap properties(origProps);
properties.removeEmpty(); properties.removeEmpty();
StringList oneValueSet; StringList oneValueSet;
@@ -140,4 +142,5 @@ PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps) {
} }
return properties; return properties;
} }

View File

@@ -30,9 +30,7 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace DSDIFF { namespace DSDIFF {
namespace DIIN { namespace DIIN {
/*! /*!
@@ -40,20 +38,19 @@ namespace DIIN {
* *
* Only Title and Artist tags are supported * Only Title and Artist tags are supported
*/ */
class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
public: public:
Tag(); Tag();
virtual ~Tag(); virtual ~Tag();
/*! /*!
* Returns the track name; if no track name is present in the tag * Returns the track name; if no track name is present in the tag String() will be returned.
* String() will be returned.
*/ */
String title() const; String title() const;
/*! /*!
* Returns the artist name; if no artist name is present in the tag * Returns the artist name; if no artist name is present in the tag String() will be returned.
* String() will be returned.
*/ */
String artist() const; String artist() const;
@@ -83,14 +80,12 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
unsigned int track() const; unsigned int track() const;
/*! /*!
* Sets the title to \a title. If \a title is String() then this * Sets the title to \a title. If \a title is String() then this value will be cleared.
* value will be cleared.
*/ */
void setTitle(const String &title); void setTitle(const String &title);
/*! /*!
* Sets the artist to \a artist. If \a artist is String() then this * Sets the artist to \a artist. If \a artist is String() then this value will be cleared.
* value will be cleared.
*/ */
void setArtist(const String &artist); void setArtist(const String &artist);
@@ -128,10 +123,9 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
/*! /*!
* Implements the unified property interface -- import function. * Implements the unified property interface -- import function.
* Because of the limitations of the DIIN file tag, any tags besides * Because of the limitations of the DIIN file tag, any tags besides
* TITLE and ARTIST, will be * TITLE and ARTIST, will be returned.
* returned. Additionally, if the map contains tags with multiple values, * Additionally, if the map contains tags with multiple values,
* all but the first will be contained in the returned map of unsupported * all but the first will be contained in the returned map of unsupported properties.
* properties.
*/ */
PropertyMap setProperties(const PropertyMap &); PropertyMap setProperties(const PropertyMap &);

View File

@@ -23,6 +23,7 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#include <tstring.h>
#include <tbytevector.h> #include <tbytevector.h>
#include <tdebug.h> #include <tdebug.h>
#include <id3v2tag.h> #include <id3v2tag.h>
@@ -48,15 +49,18 @@ struct Chunk64 {
typedef std::vector<Chunk64> ChunkList; typedef std::vector<Chunk64> ChunkList;
int chunkIndex(const ChunkList &chunks, const ByteVector &id) { int chunkIndex(const ChunkList &chunks, const ByteVector &id) {
for (unsigned long int i = 0; i < chunks.size(); i++) { for (unsigned long int i = 0; i < chunks.size(); i++) {
if (chunks[i].name == id) if (chunks[i].name == id)
return i; return i;
} }
return -1; return -1;
} }
bool isValidChunkID(const ByteVector &name) { bool isValidChunkID(const ByteVector &name) {
if (name.size() != 4) if (name.size() != 4)
return false; return false;
@@ -66,6 +70,7 @@ bool isValidChunkID(const ByteVector &name) {
} }
return true; return true;
} }
enum { enum {
@@ -84,7 +89,7 @@ class DSDIFF::File::FilePrivate {
size(0), size(0),
isID3InPropChunk(false), isID3InPropChunk(false),
duplicateID3V2chunkIndex(-1), duplicateID3V2chunkIndex(-1),
properties(0), properties(nullptr),
id3v2TagChunkID("ID3 "), id3v2TagChunkID("ID3 "),
hasID3v2(false), hasID3v2(false),
hasDiin(false) { hasDiin(false) {
@@ -128,30 +133,34 @@ class DSDIFF::File::FilePrivate {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool DSDIFF::File::isSupported(IOStream *stream) { bool DSDIFF::File::isSupported(IOStream *stream) {
// A DSDIFF file has to start with "FRM8????????DSD ". // A DSDIFF file has to start with "FRM8????????DSD ".
const ByteVector id = Utils::readHeader(stream, 16, false); const ByteVector id = Utils::readHeader(stream, 16, false);
return (id.startsWith("FRM8") && id.containsAt("DSD ", 12)); return (id.startsWith("FRM8") && id.containsAt("DSD ", 12));
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
DSDIFF::File::File(FileName file, bool readProperties, DSDIFF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(file) {
Properties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(file) {
d = new FilePrivate; d = new FilePrivate;
d->endianness = BigEndian; d->endianness = BigEndian;
if (isOpen()) if (isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
DSDIFF::File::File(IOStream *stream, bool readProperties, DSDIFF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(stream) {
Properties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(stream) {
d = new FilePrivate; d = new FilePrivate;
d->endianness = BigEndian; d->endianness = BigEndian;
if (isOpen()) if (isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
DSDIFF::File::~File() { DSDIFF::File::~File() {
@@ -179,18 +188,22 @@ bool DSDIFF::File::hasDIINTag() const {
} }
PropertyMap DSDIFF::File::properties() const { PropertyMap DSDIFF::File::properties() const {
if (d->hasID3v2) if (d->hasID3v2)
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties(); return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
return PropertyMap(); return PropertyMap();
} }
void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported) { void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported) {
if (d->hasID3v2) if (d->hasID3v2)
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(unsupported); d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(unsupported);
if (d->hasDiin) if (d->hasDiin)
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->removeUnsupportedProperties(unsupported); d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->removeUnsupportedProperties(unsupported);
} }
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties) { PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties) {
@@ -206,6 +219,7 @@ bool DSDIFF::File::save() {
} }
bool DSDIFF::File::save(TagTypes tags, StripTags, ID3v2::Version version) { bool DSDIFF::File::save(TagTypes tags, StripTags, ID3v2::Version version) {
if (readOnly()) { if (readOnly()) {
debug("DSDIFF::File::save() -- File is read only."); debug("DSDIFF::File::save() -- File is read only.");
return false; return false;
@@ -279,9 +293,11 @@ bool DSDIFF::File::save(TagTypes tags, StripTags, ID3v2::Version version) {
} }
return true; return true;
} }
void DSDIFF::File::strip(TagTypes tags) { void DSDIFF::File::strip(TagTypes tags) {
if (tags & ID3v2) { if (tags & ID3v2) {
removeRootChunk("ID3 "); removeRootChunk("ID3 ");
removeRootChunk("id3 "); removeRootChunk("id3 ");
@@ -296,6 +312,7 @@ void DSDIFF::File::strip(TagTypes tags) {
d->hasDiin = false; d->hasDiin = false;
d->tag.set(DIINIndex, new DIIN::Tag); d->tag.set(DIINIndex, new DIIN::Tag);
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -303,6 +320,7 @@ void DSDIFF::File::strip(TagTypes tags) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void DSDIFF::File::removeRootChunk(unsigned int i) { void DSDIFF::File::removeRootChunk(unsigned int i) {
unsigned long long chunkSize = d->chunks[i].size + d->chunks[i].padding + 12; unsigned long long chunkSize = d->chunks[i].size + d->chunks[i].padding + 12;
d->size -= chunkSize; d->size -= chunkSize;
@@ -316,16 +334,20 @@ void DSDIFF::File::removeRootChunk(unsigned int i) {
d->chunks[r].offset = d->chunks[r - 1].offset + 12 + d->chunks[r - 1].size + d->chunks[r - 1].padding; d->chunks[r].offset = d->chunks[r - 1].offset + 12 + d->chunks[r - 1].size + d->chunks[r - 1].padding;
d->chunks.erase(d->chunks.begin() + i); d->chunks.erase(d->chunks.begin() + i);
} }
void DSDIFF::File::removeRootChunk(const ByteVector &id) { void DSDIFF::File::removeRootChunk(const ByteVector &id) {
int i = chunkIndex(d->chunks, id); int i = chunkIndex(d->chunks, id);
if (i >= 0) if (i >= 0)
removeRootChunk(i); removeRootChunk(i);
} }
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data) { void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data) {
if (data.isEmpty()) { if (data.isEmpty()) {
removeRootChunk(i); removeRootChunk(i);
return; return;
@@ -350,9 +372,11 @@ void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data) {
// Finally update the internal offsets // Finally update the internal offsets
updateRootChunksStructure(i + 1); updateRootChunksStructure(i + 1);
} }
void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data) { void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data) {
if (d->chunks.size() == 0) { if (d->chunks.size() == 0) {
debug("DSDIFF::File::setPropChunkData - No valid chunks found."); debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
return; return;
@@ -387,9 +411,11 @@ void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &da
chunk.padding = (data.size() & 0x01) ? 1 : 0; chunk.padding = (data.size() & 0x01) ? 1 : 0;
d->chunks.push_back(chunk); d->chunks.push_back(chunk);
} }
void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum) { void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum) {
ChunkList &childChunks = d->childChunks[childChunkNum]; ChunkList &childChunks = d->childChunks[childChunkNum];
// Update global size // Update global size
@@ -424,11 +450,11 @@ void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum)
d->chunks[i].offset = d->chunks[i - 1].offset + 12 + d->chunks[i - 1].size + d->chunks[i - 1].padding; d->chunks[i].offset = d->chunks[i - 1].offset + 12 + d->chunks[i - 1].size + d->chunks[i - 1].padding;
childChunks.erase(childChunks.begin() + i); childChunks.erase(childChunks.begin() + i);
} }
void DSDIFF::File::setChildChunkData(unsigned int i, void DSDIFF::File::setChildChunkData(unsigned int i, const ByteVector &data, unsigned int childChunkNum) {
const ByteVector &data,
unsigned int childChunkNum) {
ChunkList &childChunks = d->childChunks[childChunkNum]; ChunkList &childChunks = d->childChunks[childChunkNum];
if (data.isEmpty()) { if (data.isEmpty()) {
@@ -445,11 +471,8 @@ void DSDIFF::File::setChildChunkData(unsigned int i,
// And the PROP chunk size // And the PROP chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size += d->chunks[d->childChunkIndex[childChunkNum]].size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding); insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size, d->endianness == BigEndian), d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
d->endianness == BigEndian),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Now update the specific chunk // Now update the specific chunk
@@ -468,11 +491,11 @@ void DSDIFF::File::setChildChunkData(unsigned int i,
// And for root chunks // And for root chunks
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1); updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
} }
void DSDIFF::File::setChildChunkData(const ByteVector &name, void DSDIFF::File::setChildChunkData(const ByteVector &name, const ByteVector &data, unsigned int childChunkNum) {
const ByteVector &data,
unsigned int childChunkNum) {
ChunkList &childChunks = d->childChunks[childChunkNum]; ChunkList &childChunks = d->childChunks[childChunkNum];
if (childChunks.size() == 0) { if (childChunks.size() == 0) {
@@ -530,9 +553,11 @@ void DSDIFF::File::setChildChunkData(const ByteVector &name,
chunk.padding = (data.size() & 0x01) ? 1 : 0; chunk.padding = (data.size() & 0x01) ? 1 : 0;
childChunks.push_back(chunk); childChunks.push_back(chunk);
} }
void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk) { void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk) {
for (unsigned int i = startingChunk; i < d->chunks.size(); i++) for (unsigned int i = startingChunk; i < d->chunks.size(); i++)
d->chunks[i].offset = d->chunks[i - 1].offset + 12 + d->chunks[i - 1].size + d->chunks[i - 1].padding; d->chunks[i].offset = d->chunks[i - 1].offset + 12 + d->chunks[i - 1].size + d->chunks[i - 1].padding;
@@ -554,9 +579,11 @@ void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk) {
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12 + childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding; childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12 + childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
} }
} }
} }
void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) { void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) {
bool bigEndian = (d->endianness == BigEndian); bool bigEndian = (d->endianness == BigEndian);
d->type = readBlock(4); d->type = readBlock(4);
@@ -856,11 +883,11 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
d->isID3InPropChunk = false; d->isID3InPropChunk = false;
d->hasID3v2 = false; d->hasID3v2 = false;
} }
} }
void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data, void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data, unsigned long long offset, unsigned long replace, unsigned int leadingPadding) {
unsigned long long offset, unsigned long replace,
unsigned int leadingPadding) {
ByteVector combined; ByteVector combined;
if (leadingPadding) if (leadingPadding)
combined.append(ByteVector(leadingPadding, '\x00')); combined.append(ByteVector(leadingPadding, '\x00'));
@@ -872,4 +899,5 @@ void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
combined.append('\x00'); combined.append('\x00');
insert(combined, offset, replace); insert(combined, offset, replace);
} }

View File

@@ -41,10 +41,9 @@ namespace TagLib {
* *
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
* chunk as well as properties from the file. * chunk as well as properties from the file.
* Description of the DSDIFF format is available * Description of the DSDIFF format is available at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
* at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
* DSDIFF standard does not explicitly specify the ID3V2 chunk * DSDIFF standard does not explicitly specify the ID3V2 chunk
* It can be found at the root level, but also sometimes inside the PROP chunk * It can be found at the root level, but also sometimes inside the PROP chunk.
* In addition, title and artist info are stored as part of the standard * In addition, title and artist info are stored as part of the standard
*/ */
@@ -54,9 +53,8 @@ namespace DSDIFF {
/*! /*!
* This implements and provides an interface for DSDIFF files to the * This implements and provides an interface for DSDIFF files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing the
* the abstract TagLib::File API as well as providing some additional * abstract TagLib::File API as well as providing some additional information specific to DSDIFF files.
* information specific to DSDIFF files.
*/ */
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
@@ -77,20 +75,18 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
}; };
/*! /*!
* Constructs an DSDIFF file from \a file. If \a readProperties is true * Constructs an DSDIFF file from \a file.
* the file's audio properties will also be read. * If \a readProperties is true the file's audio properties will also be read.
* *
* \note In the current implementation, \a propertiesStyle is ignored. * \note In the current implementation, \a propertiesStyle is ignored.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Constructs an DSDIFF file from \a stream. If \a readProperties is true * Constructs an DSDIFF file from \a stream.
* the file's audio properties will also be read. * If \a readProperties is true the file's audio properties will also be read.
* *
* \note TagLib will *not* take ownership of the stream, the caller is * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object.
* responsible for deleting it after the File object.
* *
* \note In the current implementation, \a propertiesStyle is ignored. * \note In the current implementation, \a propertiesStyle is ignored.
*/ */
@@ -103,17 +99,14 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
virtual ~File(); virtual ~File();
/*! /*!
* Returns a pointer to a tag that is the union of the ID3v2 and DIIN * Returns a pointer to a tag that is the union of the ID3v2 and DIIN tags.
* tags. The ID3v2 tag is given priority in reading the information -- if * The ID3v2 tag is given priority in reading the information -- if requested information exists in both the ID3v2 tag and the ID3v1 tag,
* requested information exists in both the ID3v2 tag and the ID3v1 tag,
* the information from the ID3v2 tag will be returned. * the information from the ID3v2 tag will be returned.
* *
* If you would like more granular control over the content of the tags, * If you would like more granular control over the content of the tags, with the concession of generality, use the tag-type specific calls.
* with the concession of generality, use the tag-type specific calls.
* *
* \note As this tag is not implemented as an ID3v2 tag or a DIIN tag, * \note As this tag is not implemented as an ID3v2 tag or a DIIN tag,
* but a union of the two this pointer may not be cast to the specific * but a union of the two this pointer may not be cast to the specific tag types.
* tag types.
* *
* \see ID3v2Tag() * \see ID3v2Tag()
* \see DIINTag() * \see DIINTag()
@@ -123,9 +116,8 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
/*! /*!
* Returns the ID3V2 Tag for this file. * Returns the ID3V2 Tag for this file.
* *
* \note This always returns a valid pointer regardless of whether or not * \note This always returns a valid pointer regardless of whether or not the file on disk has an ID3v2 tag.
* the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the * Use hasID3v2Tag() to check if the file on disk actually has an ID3v2 tag.
* file on disk actually has an ID3v2 tag.
* *
* \see hasID3v2Tag() * \see hasID3v2Tag()
*/ */
@@ -152,18 +144,16 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
PropertyMap setProperties(const PropertyMap &); PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the AIFF::Properties for this file. If no audio properties * Returns the AIFF::Properties for this file.
* were read then this will return a null pointer. * If no audio properties were read then this will return a null pointer.
*/ */
virtual Properties *audioProperties() const; virtual Properties *audioProperties() const;
/*! /*!
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this * Save the file. If at least one tag -- ID3v1 or DIIN -- exists this will duplicate its content into the other tag.
* will duplicate its content into the other tag. This returns true * This returns true if saving was successful.
* if saving was successful.
* *
* If neither exists or if both tags are empty, this will strip the tags * If neither exists or if both tags are empty, this will strip the tags from the file.
* from the file.
* *
* This is the same as calling save(AllTags); * This is the same as calling save(AllTags);
* *
@@ -175,17 +165,15 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
virtual bool save(); virtual bool save();
/*! /*!
* Save the file. If \a strip is specified, it is possible to choose if * Save the file. If \a strip is specified,
* tags not specified in \a tags should be stripped from the file or * it is possible to choose if tags not specified in \a tags should be stripped from the file or retained.
* retained. With \a version, it is possible to specify whether ID3v2.4 * With \a version, it is possible to specify whether ID3v2.4 or ID3v2.3 should be used.
* or ID3v2.3 should be used.
*/ */
bool save(TagTypes tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4); bool save(TagTypes tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4);
/*! /*!
* This will strip the tags that match the OR-ed together TagTypes from the * This will strip the tags that match the OR-ed together TagTypes from the file.
* file. By default it strips all tags. It returns true if the tags are * By default it strips all tags. It returns true if the tags are successfully stripped.
* successfully stripped.
* *
* \note This will update the file immediately. * \note This will update the file immediately.
*/ */
@@ -199,25 +187,24 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
bool hasID3v2Tag() const; bool hasID3v2Tag() const;
/*! /*!
* Returns whether or not the file on disk actually has the DSDIFF * Returns whether or not the file on disk actually has the DSDIFF title and artist tags.
* title and artist tags.
* *
* \see DIINTag() * \see DIINTag()
*/ */
bool hasDIINTag() const; bool hasDIINTag() const;
/*! /*!
* Returns whether or not the given \a stream can be opened as a DSDIFF * Returns whether or not the given \a stream can be opened as a DSDIFF file.
* file.
* *
* \note This method is designed to do a quick check. The result may * \note This method is designed to do a quick check. The result may not necessarily be correct.
* not necessarily be correct.
*/ */
static bool isSupported(IOStream *stream); static bool isSupported(IOStream *stream);
protected: protected:
enum Endianness { BigEndian, enum Endianness {
LittleEndian }; BigEndian,
LittleEndian
};
File(FileName file, Endianness endianness); File(FileName file, Endianness endianness);
File(IOStream *stream, Endianness endianness); File(IOStream *stream, Endianness endianness);
@@ -239,9 +226,7 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
/*! /*!
* Sets the data for the root-level chunk \a name to \a data. * Sets the data for the root-level chunk \a name to \a data.
* If a root-level chunk with the given name already exists * If a root-level chunk with the given name already exists it will be overwritten, otherwise it will be created after the existing chunks.
* it will be overwritten, otherwise it will be
* created after the existing chunks.
* *
* \warning This will update the file immediately. * \warning This will update the file immediately.
*/ */
@@ -254,13 +239,11 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
* *
* \warning This will update the file immediately. * \warning This will update the file immediately.
*/ */
void setChildChunkData(unsigned int i, const ByteVector &data, void setChildChunkData(unsigned int i, const ByteVector &data, unsigned int childChunkNum);
unsigned int childChunkNum);
/*! /*!
* Sets the data for the child chunk \a name to \a data. If a chunk with * Sets the data for the child chunk \a name to \a data.
* the given name already exists it will be overwritten, otherwise it will * If a chunk with the given name already exists it will be overwritten, otherwise it will be created after the existing chunks inside child chunk.
* be created after the existing chunks inside child chunk.
* *
* If data is null, then remove the chunks with \a name name * If data is null, then remove the chunks with \a name name
* *
@@ -272,9 +255,7 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
void updateRootChunksStructure(unsigned int startingChunk); void updateRootChunksStructure(unsigned int startingChunk);
void read(bool readProperties, Properties::ReadStyle propertiesStyle); void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void writeChunk(const ByteVector &name, const ByteVector &data, void writeChunk(const ByteVector &name, const ByteVector &data, unsigned long long offset, unsigned long replace = 0, unsigned int leadingPadding = 0);
unsigned long long offset, unsigned long replace = 0,
unsigned int leadingPadding = 0);
class FilePrivate; class FilePrivate;
FilePrivate *d; FilePrivate *d;

View File

@@ -52,11 +52,8 @@ class DSDIFF::Properties::PropertiesPrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
DSDIFF::Properties::Properties(const unsigned int sampleRate, DSDIFF::Properties::Properties(const unsigned int sampleRate, const unsigned short channels, const unsigned long long samplesCount, const int bitrate, ReadStyle style) : AudioProperties(style) {
const unsigned short channels,
const unsigned long long samplesCount,
const int bitrate,
ReadStyle style) : AudioProperties(style) {
d = new PropertiesPrivate; d = new PropertiesPrivate;
d->channels = channels; d->channels = channels;
@@ -65,6 +62,7 @@ DSDIFF::Properties::Properties(const unsigned int sampleRate,
d->sampleRate = sampleRate; d->sampleRate = sampleRate;
d->bitrate = bitrate; d->bitrate = bitrate;
d->length = d->sampleRate > 0 ? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5) : 0; d->length = d->sampleRate > 0 ? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5) : 0;
} }
DSDIFF::Properties::~Properties() { DSDIFF::Properties::~Properties() {

View File

@@ -30,7 +30,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace DSDIFF { namespace DSDIFF {
class File; class File;
@@ -38,19 +37,15 @@ class File;
//! An implementation of audio property reading for DSDIFF //! An implementation of audio property reading for DSDIFF
/*! /*!
* This reads the data from an DSDIFF stream found in the AudioProperties * This reads the data from an DSDIFF stream found in the AudioProperties API.
* API.
*/ */
class TAGLIB_EXPORT Properties : public AudioProperties { class TAGLIB_EXPORT Properties : public Strawberry_TagLib::TagLib::AudioProperties {
public: public:
/*! /*!
* Create an instance of DSDIFF::Properties with the data read from the * Create an instance of DSDIFF::Properties with the data read from the ByteVector \a data.
* ByteVector \a data.
*/ */
Properties(const unsigned int sampleRate, const unsigned short channels, Properties(const unsigned int sampleRate, const unsigned short channels, const unsigned long long samplesCount, const int bitrate, ReadStyle style);
const unsigned long long samplesCount, const int bitrate,
ReadStyle style);
/*! /*!
* Destroys this DSDIFF::Properties instance. * Destroys this DSDIFF::Properties instance.
@@ -76,6 +71,7 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
class PropertiesPrivate; class PropertiesPrivate;
PropertiesPrivate *d; PropertiesPrivate *d;
}; };
} // namespace DSDIFF } // namespace DSDIFF
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -60,9 +60,11 @@ class DSF::File::FilePrivate {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool DSF::File::isSupported(IOStream *stream) { bool DSF::File::isSupported(IOStream *stream) {
// A DSF file has to start with "DSD " // A DSF file has to start with "DSD "
const ByteVector id = Utils::readHeader(stream, 4, false); const ByteVector id = Utils::readHeader(stream, 4, false);
return id.startsWith("DSD "); return id.startsWith("DSD ");
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -70,17 +72,18 @@ bool DSF::File::isSupported(IOStream *stream) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
DSF::File::File(FileName file, bool readProperties, DSF::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(file), Properties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) {
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
DSF::File::File(IOStream *stream, bool readProperties, DSF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) {
Properties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(stream),
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
DSF::File::~File() { DSF::File::~File() {
@@ -104,6 +107,7 @@ DSF::Properties *DSF::File::audioProperties() const {
} }
bool DSF::File::save() { bool DSF::File::save() {
if (readOnly()) { if (readOnly()) {
debug("DSF::File::save() -- File is read only."); debug("DSF::File::save() -- File is read only.");
return false; return false;
@@ -158,6 +162,7 @@ bool DSF::File::save() {
} }
return true; return true;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -166,6 +171,7 @@ bool DSF::File::save() {
void DSF::File::read(bool, Properties::ReadStyle propertiesStyle) { void DSF::File::read(bool, Properties::ReadStyle propertiesStyle) {
// A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk // A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk
// The file format is not chunked in the sense of a RIFF File, though // The file format is not chunked in the sense of a RIFF File, though
@@ -223,4 +229,5 @@ void DSF::File::read(bool, Properties::ReadStyle propertiesStyle) {
d->tag = new ID3v2::Tag(); d->tag = new ID3v2::Tag();
else else
d->tag = new ID3v2::Tag(this, d->metadataOffset); d->tag = new ID3v2::Tag(this, d->metadataOffset);
} }

View File

@@ -48,24 +48,24 @@ namespace DSF {
/*! /*!
* This implements and provides an interface for DSF files to the * This implements and provides an interface for DSF files to the
* Strawberry_TagLib::TagLib::Tag and Strawberry_TagLib::TagLib::AudioProperties interfaces by way of implementing * Strawberry_TagLib::TagLib::Tag and Strawberry_TagLib::TagLib::AudioProperties interfaces by way of implementing
* the abstract Strawberry_TagLib::TagLib::File API as well as providing some additional * the abstract Strawberry_TagLib::TagLib::File API as well as providing some additional information specific to DSF files.
* information specific to DSF files. *
*/ */
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
public: public:
/*! /*!
* Constructs an DSF file from \a file. If \a readProperties is true the * Constructs an DSF file from \a file.
* file's audio properties will also be read using \a propertiesStyle. If * If \a readProperties is true the file's audio properties will also be read using \a propertiesStyle.
* false, \a propertiesStyle is ignored. * If false, \a propertiesStyle is ignored.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Constructs an DSF file from \a file. If \a readProperties is true the * Constructs an DSF file from \a file.
* file's audio properties will also be read using \a propertiesStyle. If * If \a readProperties is true the file's audio properties will also be read using \a propertiesStyle.
* false, \a propertiesStyle is ignored. * If false, \a propertiesStyle is ignored.
*/ */
File(IOStream *stream, bool readProperties = true, File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
@@ -93,8 +93,8 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
PropertyMap setProperties(const PropertyMap &); PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the DSF::AudioProperties for this file. If no audio properties * Returns the DSF::AudioProperties for this file.
* were read then this will return a null pointer. * If no audio properties were read then this will return a null pointer.
*/ */
virtual Properties *audioProperties() const; virtual Properties *audioProperties() const;
@@ -104,11 +104,10 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
virtual bool save(); virtual bool save();
/*! /*!
* Returns whether or not the given \a stream can be opened as a DSF * Returns whether or not the given \a stream can be opened as a DSF file.
* file.
* *
* \note This method is designed to do a quick check. The result may * \note This method is designed to do a quick check.
* not necessarily be correct. * The result may not necessarily be correct.
*/ */
static bool isSupported(IOStream *stream); static bool isSupported(IOStream *stream);

View File

@@ -30,7 +30,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace DSF { namespace DSF {
class File; class File;
@@ -38,15 +37,13 @@ class File;
//! An implementation of audio property reading for DSF //! An implementation of audio property reading for DSF
/*! /*!
* This reads the data from a DSF stream found in the AudioProperties * This reads the data from a DSF stream found in the AudioProperties API.
* API.
*/ */
class TAGLIB_EXPORT Properties : public Strawberry_TagLib::TagLib::AudioProperties { class TAGLIB_EXPORT Properties : public Strawberry_TagLib::TagLib::AudioProperties {
public: public:
/*! /*!
* Create an instance of DSF::AudioProperties with the data read from the * Create an instance of DSF::AudioProperties with the data read from the ByteVector \a data.
* ByteVector \a data.
*/ */
Properties(const ByteVector &data, ReadStyle style); Properties(const ByteVector &data, ReadStyle style);
@@ -68,8 +65,14 @@ class TAGLIB_EXPORT Properties : public Strawberry_TagLib::TagLib::AudioProperti
int formatID() const; int formatID() const;
/*! /*!
* Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels, * Channel type values:
* 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels * 1 = mono,
* 2 = stereo,
* 3 = 3 channels,
* 4 = quad,
* 5 = 4 channels,
* 6 = 5 channels,
* 7 = 5.1 channels
*/ */
int channelType() const; int channelType() const;
int bitsPerSample() const; int bitsPerSample() const;

View File

@@ -63,8 +63,8 @@ ResolverList fileTypeResolvers;
// Detect the file type by user-defined resolvers. // Detect the file type by user-defined resolvers.
File *detectByResolvers(FileName fileName, bool readAudioProperties, File *detectByResolvers(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) {
AudioProperties::ReadStyle audioPropertiesStyle) {
ResolverList::ConstIterator it = fileTypeResolvers.begin(); ResolverList::ConstIterator it = fileTypeResolvers.begin();
for (; it != fileTypeResolvers.end(); ++it) { for (; it != fileTypeResolvers.end(); ++it) {
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle); File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
@@ -73,12 +73,13 @@ File *detectByResolvers(FileName fileName, bool readAudioProperties,
} }
return nullptr; return nullptr;
} }
// Detect the file type based on the file extension. // Detect the file type based on the file extension.
File *detectByExtension(IOStream *stream, bool readAudioProperties, File *detectByExtension(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) {
AudioProperties::ReadStyle audioPropertiesStyle) {
#ifdef _WIN32 #ifdef _WIN32
const String s = stream->name().toString(); const String s = stream->name().toString();
#else #else
@@ -140,13 +141,14 @@ File *detectByExtension(IOStream *stream, bool readAudioProperties,
return new DSF::File(stream, readAudioProperties, audioPropertiesStyle); return new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
return nullptr; return nullptr;
} }
// Detect the file type based on the actual content of the stream. // Detect the file type based on the actual content of the stream.
File *detectByContent(IOStream *stream, bool readAudioProperties, File *detectByContent(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) {
AudioProperties::ReadStyle audioPropertiesStyle) {
File *file = 0; File *file = nullptr;
if (MPEG::File::isSupported(stream)) if (MPEG::File::isSupported(stream))
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle); file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
@@ -191,17 +193,17 @@ File *detectByContent(IOStream *stream, bool readAudioProperties,
} }
return nullptr; return nullptr;
} }
// Internal function that supports FileRef::create(). // Internal function that supports FileRef::create().
// This looks redundant, but necessary in order not to change the previous // This looks redundant, but necessary in order not to change the previous
// behavior of FileRef::create(). // behavior of FileRef::create().
File *createInternal(FileName fileName, bool readAudioProperties, File *createInternal(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) {
AudioProperties::ReadStyle audioPropertiesStyle) {
File *file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle); File *file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if (file) if (file) return file;
return file;
#ifdef _WIN32 #ifdef _WIN32
const String s = fileName.toString(); const String s = fileName.toString();
@@ -266,14 +268,13 @@ File *createInternal(FileName fileName, bool readAudioProperties,
return new DSF::File(fileName, readAudioProperties, audioPropertiesStyle); return new DSF::File(fileName, readAudioProperties, audioPropertiesStyle);
return nullptr; return nullptr;
} }
} // namespace } // namespace
class FileRef::FileRefPrivate : public RefCounter { class FileRef::FileRefPrivate : public RefCounter {
public: public:
FileRefPrivate() : RefCounter(), FileRefPrivate() : RefCounter(), file(nullptr), stream(nullptr) {}
file(0),
stream(0) {}
~FileRefPrivate() { ~FileRefPrivate() {
delete file; delete file;
@@ -288,11 +289,9 @@ class FileRef::FileRefPrivate : public RefCounter {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
FileRef::FileRef() : d(new FileRefPrivate()) { FileRef::FileRef() : d(new FileRefPrivate()) {}
}
FileRef::FileRef(FileName fileName, bool readAudioProperties, FileRef::FileRef(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) : d(new FileRefPrivate()) {
AudioProperties::ReadStyle audioPropertiesStyle) : d(new FileRefPrivate()) {
parse(fileName, readAudioProperties, audioPropertiesStyle); parse(fileName, readAudioProperties, audioPropertiesStyle);
} }
@@ -309,24 +308,30 @@ FileRef::FileRef(const FileRef &ref) : d(ref.d) {
} }
FileRef::~FileRef() { FileRef::~FileRef() {
if (d->deref()) if (d->deref())
delete d; delete d;
} }
Tag *FileRef::tag() const { Tag *FileRef::tag() const {
if (isNull()) { if (isNull()) {
debug("FileRef::tag() - Called without a valid file."); debug("FileRef::tag() - Called without a valid file.");
return nullptr; return nullptr;
} }
return d->file->tag(); return d->file->tag();
} }
AudioProperties *FileRef::audioProperties() const { AudioProperties *FileRef::audioProperties() const {
if (isNull()) { if (isNull()) {
debug("FileRef::audioProperties() - Called without a valid file."); debug("FileRef::audioProperties() - Called without a valid file.");
return nullptr; return nullptr;
} }
return d->file->audioProperties(); return d->file->audioProperties();
} }
File *FileRef::file() const { File *FileRef::file() const {
@@ -334,20 +339,22 @@ File *FileRef::file() const {
} }
bool FileRef::save() { bool FileRef::save() {
if (isNull()) { if (isNull()) {
debug("FileRef::save() - Called without a valid file."); debug("FileRef::save() - Called without a valid file.");
return false; return false;
} }
return d->file->save(); return d->file->save();
} }
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) { // static
{
fileTypeResolvers.prepend(resolver); fileTypeResolvers.prepend(resolver);
return resolver; return resolver;
} }
StringList FileRef::defaultFileExtensions() { StringList FileRef::defaultFileExtensions() {
StringList l; StringList l;
l.append("ogg"); l.append("ogg");
@@ -383,6 +390,7 @@ StringList FileRef::defaultFileExtensions() {
l.append("dsdiff"); // alias for "dff" l.append("dsdiff"); // alias for "dff"
return l; return l;
} }
bool FileRef::isNull() const { bool FileRef::isNull() const {
@@ -408,9 +416,7 @@ bool FileRef::operator!=(const FileRef &ref) const {
return (ref.d->file != d->file); return (ref.d->file != d->file);
} }
File *FileRef::create(FileName fileName, bool readAudioProperties, File *FileRef::create(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { // static
AudioProperties::ReadStyle audioPropertiesStyle) // static
{
return createInternal(fileName, readAudioProperties, audioPropertiesStyle); return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
} }
@@ -418,8 +424,8 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void FileRef::parse(FileName fileName, bool readAudioProperties, void FileRef::parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) {
AudioProperties::ReadStyle audioPropertiesStyle) {
// Try user-defined resolvers. // Try user-defined resolvers.
d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle); d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
@@ -442,11 +448,12 @@ void FileRef::parse(FileName fileName, bool readAudioProperties,
// Stream have to be closed here if failed to resolve file types. // Stream have to be closed here if failed to resolve file types.
delete d->stream; delete d->stream;
d->stream = 0; d->stream = nullptr;
} }
void FileRef::parse(IOStream *stream, bool readAudioProperties, void FileRef::parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) {
AudioProperties::ReadStyle audioPropertiesStyle) {
// User-defined resolvers won't work with a stream. // User-defined resolvers won't work with a stream.
// Try to resolve file types based on the file extension. // Try to resolve file types based on the file extension.
@@ -458,4 +465,5 @@ void FileRef::parse(IOStream *stream, bool readAudioProperties,
// At last, try to resolve file types based on the actual content of the file. // At last, try to resolve file types based on the actual content of the file.
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle); d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
} }

View File

@@ -40,18 +40,15 @@ class Tag;
//! This class provides a simple abstraction for creating and handling files //! This class provides a simple abstraction for creating and handling files
/*! /*!
* FileRef exists to provide a minimal, generic and value-based wrapper around * FileRef exists to provide a minimal, generic and value-based wrapper around a File.
* a File. It is lightweight and implicitly shared, and as such suitable for * It is lightweight and implicitly shared, and as such suitable for pass-by-value use.
* pass-by-value use. This hides some of the uglier details of TagLib::File * This hides some of the uglier details of TagLib::File and the non-generic portions of the concrete file implementations.
* and the non-generic portions of the concrete file implementations.
* *
* This class is useful in a "simple usage" situation where it is desirable * This class is useful in a "simple usage" situation where it is desirable
* to be able to get and set some of the tag information that is similar * to be able to get and set some of the tag information that is similar across file types.
* across file types.
* *
* Also note that it is probably a good idea to plug this into your mime * Also note that it is probably a good idea to plug this into your mime
* type system rather than using the constructor that accepts a file name using * type system rather than using the constructor that accepts a file name using the FileTypeResolver.
* the FileTypeResolver.
* *
* \see FileTypeResolver * \see FileTypeResolver
* \see addFileTypeResolver() * \see addFileTypeResolver()
@@ -62,8 +59,7 @@ class TAGLIB_EXPORT FileRef {
//! A class for pluggable file type resolution. //! A class for pluggable file type resolution.
/*! /*!
* This class is used to add extend TagLib's very basic file name based file * This class is used to add extend TagLib's very basic file name based file type resolution.
* type resolution.
* *
* This can be accomplished with: * This can be accomplished with:
* *
@@ -83,27 +79,23 @@ class TAGLIB_EXPORT FileRef {
* *
* \endcode * \endcode
* *
* Naturally a less contrived example would be slightly more complex. This * Naturally a less contrived example would be slightly more complex.
* can be used to plug in mime-type detection systems or to add new file types * This can be used to plug in mime-type detection systems or to add new file types to TagLib.
* to TagLib.
*/ */
class TAGLIB_EXPORT FileTypeResolver { class TAGLIB_EXPORT FileTypeResolver {
public: public:
virtual ~FileTypeResolver(); virtual ~FileTypeResolver();
/*! /*!
* This method must be overridden to provide an additional file type * This method must be overridden to provide an additional file type resolver.
* resolver. If the resolver is able to determine the file type it should * If the resolver is able to determine the file type it should return a valid File object; if not it should return 0.
* return a valid File object; if not it should return 0.
* *
* \note The created file is then owned by the FileRef and should not be * \note The created file is then owned by the FileRef and should not be deleted.
* deleted. Deletion will happen automatically when the FileRef passes * Deletion will happen automatically when the FileRef passes out of scope.
* out of scope.
*/ */
virtual File *createFile(FileName fileName, virtual File *createFile(FileName fileName,
bool readAudioProperties = true, bool readAudioProperties = true,
AudioProperties::ReadStyle AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average) const = 0;
audioPropertiesStyle = AudioProperties::Average) const = 0;
}; };
/*! /*!
@@ -112,10 +104,9 @@ class TAGLIB_EXPORT FileRef {
FileRef(); FileRef();
/*! /*!
* Create a FileRef from \a fileName. If \a readAudioProperties is true then * Create a FileRef from \a fileName.
* the audio properties will be read using \a audioPropertiesStyle. If * If \a readAudioProperties is true then the audio properties will be read using \a audioPropertiesStyle.
* \a readAudioProperties is false then \a audioPropertiesStyle will be * If \a readAudioProperties is false then \a audioPropertiesStyle will be ignored.
* ignored.
* *
* Also see the note in the class documentation about why you may not want to * Also see the note in the class documentation about why you may not want to
* use this method in your application. * use this method in your application.
@@ -126,16 +117,13 @@ class TAGLIB_EXPORT FileRef {
audioPropertiesStyle = AudioProperties::Average); audioPropertiesStyle = AudioProperties::Average);
/*! /*!
* Construct a FileRef from an opened \a IOStream. If \a readAudioProperties * Construct a FileRef from an opened \a IOStream.
* is true then the audio properties will be read using \a audioPropertiesStyle. * If \a readAudioProperties is true then the audio properties will be read using \a audioPropertiesStyle.
* If \a readAudioProperties is false then \a audioPropertiesStyle will be * If \a readAudioProperties is false then \a audioPropertiesStyle will be ignored.
* ignored.
* *
* Also see the note in the class documentation about why you may not want to * Also see the note in the class documentation about why you may not want to use this method in your application.
* use this method in your application.
* *
* \note TagLib will *not* take ownership of the stream, the caller is * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object.
* responsible for deleting it after the File object.
*/ */
explicit FileRef(IOStream *stream, explicit FileRef(IOStream *stream,
bool readAudioProperties = true, bool readAudioProperties = true,
@@ -143,8 +131,8 @@ class TAGLIB_EXPORT FileRef {
audioPropertiesStyle = AudioProperties::Average); audioPropertiesStyle = AudioProperties::Average);
/*! /*!
* Construct a FileRef using \a file. The FileRef now takes ownership of the * Construct a FileRef using \a file.
* pointer and will delete the File when it passes out of scope. * The FileRef now takes ownership of the pointer and will delete the File when it passes out of scope.
*/ */
explicit FileRef(File *file); explicit FileRef(File *file);
@@ -172,8 +160,8 @@ class TAGLIB_EXPORT FileRef {
Tag *tag() const; Tag *tag() const;
/*! /*!
* Returns the audio properties for this FileRef. If no audio properties * Returns the audio properties for this FileRef.
* were read then this will returns a null pointer. * If no audio properties were read then this will returns a null pointer.
*/ */
AudioProperties *audioProperties() const; AudioProperties *audioProperties() const;
@@ -200,14 +188,12 @@ class TAGLIB_EXPORT FileRef {
bool save(); bool save();
/*! /*!
* Adds a FileTypeResolver to the list of those used by TagLib. Each * Adds a FileTypeResolver to the list of those used by TagLib.
* additional FileTypeResolver is added to the front of a list of resolvers * Each additional FileTypeResolver is added to the front of a list of resolvers that are tried.
* that are tried. If the FileTypeResolver returns zero the next resolver * If the FileTypeResolver returns zero the next resolver is tried.
* is tried.
* *
* Returns a pointer to the added resolver (the same one that's passed in -- * Returns a pointer to the added resolver (the same one that's passed in --
* this is mostly so that static initializers have something to use for * this is mostly so that static initializers have something to use for assignment).
* assignment).
* *
* \see FileTypeResolver * \see FileTypeResolver
*/ */
@@ -215,8 +201,7 @@ class TAGLIB_EXPORT FileRef {
/*! /*!
* As is mentioned elsewhere in this class's documentation, the default file * As is mentioned elsewhere in this class's documentation, the default file
* type resolution code provided by TagLib only works by comparing file * type resolution code provided by TagLib only works by comparing file extensions.
* extensions.
* *
* This method returns the list of file extensions that are used by default. * This method returns the list of file extensions that are used by default.
* *
@@ -252,19 +237,16 @@ class TAGLIB_EXPORT FileRef {
bool operator==(const FileRef &ref) const; bool operator==(const FileRef &ref) const;
/*! /*!
* Returns true if this FileRef and \a ref do not point to the same File * Returns true if this FileRef and \a ref do not point to the same File object.
* object.
*/ */
bool operator!=(const FileRef &ref) const; bool operator!=(const FileRef &ref) const;
/*! /*!
* A simple implementation of file type guessing. If \a readAudioProperties * A simple implementation of file type guessing.
* is true then the audio properties will be read using * If \a readAudioProperties is true then the audio properties will be read using \a audioPropertiesStyle.
* \a audioPropertiesStyle. If \a readAudioProperties is false then * If \a readAudioProperties is false then \a audioPropertiesStyle will be ignored.
* \a audioPropertiesStyle will be ignored.
* *
* \note You generally shouldn't use this method, but instead the constructor * \note You generally shouldn't use this method, but instead the constructor directly.
* directly.
* *
* \deprecated * \deprecated
*/ */

View File

@@ -64,7 +64,7 @@ class FLAC::File::FilePrivate {
ID3v2Location(-1), ID3v2Location(-1),
ID3v2OriginalSize(0), ID3v2OriginalSize(0),
ID3v1Location(-1), ID3v1Location(-1),
properties(0), properties(nullptr),
flacStart(0), flacStart(0),
streamStart(0), streamStart(0),
scanned(false) { scanned(false) {
@@ -97,34 +97,30 @@ class FLAC::File::FilePrivate {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool FLAC::File::isSupported(IOStream *stream) { bool FLAC::File::isSupported(IOStream *stream) {
// A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede. // A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true); const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
return (buffer.find("fLaC") >= 0); return (buffer.find("fLaC") >= 0);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate(frameFactory)) {
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory, FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate(frameFactory)) {
bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(file),
d(new FilePrivate(frameFactory)) {
if (isOpen())
read(readProperties);
}
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream),
d(new FilePrivate(frameFactory)) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
FLAC::File::~File() { FLAC::File::~File() {
@@ -152,6 +148,7 @@ FLAC::Properties *FLAC::File::audioProperties() const {
} }
bool FLAC::File::save() { bool FLAC::File::save() {
if (readOnly()) { if (readOnly()) {
debug("FLAC::File::save() - Cannot save to a read only file."); debug("FLAC::File::save() - Cannot save to a read only file.");
return false; return false;
@@ -288,6 +285,7 @@ bool FLAC::File::save() {
} }
return true; return true;
} }
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create) { ID3v2::Tag *FLAC::File::ID3v2Tag(bool create) {
@@ -302,21 +300,8 @@ Ogg::XiphComment *FLAC::File::xiphComment(bool create) {
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create); return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create);
} }
void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory) {
d->ID3v2FrameFactory = factory;
}
ByteVector FLAC::File::streamInfoData() {
debug("FLAC::File::streamInfoData() -- This function is obsolete. Returning an empty ByteVector.");
return ByteVector();
}
long FLAC::File::streamLength() {
debug("FLAC::File::streamLength() -- This function is obsolete. Returning zero.");
return 0;
}
List<FLAC::Picture *> FLAC::File::pictureList() { List<FLAC::Picture *> FLAC::File::pictureList() {
List<Picture *> pictures; List<Picture *> pictures;
for (BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) { for (BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
Picture *picture = dynamic_cast<Picture *>(*it); Picture *picture = dynamic_cast<Picture *>(*it);
@@ -325,6 +310,7 @@ List<FLAC::Picture *> FLAC::File::pictureList() {
} }
} }
return pictures; return pictures;
} }
void FLAC::File::addPicture(Picture *picture) { void FLAC::File::addPicture(Picture *picture) {
@@ -332,15 +318,18 @@ void FLAC::File::addPicture(Picture *picture) {
} }
void FLAC::File::removePicture(Picture *picture, bool del) { void FLAC::File::removePicture(Picture *picture, bool del) {
BlockIterator it = d->blocks.find(picture); BlockIterator it = d->blocks.find(picture);
if (it != d->blocks.end()) if (it != d->blocks.end())
d->blocks.erase(it); d->blocks.erase(it);
if (del) if (del)
delete picture; delete picture;
} }
void FLAC::File::removePictures() { void FLAC::File::removePictures() {
for (BlockIterator it = d->blocks.begin(); it != d->blocks.end();) { for (BlockIterator it = d->blocks.begin(); it != d->blocks.end();) {
if (dynamic_cast<Picture *>(*it)) { if (dynamic_cast<Picture *>(*it)) {
delete *it; delete *it;
@@ -350,19 +339,22 @@ void FLAC::File::removePictures() {
++it; ++it;
} }
} }
} }
void FLAC::File::strip(int tags) { void FLAC::File::strip(int tags) {
if (tags & ID3v1) if (tags & ID3v1)
d->tag.set(FlacID3v1Index, 0); d->tag.set(FlacID3v1Index, nullptr);
if (tags & ID3v2) if (tags & ID3v2)
d->tag.set(FlacID3v2Index, 0); d->tag.set(FlacID3v2Index, nullptr);
if (tags & XiphComment) { if (tags & XiphComment) {
xiphComment()->removeAllFields(); xiphComment()->removeAllFields();
xiphComment()->removeAllPictures(); xiphComment()->removeAllPictures();
} }
} }
bool FLAC::File::hasXiphComment() const { bool FLAC::File::hasXiphComment() const {
@@ -382,6 +374,7 @@ bool FLAC::File::hasID3v2Tag() const {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void FLAC::File::read(bool readProperties) { void FLAC::File::read(bool readProperties) {
// Look for an ID3v2 tag // Look for an ID3v2 tag
d->ID3v2Location = Utils::findID3v2(this); d->ID3v2Location = Utils::findID3v2(this);
@@ -425,9 +418,11 @@ void FLAC::File::read(bool readProperties) {
d->properties = new Properties(infoData, streamLength); d->properties = new Properties(infoData, streamLength);
} }
} }
void FLAC::File::scan() { void FLAC::File::scan() {
// Scan the metadata pages // Scan the metadata pages
if (d->scanned) if (d->scanned)
@@ -494,7 +489,7 @@ void FLAC::File::scan() {
return; return;
} }
MetadataBlock *block = 0; MetadataBlock *block = nullptr;
// Found the vorbis-comment // Found the vorbis-comment
if (blockType == MetadataBlock::VorbisComment) { if (blockType == MetadataBlock::VorbisComment) {
@@ -537,4 +532,5 @@ void FLAC::File::scan() {
d->streamStart = nextBlockOffset; d->streamStart = nextBlockOffset;
d->scanned = true; d->scanned = true;
} }

View File

@@ -65,10 +65,9 @@ namespace FLAC {
//! An implementation of TagLib::File with FLAC specific methods //! An implementation of TagLib::File with FLAC specific methods
/*! /*!
* This implements and provides an interface for FLAC files to the * This implements and provides an interface for FLAC files to the TagLib::Tag and TagLib::AudioProperties interfaces
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * by way of implementing the abstract TagLib::File API as well as providing some additional information specific to FLAC files.
* the abstract TagLib::File API as well as providing some additional *
* information specific to FLAC files.
*/ */
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
@@ -91,47 +90,26 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
}; };
/*! /*!
* Constructs a FLAC file from \a file. If \a readProperties is true the * Constructs an FLAC file from \a file.
* file's audio properties will also be read. * If \a readProperties is true the file's audio properties will also be read.
*
* If this file contains and ID3v2 tag the frames will be created using \a frameFactory.
* *
* \note In the current implementation, \a propertiesStyle is ignored. * \note In the current implementation, \a propertiesStyle is ignored.
*
* \deprecated This constructor will be dropped in favor of the one below
* in a future version.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Constructs an FLAC file from \a file. If \a readProperties is true the * Constructs a FLAC file from \a stream. If \a readProperties is true the file's audio properties will also be read.
* file's audio properties will also be read.
* *
* If this file contains and ID3v2 tag the frames will be created using * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object.
* \a frameFactory. *
* If this file contains and ID3v2 tag the frames will be created using \a frameFactory.
* *
* \note In the current implementation, \a propertiesStyle is ignored. * \note In the current implementation, \a propertiesStyle is ignored.
*/ */
// BIC: merge with the above constructor // BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory, File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs a FLAC file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
// BIC: merge with the above constructor
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
@@ -139,8 +117,7 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
virtual ~File(); virtual ~File();
/*! /*!
* Returns the Tag for this file. This will be a union of XiphComment, * Returns the Tag for this file. This will be a union of XiphComment, ID3v1 and ID3v2 tags.
* ID3v1 and ID3v2 tags.
* *
* \see ID3v2Tag() * \see ID3v2Tag()
* \see ID3v1Tag() * \see ID3v1Tag()
@@ -150,9 +127,7 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
/*! /*!
* Implements the unified property interface -- export function. * Implements the unified property interface -- export function.
* If the file contains more than one tag (e.g. XiphComment and ID3v1), * If the file contains more than one tag (e.g. XiphComment and ID3v1), only the first one (in the order XiphComment, ID3v2, ID3v1) will be converted to the PropertyMap.
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
* converted to the PropertyMap.
*/ */
PropertyMap properties() const; PropertyMap properties() const;
@@ -160,23 +135,19 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
/*! /*!
* Implements the unified property interface -- import function. * Implements the unified property interface -- import function.
* This always creates a Xiph comment, if none exists. The return value * This always creates a Xiph comment, if none exists. The return value relates to the Xiph comment only.
* relates to the Xiph comment only. * Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed in the FLAC specification.
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
* in the FLAC specification.
*/ */
PropertyMap setProperties(const PropertyMap &); PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the FLAC::Properties for this file. If no audio properties * Returns the FLAC::Properties for this file. If no audio properties were read then this will return a null pointer.
* were read then this will return a null pointer.
*/ */
virtual Properties *audioProperties() const; virtual Properties *audioProperties() const;
/*! /*!
* Save the file. This will primarily save the XiphComment, but * Save the file. This will primarily save the XiphComment, but will also keep any old ID3-tags up to date.
* will also keep any old ID3-tags up to date. If the file * If the file has no XiphComment, one will be constructed from the ID3-tags.
* has no XiphComment, one will be constructed from the ID3-tags.
* *
* This returns true if the save was successful. * This returns true if the save was successful.
*/ */
@@ -186,16 +157,14 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
* Returns a pointer to the ID3v2 tag of the file. * Returns a pointer to the ID3v2 tag of the file.
* *
* If \a create is false (the default) this returns a null pointer * If \a create is false (the default) this returns a null pointer
* if there is no valid ID3v2 tag. If \a create is true it will create * if there is no valid ID3v2 tag.
* an ID3v2 tag if one does not exist and returns a valid pointer. * If \a create is true it will create an ID3v2 tag if one does not exist and returns a valid pointer.
* *
* \note This may return a valid pointer regardless of whether or not the * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v2 tag.
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file * Use hasID3v2Tag() to check if the file on disk actually has an ID3v2 tag.
* on disk actually has an ID3v2 tag.
* *
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be * \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is destroyed.
* destroyed.
* *
* \see hasID3v2Tag() * \see hasID3v2Tag()
*/ */
@@ -204,17 +173,14 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
/*! /*!
* Returns a pointer to the ID3v1 tag of the file. * Returns a pointer to the ID3v1 tag of the file.
* *
* If \a create is false (the default) this returns a null pointer * If \a create is false (the default) this returns a null pointer if there is no valid APE tag.
* if there is no valid APE tag. If \a create is true it will create * If \a create is true it will create an APE tag if one does not exist and returns a valid pointer.
* an APE tag if one does not exist and returns a valid pointer.
* *
* \note This may return a valid pointer regardless of whether or not the * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v1 tag.
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * Use hasID3v1Tag() to check if the file on disk actually has an ID3v1 tag.
* on disk actually has an ID3v1 tag.
* *
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be * \note The Tag <b>is still</b> owned by the MPEG::File and should not be deleted by the user.
* deleted by the user. It will be deleted when the file (object) is * It will be deleted when the file (object) is destroyed.
* destroyed.
* *
* \see hasID3v1Tag() * \see hasID3v1Tag()
*/ */
@@ -223,56 +189,27 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
/*! /*!
* Returns a pointer to the XiphComment for the file. * Returns a pointer to the XiphComment for the file.
* *
* If \a create is false (the default) this returns a null pointer * If \a create is false (the default) this returns a null pointer if there is no valid XiphComment.
* if there is no valid XiphComment. If \a create is true it will create * If \a create is true it will create a XiphComment if one does not exist and returns a valid pointer.
* a XiphComment if one does not exist and returns a valid pointer.
* *
* \note This may return a valid pointer regardless of whether or not the * \note This may return a valid pointer regardless of whether or not the file on disk has a XiphComment.
* file on disk has a XiphComment. Use hasXiphComment() to check if the * Use hasXiphComment() to check if the file on disk actually has a XiphComment.
* file on disk actually has a XiphComment.
* *
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be * \note The Tag <b>is still</b> owned by the FLAC::File and should not be deleted by the user.
* deleted by the user. It will be deleted when the file (object) is * It will be deleted when the file (object) is destroyed.
* destroyed.
* *
* \see hasXiphComment() * \see hasXiphComment()
*/ */
Ogg::XiphComment *xiphComment(bool create = false); Ogg::XiphComment *xiphComment(bool create = false);
/*!
* Set the ID3v2::FrameFactory to something other than the default. This
* can be used to specify the way that ID3v2 frames will be interpreted
* when
*
* \see ID3v2FrameFactory
* \deprecated This value should be passed in via the constructor
*/
TAGLIB_DEPRECATED void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
/*!
* Returns the block of data used by FLAC::Properties for parsing the
* stream properties.
*
* \deprecated Always returns an empty vector.
*/
TAGLIB_DEPRECATED ByteVector streamInfoData(); // BIC: remove
/*!
* Returns the length of the audio-stream, used by FLAC::Properties for
* calculating the bitrate.
*
* \deprecated Always returns zero.
*/
TAGLIB_DEPRECATED long streamLength(); // BIC: remove
/*! /*!
* Returns a list of pictures attached to the FLAC file. * Returns a list of pictures attached to the FLAC file.
*/ */
List<Picture *> pictureList(); List<Picture *> pictureList();
/*! /*!
* Removes an attached picture. If \a del is true the picture's memory * Removes an attached picture.
* will be freed; if it is false, it must be deleted by the user. * If \a del is true the picture's memory will be freed; if it is false, it must be deleted by the user.
*/ */
void removePicture(Picture *picture, bool del = true); void removePicture(Picture *picture, bool del = true);
@@ -282,25 +219,23 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
void removePictures(); void removePictures();
/*! /*!
* Add a new picture to the file. The file takes ownership of the * Add a new picture to the file.
* picture and will handle freeing its memory. * The file takes ownership of the picture and will handle freeing its memory.
* *
* \note The file will be saved only after calling save(). * \note The file will be saved only after calling save().
*/ */
void addPicture(Picture *picture); void addPicture(Picture *picture);
/*! /*!
* This will remove the tags that match the OR-ed together TagTypes from * This will remove the tags that match the OR-ed together TagTypes from the file.
* the file. By default it removes all tags. * By default it removes all tags.
* *
* \warning This will also invalidate pointers to the tags as their memory * \warning This will also invalidate pointers to the tags as their memory will be freed.
* will be freed.
* *
* \note In order to make the removal permanent save() still needs to be * \note In order to make the removal permanent save() still needs to be called.
* called.
* *
* \note This won't remove the Vorbis comment block completely. The * \note This won't remove the Vorbis comment block completely.
* vendor ID will be preserved. * The vendor ID will be preserved.
*/ */
void strip(int tags = AllTags); void strip(int tags = AllTags);
@@ -326,11 +261,10 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
bool hasID3v2Tag() const; bool hasID3v2Tag() const;
/*! /*!
* Returns whether or not the given \a stream can be opened as a FLAC * Returns whether or not the given \a stream can be opened as a FLAC file.
* file.
* *
* \note This method is designed to do a quick check. The result may * \note This method is designed to do a quick check.
* not necessarily be correct. * The result may not necessarily be correct.
*/ */
static bool isSupported(IOStream *stream); static bool isSupported(IOStream *stream);

View File

@@ -34,9 +34,6 @@ class FLAC::MetadataBlock::MetadataBlockPrivate {
MetadataBlockPrivate() {} MetadataBlockPrivate() {}
}; };
FLAC::MetadataBlock::MetadataBlock() { FLAC::MetadataBlock::MetadataBlock() : d(nullptr) {}
d = 0;
}
FLAC::MetadataBlock::~MetadataBlock() { FLAC::MetadataBlock::~MetadataBlock() {}
}

View File

@@ -32,7 +32,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace FLAC { namespace FLAC {
class TAGLIB_EXPORT MetadataBlock { class TAGLIB_EXPORT MetadataBlock {
@@ -69,7 +68,6 @@ class TAGLIB_EXPORT MetadataBlock {
}; };
} // namespace FLAC } // namespace FLAC
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -64,6 +64,7 @@ int FLAC::Picture::code() const {
} }
bool FLAC::Picture::parse(const ByteVector &data) { bool FLAC::Picture::parse(const ByteVector &data) {
if (data.size() < 32) { if (data.size() < 32) {
debug("A picture block must contain at least 5 bytes."); debug("A picture block must contain at least 5 bytes.");
return false; return false;
@@ -105,9 +106,11 @@ bool FLAC::Picture::parse(const ByteVector &data) {
d->data = data.mid(pos, dataLength); d->data = data.mid(pos, dataLength);
return true; return true;
} }
ByteVector FLAC::Picture::render() const { ByteVector FLAC::Picture::render() const {
ByteVector result; ByteVector result;
result.append(ByteVector::fromUInt(d->type)); result.append(ByteVector::fromUInt(d->type));
ByteVector mimeTypeData = d->mimeType.data(String::UTF8); ByteVector mimeTypeData = d->mimeType.data(String::UTF8);
@@ -123,6 +126,7 @@ ByteVector FLAC::Picture::render() const {
result.append(ByteVector::fromUInt(d->data.size())); result.append(ByteVector::fromUInt(d->data.size()));
result.append(d->data); result.append(d->data);
return result; return result;
} }
FLAC::Picture::Type FLAC::Picture::type() const { FLAC::Picture::Type FLAC::Picture::type() const {

View File

@@ -102,14 +102,12 @@ class TAGLIB_EXPORT Picture : public MetadataBlock {
void setType(Type type); void setType(Type type);
/*! /*!
* Returns the mime type of the image. This should in most cases be * Returns the mime type of the image. This should in most cases be "image/png" or "image/jpeg".
* "image/png" or "image/jpeg".
*/ */
String mimeType() const; String mimeType() const;
/*! /*!
* Sets the mime type of the image. This should in most cases be * Sets the mime type of the image. This should in most cases be "image/png" or "image/jpeg".
* "image/png" or "image/jpeg".
*/ */
void setMimeType(const String &m); void setMimeType(const String &m);
@@ -201,7 +199,6 @@ class TAGLIB_EXPORT Picture : public MetadataBlock {
typedef List<Picture> PictureList; typedef List<Picture> PictureList;
} // namespace FLAC } // namespace FLAC
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -53,24 +53,14 @@ class FLAC::Properties::PropertiesPrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) : AudioProperties(style), FLAC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style), d(new PropertiesPrivate()) {
d(new PropertiesPrivate()) {
read(data, streamLength); read(data, streamLength);
} }
FLAC::Properties::Properties(File *, ReadStyle style) : AudioProperties(style),
d(new PropertiesPrivate()) {
debug("FLAC::Properties::Properties() - This constructor is no longer used.");
}
FLAC::Properties::~Properties() { FLAC::Properties::~Properties() {
delete d; delete d;
} }
int FLAC::Properties::length() const {
return lengthInSeconds();
}
int FLAC::Properties::lengthInSeconds() const { int FLAC::Properties::lengthInSeconds() const {
return d->length / 1000; return d->length / 1000;
} }
@@ -91,10 +81,6 @@ int FLAC::Properties::bitsPerSample() const {
return d->bitsPerSample; return d->bitsPerSample;
} }
int FLAC::Properties::sampleWidth() const {
return bitsPerSample();
}
int FLAC::Properties::channels() const { int FLAC::Properties::channels() const {
return d->channels; return d->channels;
} }
@@ -112,6 +98,7 @@ ByteVector FLAC::Properties::signature() const {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void FLAC::Properties::read(const ByteVector &data, long streamLength) { void FLAC::Properties::read(const ByteVector &data, long streamLength) {
if (data.size() < 18) { if (data.size() < 18) {
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes."); debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
return; return;
@@ -155,4 +142,5 @@ void FLAC::Properties::read(const ByteVector &data, long streamLength) {
if (data.size() >= pos + 16) if (data.size() >= pos + 16)
d->signature = data.mid(pos, 16); d->signature = data.mid(pos, 16);
} }

View File

@@ -39,25 +39,15 @@ class File;
//! An implementation of audio property reading for FLAC //! An implementation of audio property reading for FLAC
/*! /*!
* This reads the data from an FLAC stream found in the AudioProperties * This reads the data from an FLAC stream found in the AudioProperties API.
* API.
*/ */
class TAGLIB_EXPORT Properties : public AudioProperties { class TAGLIB_EXPORT Properties : public AudioProperties {
public: public:
/*! /*!
* Create an instance of FLAC::Properties with the data read from the * Create an instance of FLAC::Properties with the data read from the ByteVector \a data.
* ByteVector \a data.
*/ */
// BIC: switch to const reference Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
Properties(ByteVector data, long streamLength, ReadStyle style = Average);
/*!
* Create an instance of FLAC::Properties with the data read from the
* FLAC::File \a file.
*/
// BIC: remove
Properties(File *file, ReadStyle style = Average);
/*! /*!
* Destroys this FLAC::Properties instance. * Destroys this FLAC::Properties instance.
@@ -65,18 +55,7 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
virtual ~Properties(); virtual ~Properties();
/*! /*!
* Returns the length of the file in seconds. The length is rounded down to * Returns the length of the file in seconds. The length is rounded down to the nearest whole second.
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
* *
* \see lengthInMilliseconds() * \see lengthInMilliseconds()
*/ */
@@ -107,29 +86,17 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
virtual int channels() const; virtual int channels() const;
/*! /*!
* Returns the number of bits per audio sample as read from the FLAC * Returns the number of bits per audio sample as read from the FLAC identification header.
* identification header.
*/ */
int bitsPerSample() const; int bitsPerSample() const;
/*!
* Returns the sample width as read from the FLAC identification
* header.
*
* \note This method is just an alias of bitsPerSample().
*
* \deprecated
*/
TAGLIB_DEPRECATED int sampleWidth() const;
/*! /*!
* Return the number of sample frames. * Return the number of sample frames.
*/ */
unsigned long long sampleFrames() const; unsigned long long sampleFrames() const;
/*! /*!
* Returns the MD5 signature of the uncompressed audio stream as read * Returns the MD5 signature of the uncompressed audio stream as read from the stream info header.
* from the stream info header.
*/ */
ByteVector signature() const; ByteVector signature() const;

View File

@@ -33,7 +33,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace FLAC { namespace FLAC {
class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock { class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock {
@@ -75,7 +74,6 @@ class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock {
}; };
} // namespace FLAC } // namespace FLAC
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -35,26 +35,26 @@ using namespace IT;
class IT::File::FilePrivate { class IT::File::FilePrivate {
public: public:
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle) explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle) : tag(), properties(propertiesStyle) {}
: tag(), properties(propertiesStyle) {
}
Mod::Tag tag; Mod::Tag tag;
IT::Properties properties; IT::Properties properties;
}; };
IT::File::File(FileName file, bool readProperties, IT::File::File(FileName file, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) {
d(new FilePrivate(propertiesStyle)) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
IT::File::File(IOStream *stream, bool readProperties, IT::File::File(IOStream *stream, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) {
d(new FilePrivate(propertiesStyle)) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
IT::File::~File() { IT::File::~File() {
@@ -78,6 +78,7 @@ IT::Properties *IT::File::audioProperties() const {
} }
bool IT::File::save() { bool IT::File::save() {
if (readOnly()) { if (readOnly()) {
debug("IT::File::save() - Cannot save to a read only file."); debug("IT::File::save() - Cannot save to a read only file.");
return false; return false;
@@ -317,4 +318,5 @@ void IT::File::read(bool) {
comment.append(message); comment.append(message);
d->tag.setComment(comment.toString("\n")); d->tag.setComment(comment.toString("\n"));
d->tag.setTrackerName("Impulse Tracker"); d->tag.setTrackerName("Impulse Tracker");
} }

View File

@@ -31,7 +31,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace IT { namespace IT {
class TAGLIB_EXPORT File : public Mod::FileBase { class TAGLIB_EXPORT File : public Mod::FileBase {
@@ -39,9 +38,8 @@ class TAGLIB_EXPORT File : public Mod::FileBase {
/*! /*!
* Constructs a Impulse Tracker file from \a file. * Constructs a Impulse Tracker file from \a file.
* *
* \note In the current implementation, both \a readProperties and * \note In the current implementation, both \a readProperties and \a propertiesStyle are ignored.
* \a propertiesStyle are ignored. The audio properties are always * The audio properties are always read.
* read.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::ReadStyle propertiesStyle =
@@ -50,12 +48,10 @@ class TAGLIB_EXPORT File : public Mod::FileBase {
/*! /*!
* Constructs a Impulse Tracker file from \a stream. * Constructs a Impulse Tracker file from \a stream.
* *
* \note In the current implementation, both \a readProperties and * \note In the current implementation, both \a readProperties and \a propertiesStyle are ignored.
* \a propertiesStyle are ignored. The audio properties are always * The audio properties are always read.
* read.
* *
* \note TagLib will *not* take ownership of the stream, the caller is * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object.
* responsible for deleting it after the File object.
*/ */
File(IOStream *stream, bool readProperties = true, File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::ReadStyle propertiesStyle =
@@ -104,6 +100,7 @@ class TAGLIB_EXPORT File : public Mod::FileBase {
class FilePrivate; class FilePrivate;
FilePrivate *d; FilePrivate *d;
}; };
} // namespace IT } // namespace IT
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -65,9 +65,7 @@ class IT::Properties::PropertiesPrivate {
unsigned char pitchWheelDepth; unsigned char pitchWheelDepth;
}; };
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), d(new PropertiesPrivate()) {}
d(new PropertiesPrivate()) {
}
IT::Properties::~Properties() { IT::Properties::~Properties() {
delete d; delete d;

View File

@@ -35,26 +35,24 @@ using namespace Mod;
class Mod::File::FilePrivate { class Mod::File::FilePrivate {
public: public:
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle) explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle) : properties(propertiesStyle) {}
: properties(propertiesStyle) {
}
Mod::Tag tag; Mod::Tag tag;
Mod::Properties properties; Mod::Properties properties;
}; };
Mod::File::File(FileName file, bool readProperties, Mod::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) {
AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file),
d(new FilePrivate(propertiesStyle)) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
Mod::File::File(IOStream *stream, bool readProperties, Mod::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) {
AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream),
d(new FilePrivate(propertiesStyle)) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
Mod::File::~File() { Mod::File::~File() {
@@ -78,6 +76,7 @@ PropertyMap Mod::File::setProperties(const PropertyMap &properties) {
} }
bool Mod::File::save() { bool Mod::File::save() {
if (readOnly()) { if (readOnly()) {
debug("Mod::File::save() - Cannot save to a read only file."); debug("Mod::File::save() - Cannot save to a read only file.");
return false; return false;
@@ -96,9 +95,11 @@ bool Mod::File::save() {
seek(8, Current); seek(8, Current);
} }
return true; return true;
} }
void Mod::File::read(bool) { void Mod::File::read(bool) {
if (!isOpen()) if (!isOpen())
return; return;
@@ -176,4 +177,5 @@ void Mod::File::read(bool) {
READ_BYTE(d->properties.setLengthInPatterns); READ_BYTE(d->properties.setLengthInPatterns);
d->tag.setComment(comment.toString("\n")); d->tag.setComment(comment.toString("\n"));
} }

View File

@@ -35,7 +35,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace Mod { namespace Mod {
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::Mod::FileBase { class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::Mod::FileBase {
@@ -43,27 +42,21 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::Mod::FileBase {
/*! /*!
* Constructs a Protracker file from \a file. * Constructs a Protracker file from \a file.
* *
* \note In the current implementation, both \a readProperties and * \note In the current implementation, both \a readProperties and \a propertiesStyle are ignored.
* \a propertiesStyle are ignored. The audio properties are always * The audio properties are always read.
* read.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*! /*!
* Constructs a Protracker file from \a stream. * Constructs a Protracker file from \a stream.
* *
* \note In the current implementation, both \a readProperties and * \note In the current implementation, both \a readProperties and \a propertiesStyle are ignored.
* \a propertiesStyle are ignored. The audio properties are always * The audio properties are always read.
* read.
* *
* \note TagLib will *not* take ownership of the stream, the caller is * \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object. * responsible for deleting it after the File object.
*/ */
File(IOStream *stream, bool readProperties = true, File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
@@ -84,8 +77,7 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::Mod::FileBase {
*/ */
PropertyMap setProperties(const PropertyMap &); PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the Mod::Properties for this file. If no audio properties * Returns the Mod::Properties for this file. If no audio properties were read then this will return a null pointer.
* were read then this will return a null pointer.
*/ */
Mod::Properties *audioProperties() const; Mod::Properties *audioProperties() const;
@@ -108,7 +100,6 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::Mod::FileBase {
}; };
} // namespace Mod } // namespace Mod
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -30,11 +30,9 @@
using namespace Strawberry_TagLib::TagLib; using namespace Strawberry_TagLib::TagLib;
using namespace Mod; using namespace Mod;
Mod::FileBase::FileBase(FileName file) : Strawberry_TagLib::TagLib::File(file) { Mod::FileBase::FileBase(FileName file) : Strawberry_TagLib::TagLib::File(file) {}
}
Mod::FileBase::FileBase(IOStream *stream) : Strawberry_TagLib::TagLib::File(stream) { Mod::FileBase::FileBase(IOStream *stream) : Strawberry_TagLib::TagLib::File(stream) {}
}
void Mod::FileBase::writeString(const String &s, unsigned long size, char padding) { void Mod::FileBase::writeString(const String &s, unsigned long size, char padding) {
ByteVector data(s.data(String::Latin1)); ByteVector data(s.data(String::Latin1));
@@ -43,6 +41,7 @@ void Mod::FileBase::writeString(const String &s, unsigned long size, char paddin
} }
bool Mod::FileBase::readString(String &s, unsigned long size) { bool Mod::FileBase::readString(String &s, unsigned long size) {
ByteVector data(readBlock(size)); ByteVector data(readBlock(size));
if (data.size() < size) return false; if (data.size() < size) return false;
int index = data.find((char)0); int index = data.find((char)0);
@@ -53,6 +52,7 @@ bool Mod::FileBase::readString(String &s, unsigned long size) {
s = data; s = data;
return true; return true;
} }
void Mod::FileBase::writeByte(unsigned char _byte) { void Mod::FileBase::writeByte(unsigned char _byte) {
@@ -77,36 +77,46 @@ void Mod::FileBase::writeU32B(unsigned long number) {
} }
bool Mod::FileBase::readByte(unsigned char &_byte) { bool Mod::FileBase::readByte(unsigned char &_byte) {
ByteVector data(readBlock(1)); ByteVector data(readBlock(1));
if (data.size() < 1) return false; if (data.size() < 1) return false;
_byte = data[0]; _byte = data[0];
return true; return true;
} }
bool Mod::FileBase::readU16L(unsigned short &number) { bool Mod::FileBase::readU16L(unsigned short &number) {
ByteVector data(readBlock(2)); ByteVector data(readBlock(2));
if (data.size() < 2) return false; if (data.size() < 2) return false;
number = data.toUShort(false); number = data.toUShort(false);
return true; return true;
} }
bool Mod::FileBase::readU32L(unsigned long &number) { bool Mod::FileBase::readU32L(unsigned long &number) {
ByteVector data(readBlock(4)); ByteVector data(readBlock(4));
if (data.size() < 4) return false; if (data.size() < 4) return false;
number = data.toUInt(false); number = data.toUInt(false);
return true; return true;
} }
bool Mod::FileBase::readU16B(unsigned short &number) { bool Mod::FileBase::readU16B(unsigned short &number) {
ByteVector data(readBlock(2)); ByteVector data(readBlock(2));
if (data.size() < 2) return false; if (data.size() < 2) return false;
number = data.toUShort(true); number = data.toUShort(true);
return true; return true;
} }
bool Mod::FileBase::readU32B(unsigned long &number) { bool Mod::FileBase::readU32B(unsigned long &number) {
ByteVector data(readBlock(4)); ByteVector data(readBlock(4));
if (data.size() < 4) return false; if (data.size() < 4) return false;
number = data.toUInt(true); number = data.toUInt(true);
return true; return true;
} }

View File

@@ -36,7 +36,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace Mod { namespace Mod {
class TAGLIB_EXPORT FileBase : public Strawberry_TagLib::TagLib::File { class TAGLIB_EXPORT FileBase : public Strawberry_TagLib::TagLib::File {
@@ -60,7 +59,6 @@ class TAGLIB_EXPORT FileBase : public Strawberry_TagLib::TagLib::File {
}; };
} // namespace Mod } // namespace Mod
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -31,18 +31,14 @@ using namespace Mod;
class Mod::Properties::PropertiesPrivate { class Mod::Properties::PropertiesPrivate {
public: public:
PropertiesPrivate() : channels(0), PropertiesPrivate() : channels(0), instrumentCount(0), lengthInPatterns(0) {}
instrumentCount(0),
lengthInPatterns(0) {
}
int channels; int channels;
unsigned int instrumentCount; unsigned int instrumentCount;
unsigned char lengthInPatterns; unsigned char lengthInPatterns;
}; };
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), d(new PropertiesPrivate()) {
d(new PropertiesPrivate()) {
} }
Mod::Properties::~Properties() { Mod::Properties::~Properties() {

View File

@@ -31,7 +31,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace Mod { namespace Mod {
class TAGLIB_EXPORT Properties : public AudioProperties { class TAGLIB_EXPORT Properties : public AudioProperties {
@@ -65,7 +64,6 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
}; };
} // namespace Mod } // namespace Mod
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -33,17 +33,14 @@ using namespace Mod;
class Mod::Tag::TagPrivate { class Mod::Tag::TagPrivate {
public: public:
TagPrivate() { TagPrivate() {}
}
String title; String title;
String comment; String comment;
String trackerName; String trackerName;
}; };
Mod::Tag::Tag() : Strawberry_TagLib::TagLib::Tag(), Mod::Tag::Tag() : Strawberry_TagLib::TagLib::Tag(), d(new TagPrivate()) {}
d(new TagPrivate()) {
}
Mod::Tag::~Tag() { Mod::Tag::~Tag() {
delete d; delete d;
@@ -109,15 +106,18 @@ void Mod::Tag::setTrackerName(const String &trackerName) {
} }
PropertyMap Mod::Tag::properties() const { PropertyMap Mod::Tag::properties() const {
PropertyMap properties; PropertyMap properties;
properties["TITLE"] = d->title; properties["TITLE"] = d->title;
properties["COMMENT"] = d->comment; properties["COMMENT"] = d->comment;
if (!(d->trackerName.isEmpty())) if (!(d->trackerName.isEmpty()))
properties["TRACKERNAME"] = d->trackerName; properties["TRACKERNAME"] = d->trackerName;
return properties; return properties;
} }
PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps) { PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps) {
PropertyMap properties(origProps); PropertyMap properties(origProps);
properties.removeEmpty(); properties.removeEmpty();
StringList oneValueSet; StringList oneValueSet;
@@ -151,4 +151,5 @@ PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps) {
properties[*it].erase(properties[*it].begin()); properties[*it].erase(properties[*it].begin());
} }
return properties; return properties;
} }

View File

@@ -30,20 +30,19 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace Mod { namespace Mod {
/*! /*!
* Tags for module files (Mod, S3M, IT, XM). * Tags for module files (Mod, S3M, IT, XM).
* *
* Note that only the \a title is supported as such by most * Note that only the \a title is supported as such by most module file formats.
* module file formats. Except for XM files the \a trackerName * Except for XM files the \a trackerName is derived from the file format or the flavour of the file format.
* is derived from the file format or the flavour of the file * For XM files it is stored in the file.
* format. For XM files it is stored in the file.
* *
* The \a comment tag is not strictly supported by module files, * The \a comment tag is not strictly supported by module files,
* but it is common practice to abuse instrument/sample/pattern * but it is common practice to abuse instrument/sample/pattern names as multiline comments.
* names as multiline comments. TagLib does so as well. * TagLib does so as well.
*
*/ */
class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
public: public:
@@ -51,8 +50,7 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual ~Tag(); virtual ~Tag();
/*! /*!
* Returns the track name; if no track name is present in the tag * Returns the track name; if no track name is present in the tag String::null will be returned.
* String::null will be returned.
*/ */
virtual String title() const; virtual String title() const;
@@ -68,8 +66,7 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
/*! /*!
* Returns the track comment derived from the instrument/sample/pattern * Returns the track comment derived from the instrument/sample/pattern
* names; if no comment is present in the tag String::null will be * names; if no comment is present in the tag String::null will be returned.
* returned.
*/ */
virtual String comment() const; virtual String comment() const;
@@ -91,19 +88,17 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
/*! /*!
* Returns the name of the tracker used to create/edit the module file. * 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 * 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 * (Mod, S3M, IT) this is derived from the file type or the flavour of the file type.
* the file type. Therefore only XM files might have an empty * Therefore only XM files might have an empty (String::null) tracker name.
* (String::null) tracker name.
*/ */
String trackerName() const; String trackerName() const;
/*! /*!
* Sets the title to \a title. If \a title is String::null then this * Sets the title to \a title.
* value will be cleared. * If \a title is String::null then this value will be cleared.
* *
* The length limits per file type are (1 character = 1 byte): * The length limits per file type are (1 character = 1 byte):
* Mod 20 characters, S3M 27 characters, IT 25 characters and XM 20 * Mod 20 characters, S3M 27 characters, IT 25 characters and XM 20 characters.
* characters.
*/ */
virtual void setTitle(const String &title); virtual void setTitle(const String &title);
@@ -118,21 +113,18 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual void setAlbum(const String &album); virtual void setAlbum(const String &album);
/*! /*!
* Sets the comment to \a comment. If \a comment is String::null then * Sets the comment to \a comment.
* this value will be cleared. * If \a comment is String::null then this value will be cleared.
* *
* Note that module file formats don't actually support a comment tag. * Note that module file formats don't actually support a comment tag.
* Instead the names of instruments/patterns/samples are abused as * Instead the names of instruments/patterns/samples are abused as a multiline comment.
* a multiline comment. Because of this the number of lines in a * Because of this the number of lines in a module file is fixed to the number of instruments/patterns/samples.
* module file is fixed to the number of instruments/patterns/samples.
* *
* Also note that the instrument/pattern/sample name length is limited * Also note that the instrument/pattern/sample name length is limited an thus the line length in comments are limited.
* an thus the line length in comments are limited. Too big comments * Too big comments will be truncated.
* will be truncated.
* *
* The line length limits per file type are (1 character = 1 byte): * The line length limits per file type are (1 character = 1 byte):
* Mod 22 characters, S3M 27 characters, IT 25 characters and XM 22 * Mod 22 characters, S3M 27 characters, IT 25 characters and XM 22 characters.
* characters.
*/ */
virtual void setComment(const String &comment); virtual void setComment(const String &comment);
@@ -152,14 +144,13 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual void setTrack(unsigned int track); virtual void setTrack(unsigned int track);
/*! /*!
* Sets the tracker name to \a trackerName. If \a trackerName is * Sets the tracker name to \a trackerName.
* String::null then this value will be cleared. * If \a trackerName is String::null then this value will be cleared.
* *
* Note that only XM files support this tag. Setting the * Note that only XM files support this tag.
* tracker name for other module file formats will be ignored. * Setting the tracker name for other module file formats will be ignored.
* *
* The length of this tag is limited to 20 characters (1 character * The length of this tag is limited to 20 characters (1 character = 1 byte).
* = 1 byte).
*/ */
void setTrackerName(const String &trackerName); void setTrackerName(const String &trackerName);
@@ -171,11 +162,8 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
/*! /*!
* Implements the unified property interface -- import function. * Implements the unified property interface -- import function.
* Because of the limitations of the module file tag, any tags besides * Because of the limitations of the module file tag, any tags besides COMMENT, TITLE and, if it is an XM file, TRACKERNAME, will be returned.
* COMMENT, TITLE and, if it is an XM file, TRACKERNAME, will be * Additionally, if the map contains tags with multiple values, all but the first will be contained in the returned map of unsupported properties.
* returned. Additionally, if the map contains tags with multiple values,
* all but the first will be contained in the returned map of unsupported
* properties.
*/ */
PropertyMap setProperties(const PropertyMap &); PropertyMap setProperties(const PropertyMap &);
@@ -188,7 +176,6 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
}; };
} // namespace Mod } // namespace Mod
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -31,13 +31,10 @@
using namespace Strawberry_TagLib::TagLib; using namespace Strawberry_TagLib::TagLib;
const char *MP4::Atom::containers[11] = { const char *MP4::Atom::containers[11] = { "moov", "udta", "mdia", "meta", "ilst", "stbl", "minf", "moof", "traf", "trak", "stsd" };
"moov", "udta", "mdia", "meta", "ilst",
"stbl", "minf", "moof", "traf", "trak",
"stsd"
};
MP4::Atom::Atom(File *file) { MP4::Atom::Atom(File *file) {
children.setAutoDelete(true); children.setAutoDelete(true);
offset = file->tell(); offset = file->tell();
@@ -100,14 +97,14 @@ MP4::Atom::Atom(File *file) {
} }
file->seek(offset + length); file->seek(offset + length);
} }
MP4::Atom::~Atom() { MP4::Atom::~Atom() {}
}
MP4::Atom * MP4::Atom *MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4) {
MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4) {
if (name1 == 0) { if (!name1) {
return this; return this;
} }
for (AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) { for (AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
@@ -116,10 +113,11 @@ MP4::Atom::find(const char *name1, const char *name2, const char *name3, const c
} }
} }
return nullptr; return nullptr;
} }
MP4::AtomList MP4::AtomList MP4::Atom::findall(const char *_name, bool recursive) {
MP4::Atom::findall(const char *_name, bool recursive) {
MP4::AtomList result; MP4::AtomList result;
for (AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) { for (AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
if ((*it)->name == _name) { if ((*it)->name == _name) {
@@ -130,11 +128,13 @@ MP4::Atom::findall(const char *_name, bool recursive) {
} }
} }
return result; return result;
} }
bool MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3) { bool MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3) {
path.append(this); path.append(this);
if (name1 == 0) { if (!name1) {
return true; return true;
} }
for (AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) { for (AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
@@ -143,9 +143,11 @@ bool MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2,
} }
} }
return false; return false;
} }
MP4::Atoms::Atoms(File *file) { MP4::Atoms::Atoms(File *file) {
atoms.setAutoDelete(true); atoms.setAutoDelete(true);
file->seek(0, File::End); file->seek(0, File::End);
@@ -157,23 +159,24 @@ MP4::Atoms::Atoms(File *file) {
if (atom->length == 0) if (atom->length == 0)
break; break;
} }
} }
MP4::Atoms::~Atoms() { MP4::Atoms::~Atoms() {}
}
MP4::Atom *MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4) {
MP4::Atom *
MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4) {
for (AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) { for (AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) {
if ((*it)->name == name1) { if ((*it)->name == name1) {
return (*it)->find(name2, name3, name4); return (*it)->find(name2, name3, name4);
} }
} }
return nullptr; return nullptr;
} }
MP4::AtomList MP4::AtomList MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4) {
MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4) {
MP4::AtomList path; MP4::AtomList path;
for (AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) { for (AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) {
if ((*it)->name == name1) { if ((*it)->name == name1) {
@@ -184,4 +187,5 @@ MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const
} }
} }
return path; return path;
} }

View File

@@ -35,7 +35,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace MP4 { namespace MP4 {
class Atom; class Atom;
@@ -78,6 +77,7 @@ class Atom {
public: public:
Atom(File *file); Atom(File *file);
~Atom(); ~Atom();
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0); Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0); bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
AtomList findall(const char *name, bool recursive = false); AtomList findall(const char *name, bool recursive = false);
@@ -96,13 +96,13 @@ class Atoms {
public: public:
Atoms(File *file); Atoms(File *file);
~Atoms(); ~Atoms();
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0); Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0); AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
AtomList atoms; AtomList atoms;
}; };
} // namespace MP4 } // namespace MP4
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -32,8 +32,7 @@ using namespace Strawberry_TagLib::TagLib;
class MP4::CoverArt::CoverArtPrivate : public RefCounter { class MP4::CoverArt::CoverArtPrivate : public RefCounter {
public: public:
CoverArtPrivate() : RefCounter(), CoverArtPrivate() : RefCounter(), format(MP4::CoverArt::JPEG) {}
format(MP4::CoverArt::JPEG) {}
Format format; Format format;
ByteVector data; ByteVector data;

View File

@@ -33,7 +33,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace MP4 { namespace MP4 {
class TAGLIB_EXPORT CoverArt { class TAGLIB_EXPORT CoverArt {
@@ -78,7 +77,6 @@ class TAGLIB_EXPORT CoverArt {
typedef List<CoverArt> CoverArtList; typedef List<CoverArt> CoverArtList;
} // namespace MP4 } // namespace MP4
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -36,6 +36,7 @@ using namespace Strawberry_TagLib::TagLib;
namespace { namespace {
bool checkValid(const MP4::AtomList &list) { bool checkValid(const MP4::AtomList &list) {
for (MP4::AtomList::ConstIterator it = list.begin(); it != list.end(); ++it) { for (MP4::AtomList::ConstIterator it = list.begin(); it != list.end(); ++it) {
if ((*it)->length == 0) if ((*it)->length == 0)
@@ -46,14 +47,13 @@ bool checkValid(const MP4::AtomList &list) {
} }
return true; return true;
} }
} // namespace } // namespace
class MP4::File::FilePrivate { class MP4::File::FilePrivate {
public: public:
FilePrivate() : tag(0), FilePrivate() : tag(nullptr), atoms(nullptr), properties(nullptr) {}
atoms(0),
properties(0) {}
~FilePrivate() { ~FilePrivate() {
delete atoms; delete atoms;
@@ -71,26 +71,30 @@ class MP4::File::FilePrivate {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool MP4::File::isSupported(IOStream *stream) { bool MP4::File::isSupported(IOStream *stream) {
// An MP4 file has to have an "ftyp" box first. // An MP4 file has to have an "ftyp" box first.
const ByteVector id = Utils::readHeader(stream, 8, false); const ByteVector id = Utils::readHeader(stream, 8, false);
return id.containsAt("ftyp", 4); return id.containsAt("ftyp", 4);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) {
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) {
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
MP4::File::~File() { MP4::File::~File() {
@@ -120,6 +124,7 @@ MP4::File::audioProperties() const {
} }
void MP4::File::read(bool readProperties) { void MP4::File::read(bool readProperties) {
if (!isValid()) if (!isValid())
return; return;
@@ -139,9 +144,11 @@ void MP4::File::read(bool readProperties) {
if (readProperties) { if (readProperties) {
d->properties = new Properties(this, d->atoms); d->properties = new Properties(this, d->atoms);
} }
} }
bool MP4::File::save() { bool MP4::File::save() {
if (readOnly()) { if (readOnly()) {
debug("MP4::File::save() -- File is read only."); debug("MP4::File::save() -- File is read only.");
return false; return false;
@@ -153,8 +160,9 @@ bool MP4::File::save() {
} }
return d->tag->save(); return d->tag->save();
} }
bool MP4::File::hasMP4Tag() const { bool MP4::File::hasMP4Tag() const {
return (d->atoms->find("moov", "udta", "meta", "ilst") != 0); return (d->atoms->find("moov", "udta", "meta", "ilst") != nullptr);
} }

View File

@@ -49,17 +49,16 @@ class Atoms;
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
public: public:
/*! /*!
* Constructs an MP4 file from \a file. If \a readProperties is true the * Constructs an MP4 file from \a file.
* file's audio properties will also be read. * If \a readProperties is true the file's audio properties will also be read.
* *
* \note In the current implementation, \a propertiesStyle is ignored. * \note In the current implementation, \a propertiesStyle is ignored.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average);
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*! /*!
* Constructs an MP4 file from \a stream. If \a readProperties is true the * Constructs an MP4 file from \a stream.
* file's audio properties will also be read. * If \a readProperties is true the file's audio properties will also be read.
* *
* \note TagLib will *not* take ownership of the stream, the caller is * \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object. * responsible for deleting it after the File object.
@@ -77,12 +76,10 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
/*! /*!
* Returns a pointer to the MP4 tag of the file. * Returns a pointer to the MP4 tag of the file.
* *
* MP4::Tag implements the tag interface, so this serves as the * MP4::Tag implements the tag interface, so this serves as the reimplementation of TagLib::File::tag().
* reimplementation of TagLib::File::tag().
* *
* \note The Tag <b>is still</b> owned by the MP4::File and should not be * \note The Tag <b>is still</b> owned by the MP4::File and should not be deleted by the user.
* deleted by the user. It will be deleted when the file (object) is * It will be deleted when the file (object) is destroyed.
* destroyed.
*/ */
Tag *tag() const; Tag *tag() const;
@@ -92,8 +89,7 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
PropertyMap properties() const; PropertyMap properties() const;
/*! /*!
* Removes unsupported properties. Forwards to the actual Tag's * Removes unsupported properties. Forwards to the actual Tag's removeUnsupportedProperties() function.
* removeUnsupportedProperties() function.
*/ */
void removeUnsupportedProperties(const StringList &properties); void removeUnsupportedProperties(const StringList &properties);
@@ -115,17 +111,14 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
bool save(); bool save();
/*! /*!
* Returns whether or not the file on disk actually has an MP4 tag, or the * Returns whether or not the file on disk actually has an MP4 tag, or the file has a Metadata Item List (ilst) atom.
* file has a Metadata Item List (ilst) atom.
*/ */
bool hasMP4Tag() const; bool hasMP4Tag() const;
/*! /*!
* Returns whether or not the given \a stream can be opened as an ASF * Returns whether or not the given \a stream can be opened as an ASF file.
* file.
* *
* \note This method is designed to do a quick check. The result may * \note This method is designed to do a quick check. The result may not necessarily be correct.
* not necessarily be correct.
*/ */
static bool isSupported(IOStream *stream); static bool isSupported(IOStream *stream);
@@ -137,7 +130,6 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
}; };
} // namespace MP4 } // namespace MP4
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -32,9 +32,7 @@ using namespace Strawberry_TagLib::TagLib;
class MP4::Item::ItemPrivate : public RefCounter { class MP4::Item::ItemPrivate : public RefCounter {
public: public:
ItemPrivate() : RefCounter(), ItemPrivate() : RefCounter(), valid(true), atomDataType(TypeUndefined) {}
valid(true),
atomDataType(TypeUndefined) {}
bool valid; bool valid;
AtomDataType atomDataType; AtomDataType atomDataType;
@@ -59,8 +57,7 @@ MP4::Item::Item(const Item &item) : d(item.d) {
d->ref(); d->ref();
} }
MP4::Item & MP4::Item &MP4::Item::operator=(const Item &item) {
MP4::Item::operator=(const Item &item) {
Item(item).swap(*this); Item(item).swap(*this);
return *this; return *this;
} }
@@ -129,38 +126,31 @@ int MP4::Item::toInt() const {
return d->m_int; return d->m_int;
} }
unsigned char unsigned char MP4::Item::toByte() const {
MP4::Item::toByte() const {
return d->m_byte; return d->m_byte;
} }
unsigned int unsigned int MP4::Item::toUInt() const {
MP4::Item::toUInt() const {
return d->m_uint; return d->m_uint;
} }
long long long long MP4::Item::toLongLong() const {
MP4::Item::toLongLong() const {
return d->m_longlong; return d->m_longlong;
} }
MP4::Item::IntPair MP4::Item::IntPair MP4::Item::toIntPair() const {
MP4::Item::toIntPair() const {
return d->m_intPair; return d->m_intPair;
} }
StringList StringList MP4::Item::toStringList() const {
MP4::Item::toStringList() const {
return d->m_stringList; return d->m_stringList;
} }
ByteVectorList ByteVectorList MP4::Item::toByteVectorList() const {
MP4::Item::toByteVectorList() const {
return d->m_byteVectorList; return d->m_byteVectorList;
} }
MP4::CoverArtList MP4::CoverArtList MP4::Item::toCoverArtList() const {
MP4::Item::toCoverArtList() const {
return d->m_coverArtList; return d->m_coverArtList;
} }

View File

@@ -32,7 +32,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace MP4 { namespace MP4 {
class TAGLIB_EXPORT Item { class TAGLIB_EXPORT Item {
@@ -87,7 +86,6 @@ class TAGLIB_EXPORT Item {
}; };
} // namespace MP4 } // namespace MP4
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -54,8 +54,7 @@ class MP4::Properties::PropertiesPrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) : AudioProperties(style), MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) : AudioProperties(style), d(new PropertiesPrivate()) {
d(new PropertiesPrivate()) {
read(file, atoms); read(file, atoms);
} }
@@ -71,10 +70,6 @@ int MP4::Properties::sampleRate() const {
return d->sampleRate; return d->sampleRate;
} }
int MP4::Properties::length() const {
return lengthInSeconds();
}
int MP4::Properties::lengthInSeconds() const { int MP4::Properties::lengthInSeconds() const {
return d->length / 1000; return d->length / 1000;
} }
@@ -105,13 +100,14 @@ MP4::Properties::codec() const {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void MP4::Properties::read(File *file, Atoms *atoms) { void MP4::Properties::read(File *file, Atoms *atoms) {
MP4::Atom *moov = atoms->find("moov"); MP4::Atom *moov = atoms->find("moov");
if (!moov) { if (!moov) {
debug("MP4: Atom 'moov' not found"); debug("MP4: Atom 'moov' not found");
return; return;
} }
MP4::Atom *trak = 0; MP4::Atom *trak = nullptr;
ByteVector data; ByteVector data;
const MP4::AtomList trakList = moov->findall("trak"); const MP4::AtomList trakList = moov->findall("trak");
@@ -127,7 +123,7 @@ void MP4::Properties::read(File *file, Atoms *atoms) {
if (data.containsAt("soun", 16)) { if (data.containsAt("soun", 16)) {
break; break;
} }
trak = 0; trak = nullptr;
} }
if (!trak) { if (!trak) {
debug("MP4: No audio tracks"); debug("MP4: No audio tracks");
@@ -207,4 +203,5 @@ void MP4::Properties::read(File *file, Atoms *atoms) {
if (drms) { if (drms) {
d->encrypted = true; d->encrypted = true;
} }
} }

View File

@@ -31,7 +31,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace MP4 { namespace MP4 {
class Atoms; class Atoms;
@@ -50,18 +49,7 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
virtual ~Properties(); virtual ~Properties();
/*! /*!
* Returns the length of the file in seconds. The length is rounded down to * Returns the length of the file in seconds. The length is rounded down to the nearest whole second.
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
* *
* \see lengthInMilliseconds() * \see lengthInMilliseconds()
*/ */
@@ -114,7 +102,6 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
}; };
} // namespace MP4 } // namespace MP4
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -34,18 +34,17 @@ using namespace Strawberry_TagLib::TagLib;
class MP4::Tag::TagPrivate { class MP4::Tag::TagPrivate {
public: public:
TagPrivate() : file(0), TagPrivate() : file(nullptr), atoms(nullptr) {}
atoms(0) {}
Strawberry_TagLib::TagLib::File *file; Strawberry_TagLib::TagLib::File *file;
Atoms *atoms; Atoms *atoms;
ItemMap items; ItemMap items;
}; };
MP4::Tag::Tag() : d(new TagPrivate()) { MP4::Tag::Tag() : d(new TagPrivate()) {}
}
MP4::Tag::Tag(Strawberry_TagLib::TagLib::File *file, MP4::Atoms *atoms) : d(new TagPrivate()) { MP4::Tag::Tag(Strawberry_TagLib::TagLib::File *file, MP4::Atoms *atoms) : d(new TagPrivate()) {
d->file = file; d->file = file;
d->atoms = atoms; d->atoms = atoms;
@@ -113,8 +112,8 @@ MP4::Tag::~Tag() {
delete d; delete d;
} }
MP4::AtomDataList MP4::AtomDataList MP4::Tag::parseData2(const MP4::Atom *atom, int expectedFlags, bool freeForm) {
MP4::Tag::parseData2(const MP4::Atom *atom, int expectedFlags, bool freeForm) {
AtomDataList result; AtomDataList result;
ByteVector data = d->file->readBlock(atom->length - 8); ByteVector data = d->file->readBlock(atom->length - 8);
int i = 0; int i = 0;
@@ -152,47 +151,58 @@ MP4::Tag::parseData2(const MP4::Atom *atom, int expectedFlags, bool freeForm) {
i++; i++;
} }
return result; return result;
} }
ByteVectorList ByteVectorList MP4::Tag::parseData(const MP4::Atom *atom, int expectedFlags, bool freeForm) {
MP4::Tag::parseData(const MP4::Atom *atom, int expectedFlags, bool freeForm) {
AtomDataList data = parseData2(atom, expectedFlags, freeForm); AtomDataList data = parseData2(atom, expectedFlags, freeForm);
ByteVectorList result; ByteVectorList result;
for (AtomDataList::ConstIterator it = data.begin(); it != data.end(); ++it) { for (AtomDataList::ConstIterator it = data.begin(); it != data.end(); ++it) {
result.append(it->data); result.append(it->data);
} }
return result; return result;
} }
void MP4::Tag::parseInt(const MP4::Atom *atom) { void MP4::Tag::parseInt(const MP4::Atom *atom) {
ByteVectorList data = parseData(atom); ByteVectorList data = parseData(atom);
if (!data.isEmpty()) { if (!data.isEmpty()) {
addItem(atom->name, (int)data[0].toShort()); addItem(atom->name, (int)data[0].toShort());
} }
} }
void MP4::Tag::parseUInt(const MP4::Atom *atom) { void MP4::Tag::parseUInt(const MP4::Atom *atom) {
ByteVectorList data = parseData(atom); ByteVectorList data = parseData(atom);
if (!data.isEmpty()) { if (!data.isEmpty()) {
addItem(atom->name, data[0].toUInt()); addItem(atom->name, data[0].toUInt());
} }
} }
void MP4::Tag::parseLongLong(const MP4::Atom *atom) { void MP4::Tag::parseLongLong(const MP4::Atom *atom) {
ByteVectorList data = parseData(atom); ByteVectorList data = parseData(atom);
if (!data.isEmpty()) { if (!data.isEmpty()) {
addItem(atom->name, data[0].toLongLong()); addItem(atom->name, data[0].toLongLong());
} }
} }
void MP4::Tag::parseByte(const MP4::Atom *atom) { void MP4::Tag::parseByte(const MP4::Atom *atom) {
ByteVectorList data = parseData(atom); ByteVectorList data = parseData(atom);
if (!data.isEmpty()) { if (!data.isEmpty()) {
addItem(atom->name, static_cast<unsigned char>(data[0].at(0))); addItem(atom->name, static_cast<unsigned char>(data[0].at(0)));
} }
} }
void MP4::Tag::parseGnre(const MP4::Atom *atom) { void MP4::Tag::parseGnre(const MP4::Atom *atom) {
ByteVectorList data = parseData(atom); ByteVectorList data = parseData(atom);
if (!data.isEmpty()) { if (!data.isEmpty()) {
int idx = (int)data[0].toShort(); int idx = (int)data[0].toShort();
@@ -200,26 +210,32 @@ void MP4::Tag::parseGnre(const MP4::Atom *atom) {
addItem("\251gen", StringList(ID3v1::genre(idx - 1))); addItem("\251gen", StringList(ID3v1::genre(idx - 1)));
} }
} }
} }
void MP4::Tag::parseIntPair(const MP4::Atom *atom) { void MP4::Tag::parseIntPair(const MP4::Atom *atom) {
ByteVectorList data = parseData(atom); ByteVectorList data = parseData(atom);
if (!data.isEmpty()) { if (!data.isEmpty()) {
const int a = data[0].toShort(2U); const int a = data[0].toShort(2U);
const int b = data[0].toShort(4U); const int b = data[0].toShort(4U);
addItem(atom->name, MP4::Item(a, b)); addItem(atom->name, MP4::Item(a, b));
} }
} }
void MP4::Tag::parseBool(const MP4::Atom *atom) { void MP4::Tag::parseBool(const MP4::Atom *atom) {
ByteVectorList data = parseData(atom); ByteVectorList data = parseData(atom);
if (!data.isEmpty()) { if (!data.isEmpty()) {
bool value = data[0].size() ? data[0][0] != '\0' : false; bool value = data[0].size() ? data[0][0] != '\0' : false;
addItem(atom->name, value); addItem(atom->name, value);
} }
} }
void MP4::Tag::parseText(const MP4::Atom *atom, int expectedFlags) { void MP4::Tag::parseText(const MP4::Atom *atom, int expectedFlags) {
ByteVectorList data = parseData(atom, expectedFlags); ByteVectorList data = parseData(atom, expectedFlags);
if (!data.isEmpty()) { if (!data.isEmpty()) {
StringList value; StringList value;
@@ -228,9 +244,11 @@ void MP4::Tag::parseText(const MP4::Atom *atom, int expectedFlags) {
} }
addItem(atom->name, value); addItem(atom->name, value);
} }
} }
void MP4::Tag::parseFreeForm(const MP4::Atom *atom) { void MP4::Tag::parseFreeForm(const MP4::Atom *atom) {
AtomDataList data = parseData2(atom, -1, true); AtomDataList data = parseData2(atom, -1, true);
if (data.size() > 2) { if (data.size() > 2) {
AtomDataList::ConstIterator itBegin = data.begin(); AtomDataList::ConstIterator itBegin = data.begin();
@@ -267,9 +285,11 @@ void MP4::Tag::parseFreeForm(const MP4::Atom *atom) {
addItem(name, item); addItem(name, item);
} }
} }
} }
void MP4::Tag::parseCovr(const MP4::Atom *atom) { void MP4::Tag::parseCovr(const MP4::Atom *atom) {
MP4::CoverArtList value; MP4::CoverArtList value;
ByteVector data = d->file->readBlock(atom->length - 8); ByteVector data = d->file->readBlock(atom->length - 8);
unsigned int pos = 0; unsigned int pos = 0;
@@ -299,106 +319,117 @@ void MP4::Tag::parseCovr(const MP4::Atom *atom) {
} }
if (!value.isEmpty()) if (!value.isEmpty())
addItem(atom->name, value); addItem(atom->name, value);
} }
ByteVector ByteVector MP4::Tag::padIlst(const ByteVector &data, int length) const {
MP4::Tag::padIlst(const ByteVector &data, int length) const {
if (length == -1) { if (length == -1) {
length = ((data.size() + 1023) & ~1023) - data.size(); length = ((data.size() + 1023) & ~1023) - data.size();
} }
return renderAtom("free", ByteVector(length, '\1')); return renderAtom("free", ByteVector(length, '\1'));
} }
ByteVector ByteVector MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data) const {
MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data) const {
return ByteVector::fromUInt(data.size() + 8) + name + data; return ByteVector::fromUInt(data.size() + 8) + name + data;
} }
ByteVector ByteVector MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data) const {
MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data) const {
ByteVector result; ByteVector result;
for (ByteVectorList::ConstIterator it = data.begin(); it != data.end(); ++it) { for (ByteVectorList::ConstIterator it = data.begin(); it != data.end(); ++it) {
result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + *it)); result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + *it));
} }
return renderAtom(name, result); return renderAtom(name, result);
} }
ByteVector ByteVector MP4::Tag::renderBool(const ByteVector &name, const MP4::Item &item) const {
MP4::Tag::renderBool(const ByteVector &name, const MP4::Item &item) const {
ByteVectorList data; ByteVectorList data;
data.append(ByteVector(1, item.toBool() ? '\1' : '\0')); data.append(ByteVector(1, item.toBool() ? '\1' : '\0'));
return renderData(name, TypeInteger, data); return renderData(name, TypeInteger, data);
} }
ByteVector ByteVector MP4::Tag::renderInt(const ByteVector &name, const MP4::Item &item) const {
MP4::Tag::renderInt(const ByteVector &name, const MP4::Item &item) const {
ByteVectorList data; ByteVectorList data;
data.append(ByteVector::fromShort(item.toInt())); data.append(ByteVector::fromShort(item.toInt()));
return renderData(name, TypeInteger, data); return renderData(name, TypeInteger, data);
} }
ByteVector ByteVector MP4::Tag::renderUInt(const ByteVector &name, const MP4::Item &item) const {
MP4::Tag::renderUInt(const ByteVector &name, const MP4::Item &item) const {
ByteVectorList data; ByteVectorList data;
data.append(ByteVector::fromUInt(item.toUInt())); data.append(ByteVector::fromUInt(item.toUInt()));
return renderData(name, TypeInteger, data); return renderData(name, TypeInteger, data);
} }
ByteVector ByteVector MP4::Tag::renderLongLong(const ByteVector &name, const MP4::Item &item) const {
MP4::Tag::renderLongLong(const ByteVector &name, const MP4::Item &item) const {
ByteVectorList data; ByteVectorList data;
data.append(ByteVector::fromLongLong(item.toLongLong())); data.append(ByteVector::fromLongLong(item.toLongLong()));
return renderData(name, TypeInteger, data); return renderData(name, TypeInteger, data);
} }
ByteVector ByteVector MP4::Tag::renderByte(const ByteVector &name, const MP4::Item &item) const {
MP4::Tag::renderByte(const ByteVector &name, const MP4::Item &item) const {
ByteVectorList data; ByteVectorList data;
data.append(ByteVector(1, item.toByte())); data.append(ByteVector(1, item.toByte()));
return renderData(name, TypeInteger, data); return renderData(name, TypeInteger, data);
} }
ByteVector ByteVector MP4::Tag::renderIntPair(const ByteVector &name, const MP4::Item &item) const {
MP4::Tag::renderIntPair(const ByteVector &name, const MP4::Item &item) const {
ByteVectorList data; ByteVectorList data;
data.append(ByteVector(2, '\0') + data.append(ByteVector(2, '\0') +
ByteVector::fromShort(item.toIntPair().first) + ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second) + ByteVector::fromShort(item.toIntPair().second) +
ByteVector(2, '\0')); ByteVector(2, '\0'));
return renderData(name, TypeImplicit, data); return renderData(name, TypeImplicit, data);
} }
ByteVector ByteVector MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, const MP4::Item &item) const {
MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, const MP4::Item &item) const {
ByteVectorList data; ByteVectorList data;
data.append(ByteVector(2, '\0') + data.append(ByteVector(2, '\0') +
ByteVector::fromShort(item.toIntPair().first) + ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second)); ByteVector::fromShort(item.toIntPair().second));
return renderData(name, TypeImplicit, data); return renderData(name, TypeImplicit, data);
} }
ByteVector ByteVector MP4::Tag::renderText(const ByteVector &name, const MP4::Item &item, int flags) const {
MP4::Tag::renderText(const ByteVector &name, const MP4::Item &item, int flags) const {
ByteVectorList data; ByteVectorList data;
StringList value = item.toStringList(); StringList value = item.toStringList();
for (StringList::ConstIterator it = value.begin(); it != value.end(); ++it) { for (StringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
data.append(it->data(String::UTF8)); data.append(it->data(String::UTF8));
} }
return renderData(name, flags, data); return renderData(name, flags, data);
} }
ByteVector ByteVector MP4::Tag::renderCovr(const ByteVector &name, const MP4::Item &item) const {
MP4::Tag::renderCovr(const ByteVector &name, const MP4::Item &item) const {
ByteVector data; ByteVector data;
MP4::CoverArtList value = item.toCoverArtList(); MP4::CoverArtList value = item.toCoverArtList();
for (MP4::CoverArtList::ConstIterator it = value.begin(); it != value.end(); ++it) { for (MP4::CoverArtList::ConstIterator it = value.begin(); it != value.end(); ++it) {
data.append(renderAtom("data", ByteVector::fromUInt(it->format()) + ByteVector(4, '\0') + it->data())); data.append(renderAtom("data", ByteVector::fromUInt(it->format()) + ByteVector(4, '\0') + it->data()));
} }
return renderAtom(name, data); return renderAtom(name, data);
} }
ByteVector ByteVector MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const {
MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const {
StringList header = StringList::split(name, ":"); StringList header = StringList::split(name, ":");
if (header.size() != 3) { if (header.size() != 3) {
debug("MP4: Invalid free-form item name \"" + name + "\""); debug("MP4: Invalid free-form item name \"" + name + "\"");
@@ -429,9 +460,11 @@ MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const {
} }
} }
return renderAtom("----", data); return renderAtom("----", data);
} }
bool MP4::Tag::save() { bool MP4::Tag::save() {
ByteVector data; ByteVector data;
for (MP4::ItemMap::ConstIterator it = d->items.begin(); it != d->items.end(); ++it) { for (MP4::ItemMap::ConstIterator it = d->items.begin(); it != d->items.end(); ++it) {
const String name = it->first; const String name = it->first;
@@ -496,9 +529,11 @@ bool MP4::Tag::save() {
} }
return true; return true;
} }
void MP4::Tag::updateParents(const AtomList &path, long delta, int ignore) { void MP4::Tag::updateParents(const AtomList &path, long delta, int ignore) {
if (static_cast<int>(path.size()) <= ignore) if (static_cast<int>(path.size()) <= ignore)
return; return;
@@ -525,6 +560,7 @@ void MP4::Tag::updateParents(const AtomList &path, long delta, int ignore) {
} }
void MP4::Tag::updateOffsets(long delta, long offset) { void MP4::Tag::updateOffsets(long delta, long offset) {
MP4::Atom *moov = d->atoms->find("moov"); MP4::Atom *moov = d->atoms->find("moov");
if (moov) { if (moov) {
MP4::AtomList stco = moov->findall("stco", true); MP4::AtomList stco = moov->findall("stco", true);
@@ -591,9 +627,11 @@ void MP4::Tag::updateOffsets(long delta, long offset) {
} }
} }
} }
} }
void MP4::Tag::saveNew(ByteVector data) { void MP4::Tag::saveNew(ByteVector data) {
data = renderAtom("meta", ByteVector(4, '\0') + renderAtom("hdlr", ByteVector(8, '\0') + ByteVector("mdirappl") + ByteVector(9, '\0')) + data + padIlst(data)); data = renderAtom("meta", ByteVector(4, '\0') + renderAtom("hdlr", ByteVector(8, '\0') + ByteVector("mdirappl") + ByteVector(9, '\0')) + data + padIlst(data));
AtomList path = d->atoms->path("moov", "udta"); AtomList path = d->atoms->path("moov", "udta");
@@ -612,9 +650,11 @@ void MP4::Tag::saveNew(ByteVector data) {
d->file->seek(offset); d->file->seek(offset);
path.back()->children.prepend(new Atom(d->file)); path.back()->children.prepend(new Atom(d->file));
} }
void MP4::Tag::saveExisting(ByteVector data, const AtomList &path) { void MP4::Tag::saveExisting(ByteVector data, const AtomList &path) {
AtomList::ConstIterator it = path.end(); AtomList::ConstIterator it = path.end();
MP4::Atom *ilst = *(--it); MP4::Atom *ilst = *(--it);
@@ -660,52 +700,46 @@ void MP4::Tag::saveExisting(ByteVector data, const AtomList &path) {
updateParents(path, delta, 1); updateParents(path, delta, 1);
updateOffsets(delta, offset); updateOffsets(delta, offset);
} }
} }
String String MP4::Tag::title() const {
MP4::Tag::title() const {
if (d->items.contains("\251nam")) if (d->items.contains("\251nam"))
return d->items["\251nam"].toStringList().toString(", "); return d->items["\251nam"].toStringList().toString(", ");
return String(); return String();
} }
String String MP4::Tag::artist() const {
MP4::Tag::artist() const {
if (d->items.contains("\251ART")) if (d->items.contains("\251ART"))
return d->items["\251ART"].toStringList().toString(", "); return d->items["\251ART"].toStringList().toString(", ");
return String(); return String();
} }
String String MP4::Tag::album() const {
MP4::Tag::album() const {
if (d->items.contains("\251alb")) if (d->items.contains("\251alb"))
return d->items["\251alb"].toStringList().toString(", "); return d->items["\251alb"].toStringList().toString(", ");
return String(); return String();
} }
String String MP4::Tag::comment() const {
MP4::Tag::comment() const {
if (d->items.contains("\251cmt")) if (d->items.contains("\251cmt"))
return d->items["\251cmt"].toStringList().toString(", "); return d->items["\251cmt"].toStringList().toString(", ");
return String(); return String();
} }
String String MP4::Tag::genre() const {
MP4::Tag::genre() const {
if (d->items.contains("\251gen")) if (d->items.contains("\251gen"))
return d->items["\251gen"].toStringList().toString(", "); return d->items["\251gen"].toStringList().toString(", ");
return String(); return String();
} }
unsigned int unsigned int MP4::Tag::year() const {
MP4::Tag::year() const {
if (d->items.contains("\251day")) if (d->items.contains("\251day"))
return d->items["\251day"].toStringList().toString().toInt(); return d->items["\251day"].toStringList().toString().toInt();
return 0; return 0;
} }
unsigned int unsigned int MP4::Tag::track() const {
MP4::Tag::track() const {
if (d->items.contains("trkn")) if (d->items.contains("trkn"))
return d->items["trkn"].toIntPair().first; return d->items["trkn"].toIntPair().first;
return 0; return 0;
@@ -732,31 +766,31 @@ void MP4::Tag::setGenre(const String &value) {
} }
void MP4::Tag::setYear(unsigned int value) { void MP4::Tag::setYear(unsigned int value) {
if (value == 0) { if (value == 0) {
d->items.erase("\251day"); d->items.erase("\251day");
} }
else { else {
d->items["\251day"] = StringList(String::number(value)); d->items["\251day"] = StringList(String::number(value));
} }
} }
void MP4::Tag::setTrack(unsigned int value) { void MP4::Tag::setTrack(unsigned int value) {
if (value == 0) { if (value == 0) {
d->items.erase("trkn"); d->items.erase("trkn");
} }
else { else {
d->items["trkn"] = MP4::Item(value, 0); d->items["trkn"] = MP4::Item(value, 0);
} }
} }
bool MP4::Tag::isEmpty() const { bool MP4::Tag::isEmpty() const {
return d->items.isEmpty(); return d->items.isEmpty();
} }
MP4::ItemMap &MP4::Tag::itemListMap() {
return d->items;
}
const MP4::ItemMap &MP4::Tag::itemMap() const { const MP4::ItemMap &MP4::Tag::itemMap() const {
return d->items; return d->items;
} }
@@ -835,16 +869,19 @@ const char *keyTranslation[][2] = {
const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]); const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
String translateKey(const String &key) { String translateKey(const String &key) {
for (size_t i = 0; i < keyTranslationSize; ++i) { for (size_t i = 0; i < keyTranslationSize; ++i) {
if (key == keyTranslation[i][0]) if (key == keyTranslation[i][0])
return keyTranslation[i][1]; return keyTranslation[i][1];
} }
return String(); return String();
} }
} // namespace } // namespace
PropertyMap MP4::Tag::properties() const { PropertyMap MP4::Tag::properties() const {
PropertyMap props; PropertyMap props;
for (MP4::ItemMap::ConstIterator it = d->items.begin(); it != d->items.end(); ++it) { for (MP4::ItemMap::ConstIterator it = d->items.begin(); it != d->items.end(); ++it) {
const String key = translateKey(it->first); const String key = translateKey(it->first);
@@ -872,14 +909,18 @@ PropertyMap MP4::Tag::properties() const {
} }
} }
return props; return props;
} }
void MP4::Tag::removeUnsupportedProperties(const StringList &props) { void MP4::Tag::removeUnsupportedProperties(const StringList &props) {
for (StringList::ConstIterator it = props.begin(); it != props.end(); ++it) for (StringList::ConstIterator it = props.begin(); it != props.end(); ++it)
d->items.erase(*it); d->items.erase(*it);
} }
PropertyMap MP4::Tag::setProperties(const PropertyMap &props) { PropertyMap MP4::Tag::setProperties(const PropertyMap &props) {
static Map<String, String> reverseKeyMap; static Map<String, String> reverseKeyMap;
if (reverseKeyMap.isEmpty()) { if (reverseKeyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
@@ -928,13 +969,16 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props) {
} }
return ignoredProps; return ignoredProps;
} }
void MP4::Tag::addItem(const String &name, const Item &value) { void MP4::Tag::addItem(const String &name, const Item &value) {
if (!d->items.contains(name)) { if (!d->items.contains(name)) {
d->items.insert(name, value); d->items.insert(name, value);
} }
else { else {
debug("MP4: Ignoring duplicate atom \"" + name + "\""); debug("MP4: Ignoring duplicate atom \"" + name + "\"");
} }
} }

View File

@@ -37,13 +37,8 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace MP4 { namespace MP4 {
/*!
* \deprecated
*/
TAGLIB_DEPRECATED typedef Strawberry_TagLib::TagLib::Map<String, Item> ItemListMap;
typedef Strawberry_TagLib::TagLib::Map<String, Item> ItemMap; typedef Strawberry_TagLib::TagLib::Map<String, Item> ItemMap;
class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
@@ -70,12 +65,6 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual void setTrack(unsigned int value); virtual void setTrack(unsigned int value);
virtual bool isEmpty() const; virtual bool isEmpty() const;
/*!
* \deprecated Use the item() and setItem() API instead
*/
TAGLIB_DEPRECATED ItemMap &itemListMap();
/*! /*!
* Returns a string-keyed map of the MP4::Items for this tag. * Returns a string-keyed map of the MP4::Items for this tag.
*/ */
@@ -92,8 +81,7 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
void setItem(const String &key, const Item &value); void setItem(const String &key, const Item &value);
/*! /*!
* Removes the entry with \a key from the tag, or does nothing if it does * Removes the entry with \a key from the tag, or does nothing if it does not exist.
* not exist.
*/ */
void removeItem(const String &key); void removeItem(const String &key);
@@ -107,10 +95,8 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
PropertyMap setProperties(const PropertyMap &properties); PropertyMap setProperties(const PropertyMap &properties);
private: private:
AtomDataList parseData2(const Atom *atom, int expectedFlags = -1, AtomDataList parseData2(const Atom *atom, int expectedFlags = -1, bool freeForm = false);
bool freeForm = false); ByteVectorList parseData(const Atom *atom, int expectedFlags = -1, bool freeForm = false);
ByteVectorList parseData(const Atom *atom, int expectedFlags = -1,
bool freeForm = false);
void parseText(const Atom *atom, int expectedFlags = 1); void parseText(const Atom *atom, int expectedFlags = 1);
void parseFreeForm(const Atom *atom); void parseFreeForm(const Atom *atom);
void parseInt(const Atom *atom); void parseInt(const Atom *atom);
@@ -124,10 +110,8 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
ByteVector padIlst(const ByteVector &data, int length = -1) const; ByteVector padIlst(const ByteVector &data, int length = -1) const;
ByteVector renderAtom(const ByteVector &name, const ByteVector &data) const; ByteVector renderAtom(const ByteVector &name, const ByteVector &data) const;
ByteVector renderData(const ByteVector &name, int flags, ByteVector renderData(const ByteVector &name, int flags, const ByteVectorList &data) const;
const ByteVectorList &data) const; ByteVector renderText(const ByteVector &name, const Item &item, int flags = TypeUTF8) const;
ByteVector renderText(const ByteVector &name, const Item &item,
int flags = TypeUTF8) const;
ByteVector renderFreeForm(const String &name, const Item &item) const; ByteVector renderFreeForm(const String &name, const Item &item) const;
ByteVector renderBool(const ByteVector &name, const Item &item) const; ByteVector renderBool(const ByteVector &name, const Item &item) const;
ByteVector renderInt(const ByteVector &name, const Item &item) const; ByteVector renderInt(const ByteVector &name, const Item &item) const;
@@ -151,7 +135,6 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
}; };
} // namespace MP4 } // namespace MP4
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -48,10 +48,10 @@ class MPC::File::FilePrivate {
FilePrivate() : APELocation(-1), FilePrivate() : APELocation(-1),
APESize(0), APESize(0),
ID3v1Location(-1), ID3v1Location(-1),
ID3v2Header(0), ID3v2Header(nullptr),
ID3v2Location(-1), ID3v2Location(-1),
ID3v2Size(0), ID3v2Size(0),
properties(0) {} properties(nullptr) {}
~FilePrivate() { ~FilePrivate() {
delete ID3v2Header; delete ID3v2Header;
@@ -77,27 +77,31 @@ class MPC::File::FilePrivate {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool MPC::File::isSupported(IOStream *stream) { bool MPC::File::isSupported(IOStream *stream) {
// A newer MPC file has to start with "MPCK" or "MP+", but older files don't // A newer MPC file has to start with "MPCK" or "MP+", but older files don't
// have keys to do a quick check. // have keys to do a quick check.
const ByteVector id = Utils::readHeader(stream, 4, false); const ByteVector id = Utils::readHeader(stream, 4, false);
return (id == "MPCK" || id.startsWith("MP+")); return (id == "MPCK" || id.startsWith("MP+"));
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
MPC::File::File(FileName file, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), MPC::File::File(FileName file, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) {
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
MPC::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), MPC::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) {
d(new FilePrivate()) {
if (isOpen()) if (isOpen())
read(readProperties); read(readProperties);
} }
MPC::File::~File() { MPC::File::~File() {
@@ -128,6 +132,7 @@ MPC::Properties *MPC::File::audioProperties() const {
} }
bool MPC::File::save() { bool MPC::File::save() {
if (readOnly()) { if (readOnly()) {
debug("MPC::File::save() -- File is read only."); debug("MPC::File::save() -- File is read only.");
return false; return false;
@@ -211,6 +216,7 @@ bool MPC::File::save() {
} }
return true; return true;
} }
ID3v1::Tag *MPC::File::ID3v1Tag(bool create) { ID3v1::Tag *MPC::File::ID3v1Tag(bool create) {
@@ -222,23 +228,21 @@ APE::Tag *MPC::File::APETag(bool create) {
} }
void MPC::File::strip(int tags) { void MPC::File::strip(int tags) {
if (tags & ID3v1) if (tags & ID3v1)
d->tag.set(MPCID3v1Index, 0); d->tag.set(MPCID3v1Index, nullptr);
if (tags & APE) if (tags & APE)
d->tag.set(MPCAPEIndex, 0); d->tag.set(MPCAPEIndex, nullptr);
if (!ID3v1Tag()) if (!ID3v1Tag())
APETag(true); APETag(true);
if (tags & ID3v2) { if (tags & ID3v2) {
delete d->ID3v2Header; delete d->ID3v2Header;
d->ID3v2Header = 0; d->ID3v2Header = nullptr;
} }
}
void MPC::File::remove(int tags) {
strip(tags);
} }
bool MPC::File::hasID3v1Tag() const { bool MPC::File::hasID3v1Tag() const {
@@ -254,6 +258,7 @@ bool MPC::File::hasAPETag() const {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void MPC::File::read(bool readProperties) { void MPC::File::read(bool readProperties) {
// Look for an ID3v2 tag // Look for an ID3v2 tag
d->ID3v2Location = Utils::findID3v2(this); d->ID3v2Location = Utils::findID3v2(this);
@@ -307,4 +312,5 @@ void MPC::File::read(bool readProperties) {
d->properties = new Properties(this, streamLength); d->properties = new Properties(this, streamLength);
} }
} }

View File

@@ -51,9 +51,9 @@ class Tag;
/*! /*!
* This is implementation of MPC metadata. * This is implementation of MPC metadata.
* *
* This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream properties from the file.
* properties from the file. ID3v2 tags are invalid in MPC-files, but will be skipped * ID3v2 tags are invalid in MPC-files, but will be skipped and ignored.
* and ignored. *
*/ */
namespace MPC { namespace MPC {
@@ -61,18 +61,14 @@ namespace MPC {
//! An implementation of TagLib::File with MPC specific methods //! An implementation of TagLib::File with MPC specific methods
/*! /*!
* This implements and provides an interface for MPC files to the * This implements and provides an interface for MPC files to the TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing the abstract TagLib::File API as well as providing some additional information specific to MPC files.
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to MPC files.
* The only invalid tag combination supported is an ID3v1 tag after an APE tag. * The only invalid tag combination supported is an ID3v1 tag after an APE tag.
*/ */
class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
public: public:
/*! /*!
* This set of flags is used for various operations and is suitable for * This set of flags is used for various operations and is suitable for being OR-ed together.
* being OR-ed together.
*/ */
enum TagTypes { enum TagTypes {
//! Empty set. Matches no tag types. //! Empty set. Matches no tag types.
@@ -88,25 +84,22 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
}; };
/*! /*!
* Constructs an MPC file from \a file. If \a readProperties is true the * Constructs an MPC file from \a file.
* file's audio properties will also be read. * If \a readProperties is true the file's audio properties will also be read.
* *
* \note In the current implementation, \a propertiesStyle is ignored. * \note In the current implementation, \a propertiesStyle is ignored.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Constructs an MPC file from \a stream. If \a readProperties is true the * Constructs an MPC file from \a stream.
* file's audio properties will also be read. * If \a readProperties is true the file's audio properties will also be read.
* *
* \note TagLib will *not* take ownership of the stream, the caller is * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object.
* responsible for deleting it after the File object.
* *
* \note In the current implementation, \a propertiesStyle is ignored. * \note In the current implementation, \a propertiesStyle is ignored.
*/ */
File(IOStream *stream, bool readProperties = true, File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
@@ -114,15 +107,14 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
virtual ~File(); virtual ~File();
/*! /*!
* Returns the Tag for this file. This will be an APE tag, an ID3v1 tag * Returns the Tag for this file.
* or a combination of the two. * This will be an APE tag, an ID3v1 tag or a combination of the two.
*/ */
virtual Strawberry_TagLib::TagLib::Tag *tag() const; virtual Strawberry_TagLib::TagLib::Tag *tag() const;
/*! /*!
* Implements the unified property interface -- export function. * Implements the unified property interface -- export function.
* If the file contains both an APE and an ID3v1 tag, only the APE * If the file contains both an APE and an ID3v1 tag, only the APE tag will be converted to the PropertyMap.
* tag will be converted to the PropertyMap.
*/ */
PropertyMap properties() const; PropertyMap properties() const;
@@ -136,8 +128,8 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
PropertyMap setProperties(const PropertyMap &); PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the MPC::Properties for this file. If no audio properties * Returns the MPC::Properties for this file.
* were read then this will return a null pointer. * If no audio properties were read then this will return a null pointer.
*/ */
virtual Properties *audioProperties() const; virtual Properties *audioProperties() const;
@@ -151,17 +143,14 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
/*! /*!
* Returns a pointer to the ID3v1 tag of the file. * Returns a pointer to the ID3v1 tag of the file.
* *
* If \a create is false (the default) this returns a null pointer * If \a create is false (the default) this returns a null pointer if there is no valid APE tag.
* if there is no valid APE tag. If \a create is true it will create * If \a create is true it will create an APE tag if one does not exist and returns a valid pointer.
* an APE tag if one does not exist and returns a valid pointer.
* *
* \note This may return a valid pointer regardless of whether or not the * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v1 tag.
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * Use hasID3v1Tag() to check if the file on disk actually has an ID3v1 tag.
* on disk actually has an ID3v1 tag.
* *
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be * \note The Tag <b>is still</b> owned by the MPEG::File and should not be deleted by the user.
* deleted by the user. It will be deleted when the file (object) is * It will be deleted when the file (object) is destroyed.
* destroyed.
* *
* \see hasID3v1Tag() * \see hasID3v1Tag()
*/ */
@@ -171,39 +160,29 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
* Returns a pointer to the APE tag of the file. * Returns a pointer to the APE tag of the file.
* *
* If \a create is false (the default) this may return a null pointer * If \a create is false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is true it will create * if there is no valid APE tag.
* an APE tag if one does not exist and returns a valid pointer. If * If \a create is true it will create an APE tag if one does not exist and returns a valid pointer.
* there already be an ID3v1 tag, the new APE tag will be placed before it. * If there already be an ID3v1 tag, the new APE tag will be placed before it.
* *
* \note This may return a valid pointer regardless of whether or not the * \note This may return a valid pointer regardless of whether or not the file on disk has an APE tag.
* file on disk has an APE tag. Use hasAPETag() to check if the file * Use hasAPETag() to check if the file on disk actually has an APE tag.
* on disk actually has an APE tag.
* *
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be * \note The Tag <b>is still</b> owned by the MPEG::File and should not be deleted by the user.
* deleted by the user. It will be deleted when the file (object) is * It will be deleted when the file (object) is destroyed.
* destroyed.
* *
* \see hasAPETag() * \see hasAPETag()
*/ */
APE::Tag *APETag(bool create = false); APE::Tag *APETag(bool create = false);
/*! /*!
* This will remove the tags that match the OR-ed together TagTypes from the * This will remove the tags that match the OR-ed together TagTypes from the file. By default it removes all tags.
* file. By default it removes all tags.
* *
* \warning This will also invalidate pointers to the tags * \warning This will also invalidate pointers to the tags as their memory will be freed.
* as their memory will be freed.
* *
* \note In order to make the removal permanent save() still needs to be called. * \note In order to make the removal permanent save() still needs to be called.
*/ */
void strip(int tags = AllTags); void strip(int tags = AllTags);
/*!
* \deprecated
* \see strip
*/
TAGLIB_DEPRECATED void remove(int tags = AllTags);
/*! /*!
* Returns whether or not the file on disk actually has an ID3v1 tag. * Returns whether or not the file on disk actually has an ID3v1 tag.
* *
@@ -219,11 +198,9 @@ class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File {
bool hasAPETag() const; bool hasAPETag() const;
/*! /*!
* Returns whether or not the given \a stream can be opened as an MPC * Returns whether or not the given \a stream can be opened as an MPC file.
* file.
* *
* \note This method is designed to do a quick check. The result may * \note This method is designed to do a quick check. The result may not necessarily be correct.
* not necessarily be correct.
*/ */
static bool isSupported(IOStream *stream); static bool isSupported(IOStream *stream);

View File

@@ -64,13 +64,12 @@ class MPC::Properties::PropertiesPrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style), MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style), d(new PropertiesPrivate()) {
d(new PropertiesPrivate()) {
readSV7(data, streamLength); readSV7(data, streamLength);
} }
MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style), MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style), d(new PropertiesPrivate()) {
d(new PropertiesPrivate()) {
ByteVector magic = file->readBlock(4); ByteVector magic = file->readBlock(4);
if (magic == "MPCK") { if (magic == "MPCK") {
// Musepack version 8 // Musepack version 8
@@ -80,16 +79,13 @@ MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) : Au
// Musepack version 7 or older, fixed size header // Musepack version 7 or older, fixed size header
readSV7(magic + file->readBlock(MPC::HeaderSize - 4), streamLength); readSV7(magic + file->readBlock(MPC::HeaderSize - 4), streamLength);
} }
} }
MPC::Properties::~Properties() { MPC::Properties::~Properties() {
delete d; delete d;
} }
int MPC::Properties::length() const {
return lengthInSeconds();
}
int MPC::Properties::lengthInSeconds() const { int MPC::Properties::lengthInSeconds() const {
return d->length / 1000; return d->length / 1000;
} }
@@ -144,6 +140,7 @@ int MPC::Properties::albumPeak() const {
namespace { namespace {
unsigned long readSize(File *file, unsigned int &sizeLength, bool &eof) { unsigned long readSize(File *file, unsigned int &sizeLength, bool &eof) {
sizeLength = 0; sizeLength = 0;
eof = false; eof = false;
@@ -160,19 +157,26 @@ unsigned long readSize(File *file, unsigned int &sizeLength, bool &eof) {
tmp = b[0]; tmp = b[0];
size = (size << 7) | (tmp & 0x7F); size = (size << 7) | (tmp & 0x7F);
sizeLength++; sizeLength++;
} while ((tmp & 0x80)); }
while ((tmp & 0x80));
return size; return size;
} }
unsigned long readSize(const ByteVector &data, unsigned int &pos) { unsigned long readSize(const ByteVector &data, unsigned int &pos) {
unsigned char tmp; unsigned char tmp;
unsigned long size = 0; unsigned long size = 0;
do { do {
tmp = data[pos++]; tmp = data[pos++];
size = (size << 7) | (tmp & 0x7F); size = (size << 7) | (tmp & 0x7F);
} while ((tmp & 0x80) && (pos < data.size())); }
while ((tmp & 0x80) && (pos < data.size()));
return size; return size;
} }
// This array looks weird, but the same as original MusePack code found at: // This array looks weird, but the same as original MusePack code found at:
@@ -181,6 +185,7 @@ const unsigned short sftable[8] = { 44100, 48000, 37800, 32000, 0, 0, 0, 0 };
} // namespace } // namespace
void MPC::Properties::readSV8(File *file, long streamLength) { void MPC::Properties::readSV8(File *file, long streamLength) {
bool readSH = false, readRG = false; bool readSH = false, readRG = false;
while (!readSH && !readRG) { while (!readSH && !readRG) {
@@ -269,9 +274,11 @@ void MPC::Properties::readSV8(File *file, long streamLength) {
file->seek(dataSize, File::Current); file->seek(dataSize, File::Current);
} }
} }
} }
void MPC::Properties::readSV7(const ByteVector &data, long streamLength) { void MPC::Properties::readSV7(const ByteVector &data, long streamLength) {
if (data.startsWith("MP+")) { if (data.startsWith("MP+")) {
d->version = data[3] & 15; d->version = data[3] & 15;
if (d->version < 7) if (d->version < 7)
@@ -340,4 +347,5 @@ void MPC::Properties::readSV7(const ByteVector &data, long streamLength) {
if (d->bitrate == 0) if (d->bitrate == 0)
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
} }
} }

View File

@@ -41,23 +41,20 @@ static const unsigned int HeaderSize = 8 * 7;
//! An implementation of audio property reading for MPC //! An implementation of audio property reading for MPC
/*! /*!
* This reads the data from an MPC stream found in the AudioProperties * This reads the data from an MPC stream found in the AudioProperties API.
* API.
*/ */
class TAGLIB_EXPORT Properties : public AudioProperties { class TAGLIB_EXPORT Properties : public AudioProperties {
public: public:
/*! /*!
* Create an instance of MPC::Properties with the data read from the * Create an instance of MPC::Properties with the data read from the ByteVector \a data.
* ByteVector \a data.
* *
* This constructor is deprecated. It only works for MPC version up to 7. * This constructor is deprecated. It only works for MPC version up to 7.
*/ */
Properties(const ByteVector &data, long streamLength, ReadStyle style = Average); Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
/*! /*!
* Create an instance of MPC::Properties with the data read directly * Create an instance of MPC::Properties with the data read directly from a MPC::File.
* from a MPC::File.
*/ */
Properties(File *file, long streamLength, ReadStyle style = Average); Properties(File *file, long streamLength, ReadStyle style = Average);
@@ -67,18 +64,8 @@ class TAGLIB_EXPORT Properties : public AudioProperties {
virtual ~Properties(); virtual ~Properties();
/*! /*!
* Returns the length of the file in seconds. The length is rounded down to * Returns the length of the file in seconds.
* the nearest whole second. * The length is rounded down to the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
* *
* \see lengthInMilliseconds() * \see lengthInMilliseconds()
*/ */

View File

@@ -226,35 +226,43 @@ const int genresSize = sizeof(genres) / sizeof(genres[0]);
} // namespace } // namespace
StringList ID3v1::genreList() { StringList ID3v1::genreList() {
StringList l; StringList l;
for (int i = 0; i < genresSize; i++) { for (int i = 0; i < genresSize; i++) {
l.append(genres[i]); l.append(genres[i]);
} }
return l; return l;
} }
ID3v1::GenreMap ID3v1::genreMap() { ID3v1::GenreMap ID3v1::genreMap() {
GenreMap m; GenreMap m;
for (int i = 0; i < genresSize; i++) { for (int i = 0; i < genresSize; i++) {
m.insert(genres[i], i); m.insert(genres[i], i);
} }
return m; return m;
} }
String ID3v1::genre(int i) { String ID3v1::genre(int i) {
if (i >= 0 && i < genresSize) if (i >= 0 && i < genresSize)
return String(genres[i]); // always make a copy return String(genres[i]); // always make a copy
else else
return String(); return String();
} }
int ID3v1::genreIndex(const String &name) { int ID3v1::genreIndex(const String &name) {
for (int i = 0; i < genresSize; ++i) { for (int i = 0; i < genresSize; ++i) {
if (name == genres[i]) if (name == genres[i])
return i; return i;
} }
return 255; return 255;
} }

View File

@@ -37,28 +37,24 @@ namespace ID3v1 {
typedef Map<String, int> GenreMap; typedef Map<String, int> GenreMap;
/*! /*!
* Returns the list of canonical ID3v1 genre names in the order that they * Returns the list of canonical ID3v1 genre names in the order that they are listed in the standard.
* are listed in the standard.
*/ */
StringList TAGLIB_EXPORT genreList(); StringList TAGLIB_EXPORT genreList();
/*! /*!
* A "reverse mapping" that goes from the canonical ID3v1 genre name to the * A "reverse mapping" that goes from the canonical ID3v1 genre name to the respective genre number. genreMap()["Rock"] ==
* respective genre number. genreMap()["Rock"] ==
*/ */
GenreMap TAGLIB_EXPORT genreMap(); GenreMap TAGLIB_EXPORT genreMap();
/*! /*!
* Returns the name of the genre at \a index in the ID3v1 genre list. If * Returns the name of the genre at \a index in the ID3v1 genre list.
* \a index is out of range -- less than zero or greater than 191 -- a null * If \a index is out of range -- less than zero or greater than 191 -- a null string will be returned.
* string will be returned.
*/ */
String TAGLIB_EXPORT genre(int index); String TAGLIB_EXPORT genre(int index);
/*! /*!
* Returns the genre index for the (case sensitive) genre \a name. If the * Returns the genre index for the (case sensitive) genre \a name.
* genre is not in the list 255 (which signifies an unknown genre in ID3v1) * If the genre is not in the list 255 (which signifies an unknown genre in ID3v1) will be returned.
* will be returned.
*/ */
int TAGLIB_EXPORT genreIndex(const String &name); int TAGLIB_EXPORT genreIndex(const String &name);
} // namespace ID3v1 } // namespace ID3v1

View File

@@ -39,7 +39,7 @@ const ID3v1::StringHandler *stringHandler = &defaultStringHandler;
class ID3v1::Tag::TagPrivate { class ID3v1::Tag::TagPrivate {
public: public:
TagPrivate() : file(0), TagPrivate() : file(nullptr),
tagOffset(0), tagOffset(0),
track(0), track(0),
genre(255) {} genre(255) {}
@@ -68,22 +68,22 @@ String ID3v1::StringHandler::parse(const ByteVector &data) const {
} }
ByteVector ID3v1::StringHandler::render(const String &s) const { ByteVector ID3v1::StringHandler::render(const String &s) const {
if (s.isLatin1()) if (s.isLatin1())
return s.data(String::Latin1); return s.data(String::Latin1);
else else
return ByteVector(); return ByteVector();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public methods // public methods
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ID3v1::Tag::Tag() : Strawberry_TagLib::TagLib::Tag(), ID3v1::Tag::Tag() : Strawberry_TagLib::TagLib::Tag(), d(new TagPrivate()) {}
d(new TagPrivate()) {
} ID3v1::Tag::Tag(File *file, long tagOffset) : Strawberry_TagLib::TagLib::Tag(), d(new TagPrivate()) {
ID3v1::Tag::Tag(File *file, long tagOffset) : Strawberry_TagLib::TagLib::Tag(),
d(new TagPrivate()) {
d->file = file; d->file = file;
d->tagOffset = tagOffset; d->tagOffset = tagOffset;
@@ -95,6 +95,7 @@ ID3v1::Tag::~Tag() {
} }
ByteVector ID3v1::Tag::render() const { ByteVector ID3v1::Tag::render() const {
ByteVector data; ByteVector data;
data.append(fileIdentifier()); data.append(fileIdentifier());
@@ -108,6 +109,7 @@ ByteVector ID3v1::Tag::render() const {
data.append(char(d->genre)); data.append(char(d->genre));
return data; return data;
} }
ByteVector ID3v1::Tag::fileIdentifier() { ByteVector ID3v1::Tag::fileIdentifier() {
@@ -190,6 +192,7 @@ void ID3v1::Tag::setStringHandler(const StringHandler *handler) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ID3v1::Tag::read() { void ID3v1::Tag::read() {
if (d->file && d->file->isValid()) { if (d->file && d->file->isValid()) {
d->file->seek(d->tagOffset); d->file->seek(d->tagOffset);
// read the tag -- always 128 bytes // read the tag -- always 128 bytes
@@ -201,9 +204,11 @@ void ID3v1::Tag::read() {
else else
debug("ID3v1 tag is not valid or could not be read at the specified offset."); debug("ID3v1 tag is not valid or could not be read at the specified offset.");
} }
} }
void ID3v1::Tag::parse(const ByteVector &data) { void ID3v1::Tag::parse(const ByteVector &data) {
int offset = 3; int offset = 3;
d->title = stringHandler->parse(data.mid(offset, 30)); d->title = stringHandler->parse(data.mid(offset, 30));
@@ -235,4 +240,5 @@ void ID3v1::Tag::parse(const ByteVector &data) {
offset += 30; offset += 30;
d->genre = static_cast<unsigned char>(data[offset]); d->genre = static_cast<unsigned char>(data[offset]);
} }

View File

@@ -42,42 +42,38 @@ namespace ID3v1 {
//! A abstraction for the string to data encoding in ID3v1 tags. //! A abstraction for the string to data encoding in ID3v1 tags.
/*! /*!
* ID3v1 should in theory always contain ISO-8859-1 (Latin1) data. In * ID3v1 should in theory always contain ISO-8859-1 (Latin1) data. In practice it does not.
* practice it does not. TagLib by default only supports ISO-8859-1 data * TagLib by default only supports ISO-8859-1 data in ID3v1 tags.
* in ID3v1 tags.
* *
* However by subclassing this class and reimplementing parse() and render() * However by subclassing this class and reimplementing parse() and render() and setting your reimplementation as the default with
* and setting your reimplementation as the default with * ID3v1::Tag::setStringHandler() you can define how you would like these transformations to be done.
* ID3v1::Tag::setStringHandler() you can define how you would like these
* transformations to be done.
* *
* \warning It is advisable <b>not</b> to write non-ISO-8859-1 data to ID3v1 * \warning It is advisable <b>not</b> to write non-ISO-8859-1 data to ID3v1 tags.
* tags. Please consider disabling the writing of ID3v1 tags in the case * Please consider disabling the writing of ID3v1 tags in the case that the data is not ISO-8859-1.
* that the data is not ISO-8859-1.
* *
* \see ID3v1::Tag::setStringHandler() * \see ID3v1::Tag::setStringHandler()
*/ */
class TAGLIB_EXPORT StringHandler { class TAGLIB_EXPORT StringHandler {
TAGLIB_IGNORE_MISSING_DESTRUCTOR TAGLIB_IGNORE_MISSING_DESTRUCTOR
public: public:
// BIC: Add virtual destructor. // BIC: Add virtual destructor.
StringHandler(); StringHandler();
/*! /*!
* Decode a string from \a data. The default implementation assumes that * Decode a string from \a data.
* \a data is an ISO-8859-1 (Latin1) character array. * The default implementation assumes that \a data is an ISO-8859-1 (Latin1) character array.
*/ */
virtual String parse(const ByteVector &data) const; virtual String parse(const ByteVector &data) const;
/*! /*!
* Encode a ByteVector with the data from \a s. The default implementation * Encode a ByteVector with the data from \a s.
* assumes that \a s is an ISO-8859-1 (Latin1) string. If the string is * The default implementation assumes that \a s is an ISO-8859-1 (Latin1) string.
* does not conform to ISO-8859-1, no value is written. * If the string is does not conform to ISO-8859-1, no value is written.
* *
* \warning It is recommended that you <b>not</b> override this method, but * \warning It is recommended that you <b>not</b> override this method, but
* instead do not write an ID3v1 tag in the case that the data is not * instead do not write an ID3v1 tag in the case that the data is not ISO-8859-1.
* ISO-8859-1.
*/ */
virtual ByteVector render(const String &s) const; virtual ByteVector render(const String &s) const;
}; };
@@ -85,21 +81,20 @@ class TAGLIB_EXPORT StringHandler {
//! The main class in the ID3v1 implementation //! The main class in the ID3v1 implementation
/*! /*!
* This is an implementation of the ID3v1 format. ID3v1 is both the simplest * This is an implementation of the ID3v1 format.
* and most common of tag formats but is rather limited. Because of its * ID3v1 is both the simplest and most common of tag formats but is rather limited.
* pervasiveness and the way that applications have been written around the * Because of its pervasiveness and the way that applications have been written around the
* fields that it provides, the generic TagLib::Tag API is a mirror of what is * fields that it provides, the generic TagLib::Tag API is a mirror of what is provided by ID3v1.
* provided by ID3v1.
* *
* ID3v1 tags should generally only contain Latin1 information. However because * ID3v1 tags should generally only contain Latin1 information.
* many applications do not follow this rule there is now support for overriding * However because many applications do not follow this rule there is now support for overriding
* the ID3v1 string handling using the ID3v1::StringHandler class. Please see * the ID3v1 string handling using the ID3v1::StringHandler class.
* the documentation for that class for more information. * Please see the documentation for that class for more information.
* *
* \see StringHandler * \see StringHandler
* *
* \note Most fields are truncated to a maximum of 28-30 bytes. The * \note Most fields are truncated to a maximum of 28-30 bytes.
* truncation happens automatically when the tag is rendered. * The truncation happens automatically when the tag is rendered.
*/ */
class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
@@ -110,8 +105,7 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
Tag(); Tag();
/*! /*!
* Create an ID3v1 tag and parse the data in \a file starting at * Create an ID3v1 tag and parse the data in \a file starting at \a tagOffset.
* \a tagOffset.
*/ */
Tag(File *file, long tagOffset); Tag(File *file, long tagOffset);
@@ -121,14 +115,12 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
virtual ~Tag(); virtual ~Tag();
/*! /*!
* Renders the in memory values to a ByteVector suitable for writing to * Renders the in memory values to a ByteVector suitable for writing to the file.
* the file.
*/ */
ByteVector render() const; ByteVector render() const;
/*! /*!
* Returns the string "TAG" suitable for usage in locating the tag in a * Returns the string "TAG" suitable for usage in locating the tag in a file.
* file.
*/ */
static ByteVector fileIdentifier(); static ByteVector fileIdentifier();
@@ -160,19 +152,15 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
/*! /*!
* Sets the genre in number to \a i. * Sets the genre in number to \a i.
* *
* \note Valid value is from 0 up to 255. Normally 255 indicates that * \note Valid value is from 0 up to 255. Normally 255 indicates that this tag contains no genre.
* this tag contains no genre.
*/ */
void setGenreNumber(unsigned int i); void setGenreNumber(unsigned int i);
/*! /*!
* Sets the string handler that decides how the ID3v1 data will be * Sets the string handler that decides how the ID3v1 data will be converted to and from binary data.
* converted to and from binary data. * If the parameter \a handler is null, the previous handler is released and default ISO-8859-1 handler is restored.
* If the parameter \a handler is null, the previous handler is
* released and default ISO-8859-1 handler is restored.
* *
* \note The caller is responsible for deleting the previous handler * \note The caller is responsible for deleting the previous handler as needed after it is released.
* as needed after it is released.
* *
* \see StringHandler * \see StringHandler
*/ */
@@ -195,6 +183,7 @@ class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag {
class TagPrivate; class TagPrivate;
TagPrivate *d; TagPrivate *d;
}; };
} // namespace ID3v1 } // namespace ID3v1
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -33,8 +33,7 @@ using namespace ID3v2;
class AttachedPictureFrame::AttachedPictureFramePrivate { class AttachedPictureFrame::AttachedPictureFramePrivate {
public: public:
AttachedPictureFramePrivate() : textEncoding(String::Latin1), AttachedPictureFramePrivate() : textEncoding(String::Latin1), type(AttachedPictureFrame::Other) {}
type(AttachedPictureFrame::Other) {}
String::Type textEncoding; String::Type textEncoding;
String mimeType; String mimeType;
@@ -47,12 +46,9 @@ class AttachedPictureFrame::AttachedPictureFramePrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AttachedPictureFrame::AttachedPictureFrame() : Frame("APIC"), AttachedPictureFrame::AttachedPictureFrame() : Frame("APIC"), d(new AttachedPictureFramePrivate()) {}
d(new AttachedPictureFramePrivate()) {
}
AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) : Frame(data), AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) : Frame(data), d(new AttachedPictureFramePrivate()) {
d(new AttachedPictureFramePrivate()) {
setData(data); setData(data);
} }
@@ -110,6 +106,7 @@ void AttachedPictureFrame::setPicture(const ByteVector &p) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void AttachedPictureFrame::parseFields(const ByteVector &data) { void AttachedPictureFrame::parseFields(const ByteVector &data) {
if (data.size() < 5) { if (data.size() < 5) {
debug("A picture frame must contain at least 5 bytes."); debug("A picture frame must contain at least 5 bytes.");
return; return;
@@ -130,9 +127,11 @@ void AttachedPictureFrame::parseFields(const ByteVector &data) {
d->description = readStringField(data, d->textEncoding, &pos); d->description = readStringField(data, d->textEncoding, &pos);
d->data = data.mid(pos); d->data = data.mid(pos);
} }
ByteVector AttachedPictureFrame::renderFields() const { ByteVector AttachedPictureFrame::renderFields() const {
ByteVector data; ByteVector data;
String::Type encoding = checkTextEncoding(d->description, d->textEncoding); String::Type encoding = checkTextEncoding(d->description, d->textEncoding);
@@ -146,14 +145,14 @@ ByteVector AttachedPictureFrame::renderFields() const {
data.append(d->data); data.append(d->data);
return data; return data;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) : Frame(h), AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) : Frame(h), d(new AttachedPictureFramePrivate()) {
d(new AttachedPictureFramePrivate()) {
parseFields(fieldData(data)); parseFields(fieldData(data));
} }
@@ -162,6 +161,7 @@ AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) :
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void AttachedPictureFrameV22::parseFields(const ByteVector &data) { void AttachedPictureFrameV22::parseFields(const ByteVector &data) {
if (data.size() < 5) { if (data.size() < 5) {
debug("A picture frame must contain at least 5 bytes."); debug("A picture frame must contain at least 5 bytes.");
return; return;
@@ -189,9 +189,11 @@ void AttachedPictureFrameV22::parseFields(const ByteVector &data) {
d->description = readStringField(data, d->textEncoding, &pos); d->description = readStringField(data, d->textEncoding, &pos);
d->data = data.mid(pos); d->data = data.mid(pos);
} }
AttachedPictureFrameV22::AttachedPictureFrameV22(const ByteVector &data, Header *h) { AttachedPictureFrameV22::AttachedPictureFrameV22(const ByteVector &data, Header *h) {
// set v2.2 header to make fieldData work correctly // set v2.2 header to make fieldData work correctly
setHeader(h, true); setHeader(h, true);
@@ -201,4 +203,5 @@ AttachedPictureFrameV22::AttachedPictureFrameV22(const ByteVector &data, Header
Frame::Header *newHeader = new Frame::Header("APIC"); Frame::Header *newHeader = new Frame::Header("APIC");
newHeader->setFrameSize(h->frameSize()); newHeader->setFrameSize(h->frameSize());
setHeader(newHeader, true); setHeader(newHeader, true);
} }

View File

@@ -32,16 +32,14 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
//! An ID3v2 attached picture frame implementation //! An ID3v2 attached picture frame implementation
/*! /*!
* This is an implementation of ID3v2 attached pictures. Pictures may be * This is an implementation of ID3v2 attached pictures.
* included in tags, one per APIC frame (but there may be multiple APIC * Pictures may be included in tags, one per APIC frame (but there may be multiple APIC frames in a single tag).
* frames in a single tag). These pictures are usually in either JPEG or * These pictures are usually in either JPEG or PNG format.
* PNG format.
*/ */
class TAGLIB_EXPORT AttachedPictureFrame : public Frame { class TAGLIB_EXPORT AttachedPictureFrame : public Frame {
@@ -97,8 +95,8 @@ class TAGLIB_EXPORT AttachedPictureFrame : public Frame {
}; };
/*! /*!
* Constructs an empty picture frame. The description, content and text * Constructs an empty picture frame.
* encoding should be set manually. * The description, content and text encoding should be set manually.
*/ */
AttachedPictureFrame(); AttachedPictureFrame();
@@ -133,14 +131,14 @@ class TAGLIB_EXPORT AttachedPictureFrame : public Frame {
void setTextEncoding(String::Type t); void setTextEncoding(String::Type t);
/*! /*!
* Returns the mime type of the image. This should in most cases be * Returns the mime type of the image.
* "image/png" or "image/jpeg". * This should in most cases be "image/png" or "image/jpeg".
*/ */
String mimeType() const; String mimeType() const;
/*! /*!
* Sets the mime type of the image. This should in most cases be * Sets the mime type of the image.
* "image/png" or "image/jpeg". * This should in most cases be "image/png" or "image/jpeg".
*/ */
void setMimeType(const String &m); void setMimeType(const String &m);
@@ -183,8 +181,7 @@ class TAGLIB_EXPORT AttachedPictureFrame : public Frame {
/*! /*!
* Returns the image data as a ByteVector. * Returns the image data as a ByteVector.
* *
* \note ByteVector has a data() method that returns a const char * which * \note ByteVector has a data() method that returns a const char * which should make it easy to export this data to external programs.
* should make it easy to export this data to external programs.
* *
* \see setPicture() * \see setPicture()
* \see mimeType() * \see mimeType()
@@ -192,8 +189,7 @@ class TAGLIB_EXPORT AttachedPictureFrame : public Frame {
ByteVector picture() const; ByteVector picture() const;
/*! /*!
* Sets the image data to \a p. \a p should be of the type specified in * Sets the image data to \a p. \a p should be of the type specified in this frame's mime-type specification.
* this frame's mime-type specification.
* *
* \see picture() * \see picture()
* \see mimeType() * \see mimeType()
@@ -222,6 +218,7 @@ class TAGLIB_EXPORT AttachedPictureFrameV22 : public AttachedPictureFrame {
AttachedPictureFrameV22(const ByteVector &data, Header *h); AttachedPictureFrameV22(const ByteVector &data, Header *h);
friend class FrameFactory; friend class FrameFactory;
}; };
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -35,7 +35,7 @@ using namespace ID3v2;
class ChapterFrame::ChapterFramePrivate { class ChapterFrame::ChapterFramePrivate {
public: public:
ChapterFramePrivate() : tagHeader(0), ChapterFramePrivate() : tagHeader(nullptr),
startTime(0), startTime(0),
endTime(0), endTime(0),
startOffset(0), startOffset(0),
@@ -57,19 +57,14 @@ class ChapterFrame::ChapterFramePrivate {
// public methods // public methods
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : ID3v2::Frame(data), ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : ID3v2::Frame(data), d(new ChapterFramePrivate()) {
d(new ChapterFramePrivate()) {
d->tagHeader = tagHeader; d->tagHeader = tagHeader;
setData(data); setData(data);
} }
ChapterFrame::ChapterFrame(const ByteVector &elementID, ChapterFrame::ChapterFrame(const ByteVector &elementID, unsigned int startTime, unsigned int endTime, unsigned int startOffset, unsigned int endOffset, const FrameList &embeddedFrames) : ID3v2::Frame("CHAP"), d(new ChapterFramePrivate()) {
unsigned int startTime, unsigned int endTime,
unsigned int startOffset, unsigned int endOffset, // setElementID has a workaround for a previously silly API where you had to specifically include the null byte.
const FrameList &embeddedFrames) : ID3v2::Frame("CHAP"),
d(new ChapterFramePrivate()) {
// setElementID has a workaround for a previously silly API where you had to
// specifically include the null byte.
setElementID(elementID); setElementID(elementID);
@@ -82,6 +77,7 @@ ChapterFrame::ChapterFrame(const ByteVector &elementID,
it != embeddedFrames.end(); it != embeddedFrames.end();
++it) ++it)
addEmbeddedFrame(*it); addEmbeddedFrame(*it);
} }
ChapterFrame::~ChapterFrame() { ChapterFrame::~ChapterFrame() {
@@ -109,10 +105,12 @@ unsigned int ChapterFrame::endOffset() const {
} }
void ChapterFrame::setElementID(const ByteVector &eID) { void ChapterFrame::setElementID(const ByteVector &eID) {
d->elementID = eID; d->elementID = eID;
if (d->elementID.endsWith(char(0))) if (d->elementID.endsWith(char(0)))
d->elementID = d->elementID.mid(0, d->elementID.size() - 1); d->elementID = d->elementID.mid(0, d->elementID.size() - 1);
} }
void ChapterFrame::setStartTime(const unsigned int &sT) { void ChapterFrame::setStartTime(const unsigned int &sT) {
@@ -149,6 +147,7 @@ void ChapterFrame::addEmbeddedFrame(Frame *frame) {
} }
void ChapterFrame::removeEmbeddedFrame(Frame *frame, bool del) { void ChapterFrame::removeEmbeddedFrame(Frame *frame, bool del) {
// remove the frame from the frame list // remove the frame from the frame list
FrameList::Iterator it = d->embeddedFrameList.find(frame); FrameList::Iterator it = d->embeddedFrameList.find(frame);
d->embeddedFrameList.erase(it); d->embeddedFrameList.erase(it);
@@ -160,15 +159,19 @@ void ChapterFrame::removeEmbeddedFrame(Frame *frame, bool del) {
// ...and delete as desired // ...and delete as desired
if (del) if (del)
delete frame; delete frame;
} }
void ChapterFrame::removeEmbeddedFrames(const ByteVector &id) { void ChapterFrame::removeEmbeddedFrames(const ByteVector &id) {
FrameList l = d->embeddedFrameListMap[id]; FrameList l = d->embeddedFrameListMap[id];
for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it)
removeEmbeddedFrame(*it, true); removeEmbeddedFrame(*it, true);
} }
String ChapterFrame::toString() const { String ChapterFrame::toString() const {
String s = String(d->elementID) + String s = String(d->elementID) +
": start time: " + String::number(d->startTime) + ": start time: " + String::number(d->startTime) +
", end time: " + String::number(d->endTime); ", end time: " + String::number(d->endTime);
@@ -189,18 +192,21 @@ String ChapterFrame::toString() const {
} }
return s; return s;
} }
PropertyMap ChapterFrame::asProperties() const { PropertyMap ChapterFrame::asProperties() const {
PropertyMap map; PropertyMap map;
map.unsupportedData().append(frameID() + String("/") + d->elementID); map.unsupportedData().append(frameID() + String("/") + d->elementID);
return map; return map;
} }
ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) // static ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) { // static
{
ID3v2::FrameList comments = tag->frameList("CHAP"); ID3v2::FrameList comments = tag->frameList("CHAP");
for (ID3v2::FrameList::ConstIterator it = comments.begin(); for (ID3v2::FrameList::ConstIterator it = comments.begin();
@@ -212,9 +218,11 @@ ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVec
} }
return nullptr; return nullptr;
} }
void ChapterFrame::parseFields(const ByteVector &data) { void ChapterFrame::parseFields(const ByteVector &data) {
unsigned int size = data.size(); unsigned int size = data.size();
if (size < 18) { if (size < 18) {
debug("A CHAP frame must contain at least 18 bytes (1 byte element ID " debug("A CHAP frame must contain at least 18 bytes (1 byte element ID "
@@ -255,9 +263,11 @@ void ChapterFrame::parseFields(const ByteVector &data) {
embPos += frame->size() + header()->size(); embPos += frame->size() + header()->size();
addEmbeddedFrame(frame); addEmbeddedFrame(frame);
} }
} }
ByteVector ChapterFrame::renderFields() const { ByteVector ChapterFrame::renderFields() const {
ByteVector data; ByteVector data;
data.append(d->elementID); data.append(d->elementID);
@@ -271,10 +281,12 @@ ByteVector ChapterFrame::renderFields() const {
data.append((*it)->render()); data.append((*it)->render());
return data; return data;
} }
ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : Frame(h), ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : Frame(h), d(new ChapterFramePrivate()) {
d(new ChapterFramePrivate()) {
d->tagHeader = tagHeader; d->tagHeader = tagHeader;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@@ -32,12 +32,11 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
/*! /*!
* This is an implementation of ID3v2 chapter frames. The purpose of this * This is an implementation of ID3v2 chapter frames.
* frame is to describe a single chapter within an audio file. * The purpose of this frame is to describe a single chapter within an audio file.
*/ */
//! An implementation of ID3v2 chapter frames //! An implementation of ID3v2 chapter frames
@@ -47,17 +46,16 @@ class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame {
public: public:
/*! /*!
* Creates a chapter frame based on \a data. \a tagHeader is required as * Creates a chapter frame based on \a data.
* the internal frames are parsed based on the tag version. * \a tagHeader is required as the internal frames are parsed based on the tag version.
*/ */
ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data); ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data);
/*! /*!
* Creates a chapter frame with the element ID \a elementID, start time * Creates a chapter frame with the element ID \a elementID,
* \a startTime, end time \a endTime, start offset \a startOffset, * start time \a startTime, end time \a endTime, start offset \a startOffset,
* end offset \a endOffset and optionally a list of embedded frames, * end offset \a endOffset and optionally a list of embedded frames,
* whose ownership will then be taken over by this Frame, in * whose ownership will then be taken over by this Frame, in \a embeededFrames;
* \a embeededFrames;
* *
* All times are in milliseconds. * All times are in milliseconds.
*/ */
@@ -72,8 +70,8 @@ class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame {
virtual ~ChapterFrame(); virtual ~ChapterFrame();
/*! /*!
* Returns the element ID of the frame. Element ID * Returns the element ID of the frame.
* is a null terminated string, however it's not human-readable. * Element ID is a null terminated string, however it's not human-readable.
* *
* \see setElementID() * \see setElementID()
*/ */
@@ -94,8 +92,7 @@ class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame {
unsigned int endTime() const; unsigned int endTime() const;
/*! /*!
* Returns zero based byte offset (count of bytes from the beginning * Returns zero based byte offset (count of bytes from the beginning of the audio file) of chapter's start.
* of the audio file) of chapter's start.
* *
* \note If returned value is 0xFFFFFFFF, start time should be used instead. * \note If returned value is 0xFFFFFFFF, start time should be used instead.
* \see setStartOffset() * \see setStartOffset()
@@ -103,8 +100,7 @@ class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame {
unsigned int startOffset() const; unsigned int startOffset() const;
/*! /*!
* Returns zero based byte offset (count of bytes from the beginning * Returns zero based byte offset (count of bytes from the beginning of the audio file) of chapter's end.
* of the audio file) of chapter's end.
* *
* \note If returned value is 0xFFFFFFFF, end time should be used instead. * \note If returned value is 0xFFFFFFFF, end time should be used instead.
* \see setEndOffset() * \see setEndOffset()
@@ -112,8 +108,7 @@ class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame {
unsigned int endOffset() const; unsigned int endOffset() const;
/*! /*!
* Sets the element ID of the frame to \a eID. If \a eID isn't * Sets the element ID of the frame to \a eID. If \a eID isn't null terminated, a null char is appended automatically.
* null terminated, a null char is appended automatically.
* *
* \see elementID() * \see elementID()
*/ */
@@ -134,29 +129,26 @@ class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame {
void setEndTime(const unsigned int &eT); void setEndTime(const unsigned int &eT);
/*! /*!
* Sets zero based byte offset (count of bytes from the beginning * Sets zero based byte offset (count of bytes from the beginning of the audio file) of chapter's start to \a sO.
* of the audio file) of chapter's start to \a sO.
* *
* \see startOffset() * \see startOffset()
*/ */
void setStartOffset(const unsigned int &sO); void setStartOffset(const unsigned int &sO);
/*! /*!
* Sets zero based byte offset (count of bytes from the beginning * Sets zero based byte offset (count of bytes from the beginning of the audio file) of chapter's end to \a eO.
* of the audio file) of chapter's end to \a eO.
* *
* \see endOffset() * \see endOffset()
*/ */
void setEndOffset(const unsigned int &eO); void setEndOffset(const unsigned int &eO);
/*! /*!
* Returns a reference to the frame list map. This is an FrameListMap of * Returns a reference to the frame list map.
* all of the frames embedded in the CHAP frame. * This is an FrameListMap of all of the frames embedded in the CHAP frame.
* *
* This is the most convenient structure for accessing the CHAP frame's * This is the most convenient structure for accessing the CHAP frame's embedded frames.
* embedded frames. Many frame types allow multiple instances of the same * Many frame types allow multiple instances of the same rame type so this is a map of lists.
* frame type so this is a map of lists. In most cases however there will * In most cases however there will only be a single frame of a certain type.
* only be a single frame of a certain type.
* *
* \warning You should not modify this data structure directly, instead * \warning You should not modify this data structure directly, instead
* use addEmbeddedFrame() and removeEmbeddedFrame(). * use addEmbeddedFrame() and removeEmbeddedFrame().
@@ -166,22 +158,20 @@ class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame {
const FrameListMap &embeddedFrameListMap() const; const FrameListMap &embeddedFrameListMap() const;
/*! /*!
* Returns a reference to the embedded frame list. This is an FrameList * Returns a reference to the embedded frame list.
* of all of the frames embedded in the CHAP frame in the order that they * This is an FrameList of all of the frames embedded in the CHAP frame in the order that they were parsed.
* were parsed.
* *
* This can be useful if for example you want iterate over the CHAP frame's * This can be useful if for example you want iterate over the CHAP frame's
* embedded frames in the order that they occur in the CHAP frame. * embedded frames in the order that they occur in the CHAP frame.
* *
* \warning You should not modify this data structure directly, instead * \warning You should not modify this data structure directly,
* use addEmbeddedFrame() and removeEmbeddedFrame(). * instead use addEmbeddedFrame() and removeEmbeddedFrame().
*/ */
const FrameList &embeddedFrameList() const; const FrameList &embeddedFrameList() const;
/*! /*!
* Returns the embedded frame list for frames with the id \a frameID * Returns the embedded frame list for frames with the id \a frameID or an empty list if there are no embedded frames of that type.
* or an empty list if there are no embedded frames of that type. This * This is just a convenience and is equivalent to:
* is just a convenience and is equivalent to:
* *
* \code * \code
* embeddedFrameListMap()[frameID]; * embeddedFrameListMap()[frameID];
@@ -192,29 +182,25 @@ class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame {
const FrameList &embeddedFrameList(const ByteVector &frameID) const; const FrameList &embeddedFrameList(const ByteVector &frameID) const;
/*! /*!
* Add an embedded frame to the CHAP frame. At this point the CHAP frame * Add an embedded frame to the CHAP frame.
* takes ownership of the embedded frame and will handle freeing its memory. * At this point the CHAP frame takes ownership of the embedded frame and will handle freeing its memory.
* *
* \note Using this method will invalidate any pointers on the list * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList()
* returned by embeddedFrameList()
*/ */
void addEmbeddedFrame(Frame *frame); void addEmbeddedFrame(Frame *frame);
/*! /*!
* Remove an embedded frame from the CHAP frame. If \a del is true the frame's * Remove an embedded frame from the CHAP frame.
* memory will be freed; if it is false, it must be deleted by the user. * If \a del is true the frame's memory will be freed; if it is false, it must be deleted by the user.
* *
* \note Using this method will invalidate any pointers on the list * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList()
* returned by embeddedFrameList()
*/ */
void removeEmbeddedFrame(Frame *frame, bool del = true); void removeEmbeddedFrame(Frame *frame, bool del = true);
/*! /*!
* Remove all embedded frames of type \a id from the CHAP frame and free their * Remove all embedded frames of type \a id from the CHAP frame and free their memory.
* memory.
* *
* \note Using this method will invalidate any pointers on the list * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList()
* returned by embeddedFrameList()
*/ */
void removeEmbeddedFrames(const ByteVector &id); void removeEmbeddedFrames(const ByteVector &id);
@@ -223,9 +209,8 @@ class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame {
PropertyMap asProperties() const; PropertyMap asProperties() const;
/*! /*!
* CHAP frames each have a unique element ID. This searches for a CHAP * CHAP frames each have a unique element ID. This searches for a CHAP frame with the element ID \a eID and returns a pointer to it.
* frame with the element ID \a eID and returns a pointer to it. This * This can be used to link CTOC and CHAP frames together.
* can be used to link CTOC and CHAP frames together.
* *
* \see elementID() * \see elementID()
*/ */
@@ -243,6 +228,7 @@ class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame {
class ChapterFramePrivate; class ChapterFramePrivate;
ChapterFramePrivate *d; ChapterFramePrivate *d;
}; };
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -47,13 +47,11 @@ class CommentsFrame::CommentsFramePrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM"), CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM"), d(new CommentsFramePrivate()) {
d(new CommentsFramePrivate()) {
d->textEncoding = encoding; d->textEncoding = encoding;
} }
CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data), CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data), d(new CommentsFramePrivate()) {
d(new CommentsFramePrivate()) {
setData(data); setData(data);
} }
@@ -98,6 +96,7 @@ void CommentsFrame::setTextEncoding(String::Type encoding) {
} }
PropertyMap CommentsFrame::asProperties() const { PropertyMap CommentsFrame::asProperties() const {
String key = description().upper(); String key = description().upper();
PropertyMap map; PropertyMap map;
if (key.isEmpty() || key == "COMMENT") if (key.isEmpty() || key == "COMMENT")
@@ -105,10 +104,11 @@ PropertyMap CommentsFrame::asProperties() const {
else else
map.insert("COMMENT:" + key, text()); map.insert("COMMENT:" + key, text());
return map; return map;
} }
CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) { // static
{
ID3v2::FrameList comments = tag->frameList("COMM"); ID3v2::FrameList comments = tag->frameList("COMM");
for (ID3v2::FrameList::ConstIterator it = comments.begin(); for (ID3v2::FrameList::ConstIterator it = comments.begin();
@@ -120,6 +120,7 @@ CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const Str
} }
return nullptr; return nullptr;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -127,6 +128,7 @@ CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const Str
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void CommentsFrame::parseFields(const ByteVector &data) { void CommentsFrame::parseFields(const ByteVector &data) {
if (data.size() < 5) { if (data.size() < 5) {
debug("A comment frame must contain at least 5 bytes."); debug("A comment frame must contain at least 5 bytes.");
return; return;
@@ -149,9 +151,11 @@ void CommentsFrame::parseFields(const ByteVector &data) {
d->text = String(l.back(), d->textEncoding); d->text = String(l.back(), d->textEncoding);
} }
} }
} }
ByteVector CommentsFrame::renderFields() const { ByteVector CommentsFrame::renderFields() const {
ByteVector v; ByteVector v;
String::Type encoding = d->textEncoding; String::Type encoding = d->textEncoding;
@@ -166,13 +170,13 @@ ByteVector CommentsFrame::renderFields() const {
v.append(d->text.data(encoding)); v.append(d->text.data(encoding));
return v; return v;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h), CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h), d(new CommentsFramePrivate()) {
d(new CommentsFramePrivate()) {
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@@ -31,7 +31,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
//! An implementation of ID3v2 comments //! An implementation of ID3v2 comments
@@ -46,8 +45,7 @@ class TAGLIB_EXPORT CommentsFrame : public Frame {
public: public:
/*! /*!
* Construct an empty comment frame that will use the text encoding * Construct an empty comment frame that will use the text encoding \a encoding.
* \a encoding.
*/ */
explicit CommentsFrame(String::Type encoding = String::Latin1); explicit CommentsFrame(String::Type encoding = String::Latin1);
@@ -96,8 +94,7 @@ class TAGLIB_EXPORT CommentsFrame : public Frame {
/*! /*!
* Set the language using the 3 byte language code from * Set the language using the 3 byte language code from
* <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to \a languageCode.
* \a languageCode.
* *
* \see language() * \see language()
*/ */
@@ -119,8 +116,7 @@ class TAGLIB_EXPORT CommentsFrame : public Frame {
/*! /*!
* Returns the text encoding that will be used in rendering this frame. * Returns the text encoding that will be used in rendering this frame.
* This defaults to the type that was either specified in the constructor * This defaults to the type that was either specified in the constructor or read from the frame when parsed.
* or read from the frame when parsed.
* *
* \see setTextEncoding() * \see setTextEncoding()
* \see render() * \see render()
@@ -128,8 +124,7 @@ class TAGLIB_EXPORT CommentsFrame : public Frame {
String::Type textEncoding() const; String::Type textEncoding() const;
/*! /*!
* Sets the text encoding to be used when rendering this frame to * Sets the text encoding to be used when rendering this frame to \a encoding.
* \a encoding.
* *
* \see textEncoding() * \see textEncoding()
* \see render() * \see render()
@@ -148,9 +143,9 @@ class TAGLIB_EXPORT CommentsFrame : public Frame {
PropertyMap asProperties() const; PropertyMap asProperties() const;
/*! /*!
* Comments each have a unique description. This searches for a comment * Comments each have a unique description.
* frame with the description \a d and returns a pointer to it. If no * This searches for a comment frame with the description \a d and returns a pointer to it.
* frame is found that matches the given description null is returned. * If no frame is found that matches the given description null is returned.
* *
* \see description() * \see description()
*/ */
@@ -177,4 +172,5 @@ class TAGLIB_EXPORT CommentsFrame : public Frame {
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib
#endif #endif

View File

@@ -43,12 +43,9 @@ class EventTimingCodesFrame::EventTimingCodesFramePrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
EventTimingCodesFrame::EventTimingCodesFrame() : Frame("ETCO"), EventTimingCodesFrame::EventTimingCodesFrame() : Frame("ETCO"), d(new EventTimingCodesFramePrivate()) {}
d(new EventTimingCodesFramePrivate()) {
}
EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data) : Frame(data), EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data) : Frame(data), d(new EventTimingCodesFramePrivate()) {
d(new EventTimingCodesFramePrivate()) {
setData(data); setData(data);
} }
@@ -85,6 +82,7 @@ void EventTimingCodesFrame::setSynchedEvents(
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void EventTimingCodesFrame::parseFields(const ByteVector &data) { void EventTimingCodesFrame::parseFields(const ByteVector &data) {
const int end = data.size(); const int end = data.size();
if (end < 1) { if (end < 1) {
debug("An event timing codes frame must contain at least 1 byte."); debug("An event timing codes frame must contain at least 1 byte.");
@@ -101,28 +99,28 @@ void EventTimingCodesFrame::parseFields(const ByteVector &data) {
pos += 4; pos += 4;
d->synchedEvents.append(SynchedEvent(time, type)); d->synchedEvents.append(SynchedEvent(time, type));
} }
} }
ByteVector EventTimingCodesFrame::renderFields() const { ByteVector EventTimingCodesFrame::renderFields() const {
ByteVector v; ByteVector v;
v.append(char(d->timestampFormat)); v.append(char(d->timestampFormat));
for (SynchedEventList::ConstIterator it = d->synchedEvents.begin(); for (SynchedEventList::ConstIterator it = d->synchedEvents.begin(); it != d->synchedEvents.end(); ++it) {
it != d->synchedEvents.end();
++it) {
const SynchedEvent &entry = *it; const SynchedEvent &entry = *it;
v.append(char(entry.type)); v.append(char(entry.type));
v.append(ByteVector::fromUInt(entry.time)); v.append(ByteVector::fromUInt(entry.time));
} }
return v; return v;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h) : Frame(h), EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h) : Frame(h), d(new EventTimingCodesFramePrivate()) {
d(new EventTimingCodesFramePrivate()) {
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@@ -31,7 +31,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
//! ID3v2 event timing codes frame //! ID3v2 event timing codes frame
@@ -182,4 +181,5 @@ class TAGLIB_EXPORT EventTimingCodesFrame : public Frame {
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib
#endif #endif

View File

@@ -49,12 +49,9 @@ class GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFramePrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : Frame("GEOB"), GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : Frame("GEOB"), d(new GeneralEncapsulatedObjectFramePrivate()) {}
d(new GeneralEncapsulatedObjectFramePrivate()) {
}
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : Frame(data), GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : Frame(data), d(new GeneralEncapsulatedObjectFramePrivate()) {
d(new GeneralEncapsulatedObjectFramePrivate()) {
setData(data); setData(data);
} }
@@ -63,6 +60,7 @@ GeneralEncapsulatedObjectFrame::~GeneralEncapsulatedObjectFrame() {
} }
String GeneralEncapsulatedObjectFrame::toString() const { String GeneralEncapsulatedObjectFrame::toString() const {
String text = "[" + d->mimeType + "]"; String text = "[" + d->mimeType + "]";
if (!d->fileName.isEmpty()) if (!d->fileName.isEmpty())
@@ -72,6 +70,7 @@ String GeneralEncapsulatedObjectFrame::toString() const {
text += " \"" + d->description + "\""; text += " \"" + d->description + "\"";
return text; return text;
} }
String::Type GeneralEncapsulatedObjectFrame::textEncoding() const { String::Type GeneralEncapsulatedObjectFrame::textEncoding() const {
@@ -119,6 +118,7 @@ void GeneralEncapsulatedObjectFrame::setObject(const ByteVector &data) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data) { void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data) {
if (data.size() < 4) { if (data.size() < 4) {
debug("An object frame must contain at least 4 bytes."); debug("An object frame must contain at least 4 bytes.");
return; return;
@@ -133,9 +133,11 @@ void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data) {
d->description = readStringField(data, d->textEncoding, &pos); d->description = readStringField(data, d->textEncoding, &pos);
d->data = data.mid(pos); d->data = data.mid(pos);
} }
ByteVector GeneralEncapsulatedObjectFrame::renderFields() const { ByteVector GeneralEncapsulatedObjectFrame::renderFields() const {
StringList sl; StringList sl;
sl.append(d->fileName); sl.append(d->fileName);
sl.append(d->description); sl.append(d->description);
@@ -154,13 +156,13 @@ ByteVector GeneralEncapsulatedObjectFrame::renderFields() const {
data.append(d->data); data.append(d->data);
return data; return data;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : Frame(h), GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : Frame(h), d(new GeneralEncapsulatedObjectFramePrivate()) {
d(new GeneralEncapsulatedObjectFramePrivate()) {
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@@ -35,7 +35,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
//! An ID3v2 general encapsulated object frame implementation //! An ID3v2 general encapsulated object frame implementation
@@ -43,10 +42,10 @@ namespace ID3v2 {
/*! /*!
* This is an implementation of ID3v2 general encapsulated objects. * This is an implementation of ID3v2 general encapsulated objects.
* Arbitrary binary data may be included in tags, stored in GEOB frames. * Arbitrary binary data may be included in tags, stored in GEOB frames.
* There may be multiple GEOB frames in a single tag. Each GEOB it * There may be multiple GEOB frames in a single tag.
* labelled with a content description (which may be blank), a required * Each GEOB it labelled with a content description (which may be blank),
* mime-type, and a file name (may be blank). The content description * a required mime-type, and a file name (may be blank).
* uniquely identifies the GEOB frame in the tag. * The content description uniquely identifies the GEOB frame in the tag.
*/ */
class TAGLIB_EXPORT GeneralEncapsulatedObjectFrame : public Frame { class TAGLIB_EXPORT GeneralEncapsulatedObjectFrame : public Frame {
@@ -54,8 +53,8 @@ class TAGLIB_EXPORT GeneralEncapsulatedObjectFrame : public Frame {
public: public:
/*! /*!
* Constructs an empty object frame. The description, file name and text * Constructs an empty object frame.
* encoding should be set manually. * The description, file name and text encoding should be set manually.
*/ */
GeneralEncapsulatedObjectFrame(); GeneralEncapsulatedObjectFrame();
@@ -63,8 +62,8 @@ class TAGLIB_EXPORT GeneralEncapsulatedObjectFrame : public Frame {
* Constructs a GeneralEncapsulatedObjectFrame frame based on \a data. * Constructs a GeneralEncapsulatedObjectFrame frame based on \a data.
* *
* \warning This is \em not data for the encapsulated object, for that use * \warning This is \em not data for the encapsulated object, for that use
* setObject(). This constructor is used when reading the frame from the * setObject().
* disk. * This constructor is used when reading the frame from the disk.
*/ */
explicit GeneralEncapsulatedObjectFrame(const ByteVector &data); explicit GeneralEncapsulatedObjectFrame(const ByteVector &data);
@@ -151,8 +150,8 @@ class TAGLIB_EXPORT GeneralEncapsulatedObjectFrame : public Frame {
ByteVector object() const; ByteVector object() const;
/*! /*!
* Sets the object data to \a data. \a data should be of the type specified in * Sets the object data to \a data.
* this frame's mime-type specification. * \a data should be of the type specified in this frame's mime-type specification.
* *
* \see object() * \see object()
* \see mimeType() * \see mimeType()
@@ -172,6 +171,7 @@ class TAGLIB_EXPORT GeneralEncapsulatedObjectFrame : public Frame {
class GeneralEncapsulatedObjectFramePrivate; class GeneralEncapsulatedObjectFramePrivate;
GeneralEncapsulatedObjectFramePrivate *d; GeneralEncapsulatedObjectFramePrivate *d;
}; };
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -44,13 +44,11 @@ class OwnershipFrame::OwnershipFramePrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE"), OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE"),d(new OwnershipFramePrivate()) {
d(new OwnershipFramePrivate()) {
d->textEncoding = encoding; d->textEncoding = encoding;
} }
OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data), OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data),d(new OwnershipFramePrivate()) {
d(new OwnershipFramePrivate()) {
setData(data); setData(data);
} }
@@ -99,6 +97,7 @@ void OwnershipFrame::setTextEncoding(String::Type encoding) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void OwnershipFrame::parseFields(const ByteVector &data) { void OwnershipFrame::parseFields(const ByteVector &data) {
int pos = 0; int pos = 0;
// Get the text encoding // Get the text encoding
@@ -123,9 +122,11 @@ void OwnershipFrame::parseFields(const ByteVector &data) {
d->seller = Tag::latin1StringHandler()->parse(data.mid(pos)); d->seller = Tag::latin1StringHandler()->parse(data.mid(pos));
else else
d->seller = String(data.mid(pos), d->textEncoding); d->seller = String(data.mid(pos), d->textEncoding);
} }
ByteVector OwnershipFrame::renderFields() const { ByteVector OwnershipFrame::renderFields() const {
StringList sl; StringList sl;
sl.append(d->seller); sl.append(d->seller);
@@ -140,13 +141,13 @@ ByteVector OwnershipFrame::renderFields() const {
v.append(d->seller.data(encoding)); v.append(d->seller.data(encoding));
return v; return v;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h), OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h), d(new OwnershipFramePrivate()) {
d(new OwnershipFramePrivate()) {
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@@ -31,14 +31,13 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
//! An implementation of ID3v2 "ownership" //! An implementation of ID3v2 "ownership"
/*! /*!
* This implements the ID3v2 ownership (OWNE frame). It consists of * This implements the ID3v2 ownership (OWNE frame).
* a price paid, a date purchased (YYYYMMDD) and the name of the seller. * It consists of a price paid, a date purchased (YYYYMMDD) and the name of the seller.
*/ */
class TAGLIB_EXPORT OwnershipFrame : public Frame { class TAGLIB_EXPORT OwnershipFrame : public Frame {
@@ -111,8 +110,7 @@ class TAGLIB_EXPORT OwnershipFrame : public Frame {
/*! /*!
* Returns the text encoding that will be used in rendering this frame. * Returns the text encoding that will be used in rendering this frame.
* This defaults to the type that was either specified in the constructor * This defaults to the type that was either specified in the constructor or read from the frame when parsed.
* or read from the frame when parsed.
* *
* \see setTextEncoding() * \see setTextEncoding()
* \see render() * \see render()
@@ -120,8 +118,7 @@ class TAGLIB_EXPORT OwnershipFrame : public Frame {
String::Type textEncoding() const; String::Type textEncoding() const;
/*! /*!
* Sets the text encoding to be used when rendering this frame to * Sets the text encoding to be used when rendering this frame to \a encoding.
* \a encoding.
* *
* \see textEncoding() * \see textEncoding()
* \see render() * \see render()
@@ -149,4 +146,5 @@ class TAGLIB_EXPORT OwnershipFrame : public Frame {
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib
#endif #endif

View File

@@ -37,8 +37,7 @@ class PodcastFrame::PodcastFramePrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PodcastFrame::PodcastFrame() : Frame("PCST"), PodcastFrame::PodcastFrame() : Frame("PCST"), d(new PodcastFramePrivate()) {
d(new PodcastFramePrivate()) {
d->fieldData = ByteVector(4, '\0'); d->fieldData = ByteVector(4, '\0');
} }
@@ -66,7 +65,6 @@ ByteVector PodcastFrame::renderFields() const {
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PodcastFrame::PodcastFrame(const ByteVector &data, Header *h) : Frame(h), PodcastFrame::PodcastFrame(const ByteVector &data, Header *h) : Frame(h), d(new PodcastFramePrivate()) {
d(new PodcastFramePrivate()) {
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@@ -31,7 +31,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
//! ID3v2 podcast frame //! ID3v2 podcast frame
@@ -78,4 +77,5 @@ class TAGLIB_EXPORT PodcastFrame : public Frame {
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib
#endif #endif

View File

@@ -42,12 +42,9 @@ class PopularimeterFrame::PopularimeterFramePrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PopularimeterFrame::PopularimeterFrame() : Frame("POPM"), PopularimeterFrame::PopularimeterFrame() : Frame("POPM"), d(new PopularimeterFramePrivate()) {}
d(new PopularimeterFramePrivate()) {
}
PopularimeterFrame::PopularimeterFrame(const ByteVector &data) : Frame(data), PopularimeterFrame::PopularimeterFrame(const ByteVector &data) : Frame(data), d(new PopularimeterFramePrivate()) {
d(new PopularimeterFramePrivate()) {
setData(data); setData(data);
} }
@@ -88,6 +85,7 @@ void PopularimeterFrame::setCounter(unsigned int s) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void PopularimeterFrame::parseFields(const ByteVector &data) { void PopularimeterFrame::parseFields(const ByteVector &data) {
int pos = 0, size = int(data.size()); int pos = 0, size = int(data.size());
d->email = readStringField(data, String::Latin1, &pos); d->email = readStringField(data, String::Latin1, &pos);
@@ -100,9 +98,11 @@ void PopularimeterFrame::parseFields(const ByteVector &data) {
d->counter = data.toUInt(static_cast<unsigned int>(pos)); d->counter = data.toUInt(static_cast<unsigned int>(pos));
} }
} }
} }
ByteVector PopularimeterFrame::renderFields() const { ByteVector PopularimeterFrame::renderFields() const {
ByteVector data; ByteVector data;
data.append(d->email.data(String::Latin1)); data.append(d->email.data(String::Latin1));
@@ -111,13 +111,13 @@ ByteVector PopularimeterFrame::renderFields() const {
data.append(ByteVector::fromUInt(d->counter)); data.append(ByteVector::fromUInt(d->counter));
return data; return data;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) : Frame(h), PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) : Frame(h), d(new PopularimeterFramePrivate()) {
d(new PopularimeterFramePrivate()) {
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@@ -31,14 +31,13 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
//! An implementation of ID3v2 "popularimeter" //! An implementation of ID3v2 "popularimeter"
/*! /*!
* This implements the ID3v2 popularimeter (POPM frame). It consists of * This implements the ID3v2 popularimeter (POPM frame).
* an email, a rating and an optional counter. * It consists of an email, a rating and an optional counter.
*/ */
class TAGLIB_EXPORT PopularimeterFrame : public Frame { class TAGLIB_EXPORT PopularimeterFrame : public Frame {
@@ -130,4 +129,5 @@ class TAGLIB_EXPORT PopularimeterFrame : public Frame {
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib
#endif #endif

View File

@@ -44,12 +44,9 @@ class PrivateFrame::PrivateFramePrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PrivateFrame::PrivateFrame() : Frame("PRIV"), PrivateFrame::PrivateFrame() : Frame("PRIV"), d(new PrivateFramePrivate()) {}
d(new PrivateFramePrivate()) {
}
PrivateFrame::PrivateFrame(const ByteVector &data) : Frame(data), PrivateFrame::PrivateFrame(const ByteVector &data) : Frame(data), d(new PrivateFramePrivate()) {
d(new PrivateFramePrivate()) {
setData(data); setData(data);
} }
@@ -82,6 +79,7 @@ void PrivateFrame::setData(const ByteVector &data) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void PrivateFrame::parseFields(const ByteVector &data) { void PrivateFrame::parseFields(const ByteVector &data) {
if (data.size() < 2) { if (data.size() < 2) {
debug("A private frame must contain at least 2 bytes."); debug("A private frame must contain at least 2 bytes.");
return; return;
@@ -94,9 +92,11 @@ void PrivateFrame::parseFields(const ByteVector &data) {
d->owner = String(data.mid(0, endOfOwner)); d->owner = String(data.mid(0, endOfOwner));
d->data = data.mid(endOfOwner + 1); d->data = data.mid(endOfOwner + 1);
} }
ByteVector PrivateFrame::renderFields() const { ByteVector PrivateFrame::renderFields() const {
ByteVector v; ByteVector v;
v.append(d->owner.data(String::Latin1)); v.append(d->owner.data(String::Latin1));
@@ -104,13 +104,13 @@ ByteVector PrivateFrame::renderFields() const {
v.append(d->data); v.append(d->data);
return v; return v;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) : Frame(h), PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) : Frame(h), d(new PrivateFramePrivate()) {
d(new PrivateFramePrivate()) {
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@@ -32,7 +32,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
//! An implementation of ID3v2 privateframe //! An implementation of ID3v2 privateframe
@@ -109,4 +108,5 @@ class TAGLIB_EXPORT PrivateFrame : public Frame {
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib
#endif #endif

View File

@@ -49,12 +49,9 @@ class RelativeVolumeFrame::RelativeVolumeFramePrivate {
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
RelativeVolumeFrame::RelativeVolumeFrame() : Frame("RVA2"), RelativeVolumeFrame::RelativeVolumeFrame() : Frame("RVA2"), d(new RelativeVolumeFramePrivate()) {}
d(new RelativeVolumeFramePrivate()) {
}
RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) : Frame(data), RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) : Frame(data), d(new RelativeVolumeFramePrivate()) {
d(new RelativeVolumeFramePrivate()) {
setData(data); setData(data);
} }
@@ -67,6 +64,7 @@ String RelativeVolumeFrame::toString() const {
} }
List<RelativeVolumeFrame::ChannelType> RelativeVolumeFrame::channels() const { List<RelativeVolumeFrame::ChannelType> RelativeVolumeFrame::channels() const {
List<ChannelType> l; List<ChannelType> l;
Map<ChannelType, ChannelData>::ConstIterator it = d->channels.begin(); Map<ChannelType, ChannelData>::ConstIterator it = d->channels.begin();
@@ -74,17 +72,7 @@ List<RelativeVolumeFrame::ChannelType> RelativeVolumeFrame::channels() const {
l.append((*it).first); l.append((*it).first);
return l; return l;
}
// deprecated
RelativeVolumeFrame::ChannelType RelativeVolumeFrame::channelType() const {
return MasterVolume;
}
// deprecated
void RelativeVolumeFrame::setChannelType(ChannelType) {
} }
short RelativeVolumeFrame::volumeAdjustmentIndex(ChannelType type) const { short RelativeVolumeFrame::volumeAdjustmentIndex(ChannelType type) const {
@@ -148,6 +136,7 @@ void RelativeVolumeFrame::setIdentification(const String &s) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void RelativeVolumeFrame::parseFields(const ByteVector &data) { void RelativeVolumeFrame::parseFields(const ByteVector &data) {
int pos = 0; int pos = 0;
d->identification = readStringField(data, String::Latin1, &pos); d->identification = readStringField(data, String::Latin1, &pos);
@@ -170,9 +159,11 @@ void RelativeVolumeFrame::parseFields(const ByteVector &data) {
channel.peakVolume.peakVolume = data.mid(pos, bytes); channel.peakVolume.peakVolume = data.mid(pos, bytes);
pos += bytes; pos += bytes;
} }
} }
ByteVector RelativeVolumeFrame::renderFields() const { ByteVector RelativeVolumeFrame::renderFields() const {
ByteVector data; ByteVector data;
data.append(d->identification.data(String::Latin1)); data.append(d->identification.data(String::Latin1));
@@ -191,13 +182,13 @@ ByteVector RelativeVolumeFrame::renderFields() const {
} }
return data; return data;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) : Frame(h), RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) : Frame(h), d(new RelativeVolumeFramePrivate()) {
d(new RelativeVolumeFramePrivate()) {
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@@ -32,19 +32,15 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
//! An ID3v2 relative volume adjustment frame implementation //! An ID3v2 relative volume adjustment frame implementation
/*! /*!
* This is an implementation of ID3v2 relative volume adjustment. The * This is an implementation of ID3v2 relative volume adjustment.
* presence of this frame makes it possible to specify an increase in volume * The presence of this frame makes it possible to specify an increase in volume for an audio file or specific audio tracks in that file.
* for an audio file or specific audio tracks in that file.
* *
* Multiple relative volume adjustment frames may be present in the tag * Multiple relative volume adjustment frames may be present in the tag each with a unique identification and describing volume adjustment for different channel types.
* each with a unique identification and describing volume adjustment for
* different channel types.
*/ */
class TAGLIB_EXPORT RelativeVolumeFrame : public Frame { class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
@@ -78,8 +74,8 @@ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
//! Struct that stores the relevant values for ID3v2 peak volume //! Struct that stores the relevant values for ID3v2 peak volume
/*! /*!
* The peak volume is described as a series of bits that is padded to fill * The peak volume is described as a series of bits that is padded to fill a block of bytes.
* a block of bytes. These two values should always be updated in tandem. * These two values should always be updated in tandem.
*/ */
struct PeakVolume { struct PeakVolume {
/*! /*!
@@ -87,20 +83,17 @@ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
*/ */
PeakVolume() : bitsRepresentingPeak(0) {} PeakVolume() : bitsRepresentingPeak(0) {}
/*! /*!
* The number of bits (in the range of 0 to 255) used to describe the * The number of bits (in the range of 0 to 255) used to describe the peak volume.
* peak volume.
*/ */
unsigned char bitsRepresentingPeak; unsigned char bitsRepresentingPeak;
/*! /*!
* The array of bits (represented as a series of bytes) used to describe * The array of bits (represented as a series of bytes) used to describe the peak volume.
* the peak volume.
*/ */
ByteVector peakVolume; ByteVector peakVolume;
}; };
/*! /*!
* Constructs a RelativeVolumeFrame. The relevant data should be set * Constructs a RelativeVolumeFrame. The relevant data should be set manually.
* manually.
*/ */
RelativeVolumeFrame(); RelativeVolumeFrame();
@@ -126,31 +119,17 @@ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
*/ */
List<ChannelType> channels() const; List<ChannelType> channels() const;
/*!
* \deprecated Always returns master volume.
*/
TAGLIB_DEPRECATED ChannelType channelType() const;
/*!
* \deprecated This method no longer has any effect.
*/
TAGLIB_DEPRECATED void setChannelType(ChannelType t);
/* /*
* There was a terrible API goof here, and while this can't be changed to * There was a terrible API goof here, and while this can't be changed to the way it appears below for binary compatibility reasons, let's at least pretend that it looks clean.
* the way it appears below for binary compatibility reasons, let's at
* least pretend that it looks clean.
*/ */
#ifdef DOXYGEN #ifdef DOXYGEN
/*! /*!
* Returns the relative volume adjustment "index". As indicated by the * Returns the relative volume adjustment "index".
* ID3v2 standard this is a 16-bit signed integer that reflects the * As indicated by the ID3v2 standard this is a 16-bit signed integer that reflects the decibels of adjustment when divided by 512.
* decibels of adjustment when divided by 512.
* *
* This defaults to returning the value for the master volume channel if * This defaults to returning the value for the master volume channel if available and returns 0 if the specified channel does not exist.
* available and returns 0 if the specified channel does not exist.
* *
* \see setVolumeAdjustmentIndex() * \see setVolumeAdjustmentIndex()
* \see volumeAjustment() * \see volumeAjustment()
@@ -158,9 +137,8 @@ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
short volumeAdjustmentIndex(ChannelType type = MasterVolume) const; short volumeAdjustmentIndex(ChannelType type = MasterVolume) const;
/*! /*!
* Set the volume adjustment to \a index. As indicated by the ID3v2 * Set the volume adjustment to \a index.
* standard this is a 16-bit signed integer that reflects the decibels of * As indicated by the ID3v2 standard this is a 16-bit signed integer that reflects the decibels of adjustment when divided by 512.
* adjustment when divided by 512.
* *
* By default this sets the value for the master volume. * By default this sets the value for the master volume.
* *
@@ -173,11 +151,9 @@ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
* Returns the relative volume adjustment in decibels. * Returns the relative volume adjustment in decibels.
* *
* \note Because this is actually stored internally as an "index" to this * \note Because this is actually stored internally as an "index" to this
* value the value returned by this method may not be identical to the * value the value returned by this method may not be identical to the value set using setVolumeAdjustment().
* value set using setVolumeAdjustment().
* *
* This defaults to returning the value for the master volume channel if * This defaults to returning the value for the master volume channel if available and returns 0 if the specified channel does not exist.
* available and returns 0 if the specified channel does not exist.
* *
* \see setVolumeAdjustment() * \see setVolumeAdjustment()
* \see volumeAdjustmentIndex() * \see volumeAdjustmentIndex()
@@ -189,9 +165,7 @@ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
* *
* By default this sets the value for the master volume. * By default this sets the value for the master volume.
* *
* \note Because this is actually stored internally as an "index" to this * \note Because this is actually stored internally as an "index" to this value the value set by this method may not be identical to the one returned by volumeAdjustment().
* value the value set by this method may not be identical to the one
* returned by volumeAdjustment().
* *
* \see setVolumeAdjustment() * \see setVolumeAdjustment()
* \see volumeAdjustmentIndex() * \see volumeAdjustmentIndex()
@@ -201,8 +175,7 @@ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
/*! /*!
* Returns the peak volume (represented as a length and a string of bits). * Returns the peak volume (represented as a length and a string of bits).
* *
* This defaults to returning the value for the master volume channel if * This defaults to returning the value for the master volume channel if available and returns 0 if the specified channel does not exist.
* available and returns 0 if the specified channel does not exist.
* *
* \see setPeakVolume() * \see setPeakVolume()
*/ */
@@ -219,8 +192,7 @@ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
#else #else
// BIC: Combine each of the following pairs of functions (or maybe just // BIC: Combine each of the following pairs of functions (or maybe just rework this junk altogether).
// rework this junk altogether).
short volumeAdjustmentIndex(ChannelType type) const; short volumeAdjustmentIndex(ChannelType type) const;
short volumeAdjustmentIndex() const; short volumeAdjustmentIndex() const;
@@ -248,9 +220,8 @@ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
String identification() const; String identification() const;
/*! /*!
* Sets the identification of the frame to \a s. The string * Sets the identification of the frame to \a s.
* is used to identify the situation and/or device where this * The string is used to identify the situation and/or device where this adjustment should apply.
* adjustment should apply.
*/ */
void setIdentification(const String &s); void setIdentification(const String &s);
@@ -270,4 +241,5 @@ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame {
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib
#endif #endif

View File

@@ -123,6 +123,7 @@ void SynchronizedLyricsFrame::setSynchedText(
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void SynchronizedLyricsFrame::parseFields(const ByteVector &data) { void SynchronizedLyricsFrame::parseFields(const ByteVector &data) {
const int end = data.size(); const int end = data.size();
if (end < 7) { if (end < 7) {
debug("A synchronized lyrics frame must contain at least 7 bytes."); debug("A synchronized lyrics frame must contain at least 7 bytes.");
@@ -177,9 +178,11 @@ void SynchronizedLyricsFrame::parseFields(const ByteVector &data) {
d->synchedText.append(SynchedText(time, text)); d->synchedText.append(SynchedText(time, text));
} }
} }
ByteVector SynchronizedLyricsFrame::renderFields() const { ByteVector SynchronizedLyricsFrame::renderFields() const {
ByteVector v; ByteVector v;
String::Type encoding = d->textEncoding; String::Type encoding = d->textEncoding;
@@ -207,6 +210,7 @@ ByteVector SynchronizedLyricsFrame::renderFields() const {
} }
return v; return v;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -31,7 +31,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
//! ID3v2 synchronized lyrics frame //! ID3v2 synchronized lyrics frame
@@ -95,8 +94,7 @@ class TAGLIB_EXPORT SynchronizedLyricsFrame : public Frame {
typedef Strawberry_TagLib::TagLib::List<SynchedText> SynchedTextList; typedef Strawberry_TagLib::TagLib::List<SynchedText> SynchedTextList;
/*! /*!
* Construct an empty synchronized lyrics frame that will use the text * Construct an empty synchronized lyrics frame that will use the text encoding \a encoding.
* encoding \a encoding.
*/ */
explicit SynchronizedLyricsFrame(String::Type encoding = String::Latin1); explicit SynchronizedLyricsFrame(String::Type encoding = String::Latin1);
@@ -119,8 +117,7 @@ class TAGLIB_EXPORT SynchronizedLyricsFrame : public Frame {
/*! /*!
* Returns the text encoding that will be used in rendering this frame. * Returns the text encoding that will be used in rendering this frame.
* This defaults to the type that was either specified in the constructor * This defaults to the type that was either specified in the constructor or read from the frame when parsed.
* or read from the frame when parsed.
* *
* \see setTextEncoding() * \see setTextEncoding()
* \see render() * \see render()
@@ -162,8 +159,7 @@ class TAGLIB_EXPORT SynchronizedLyricsFrame : public Frame {
SynchedTextList synchedText() const; SynchedTextList synchedText() const;
/*! /*!
* Sets the text encoding to be used when rendering this frame to * Sets the text encoding to be used when rendering this frame to \a encoding.
* \a encoding.
* *
* \see textEncoding() * \see textEncoding()
* \see render() * \see render()
@@ -172,8 +168,7 @@ class TAGLIB_EXPORT SynchronizedLyricsFrame : public Frame {
/*! /*!
* Set the language using the 3 byte language code from * Set the language using the 3 byte language code from
* <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to \a languageCode.
* \a languageCode.
* *
* \see language() * \see language()
*/ */
@@ -228,4 +223,5 @@ class TAGLIB_EXPORT SynchronizedLyricsFrame : public Frame {
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib
#endif #endif

View File

@@ -34,9 +34,7 @@ using namespace ID3v2;
class TableOfContentsFrame::TableOfContentsFramePrivate { class TableOfContentsFrame::TableOfContentsFramePrivate {
public: public:
TableOfContentsFramePrivate() : tagHeader(0), TableOfContentsFramePrivate() : tagHeader(nullptr), isTopLevel(false), isOrdered(false) {
isTopLevel(false),
isOrdered(false) {
embeddedFrameList.setAutoDelete(true); embeddedFrameList.setAutoDelete(true);
} }
@@ -69,6 +67,7 @@ ByteVectorList &strip(ByteVectorList &l) {
} }
return l; return l;
} }
} // namespace } // namespace
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -81,16 +80,15 @@ TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const
setData(data); setData(data);
} }
TableOfContentsFrame::TableOfContentsFrame(const ByteVector &elementID, TableOfContentsFrame::TableOfContentsFrame(const ByteVector &elementID, const ByteVectorList &children, const FrameList &embeddedFrames) : ID3v2::Frame("CTOC"), d(new TableOfContentsFramePrivate()) {
const ByteVectorList &children,
const FrameList &embeddedFrames) : ID3v2::Frame("CTOC"),
d(new TableOfContentsFramePrivate()) {
d->elementID = elementID; d->elementID = elementID;
strip(d->elementID); strip(d->elementID);
d->childElements = children; d->childElements = children;
for (FrameList::ConstIterator it = embeddedFrames.begin(); it != embeddedFrames.end(); ++it) for (FrameList::ConstIterator it = embeddedFrames.begin(); it != embeddedFrames.end(); ++it)
addEmbeddedFrame(*it); addEmbeddedFrame(*it);
} }
TableOfContentsFrame::~TableOfContentsFrame() { TableOfContentsFrame::~TableOfContentsFrame() {
@@ -167,6 +165,7 @@ void TableOfContentsFrame::addEmbeddedFrame(Frame *frame) {
} }
void TableOfContentsFrame::removeEmbeddedFrame(Frame *frame, bool del) { void TableOfContentsFrame::removeEmbeddedFrame(Frame *frame, bool del) {
// remove the frame from the frame list // remove the frame from the frame list
FrameList::Iterator it = d->embeddedFrameList.find(frame); FrameList::Iterator it = d->embeddedFrameList.find(frame);
d->embeddedFrameList.erase(it); d->embeddedFrameList.erase(it);
@@ -178,15 +177,19 @@ void TableOfContentsFrame::removeEmbeddedFrame(Frame *frame, bool del) {
// ...and delete as desired // ...and delete as desired
if (del) if (del)
delete frame; delete frame;
} }
void TableOfContentsFrame::removeEmbeddedFrames(const ByteVector &id) { void TableOfContentsFrame::removeEmbeddedFrames(const ByteVector &id) {
FrameList l = d->embeddedFrameListMap[id]; FrameList l = d->embeddedFrameListMap[id];
for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it)
removeEmbeddedFrame(*it, true); removeEmbeddedFrame(*it, true);
} }
String TableOfContentsFrame::toString() const { String TableOfContentsFrame::toString() const {
String s = String(d->elementID) + String s = String(d->elementID) +
": top level: " + (d->isTopLevel ? "true" : "false") + ": top level: " + (d->isTopLevel ? "true" : "false") +
", ordered: " + (d->isOrdered ? "true" : "false"); ", ordered: " + (d->isOrdered ? "true" : "false");
@@ -205,19 +208,21 @@ String TableOfContentsFrame::toString() const {
} }
return s; return s;
} }
PropertyMap TableOfContentsFrame::asProperties() const { PropertyMap TableOfContentsFrame::asProperties() const {
PropertyMap map; PropertyMap map;
map.unsupportedData().append(frameID() + String("/") + d->elementID); map.unsupportedData().append(frameID() + String("/") + d->elementID);
return map; return map;
} }
TableOfContentsFrame *TableOfContentsFrame::findByElementID(const ID3v2::Tag *tag, TableOfContentsFrame *TableOfContentsFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) { // static
const ByteVector &eID) // static
{
ID3v2::FrameList tablesOfContents = tag->frameList("CTOC"); ID3v2::FrameList tablesOfContents = tag->frameList("CTOC");
for (ID3v2::FrameList::ConstIterator it = tablesOfContents.begin(); for (ID3v2::FrameList::ConstIterator it = tablesOfContents.begin();
@@ -229,10 +234,11 @@ TableOfContentsFrame *TableOfContentsFrame::findByElementID(const ID3v2::Tag *ta
} }
return nullptr; return nullptr;
} }
TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const ID3v2::Tag *tag) // static TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const ID3v2::Tag *tag) { // static
{
ID3v2::FrameList tablesOfContents = tag->frameList("CTOC"); ID3v2::FrameList tablesOfContents = tag->frameList("CTOC");
for (ID3v2::FrameList::ConstIterator it = tablesOfContents.begin(); for (ID3v2::FrameList::ConstIterator it = tablesOfContents.begin();
@@ -244,9 +250,11 @@ TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const ID3v2::Tag *tag)
} }
return nullptr; return nullptr;
} }
void TableOfContentsFrame::parseFields(const ByteVector &data) { void TableOfContentsFrame::parseFields(const ByteVector &data) {
unsigned int size = data.size(); unsigned int size = data.size();
if (size < 6) { if (size < 6) {
debug("A CTOC frame must contain at least 6 bytes (1 byte element ID terminated by " debug("A CTOC frame must contain at least 6 bytes (1 byte element ID terminated by "
@@ -286,9 +294,11 @@ void TableOfContentsFrame::parseFields(const ByteVector &data) {
embPos += frame->size() + header()->size(); embPos += frame->size() + header()->size();
addEmbeddedFrame(frame); addEmbeddedFrame(frame);
} }
} }
ByteVector TableOfContentsFrame::renderFields() const { ByteVector TableOfContentsFrame::renderFields() const {
ByteVector data; ByteVector data;
data.append(d->elementID); data.append(d->elementID);
@@ -311,11 +321,10 @@ ByteVector TableOfContentsFrame::renderFields() const {
data.append((*it2)->render()); data.append((*it2)->render());
return data; return data;
} }
TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : Frame(h), d(new TableOfContentsFramePrivate()) {
const ByteVector &data, Header *h) : Frame(h),
d(new TableOfContentsFramePrivate()) {
d->tagHeader = tagHeader; d->tagHeader = tagHeader;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@@ -33,12 +33,11 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
/*! /*!
* This is an implementation of ID3v2 table of contents frames. Purpose * This is an implementation of ID3v2 table of contents frames.
* of this frame is to allow a table of contents to be defined. * Purpose of this frame is to allow a table of contents to be defined.
*/ */
//! An implementation of ID3v2 table of contents frames //! An implementation of ID3v2 table of contents frames
@@ -48,15 +47,14 @@ class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame {
public: public:
/*! /*!
* Creates a table of contents frame based on \a data. \a tagHeader is * Creates a table of contents frame based on \a data.
* required as the internal frames are parsed based on the tag version. * \a tagHeader is required as the internal frames are parsed based on the tag version.
*/ */
TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data); TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data);
/*! /*!
* Creates a table of contents frame with the element ID \a elementID, * Creates a table of contents frame with the element ID \a elementID,
* the child elements \a children and embedded frames, which become owned * the child elements \a children and embedded frames, which become owned by this frame, in \a embeddedFrames.
* by this frame, in \a embeddedFrames.
*/ */
TableOfContentsFrame(const ByteVector &elementID, TableOfContentsFrame(const ByteVector &elementID,
const ByteVectorList &children = ByteVectorList(), const ByteVectorList &children = ByteVectorList(),
@@ -68,32 +66,29 @@ class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame {
~TableOfContentsFrame(); ~TableOfContentsFrame();
/*! /*!
* Returns the elementID of the frame. Element ID * Returns the elementID of the frame.
* is a null terminated string, however it's not human-readable. * Element ID is a null terminated string, however it's not human-readable.
* *
* \see setElementID() * \see setElementID()
*/ */
ByteVector elementID() const; ByteVector elementID() const;
/*! /*!
* Returns true, if the frame is top-level (doesn't have * Returns true, if the frame is top-level (doesn't have any parent CTOC frame).
* any parent CTOC frame).
* *
* \see setIsTopLevel() * \see setIsTopLevel()
*/ */
bool isTopLevel() const; bool isTopLevel() const;
/*! /*!
* Returns true, if the child elements list entries * Returns true, if the child elements list entries are ordered.
* are ordered.
* *
* \see setIsOrdered() * \see setIsOrdered()
*/ */
bool isOrdered() const; bool isOrdered() const;
/*! /*!
* Returns count of child elements of the frame. It always * Returns count of child elements of the frame. It always corresponds to size of child elements list.
* corresponds to size of child elements list.
* *
* \see childElements() * \see childElements()
*/ */
@@ -107,24 +102,21 @@ class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame {
ByteVectorList childElements() const; ByteVectorList childElements() const;
/*! /*!
* Sets the elementID of the frame to \a eID. If \a eID isn't * Sets the elementID of the frame to \a eID. If \a eID isn't null terminated, a null char is appended automatically.
* null terminated, a null char is appended automatically.
* *
* \see elementID() * \see elementID()
*/ */
void setElementID(const ByteVector &eID); void setElementID(const ByteVector &eID);
/*! /*!
* Sets, if the frame is top-level (doesn't have * Sets, if the frame is top-level (doesn't have any parent CTOC frame).
* any parent CTOC frame).
* *
* \see isTopLevel() * \see isTopLevel()
*/ */
void setIsTopLevel(const bool &t); void setIsTopLevel(const bool &t);
/*! /*!
* Sets, if the child elements list entries * Sets, if the child elements list entries are ordered.
* are ordered.
* *
* \see isOrdered() * \see isOrdered()
*/ */
@@ -152,38 +144,32 @@ class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame {
void removeChildElement(const ByteVector &cE); void removeChildElement(const ByteVector &cE);
/*! /*!
* Returns a reference to the frame list map. This is an FrameListMap of * Returns a reference to the frame list map.
* all of the frames embedded in the CTOC frame. * This is an FrameListMap of all of the frames embedded in the CTOC frame.
* *
* This is the most convenient structure for accessing the CTOC frame's * This is the most convenient structure for accessing the CTOC frame's embedded frames.
* embedded frames. Many frame types allow multiple instances of the same * Many frame types allow multiple instances of the same frame type so this is a map of lists.
* frame type so this is a map of lists. In most cases however there will * In most cases however there will only be a single frame of a certain type.
* only be a single frame of a certain type.
* *
* \warning You should not modify this data structure directly, instead * \warning You should not modify this data structure directly, instead use addEmbeddedFrame() and removeEmbeddedFrame().
* use addEmbeddedFrame() and removeEmbeddedFrame().
* *
* \see embeddedFrameList() * \see embeddedFrameList()
*/ */
const FrameListMap &embeddedFrameListMap() const; const FrameListMap &embeddedFrameListMap() const;
/*! /*!
* Returns a reference to the embedded frame list. This is an FrameList * Returns a reference to the embedded frame list.
* of all of the frames embedded in the CTOC frame in the order that they * This is an FrameList of all of the frames embedded in the CTOC frame in the order that they were parsed.
* were parsed.
* *
* This can be useful if for example you want iterate over the CTOC frame's * This can be useful if for example you want iterate over the CTOC frame's embedded frames in the order that they occur in the CTOC frame.
* embedded frames in the order that they occur in the CTOC frame.
* *
* \warning You should not modify this data structure directly, instead * \warning You should not modify this data structure directly, instead use addEmbeddedFrame() and removeEmbeddedFrame().
* use addEmbeddedFrame() and removeEmbeddedFrame().
*/ */
const FrameList &embeddedFrameList() const; const FrameList &embeddedFrameList() const;
/*! /*!
* Returns the embedded frame list for frames with the id \a frameID * Returns the embedded frame list for frames with the id \a frameID or an empty list if there are no embedded frames of that type.
* or an empty list if there are no embedded frames of that type. This * This is just a convenience and is equivalent to:
* is just a convenience and is equivalent to:
* *
* \code * \code
* embeddedFrameListMap()[frameID]; * embeddedFrameListMap()[frameID];
@@ -194,29 +180,25 @@ class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame {
const FrameList &embeddedFrameList(const ByteVector &frameID) const; const FrameList &embeddedFrameList(const ByteVector &frameID) const;
/*! /*!
* Add an embedded frame to the CTOC frame. At this point the CTOC frame * Add an embedded frame to the CTOC frame.
* takes ownership of the embedded frame and will handle freeing its memory. * At this point the CTOC frame takes ownership of the embedded frame and will handle freeing its memory.
* *
* \note Using this method will invalidate any pointers on the list * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList()
* returned by embeddedFrameList()
*/ */
void addEmbeddedFrame(Frame *frame); void addEmbeddedFrame(Frame *frame);
/*! /*!
* Remove an embedded frame from the CTOC frame. If \a del is true the frame's * Remove an embedded frame from the CTOC frame.
* memory will be freed; if it is false, it must be deleted by the user. * If \a del is true the frame's memory will be freed; if it is false, it must be deleted by the user.
* *
* \note Using this method will invalidate any pointers on the list * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList()
* returned by embeddedFrameList()
*/ */
void removeEmbeddedFrame(Frame *frame, bool del = true); void removeEmbeddedFrame(Frame *frame, bool del = true);
/*! /*!
* Remove all embedded frames of type \a id from the CTOC frame and free their * Remove all embedded frames of type \a id from the CTOC frame and free their memory.
* memory.
* *
* \note Using this method will invalidate any pointers on the list * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList()
* returned by embeddedFrameList()
*/ */
void removeEmbeddedFrames(const ByteVector &id); void removeEmbeddedFrames(const ByteVector &id);
@@ -225,18 +207,18 @@ class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame {
PropertyMap asProperties() const; PropertyMap asProperties() const;
/*! /*!
* CTOC frames each have a unique element ID. This searches for a CTOC * CTOC frames each have a unique element ID.
* frame with the element ID \a eID and returns a pointer to it. This * This searches for a CTOC frame with the element ID \a eID and returns a pointer to it.
* can be used to link together parent and child CTOC frames. * This can be used to link together parent and child CTOC frames.
* *
* \see elementID() * \see elementID()
*/ */
static TableOfContentsFrame *findByElementID(const Tag *tag, const ByteVector &eID); static TableOfContentsFrame *findByElementID(const Tag *tag, const ByteVector &eID);
/*! /*!
* CTOC frames each contain a flag that indicates, if CTOC frame is top-level (there isn't * CTOC frames each contain a flag that indicates,
* any frame, which contains this frame in its child elements list). Only a single frame * if CTOC frame is top-level (there isn't any frame, which contains this frame in its child elements list).
* within tag can be top-level. This searches for a top-level CTOC frame. * Only a single frame within tag can be top-level. This searches for a top-level CTOC frame.
* *
* \see isTopLevel() * \see isTopLevel()
*/ */
@@ -254,6 +236,7 @@ class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame {
class TableOfContentsFramePrivate; class TableOfContentsFramePrivate;
TableOfContentsFramePrivate *d; TableOfContentsFramePrivate *d;
}; };
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib

View File

@@ -53,8 +53,8 @@ TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) : Frame
setData(data); setData(data);
} }
TextIdentificationFrame *TextIdentificationFrame::createTIPLFrame(const PropertyMap &properties) // static TextIdentificationFrame *TextIdentificationFrame::createTIPLFrame(const PropertyMap &properties) { // static
{
TextIdentificationFrame *frame = new TextIdentificationFrame("TIPL"); TextIdentificationFrame *frame = new TextIdentificationFrame("TIPL");
StringList l; StringList l;
for (PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it) { for (PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it) {
@@ -63,10 +63,11 @@ TextIdentificationFrame *TextIdentificationFrame::createTIPLFrame(const Property
} }
frame->setText(l); frame->setText(l);
return frame; return frame;
} }
TextIdentificationFrame *TextIdentificationFrame::createTMCLFrame(const PropertyMap &properties) // static TextIdentificationFrame *TextIdentificationFrame::createTMCLFrame(const PropertyMap &properties) { // static
{
TextIdentificationFrame *frame = new TextIdentificationFrame("TMCL"); TextIdentificationFrame *frame = new TextIdentificationFrame("TMCL");
StringList l; StringList l;
for (PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it) { for (PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it) {
@@ -77,6 +78,7 @@ TextIdentificationFrame *TextIdentificationFrame::createTMCLFrame(const Property
} }
frame->setText(l); frame->setText(l);
return frame; return frame;
} }
TextIdentificationFrame::~TextIdentificationFrame() { TextIdentificationFrame::~TextIdentificationFrame() {
@@ -119,17 +121,19 @@ const char *involvedPeople[][2] = {
const size_t involvedPeopleSize = sizeof(involvedPeople) / sizeof(involvedPeople[0]); const size_t involvedPeopleSize = sizeof(involvedPeople) / sizeof(involvedPeople[0]);
} // namespace } // namespace
const KeyConversionMap &TextIdentificationFrame::involvedPeopleMap() // static const KeyConversionMap &TextIdentificationFrame::involvedPeopleMap() { // static
{
static KeyConversionMap m; static KeyConversionMap m;
if (m.isEmpty()) { if (m.isEmpty()) {
for (size_t i = 0; i < involvedPeopleSize; ++i) for (size_t i = 0; i < involvedPeopleSize; ++i)
m.insert(involvedPeople[i][1], involvedPeople[i][0]); m.insert(involvedPeople[i][1], involvedPeople[i][0]);
} }
return m; return m;
} }
PropertyMap TextIdentificationFrame::asProperties() const { PropertyMap TextIdentificationFrame::asProperties() const {
if (frameID() == "TIPL") if (frameID() == "TIPL")
return makeTIPLProperties(); return makeTIPLProperties();
if (frameID() == "TMCL") if (frameID() == "TMCL")
@@ -163,6 +167,7 @@ PropertyMap TextIdentificationFrame::asProperties() const {
PropertyMap ret; PropertyMap ret;
ret.insert(tagName, values); ret.insert(tagName, values);
return ret; return ret;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -170,6 +175,7 @@ PropertyMap TextIdentificationFrame::asProperties() const {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TextIdentificationFrame::parseFields(const ByteVector &data) { void TextIdentificationFrame::parseFields(const ByteVector &data) {
// Don't try to parse invalid frames // Don't try to parse invalid frames
if (data.size() < 2) if (data.size() < 2)
@@ -209,9 +215,11 @@ void TextIdentificationFrame::parseFields(const ByteVector &data) {
d->fieldList.append(String(*it, d->textEncoding)); d->fieldList.append(String(*it, d->textEncoding));
} }
} }
} }
ByteVector TextIdentificationFrame::renderFields() const { ByteVector TextIdentificationFrame::renderFields() const {
String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding); String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding);
ByteVector v; ByteVector v;
@@ -231,6 +239,7 @@ ByteVector TextIdentificationFrame::renderFields() const {
} }
return v; return v;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -243,6 +252,7 @@ TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header
} }
PropertyMap TextIdentificationFrame::makeTIPLProperties() const { PropertyMap TextIdentificationFrame::makeTIPLProperties() const {
PropertyMap map; PropertyMap map;
if (fieldList().size() % 2 != 0) { if (fieldList().size() % 2 != 0) {
// according to the ID3 spec, TIPL must contain an even number of entries // according to the ID3 spec, TIPL must contain an even number of entries
@@ -266,9 +276,11 @@ PropertyMap TextIdentificationFrame::makeTIPLProperties() const {
} }
} }
return map; return map;
} }
PropertyMap TextIdentificationFrame::makeTMCLProperties() const { PropertyMap TextIdentificationFrame::makeTMCLProperties() const {
PropertyMap map; PropertyMap map;
if (fieldList().size() % 2 != 0) { if (fieldList().size() % 2 != 0) {
// according to the ID3 spec, TMCL must contain an even number of entries // according to the ID3 spec, TMCL must contain an even number of entries
@@ -287,14 +299,14 @@ PropertyMap TextIdentificationFrame::makeTMCLProperties() const {
map.insert(L"PERFORMER:" + instrument, (++it)->split(",")); map.insert(L"PERFORMER:" + instrument, (++it)->split(","));
} }
return map; return map;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// UserTextIdentificationFrame public members // UserTextIdentificationFrame public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
UserTextIdentificationFrame::UserTextIdentificationFrame(String::Type encoding) : TextIdentificationFrame("TXXX", encoding), UserTextIdentificationFrame::UserTextIdentificationFrame(String::Type encoding) : TextIdentificationFrame("TXXX", encoding), d(nullptr) {
d(0) {
StringList l; StringList l;
l.append(String()); l.append(String());
l.append(String()); l.append(String());
@@ -306,13 +318,13 @@ UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data)
checkFields(); checkFields();
} }
UserTextIdentificationFrame::UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding) : TextIdentificationFrame("TXXX", encoding), UserTextIdentificationFrame::UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding) : TextIdentificationFrame("TXXX", encoding), d(nullptr) {
d(0) {
setDescription(description); setDescription(description);
setText(values); setText(values);
} }
String UserTextIdentificationFrame::toString() const { String UserTextIdentificationFrame::toString() const {
// first entry is the description itself, drop from values list // first entry is the description itself, drop from values list
StringList l = fieldList(); StringList l = fieldList();
for (StringList::Iterator it = l.begin(); it != l.end(); ++it) { for (StringList::Iterator it = l.begin(); it != l.end(); ++it) {
@@ -320,6 +332,7 @@ String UserTextIdentificationFrame::toString() const {
break; break;
} }
return "[" + description() + "] " + l.toString(); return "[" + description() + "] " + l.toString();
} }
String UserTextIdentificationFrame::description() const { String UserTextIdentificationFrame::description() const {
@@ -347,6 +360,7 @@ void UserTextIdentificationFrame::setText(const StringList &fields) {
} }
void UserTextIdentificationFrame::setDescription(const String &s) { void UserTextIdentificationFrame::setDescription(const String &s) {
StringList l = fieldList(); StringList l = fieldList();
if (l.isEmpty()) if (l.isEmpty())
@@ -355,21 +369,22 @@ void UserTextIdentificationFrame::setDescription(const String &s) {
l[0] = s; l[0] = s;
TextIdentificationFrame::setText(l); TextIdentificationFrame::setText(l);
} }
PropertyMap UserTextIdentificationFrame::asProperties() const { PropertyMap UserTextIdentificationFrame::asProperties() const {
PropertyMap map; PropertyMap map;
String tagName = txxxToKey(description()); String tagName = txxxToKey(description());
StringList v = fieldList(); StringList v = fieldList();
for (StringList::ConstIterator it = v.begin(); it != v.end(); ++it) for (StringList::ConstIterator it = v.begin(); it != v.end(); ++it)
if (it != v.begin()) if (it != v.begin()) map.insert(tagName, *it);
map.insert(tagName, *it);
return map; return map;
} }
UserTextIdentificationFrame *UserTextIdentificationFrame::find( UserTextIdentificationFrame *UserTextIdentificationFrame::find(ID3v2::Tag *tag, const String &description) { // static
ID3v2::Tag *tag, const String &description) // static
{
FrameList l = tag->frameList("TXXX"); FrameList l = tag->frameList("TXXX");
for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) { for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) {
UserTextIdentificationFrame *f = dynamic_cast<UserTextIdentificationFrame *>(*it); UserTextIdentificationFrame *f = dynamic_cast<UserTextIdentificationFrame *>(*it);
@@ -377,6 +392,7 @@ UserTextIdentificationFrame *UserTextIdentificationFrame::find(
return f; return f;
} }
return nullptr; return nullptr;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -388,10 +404,12 @@ UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data,
} }
void UserTextIdentificationFrame::checkFields() { void UserTextIdentificationFrame::checkFields() {
int fields = fieldList().size(); int fields = fieldList().size();
if (fields == 0) if (fields == 0)
setDescription(String()); setDescription(String());
if (fields <= 1) if (fields <= 1)
setText(String()); setText(String());
} }

View File

@@ -34,7 +34,6 @@
namespace Strawberry_TagLib { namespace Strawberry_TagLib {
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
class Tag; class Tag;
@@ -43,9 +42,8 @@ typedef Map<String, String> KeyConversionMap;
//! An ID3v2 text identification frame implementation //! An ID3v2 text identification frame implementation
/*! /*!
* This is an implementation of the most common type of ID3v2 frame -- text * This is an implementation of the most common type of ID3v2 frame -- text identification frames.
* identification frames. There are a number of variations on this. Those * There are a number of variations on this. Those enumerated in the ID3v2.4 standard are:
* enumerated in the ID3v2.4 standard are:
* *
* <ul> * <ul>
* <li><b>TALB</b> Album/Movie/Show title</li> * <li><b>TALB</b> Album/Movie/Show title</li>
@@ -96,11 +94,11 @@ typedef Map<String, String> KeyConversionMap;
* </ul> * </ul>
* *
* The ID3v2 Frames document gives a description of each of these formats * The ID3v2 Frames document gives a description of each of these formats
* and the expected order of strings in each. ID3v2::Header::frameID() can * and the expected order of strings in each.
* be used to determine the frame type. * ID3v2::Header::frameID() can be used to determine the frame type.
* *
* \note If non-Latin1 compatible strings are used with this class, even if * \note If non-Latin1 compatible strings are used with this class,
* the text encoding is set to Latin1, the frame will be written using UTF8 * even if the text encoding is set to Latin1, the frame will be written using UTF8
* (with the encoding flag appropriately set in the output). * (with the encoding flag appropriately set in the output).
*/ */
@@ -109,34 +107,30 @@ class TAGLIB_EXPORT TextIdentificationFrame : public Frame {
public: public:
/*! /*!
* Construct an empty frame of type \a type. Uses \a encoding as the * Construct an empty frame of type \a type.
* default text encoding. * Uses \a encoding as the default text encoding.
* *
* \note In this case you must specify the text encoding as it * \note In this case you must specify the text encoding as it resolves the ambiguity between constructors.
* resolves the ambiguity between constructors.
* *
* \note Please see the note in the class description regarding Latin1. * \note Please see the note in the class description regarding Latin1.
*/ */
TextIdentificationFrame(const ByteVector &type, String::Type encoding); TextIdentificationFrame(const ByteVector &type, String::Type encoding);
/*! /*!
* This is a dual purpose constructor. \a data can either be binary data * This is a dual purpose constructor.
* that should be parsed or (at a minimum) the frame ID. * \a data can either be binary data that should be parsed or (at a minimum) the frame ID.
*/ */
explicit TextIdentificationFrame(const ByteVector &data); explicit TextIdentificationFrame(const ByteVector &data);
/*! /*!
* This is a special factory method to create a TIPL (involved people list) * This is a special factory method to create a TIPL (involved people list) frame from the given \a properties.
* frame from the given \a properties. Will parse key=[list of values] data * Will parse key=[list of values] data into the TIPL format as specified in the ID3 standard.
* into the TIPL format as specified in the ID3 standard.
*/ */
static TextIdentificationFrame *createTIPLFrame(const PropertyMap &properties); static TextIdentificationFrame *createTIPLFrame(const PropertyMap &properties);
/*! /*!
* This is a special factory method to create a TMCL (musician credits list) * This is a special factory method to create a TMCL (musician credits list) frame from the given \a properties.
* frame from the given \a properties. Will parse key=[list of values] data * Will parse key=[list of values] data into the TMCL format as specified in the ID3 standard, where key should be of the form instrumentPrefix:instrument.
* into the TMCL format as specified in the ID3 standard, where key should be
* of the form instrumentPrefix:instrument.
*/ */
static TextIdentificationFrame *createTMCLFrame(const PropertyMap &properties); static TextIdentificationFrame *createTMCLFrame(const PropertyMap &properties);
/*! /*!
@@ -150,9 +144,8 @@ class TAGLIB_EXPORT TextIdentificationFrame : public Frame {
* This function will accept either a StringList or a String (using the * This function will accept either a StringList or a String (using the
* StringList constructor that accepts a single String). * StringList constructor that accepts a single String).
* *
* \note This will not change the text encoding of the frame even if the * \note This will not change the text encoding of the frame even if the strings passed in are not of the same encoding.
* strings passed in are not of the same encoding. Please use * Please use setEncoding(s.type()) if you wish to change the encoding of the frame.
* setEncoding(s.type()) if you wish to change the encoding of the frame.
*/ */
void setText(const StringList &l); void setText(const StringList &l);
@@ -163,8 +156,7 @@ class TAGLIB_EXPORT TextIdentificationFrame : public Frame {
/*! /*!
* Returns the text encoding that will be used in rendering this frame. * Returns the text encoding that will be used in rendering this frame.
* This defaults to the type that was either specified in the constructor * This defaults to the type that was either specified in the constructor or read from the frame when parsed.
* or read from the frame when parsed.
* *
* \note Please see the note in the class description regarding Latin1. * \note Please see the note in the class description regarding Latin1.
* *
@@ -174,8 +166,7 @@ class TAGLIB_EXPORT TextIdentificationFrame : public Frame {
String::Type textEncoding() const; String::Type textEncoding() const;
/*! /*!
* Sets the text encoding to be used when rendering this frame to * Sets the text encoding to be used when rendering this frame to \a encoding.
* \a encoding.
* *
* \note Please see the note in the class description regarding Latin1. * \note Please see the note in the class description regarding Latin1.
* *
@@ -190,8 +181,7 @@ class TAGLIB_EXPORT TextIdentificationFrame : public Frame {
StringList fieldList() const; StringList fieldList() const;
/*! /*!
* Returns a KeyConversionMap mapping a role as it would be used in a PropertyMap * Returns a KeyConversionMap mapping a role as it would be used in a PropertyMap to the corresponding key used in a TIPL ID3 frame to describe that role.
* to the corresponding key used in a TIPL ID3 frame to describe that role.
*/ */
static const KeyConversionMap &involvedPeopleMap(); static const KeyConversionMap &involvedPeopleMap();
@@ -227,9 +217,8 @@ class TAGLIB_EXPORT TextIdentificationFrame : public Frame {
}; };
/*! /*!
* This is a specialization of text identification frames that allows for * This is a specialization of text identification frames that allows for user defined entries.
* user defined entries. Each entry has a description in addition to the * Each entry has a description in addition to the normal list of fields that a text identification frame has.
* normal list of fields that a text identification frame has.
* *
* This description identifies the frame and must be unique. * This description identifies the frame and must be unique.
*/ */
@@ -241,8 +230,8 @@ class TAGLIB_EXPORT UserTextIdentificationFrame : public TextIdentificationFrame
public: public:
/*! /*!
* Constructs an empty user defined text identification frame. For this to be * Constructs an empty user defined text identification frame.
* a useful frame both a description and text must be set. * For this to be a useful frame both a description and text must be set.
*/ */
explicit UserTextIdentificationFrame(String::Type encoding = String::Latin1); explicit UserTextIdentificationFrame(String::Type encoding = String::Latin1);
@@ -252,8 +241,7 @@ class TAGLIB_EXPORT UserTextIdentificationFrame : public TextIdentificationFrame
explicit UserTextIdentificationFrame(const ByteVector &data); explicit UserTextIdentificationFrame(const ByteVector &data);
/*! /*!
* Creates a user defined text identification frame with the given \a description * Creates a user defined text identification frame with the given \a description and \a values.
* and \a values.
*/ */
UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding = String::UTF8); UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding = String::UTF8);
@@ -265,9 +253,8 @@ class TAGLIB_EXPORT UserTextIdentificationFrame : public TextIdentificationFrame
String description() const; String description() const;
/*! /*!
* Sets the description of the frame to \a s. \a s must be unique. You can * Sets the description of the frame to \a s. \a s must be unique.
* check for the presence of another user defined text frame of the same type * You can check for the presence of another user defined text frame of the same type using find() and testing for null.
* using find() and testing for null.
*/ */
void setDescription(const String &s); void setDescription(const String &s);
@@ -278,21 +265,16 @@ class TAGLIB_EXPORT UserTextIdentificationFrame : public TextIdentificationFrame
/*! /*!
* A UserTextIdentificationFrame is parsed into a PropertyMap as follows: * A UserTextIdentificationFrame is parsed into a PropertyMap as follows:
* - the key is the frame's description, uppercased * - the key is the frame's description, uppercased
* - if the description contains '::', only the substring after that * - if the description contains '::', only the substring after that separator is considered as key (compatibility with exfalso)
* separator is considered as key (compatibility with exfalso) * - if the above rules don't yield a valid key (e.g. containing non-ASCII characters), the returned map will contain an entry "TXXX/<description>" in its unsupportedData() list.
* - if the above rules don't yield a valid key (e.g. containing non-ASCII
* characters), the returned map will contain an entry "TXXX/<description>"
* in its unsupportedData() list.
* - The values will be copies of the fieldList(). * - The values will be copies of the fieldList().
* - If the description() appears as value in fieldList(), it will be omitted * - If the description() appears as value in fieldList(), it will be omitted in the value list, in order to be compatible with TagLib which copies the description() into the fieldList().
* in the value list, in order to be compatible with TagLib which copies
* the description() into the fieldList().
*/ */
PropertyMap asProperties() const; PropertyMap asProperties() const;
/*! /*!
* Searches for the user defined text frame with the description \a description * Searches for the user defined text frame with the description \a description in \a tag.
* in \a tag. This returns null if no matching frames were found. * This returns null if no matching frames were found.
*/ */
static UserTextIdentificationFrame *find(Tag *tag, const String &description); static UserTextIdentificationFrame *find(Tag *tag, const String &description);
@@ -310,4 +292,5 @@ class TAGLIB_EXPORT UserTextIdentificationFrame : public TextIdentificationFrame
} // namespace ID3v2 } // namespace ID3v2
} // namespace TagLib } // namespace TagLib
} // namespace Strawberry_TagLib } // namespace Strawberry_TagLib
#endif #endif

Some files were not shown because too many files have changed in this diff Show More