mirror of
https://github.com/taglib/taglib.git
synced 2025-06-04 01:28:21 -04:00
Merge remote-tracking branch 'TsudaKageyu/infotag-patch'
This commit is contained in:
commit
57e5cc8c17
@ -103,6 +103,7 @@ set(tag_HDRS
|
||||
riff/aiff/aiffproperties.h
|
||||
riff/wav/wavfile.h
|
||||
riff/wav/wavproperties.h
|
||||
riff/wav/infotag.h
|
||||
asf/asffile.h
|
||||
asf/asfproperties.h
|
||||
asf/asftag.h
|
||||
@ -244,6 +245,7 @@ set(aiff_SRCS
|
||||
set(wav_SRCS
|
||||
riff/wav/wavfile.cpp
|
||||
riff/wav/wavproperties.cpp
|
||||
riff/wav/infotag.cpp
|
||||
)
|
||||
|
||||
set(mod_SRCS
|
||||
|
@ -138,34 +138,44 @@ ByteVector RIFF::File::chunkData(uint i)
|
||||
return readBlock(d->chunks[i].size);
|
||||
}
|
||||
|
||||
void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
|
||||
void RIFF::File::setChunkData(uint i, const ByteVector &data)
|
||||
{
|
||||
// First we update the global size
|
||||
|
||||
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
|
||||
insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
|
||||
|
||||
// Now update the specific chunk
|
||||
|
||||
writeChunk(chunkName(i), data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8);
|
||||
|
||||
d->chunks[i].size = data.size();
|
||||
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Now update the internal offsets
|
||||
|
||||
for(i++; i < d->chunks.size(); i++)
|
||||
d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding;
|
||||
}
|
||||
|
||||
void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate)
|
||||
{
|
||||
if(d->chunks.size() == 0) {
|
||||
debug("RIFF::File::setChunkData - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
|
||||
for(uint i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == name) {
|
||||
if(alwaysCreate && name != "LIST") {
|
||||
debug("RIFF::File::setChunkData - alwaysCreate should be used for only \"LIST\" chunks.");
|
||||
return;
|
||||
}
|
||||
|
||||
// First we update the global size
|
||||
|
||||
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
|
||||
insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
|
||||
|
||||
// Now update the specific chunk
|
||||
|
||||
writeChunk(name, data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8);
|
||||
|
||||
d->chunks[i].size = data.size();
|
||||
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Now update the internal offsets
|
||||
|
||||
for(i++; i < d->chunks.size(); i++)
|
||||
d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding;
|
||||
|
||||
return;
|
||||
if(!alwaysCreate) {
|
||||
for(uint i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == name) {
|
||||
setChunkData(i, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +191,8 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
|
||||
|
||||
// Now add the chunk to the file
|
||||
|
||||
writeChunk(name, data, offset, std::max(ulong(0), length() - offset), (offset & 1) ? 1 : 0);
|
||||
long len = length();
|
||||
writeChunk(name, data, offset, std::max<long>(0, length() - offset), (offset & 1) ? 1 : 0);
|
||||
|
||||
// And update our internal structure
|
||||
|
||||
@ -199,6 +210,28 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
|
||||
d->chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
void RIFF::File::removeChunk(uint i)
|
||||
{
|
||||
if(i >= d->chunks.size())
|
||||
return;
|
||||
|
||||
removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8);
|
||||
d->chunks.erase(d->chunks.begin() + i);
|
||||
}
|
||||
|
||||
void RIFF::File::removeChunk(const ByteVector &name)
|
||||
{
|
||||
std::vector<Chunk> newChunks;
|
||||
for(size_t i = 0; i < d->chunks.size(); ++i) {
|
||||
if(d->chunks[i].name == name)
|
||||
removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8);
|
||||
else
|
||||
newChunks.push_back(d->chunks[i]);
|
||||
}
|
||||
|
||||
d->chunks.swap(newChunks);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -95,14 +95,39 @@ namespace TagLib {
|
||||
*/
|
||||
ByteVector chunkData(uint i);
|
||||
|
||||
/*!
|
||||
* Sets the data for the the specified chunk to \a data.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setChunkData(uint i, const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Sets the data for the chunk \a name to \a data. If a chunk with the
|
||||
* given name already exists it will be overwritten, otherwise it will be
|
||||
* created after the existing chunks.
|
||||
*
|
||||
* \note If \a alwaysCreate is true, a new chunk is created regardless of
|
||||
* existence of chunk \a name. It should be used for only "LIST" chunks.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setChunkData(const ByteVector &name, const ByteVector &data);
|
||||
void setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate = false);
|
||||
|
||||
/*!
|
||||
* Removes the specified chunk.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void removeChunk(uint i);
|
||||
|
||||
/*!
|
||||
* Removes the chunk \a name.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
* \warning This removes all the chunks with the given name.
|
||||
*/
|
||||
void removeChunk(const ByteVector &name);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
|
258
taglib/riff/wav/infotag.cpp
Normal file
258
taglib/riff/wav/infotag.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
||||
email : wheeler@kde.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tfile.h>
|
||||
|
||||
#include "infotag.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace RIFF::Info;
|
||||
|
||||
namespace {
|
||||
static bool isValidChunkID(const ByteVector &name)
|
||||
{
|
||||
if(name.size() != 4)
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(name[i] < 32 || name[i] > 127)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class RIFF::Info::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate()
|
||||
{}
|
||||
|
||||
FieldListMap fieldListMap;
|
||||
|
||||
static const StringHandler *stringHandler;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// StringHandler implementation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
StringHandler::StringHandler()
|
||||
{
|
||||
}
|
||||
|
||||
StringHandler::~StringHandler()
|
||||
{
|
||||
}
|
||||
|
||||
String RIFF::Info::StringHandler::parse(const ByteVector &data) const
|
||||
{
|
||||
return String(data, String::UTF8);
|
||||
}
|
||||
|
||||
ByteVector RIFF::Info::StringHandler::render(const String &s) const
|
||||
{
|
||||
return s.data(String::UTF8);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const StringHandler defaultStringHandler;
|
||||
const RIFF::Info::StringHandler *RIFF::Info::Tag::TagPrivate::stringHandler = &defaultStringHandler;
|
||||
|
||||
RIFF::Info::Tag::Tag(const ByteVector &data) : TagLib::Tag()
|
||||
{
|
||||
d = new TagPrivate;
|
||||
parse(data);
|
||||
}
|
||||
|
||||
RIFF::Info::Tag::Tag() : TagLib::Tag()
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
RIFF::Info::Tag::~Tag()
|
||||
{
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::title() const
|
||||
{
|
||||
return fieldText("INAM");
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::artist() const
|
||||
{
|
||||
return fieldText("IART");
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::album() const
|
||||
{
|
||||
return fieldText("IPRD");
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::comment() const
|
||||
{
|
||||
return fieldText("ICMT");
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::genre() const
|
||||
{
|
||||
return fieldText("IGNR");
|
||||
}
|
||||
|
||||
TagLib::uint RIFF::Info::Tag::year() const
|
||||
{
|
||||
return fieldText("ICRD").substr(0, 4).toInt();
|
||||
}
|
||||
|
||||
TagLib::uint RIFF::Info::Tag::track() const
|
||||
{
|
||||
return fieldText("IPRT").toInt();
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setTitle(const String &s)
|
||||
{
|
||||
setFieldText("INAM", s);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setArtist(const String &s)
|
||||
{
|
||||
setFieldText("IART", s);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setAlbum(const String &s)
|
||||
{
|
||||
setFieldText("IPRD", s);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setComment(const String &s)
|
||||
{
|
||||
setFieldText("ICMT", s);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setGenre(const String &s)
|
||||
{
|
||||
setFieldText("IGNR", s);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setYear(uint i)
|
||||
{
|
||||
if(i != 0)
|
||||
setFieldText("ICRD", String::number(i));
|
||||
else
|
||||
d->fieldListMap.erase("ICRD");
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setTrack(uint i)
|
||||
{
|
||||
if(i != 0)
|
||||
setFieldText("IPRT", String::number(i));
|
||||
else
|
||||
d->fieldListMap.erase("IPRT");
|
||||
}
|
||||
|
||||
bool RIFF::Info::Tag::isEmpty() const
|
||||
{
|
||||
return d->fieldListMap.isEmpty();
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::fieldText(const ByteVector &id) const
|
||||
{
|
||||
if(d->fieldListMap.contains(id))
|
||||
return String(d->fieldListMap[id]);
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s)
|
||||
{
|
||||
// id must be four-byte long pure ascii string.
|
||||
if(!isValidChunkID(id))
|
||||
return;
|
||||
|
||||
if(!s.isEmpty())
|
||||
d->fieldListMap[id] = s;
|
||||
else
|
||||
removeField(id);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::removeField(const ByteVector &id)
|
||||
{
|
||||
if(d->fieldListMap.contains(id))
|
||||
d->fieldListMap.erase(id);
|
||||
}
|
||||
|
||||
ByteVector RIFF::Info::Tag::render() const
|
||||
{
|
||||
ByteVector data("INFO");
|
||||
|
||||
FieldListMap::ConstIterator it = d->fieldListMap.begin();
|
||||
for(; it != d->fieldListMap.end(); ++it) {
|
||||
ByteVector text = TagPrivate::stringHandler->render(it->second);
|
||||
if(text.isEmpty())
|
||||
continue;
|
||||
|
||||
data.append(it->first);
|
||||
data.append(ByteVector::fromUInt(text.size() + 1, false));
|
||||
data.append(text);
|
||||
|
||||
do {
|
||||
data.append('\0');
|
||||
} while(data.size() & 1);
|
||||
}
|
||||
|
||||
if(data.size() == 4)
|
||||
return ByteVector();
|
||||
else
|
||||
return data;
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setStringHandler(const StringHandler *handler)
|
||||
{
|
||||
if(handler)
|
||||
TagPrivate::stringHandler = handler;
|
||||
else
|
||||
TagPrivate::stringHandler = &defaultStringHandler;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RIFF::Info::Tag::parse(const ByteVector &data)
|
||||
{
|
||||
uint p = 4;
|
||||
while(p < data.size()) {
|
||||
uint size = data.mid(p + 4, 4).toUInt(false);
|
||||
d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size));
|
||||
|
||||
p += ((size + 1) & ~1) + 8;
|
||||
}
|
||||
}
|
||||
|
179
taglib/riff/wav/infotag.h
Normal file
179
taglib/riff/wav/infotag.h
Normal file
@ -0,0 +1,179 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
||||
email : wheeler@kde.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_INFOTAG_H
|
||||
#define TAGLIB_INFOTAG_H
|
||||
|
||||
#include "tag.h"
|
||||
#include "tmap.h"
|
||||
#include "tstring.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
class File;
|
||||
|
||||
//! A RIFF Info tag implementation.
|
||||
namespace RIFF {
|
||||
namespace Info {
|
||||
|
||||
typedef Map<ByteVector, String> FieldListMap;
|
||||
|
||||
//! A abstraction for the string to data encoding in Info tags.
|
||||
|
||||
/*!
|
||||
* RIFF Info tag has no clear definitions about character encodings.
|
||||
* In practice, local encoding of each system is largely used and UTF-8 is
|
||||
* popular too.
|
||||
*
|
||||
* Here is an option to read and write tags in your preferrd encoding
|
||||
* by subclassing this class, reimplementing parse() and render() and setting
|
||||
* your reimplementation as the default with Info::Tag::setStringHandler().
|
||||
*
|
||||
* \see ID3v1::Tag::setStringHandler()
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT StringHandler
|
||||
{
|
||||
public:
|
||||
StringHandler();
|
||||
~StringHandler();
|
||||
|
||||
/*!
|
||||
* Decode a string from \a data. The default implementation assumes that
|
||||
* \a data is an UTF-8 character array.
|
||||
*/
|
||||
virtual String parse(const ByteVector &data) const;
|
||||
|
||||
/*!
|
||||
* Encode a ByteVector with the data from \a s. The default implementation
|
||||
* assumes that \a s is an UTF-8 string.
|
||||
*/
|
||||
virtual ByteVector render(const String &s) const;
|
||||
};
|
||||
|
||||
//! The main class in the ID3v2 implementation
|
||||
|
||||
/*!
|
||||
* This is the main class in the INFO tag implementation. RIFF INFO tag is a
|
||||
* metadata format found in WAV audio and AVI video files. Though it is a part
|
||||
* of Microsoft/IBM's RIFF specification, the author could not find the official
|
||||
* documents about it. So, this implementation is refering to unofficial documents
|
||||
* online and some applications' behaviors especially Windows Explorer.
|
||||
*/
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Constructs an empty Info tag.
|
||||
*/
|
||||
Tag();
|
||||
|
||||
/*!
|
||||
* Constructs an Info tag read from \a data which is contents of "LIST" chunk.
|
||||
*/
|
||||
Tag(const ByteVector &data);
|
||||
|
||||
virtual ~Tag();
|
||||
|
||||
// Reimplementations
|
||||
|
||||
virtual String title() const;
|
||||
virtual String artist() const;
|
||||
virtual String album() const;
|
||||
virtual String comment() const;
|
||||
virtual String genre() const;
|
||||
virtual uint year() const;
|
||||
virtual uint track() const;
|
||||
|
||||
virtual void setTitle(const String &s);
|
||||
virtual void setArtist(const String &s);
|
||||
virtual void setAlbum(const String &s);
|
||||
virtual void setComment(const String &s);
|
||||
virtual void setGenre(const String &s);
|
||||
virtual void setYear(uint i);
|
||||
virtual void setTrack(uint i);
|
||||
|
||||
virtual bool isEmpty() const;
|
||||
/*
|
||||
* Gets the value of the field with the ID \a id.
|
||||
*/
|
||||
String fieldText(const ByteVector &id) const;
|
||||
|
||||
/*
|
||||
* Sets the value of the field with the ID \a id to \a s.
|
||||
* If the field does not exist, it is created.
|
||||
* If \s is empty, the field is removed.
|
||||
*
|
||||
* \note fieldId must be four-byte long pure ascii string. This function
|
||||
* performs nothing if fieldId is invalid.
|
||||
*/
|
||||
void setFieldText(const ByteVector &id, const String &s);
|
||||
|
||||
/*
|
||||
* Removes the field with the ID \a id.
|
||||
*/
|
||||
void removeField(const ByteVector &id);
|
||||
|
||||
/*!
|
||||
* Render the tag back to binary data, suitable to be written to disk.
|
||||
*
|
||||
* \note Returns empty ByteVector is the tag contains no fields.
|
||||
*/
|
||||
ByteVector render() const;
|
||||
|
||||
/*!
|
||||
* Sets the string handler that decides how the text data will be
|
||||
* converted to and from binary data.
|
||||
* If the parameter \a handler is null, the previous handler is
|
||||
* released and default UTF-8 handler is restored.
|
||||
*
|
||||
* \note The caller is responsible for deleting the previous handler
|
||||
* as needed after it is released.
|
||||
*
|
||||
* \see StringHandler
|
||||
*/
|
||||
static void setStringHandler(const StringHandler *handler);
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Pareses the body of the tag in \a data.
|
||||
*/
|
||||
void parse(const ByteVector &data);
|
||||
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
Tag &operator=(const Tag &);
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
};
|
||||
}}
|
||||
}
|
||||
|
||||
#endif
|
@ -23,36 +23,42 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
#include <tdebug.h>
|
||||
#include <id3v2tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "tbytevector.h"
|
||||
#include "tdebug.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
#include "wavfile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "infotag.h"
|
||||
#include "tagunion.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum { ID3v2Index = 0, InfoIndex = 1 };
|
||||
}
|
||||
|
||||
class RIFF::WAV::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
properties(0),
|
||||
tag(0),
|
||||
tagChunkID("ID3 ")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete properties;
|
||||
delete tag;
|
||||
}
|
||||
|
||||
Properties *properties;
|
||||
ID3v2::Tag *tag;
|
||||
|
||||
ByteVector tagChunkID;
|
||||
|
||||
TagUnion tag;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -82,26 +88,40 @@ RIFF::WAV::File::~File()
|
||||
|
||||
ID3v2::Tag *RIFF::WAV::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
return ID3v2Tag();
|
||||
}
|
||||
|
||||
ID3v2::Tag *RIFF::WAV::File::ID3v2Tag() const
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
||||
}
|
||||
|
||||
RIFF::Info::Tag *RIFF::WAV::File::InfoTag() const
|
||||
{
|
||||
return d->tag.access<RIFF::Info::Tag>(InfoIndex, false);
|
||||
}
|
||||
|
||||
PropertyMap RIFF::WAV::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
PropertyMap RIFF::WAV::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
return d->tag.setProperties(properties);
|
||||
}
|
||||
|
||||
|
||||
RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
}
|
||||
|
||||
bool RIFF::WAV::File::save()
|
||||
{
|
||||
return RIFF::WAV::File::save(AllTags);
|
||||
}
|
||||
|
||||
bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("RIFF::WAV::File::save() -- File is read only.");
|
||||
@ -113,7 +133,25 @@ bool RIFF::WAV::File::save()
|
||||
return false;
|
||||
}
|
||||
|
||||
setChunkData(d->tagChunkID, d->tag->render());
|
||||
if(stripOthers)
|
||||
strip(static_cast<TagTypes>(AllTags & ~tags));
|
||||
|
||||
ID3v2::Tag *id3v2tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
||||
if(!id3v2tag->isEmpty()) {
|
||||
if(tags & ID3v2)
|
||||
setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version));
|
||||
}
|
||||
|
||||
Info::Tag *infotag = d->tag.access<Info::Tag>(InfoIndex, false);
|
||||
if(!infotag->isEmpty()) {
|
||||
if(tags & Info) {
|
||||
int chunkId = findInfoTagChunk();
|
||||
if(chunkId != -1)
|
||||
setChunkData(chunkId, infotag->render());
|
||||
else
|
||||
setChunkData("LIST", infotag->render(), true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -127,19 +165,53 @@ void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle properties
|
||||
ByteVector formatData;
|
||||
uint streamLength = 0;
|
||||
for(uint i = 0; i < chunkCount(); i++) {
|
||||
if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") {
|
||||
String name = chunkName(i);
|
||||
if(name == "ID3 " || name == "id3 ") {
|
||||
d->tagChunkID = chunkName(i);
|
||||
d->tag = new ID3v2::Tag(this, chunkOffset(i));
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i)));
|
||||
}
|
||||
else if(chunkName(i) == "fmt " && readProperties)
|
||||
else if(name == "fmt " && readProperties)
|
||||
formatData = chunkData(i);
|
||||
else if(chunkName(i) == "data" && readProperties)
|
||||
else if(name == "data" && readProperties)
|
||||
streamLength = chunkDataSize(i);
|
||||
else if(name == "LIST") {
|
||||
ByteVector data = chunkData(i);
|
||||
ByteVector type = data.mid(0, 4);
|
||||
|
||||
if(type == "INFO")
|
||||
d->tag.set(InfoIndex, new RIFF::Info::Tag(data));
|
||||
}
|
||||
}
|
||||
|
||||
if (!d->tag[ID3v2Index])
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag);
|
||||
|
||||
if (!d->tag[InfoIndex])
|
||||
d->tag.set(InfoIndex, new RIFF::Info::Tag);
|
||||
|
||||
if(!formatData.isEmpty())
|
||||
d->properties = new Properties(formatData, streamLength, propertiesStyle);
|
||||
|
||||
if(!d->tag)
|
||||
d->tag = new ID3v2::Tag;
|
||||
}
|
||||
|
||||
void RIFF::WAV::File::strip(TagTypes tags)
|
||||
{
|
||||
if(tags & ID3v2)
|
||||
removeChunk(d->tagChunkID);
|
||||
|
||||
if(tags & Info){
|
||||
uint chunkId = findInfoTagChunk();
|
||||
if(chunkId != -1)
|
||||
removeChunk(chunkId);
|
||||
}
|
||||
}
|
||||
|
||||
uint RIFF::WAV::File::findInfoTagChunk()
|
||||
{
|
||||
for(uint i = 0; i < chunkCount(); ++i) {
|
||||
if(chunkName(i) == "LIST" && chunkData(i).mid(0, 4) == "INFO") {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
#include "rifffile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "infotag.h"
|
||||
#include "wavproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
@ -57,6 +58,17 @@ namespace TagLib {
|
||||
class TAGLIB_EXPORT File : public TagLib::RIFF::File
|
||||
{
|
||||
public:
|
||||
enum TagTypes {
|
||||
//! Empty set. Matches no tag types.
|
||||
NoTags = 0x0000,
|
||||
//! Matches ID3v2 tags.
|
||||
ID3v2 = 0x0001,
|
||||
//! Matches Info tags.
|
||||
Info = 0x0002,
|
||||
//! Matches all tag types.
|
||||
AllTags = 0xffff
|
||||
};
|
||||
|
||||
/*!
|
||||
* Contructs an WAV file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
@ -82,9 +94,22 @@ namespace TagLib {
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file.
|
||||
* Returns the ID3v2 Tag for this file.
|
||||
*
|
||||
* \note This method does not return all the tags for this file for
|
||||
* backward compatibility. Will be fixed in TagLib 2.0.
|
||||
*/
|
||||
virtual ID3v2::Tag *tag() const;
|
||||
ID3v2::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Returns the ID3v2 Tag for this file.
|
||||
*/
|
||||
ID3v2::Tag *ID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns the RIFF INFO Tag for this file.
|
||||
*/
|
||||
Info::Tag *InfoTag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
@ -109,12 +134,21 @@ namespace TagLib {
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
bool save(TagTypes tags, bool stripOthers = true, int id3v2Version = 4);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
|
||||
void strip(TagTypes tags);
|
||||
|
||||
/*!
|
||||
* Returns the index of the chunk that its name is "LIST" and list type is "INFO".
|
||||
*/
|
||||
uint findInfoTagChunk();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
|
@ -29,6 +29,7 @@ class TestFileRef : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testMP4_3);
|
||||
CPPUNIT_TEST(testTrueAudio);
|
||||
CPPUNIT_TEST(testAPE);
|
||||
CPPUNIT_TEST(testWav);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -127,6 +128,11 @@ public:
|
||||
fileRefSave("no-tags", ".3g2");
|
||||
}
|
||||
|
||||
void testWav()
|
||||
{
|
||||
fileRefSave("empty", ".wav");
|
||||
}
|
||||
|
||||
void testOGA_FLAC()
|
||||
{
|
||||
FileRef *f = new FileRef(TEST_FILE_PATH_C("empty_flac.oga"));
|
||||
@ -143,7 +149,7 @@ public:
|
||||
|
||||
void testAPE()
|
||||
{
|
||||
fileRefSave("mac-399.ape", ".ape");
|
||||
fileRefSave("mac-399", ".ape");
|
||||
}
|
||||
};
|
||||
|
||||
|
45
tests/test_info.cpp
Normal file
45
tests/test_info.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <infotag.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tdebug.h>
|
||||
#include "utils.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
|
||||
class TestInfoTag : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestInfoTag);
|
||||
CPPUNIT_TEST(testTitle);
|
||||
CPPUNIT_TEST(testNumericFields);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
void testTitle()
|
||||
{
|
||||
RIFF::Info::Tag tag;
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(String(""), tag.title());
|
||||
tag.setTitle("Test title 1");
|
||||
CPPUNIT_ASSERT_EQUAL(String("Test title 1"), tag.title());
|
||||
}
|
||||
|
||||
void testNumericFields()
|
||||
{
|
||||
RIFF::Info::Tag tag;
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((uint)0, tag.track());
|
||||
tag.setTrack(1234);
|
||||
CPPUNIT_ASSERT_EQUAL((uint)1234, tag.track());
|
||||
CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("IPRT"));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((uint)0, tag.year());
|
||||
tag.setYear(1234);
|
||||
CPPUNIT_ASSERT_EQUAL((uint)1234, tag.year());
|
||||
CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("ICRD"));
|
||||
}
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestInfoTag);
|
Loading…
x
Reference in New Issue
Block a user