mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
Merge branch 'master' into merge-master-to-taglib2
# Conflicts: # taglib/fileref.cpp # taglib/fileref.h # taglib/mpeg/mpegheader.cpp # taglib/tagutils.h
This commit is contained in:
commit
3ae0d4aa90
4
NEWS
4
NEWS
@ -2,13 +2,15 @@
|
||||
|
||||
* Added support for WinRT.
|
||||
* Added support for classical music tags of iTunes 12.5.
|
||||
* Enabled FileRef to detect file types based on the stream content.
|
||||
* Dropped support for Windows 9x and NT 4.0 or older.
|
||||
* Fixed reading MP4 atoms with zero length.
|
||||
* Fixed reading FLAC files with zero-sized seektables.
|
||||
* Fixed handling of lowercase field names in Vorbis Comments.
|
||||
* Fixed handling of 'rate' atoms in MP4 files.
|
||||
* Fixed handling of invalid UTF-8 sequences.
|
||||
* Fixed possible file corruptions when saving Ogg files.
|
||||
* Better handling of invalid UTF-8 sequences.
|
||||
* Marked FileRef::create() deprecated.
|
||||
* Several smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.11.1 (Oct 24, 2016)
|
||||
|
@ -76,6 +76,18 @@ public:
|
||||
SCOPED_PTR<AudioProperties> properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool APE::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede.
|
||||
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
|
||||
return (buffer.find("MAC ") != ByteVector::npos());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -198,6 +198,15 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasID3v1Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an APE
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tstring.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "asffile.h"
|
||||
#include "asftag.h"
|
||||
@ -473,6 +474,18 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool ASF::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An ASF file has to start with the designated GUID.
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 16, false);
|
||||
return (id == headerGuid);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -103,6 +103,15 @@ namespace TagLib {
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an ASF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
void read();
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tfilestream.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tsmartptr.h>
|
||||
@ -60,41 +61,14 @@ namespace
|
||||
typedef List<const FileRef::FileTypeResolver *> ResolverList;
|
||||
ResolverList fileTypeResolvers;
|
||||
|
||||
// Templatized internal functions. T should be String or IOStream*.
|
||||
// Detect the file type by user-defined resolvers.
|
||||
|
||||
template <typename T>
|
||||
FileName toFileName(T arg);
|
||||
|
||||
template <>
|
||||
FileName toFileName<IOStream *>(IOStream *arg)
|
||||
{
|
||||
return arg->name();
|
||||
}
|
||||
|
||||
template <>
|
||||
FileName toFileName<FileName>(FileName arg)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
File *resolveFileType(T arg, bool readProperties,
|
||||
AudioProperties::ReadStyle style);
|
||||
|
||||
template <>
|
||||
File *resolveFileType<IOStream *>(IOStream *arg, bool readProperties,
|
||||
AudioProperties::ReadStyle style)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
File *resolveFileType<FileName>(FileName arg, bool readProperties,
|
||||
AudioProperties::ReadStyle style)
|
||||
File *detectByResolvers(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
ResolverList::ConstIterator it = fileTypeResolvers.begin();
|
||||
for(; it != fileTypeResolvers.end(); ++it) {
|
||||
File *file = (*it)->createFile(arg, readProperties, style);
|
||||
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
}
|
||||
@ -102,18 +76,15 @@ namespace
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
File* createInternal(T arg, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
File *file = resolveFileType(arg, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
// Detect the file type based on the file extension.
|
||||
|
||||
File* detectByExtension(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const String s(toFileName(arg).wstr());
|
||||
const String s(stream->name().wstr());
|
||||
#else
|
||||
const String s(toFileName(arg));
|
||||
const String s(stream->name());
|
||||
#endif
|
||||
|
||||
String ext;
|
||||
@ -128,66 +99,192 @@ namespace
|
||||
if(ext.isEmpty())
|
||||
return 0;
|
||||
|
||||
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
|
||||
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGG")
|
||||
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGA") {
|
||||
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
|
||||
File *file = new Ogg::FLAC::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
if(file->isValid())
|
||||
return file;
|
||||
delete file;
|
||||
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "FLAC")
|
||||
return new FLAC::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "MPC")
|
||||
return new MPC::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WV")
|
||||
return new WavPack::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "SPX")
|
||||
return new Ogg::Speex::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OPUS")
|
||||
return new Ogg::Opus::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "TTA")
|
||||
return new TrueAudio::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
||||
return new MP4::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WMA" || ext == "ASF")
|
||||
return new ASF::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
||||
return new RIFF::AIFF::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WAV")
|
||||
return new RIFF::WAV::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "APE")
|
||||
return new APE::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
return new Mod::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "S3M")
|
||||
return new S3M::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "IT")
|
||||
return new IT::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "XM")
|
||||
return new XM::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
if (ext == "DSF")
|
||||
return new DSF::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Detect the file type based on the actual content of the stream.
|
||||
|
||||
File *detectByContent(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
File *file = 0;
|
||||
|
||||
if(MPEG::File::isSupported(stream))
|
||||
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::Vorbis::File::isSupported(stream))
|
||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::FLAC::File::isSupported(stream))
|
||||
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(FLAC::File::isSupported(stream))
|
||||
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(MPC::File::isSupported(stream))
|
||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(WavPack::File::isSupported(stream))
|
||||
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::Speex::File::isSupported(stream))
|
||||
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::Opus::File::isSupported(stream))
|
||||
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(TrueAudio::File::isSupported(stream))
|
||||
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(MP4::File::isSupported(stream))
|
||||
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ASF::File::isSupported(stream))
|
||||
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(RIFF::AIFF::File::isSupported(stream))
|
||||
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(RIFF::WAV::File::isSupported(stream))
|
||||
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(APE::File::isSupported(stream))
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
// isSupported() only does a quick check, so double check the file here.
|
||||
|
||||
if(file) {
|
||||
if(file->isValid())
|
||||
return file;
|
||||
else
|
||||
delete file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Internal function that supports FileRef::create().
|
||||
// This looks redundant, but necessary in order not to change the previous
|
||||
// behavior of FileRef::create().
|
||||
|
||||
File* createInternal(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
File *file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
|
||||
#ifdef _WIN32
|
||||
const String s(fileName.wstr());
|
||||
#else
|
||||
const String s(fileName);
|
||||
#endif
|
||||
|
||||
String ext;
|
||||
const size_t pos = s.rfind(".");
|
||||
if(pos != String::npos())
|
||||
ext = s.substr(pos + 1).upper();
|
||||
|
||||
if(ext.isEmpty())
|
||||
return 0;
|
||||
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGG")
|
||||
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGA") {
|
||||
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
|
||||
File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file->isValid())
|
||||
return file;
|
||||
delete file;
|
||||
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
if(ext == "FLAC")
|
||||
return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "MPC")
|
||||
return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WV")
|
||||
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "SPX")
|
||||
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OPUS")
|
||||
return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "TTA")
|
||||
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
||||
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WMA" || ext == "ASF")
|
||||
return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
||||
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WAV")
|
||||
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "APE")
|
||||
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "S3M")
|
||||
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "IT")
|
||||
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "XM")
|
||||
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "DSF")
|
||||
return new DSF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct FileRefData
|
||||
{
|
||||
FileRefData() :
|
||||
file(0),
|
||||
stream(0) {}
|
||||
|
||||
~FileRefData() {
|
||||
delete file;
|
||||
delete stream;
|
||||
}
|
||||
|
||||
File *file;
|
||||
IOStream *stream;
|
||||
};
|
||||
}
|
||||
|
||||
class FileRef::FileRefPrivate
|
||||
{
|
||||
public:
|
||||
FileRefPrivate() :
|
||||
file() {}
|
||||
data(new FileRefData()) {}
|
||||
|
||||
explicit FileRefPrivate(File *f) :
|
||||
file(f) {}
|
||||
|
||||
SHARED_PTR<File> file;
|
||||
SHARED_PTR<FileRefData> data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -201,18 +298,21 @@ FileRef::FileRef() :
|
||||
|
||||
FileRef::FileRef(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(new FileRefPrivate(createInternal(fileName, readAudioProperties, audioPropertiesStyle)))
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
parse(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(new FileRefPrivate(createInternal(stream, readAudioProperties, audioPropertiesStyle)))
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
parse(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(File *file) :
|
||||
d(new FileRefPrivate(file))
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
d->data->file = file;
|
||||
}
|
||||
|
||||
FileRef::FileRef(const FileRef &ref) :
|
||||
@ -231,7 +331,7 @@ Tag *FileRef::tag() const
|
||||
debug("FileRef::tag() - Called without a valid file.");
|
||||
return 0;
|
||||
}
|
||||
return d->file->tag();
|
||||
return d->data->file->tag();
|
||||
}
|
||||
|
||||
PropertyMap FileRef::properties() const
|
||||
@ -241,7 +341,7 @@ PropertyMap FileRef::properties() const
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
return d->file->properties();
|
||||
return d->data->file->properties();
|
||||
}
|
||||
|
||||
void FileRef::removeUnsupportedProperties(const StringList& properties)
|
||||
@ -251,7 +351,7 @@ void FileRef::removeUnsupportedProperties(const StringList& properties)
|
||||
return;
|
||||
}
|
||||
|
||||
d->file->removeUnsupportedProperties(properties);
|
||||
d->data->file->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap FileRef::setProperties(const PropertyMap &properties)
|
||||
@ -261,7 +361,7 @@ PropertyMap FileRef::setProperties(const PropertyMap &properties)
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
return d->file->setProperties(properties);
|
||||
return d->data->file->setProperties(properties);
|
||||
}
|
||||
|
||||
AudioProperties *FileRef::audioProperties() const
|
||||
@ -270,12 +370,12 @@ AudioProperties *FileRef::audioProperties() const
|
||||
debug("FileRef::audioProperties() - Called without a valid file.");
|
||||
return 0;
|
||||
}
|
||||
return d->file->audioProperties();
|
||||
return d->data->file->audioProperties();
|
||||
}
|
||||
|
||||
File *FileRef::file() const
|
||||
{
|
||||
return d->file.get();
|
||||
return d->data->file;
|
||||
}
|
||||
|
||||
bool FileRef::save()
|
||||
@ -284,7 +384,7 @@ bool FileRef::save()
|
||||
debug("FileRef::save() - Called without a valid file.");
|
||||
return false;
|
||||
}
|
||||
return d->file->save();
|
||||
return d->data->file->save();
|
||||
}
|
||||
|
||||
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
|
||||
@ -332,7 +432,7 @@ StringList FileRef::defaultFileExtensions()
|
||||
|
||||
bool FileRef::isValid() const
|
||||
{
|
||||
return (d->file && d->file->isValid());
|
||||
return (d->data->file && d->data->file->isValid());
|
||||
}
|
||||
|
||||
bool FileRef::isNull() const
|
||||
@ -355,10 +455,64 @@ void FileRef::swap(FileRef &ref)
|
||||
|
||||
bool FileRef::operator==(const FileRef &ref) const
|
||||
{
|
||||
return (ref.d->file == d->file);
|
||||
return (ref.d->data == d->data);
|
||||
}
|
||||
|
||||
bool FileRef::operator!=(const FileRef &ref) const
|
||||
{
|
||||
return (ref.d->file != d->file);
|
||||
return (ref.d->data != d->data);
|
||||
}
|
||||
|
||||
File *FileRef::create(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) // static
|
||||
{
|
||||
return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FileRef::parse(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
// Try user-defined resolvers.
|
||||
|
||||
d->data->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// Try to resolve file types based on the file extension.
|
||||
|
||||
d->data->stream = new FileStream(fileName);
|
||||
d->data->file = detectByExtension(d->data->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// At last, try to resolve file types based on the actual content.
|
||||
|
||||
d->data->file = detectByContent(d->data->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// Stream have to be closed here if failed to resolve file types.
|
||||
|
||||
delete d->data->stream;
|
||||
d->data->stream = 0;
|
||||
}
|
||||
|
||||
void FileRef::parse(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
// User-defined resolvers won't work with a stream.
|
||||
|
||||
// Try to resolve file types based on the file extension.
|
||||
|
||||
d->data->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// At last, try to resolve file types based on the actual content of the file.
|
||||
|
||||
d->data->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
@ -304,7 +304,25 @@ namespace TagLib {
|
||||
*/
|
||||
bool operator!=(const FileRef &ref) const;
|
||||
|
||||
/*!
|
||||
* A simple implementation of file type guessing. 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 ignored.
|
||||
*
|
||||
* \note You generally shouldn't use this method, but instead the constructor
|
||||
* directly.
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
static File *create(FileName fileName,
|
||||
bool readAudioProperties = true,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
|
||||
|
||||
private:
|
||||
void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
|
||||
void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
|
||||
|
||||
class FileRefPrivate;
|
||||
FileRefPrivate *d;
|
||||
};
|
||||
|
@ -98,6 +98,18 @@ public:
|
||||
bool scanned;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool FLAC::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
|
||||
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
|
||||
return (buffer.find("fLaC") != ByteVector::npos());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -280,6 +280,15 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a FLAC
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "mp4atom.h"
|
||||
#include "mp4tag.h"
|
||||
#include "mp4file.h"
|
||||
@ -69,6 +71,22 @@ public:
|
||||
MP4::AudioProperties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool MP4::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An MP4 file has to have an "ftyp" box first.
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 8, false);
|
||||
return id.containsAt("ftyp", 4);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
|
@ -104,6 +104,15 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasMP4Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an ASF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
void read(bool readProperties);
|
||||
|
||||
|
@ -75,6 +75,19 @@ public:
|
||||
AudioProperties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool MPC::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A newer MPC file has to start with "MPCK" or "MP+", but older files don't
|
||||
// have keys to do a quick check.
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 4, false);
|
||||
return (id == "MPCK" || id.startsWith("MP+"));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -205,6 +205,15 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasAPETag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an MPC
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include "id3v2tag.h"
|
||||
#include "id3v2frame.h"
|
||||
|
||||
#include "tbytevectorlist.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace ID3v2 {
|
||||
|
@ -76,6 +76,55 @@ public:
|
||||
AudioProperties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace
|
||||
{
|
||||
// Dummy file class to make a stream work with MPEG::Header.
|
||||
|
||||
class AdapterFile : public TagLib::File
|
||||
{
|
||||
public:
|
||||
AdapterFile(IOStream *stream) : File(stream) {}
|
||||
|
||||
Tag *tag() const { return 0; }
|
||||
AudioProperties *audioProperties() const { return 0; }
|
||||
bool save() { return false; }
|
||||
};
|
||||
}
|
||||
|
||||
bool MPEG::File::isSupported(IOStream *stream)
|
||||
{
|
||||
if(!stream || !stream->isOpen())
|
||||
return false;
|
||||
|
||||
// An MPEG file has MPEG frame headers. An ID3v2 tag may precede.
|
||||
|
||||
// MPEG frame headers are really confusing with irrelevant binary data.
|
||||
// So we check if a frame header is really valid.
|
||||
|
||||
long long headerOffset;
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true, &headerOffset);
|
||||
|
||||
const long long originalPosition = stream->tell();
|
||||
AdapterFile file(stream);
|
||||
|
||||
for(unsigned int i = 0; i < buffer.size() - 1; ++i) {
|
||||
if(isFrameSync(buffer, i)) {
|
||||
const Header header(&file, headerOffset + i, true);
|
||||
if(header.isValid()) {
|
||||
stream->seek(originalPosition);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stream->seek(originalPosition);
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -321,6 +321,15 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasAPETag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an MPEG
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -197,10 +197,8 @@ void MPEG::Header::parse(File *file, long long offset, bool checkLength)
|
||||
d->data->version = Version2;
|
||||
else if(versionBits == 3)
|
||||
d->data->version = Version1;
|
||||
else {
|
||||
debug("MPEG::Header::parse() -- Invalid MPEG version bits.");
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the MPEG layer
|
||||
|
||||
@ -212,10 +210,8 @@ void MPEG::Header::parse(File *file, long long offset, bool checkLength)
|
||||
d->data->layer = 2;
|
||||
else if(layerBits == 3)
|
||||
d->data->layer = 1;
|
||||
else {
|
||||
debug("MPEG::Header::parse() -- Invalid MPEG layer bits.");
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
d->data->protectionEnabled = (static_cast<unsigned char>(data[1] & 0x01) == 0);
|
||||
|
||||
@ -244,10 +240,8 @@ void MPEG::Header::parse(File *file, long long offset, bool checkLength)
|
||||
|
||||
d->data->bitrate = bitrates[versionIndex][layerIndex][bitrateIndex];
|
||||
|
||||
if(d->data->bitrate == 0) {
|
||||
debug("MPEG::Header::parse() -- Invalid bit rate.");
|
||||
if(d->data->bitrate == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the sample rate
|
||||
|
||||
@ -264,7 +258,6 @@ void MPEG::Header::parse(File *file, long long offset, bool checkLength)
|
||||
d->data->sampleRate = sampleRates[d->data->version][samplerateIndex];
|
||||
|
||||
if(d->data->sampleRate == 0) {
|
||||
debug("MPEG::Header::parse() -- Invalid sample rate.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -311,20 +304,16 @@ void MPEG::Header::parse(File *file, long long offset, bool checkLength)
|
||||
file->seek(offset + d->data->frameLength);
|
||||
const ByteVector nextData = file->readBlock(4);
|
||||
|
||||
if(nextData.size() < 4) {
|
||||
debug("MPEG::Header::parse() -- Could not read the next frame header.");
|
||||
if(nextData.size() < 4)
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int HeaderMask = 0xfffe0c00;
|
||||
|
||||
const unsigned int header = data.toUInt32BE(0) & HeaderMask;
|
||||
const unsigned int nextHeader = nextData.toUInt32BE(0) & HeaderMask;
|
||||
|
||||
if(header != nextHeader) {
|
||||
debug("MPEG::Header::parse() -- The next frame was not consistent with this frame.");
|
||||
if(header != nextHeader)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we're done parsing, set this to be a valid frame.
|
||||
|
@ -45,12 +45,12 @@ namespace TagLib
|
||||
* \note This does not check the length of the vector, since this is an
|
||||
* internal utility function.
|
||||
*/
|
||||
inline bool isFrameSync(const ByteVector &bytes)
|
||||
inline bool isFrameSync(const ByteVector &bytes, unsigned int offset = 0)
|
||||
{
|
||||
// 0xFF in the second byte is possible in theory, but it's very unlikely.
|
||||
|
||||
const unsigned char b1 = bytes[0];
|
||||
const unsigned char b2 = bytes[1];
|
||||
const unsigned char b1 = bytes[offset + 0];
|
||||
const unsigned char b2 = bytes[offset + 1];
|
||||
return (b1 == 0xFF && b2 != 0xFF && (b2 & 0xE0) == 0xE0);
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include <xiphcomment.h>
|
||||
#include "oggflacfile.h"
|
||||
@ -65,6 +66,19 @@ public:
|
||||
int commentPacket;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool Ogg::FLAC::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An Ogg FLAC file has IDs "OggS" and "fLaC" somewhere.
|
||||
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
|
||||
return (buffer.find("OggS") != ByteVector::npos()
|
||||
&& buffer.find("fLaC") != ByteVector::npos());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -129,6 +129,14 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasXiphComment() const;
|
||||
|
||||
/*!
|
||||
* Check if the given \a stream can be opened as an Ogg FLAC file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "opusfile.h"
|
||||
|
||||
@ -53,6 +54,19 @@ public:
|
||||
AudioProperties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool Ogg::Opus::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An Opus file has IDs "OggS" and "OpusHead" somewhere.
|
||||
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
|
||||
return (buffer.find("OggS") != ByteVector::npos()
|
||||
&& buffer.find("OpusHead") != ByteVector::npos());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -101,6 +101,15 @@ namespace TagLib {
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an Opus
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "speexfile.h"
|
||||
|
||||
@ -53,6 +54,19 @@ public:
|
||||
AudioProperties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool Ogg::Speex::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A Speex file has IDs "OggS" and "Speex " somewhere.
|
||||
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
|
||||
return (buffer.find("OggS") != ByteVector::npos()
|
||||
&& buffer.find("Speex ") != ByteVector::npos());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -101,6 +101,15 @@ namespace TagLib {
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a Speex
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -28,10 +28,10 @@
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "vorbisfile.h"
|
||||
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Ogg::Vorbis::File::FilePrivate
|
||||
@ -59,6 +59,19 @@ namespace TagLib {
|
||||
static const char vorbisCommentHeaderID[] = { 0x03, 'v', 'o', 'r', 'b', 'i', 's', 0 };
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool Ogg::Vorbis::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An Ogg Vorbis file has IDs "OggS" and "\x01vorbis" somewhere.
|
||||
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
|
||||
return (buffer.find("OggS") != ByteVector::npos()
|
||||
&& buffer.find("\x01vorbis") != ByteVector::npos());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -99,6 +99,14 @@ namespace TagLib {
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Check if the given \a stream can be opened as an Ogg Vorbis file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <id3v2tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "aifffile.h"
|
||||
|
||||
@ -53,6 +54,18 @@ public:
|
||||
bool hasID3v2;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool RIFF::AIFF::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An AIFF file has to start with "FORM????AIFF" or "FORM????AIFC".
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 12, false);
|
||||
return (id.startsWith("FORM") && (id.containsAt("AIFF", 8) || id.containsAt("AIFC", 8)));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -112,6 +112,14 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Check if the given \a stream can be opened as an AIFF file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -23,10 +23,11 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "tbytevector.h"
|
||||
#include "tdebug.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
#include <tbytevector.h>
|
||||
#include <tdebug.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "wavfile.h"
|
||||
#include "id3v2tag.h"
|
||||
@ -60,6 +61,18 @@ public:
|
||||
bool hasInfo;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool RIFF::WAV::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A WAV file has to start with "RIFF????WAVE".
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 12, false);
|
||||
return (id.startsWith("RIFF") && id.containsAt("WAVE", 8));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -165,6 +165,15 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasInfoTag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a WAV
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -77,3 +77,29 @@ long long Utils::findAPE(File *file, long long id3v1Location)
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ByteVector TagLib::Utils::readHeader(IOStream *stream, size_t length,
|
||||
bool skipID3v2, long long *headerOffset)
|
||||
{
|
||||
if(!stream || !stream->isOpen())
|
||||
return ByteVector();
|
||||
|
||||
const long long originalPosition = stream->tell();
|
||||
long long bufferOffset = 0;
|
||||
|
||||
if(skipID3v2) {
|
||||
stream->seek(0);
|
||||
const ByteVector data = stream->readBlock(ID3v2::Header::size());
|
||||
if(data.startsWith(ID3v2::Header::fileIdentifier()))
|
||||
bufferOffset = ID3v2::Header(data).completeTagSize();
|
||||
}
|
||||
|
||||
stream->seek(bufferOffset);
|
||||
const ByteVector header = stream->readBlock(length);
|
||||
stream->seek(originalPosition);
|
||||
|
||||
if(headerOffset)
|
||||
*headerOffset = bufferOffset;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
@ -30,9 +30,12 @@
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
|
||||
|
||||
#include <tbytevector.h>
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
class File;
|
||||
class IOStream;
|
||||
|
||||
namespace Utils {
|
||||
|
||||
@ -41,6 +44,9 @@ namespace TagLib {
|
||||
long long findID3v2(File *file);
|
||||
|
||||
long long findAPE(File *file, long long id3v1Location);
|
||||
|
||||
ByteVector readHeader(IOStream *stream, size_t length, bool skipID3v2,
|
||||
long long *headerOffset = 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,18 @@ public:
|
||||
AudioProperties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool TrueAudio::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A TrueAudio file has to start with "TTA". An ID3v2 tag may precede.
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 3, true);
|
||||
return (id == "TTA");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -226,6 +226,15 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a TrueAudio
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -71,6 +71,18 @@ public:
|
||||
AudioProperties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool WavPack::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A WavPack file has to start with "wvpk".
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 4, false);
|
||||
return (id == "wvpk");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -191,6 +191,14 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasAPETag() const;
|
||||
|
||||
/*!
|
||||
* Check if the given \a stream can be opened as a WavPack file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
@ -39,9 +39,10 @@
|
||||
#include <wavfile.h>
|
||||
#include <apefile.h>
|
||||
#include <aifffile.h>
|
||||
#include <tfilestream.h>
|
||||
#include <tbytevectorstream.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include "utils.h"
|
||||
#include <tfilestream.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
@ -79,6 +80,7 @@ class TestFileRef : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testAIFF_1);
|
||||
CPPUNIT_TEST(testAIFF_2);
|
||||
CPPUNIT_TEST(testUnsupported);
|
||||
CPPUNIT_TEST(testCreate);
|
||||
CPPUNIT_TEST(testFileResolver);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
@ -129,6 +131,7 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7);
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080);
|
||||
}
|
||||
|
||||
{
|
||||
FileStream fs(newname.c_str());
|
||||
FileRef f(&fs);
|
||||
@ -140,6 +143,64 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7);
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080);
|
||||
f.tag()->setArtist("test artist");
|
||||
f.tag()->setTitle("test title");
|
||||
f.tag()->setGenre("Test!");
|
||||
f.tag()->setAlbum("albummmm");
|
||||
f.tag()->setTrack(5);
|
||||
f.tag()->setYear(2020);
|
||||
f.save();
|
||||
}
|
||||
|
||||
ByteVector fileContent;
|
||||
{
|
||||
FileStream fs(newname.c_str());
|
||||
FileRef f(&fs);
|
||||
CPPUNIT_ASSERT(dynamic_cast<T*>(f.file()));
|
||||
CPPUNIT_ASSERT(!f.isNull());
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5);
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020);
|
||||
|
||||
fs.seek(0);
|
||||
fileContent = fs.readBlock(fs.length());
|
||||
}
|
||||
|
||||
{
|
||||
ByteVectorStream bs(fileContent);
|
||||
FileRef f(&bs);
|
||||
CPPUNIT_ASSERT(dynamic_cast<T*>(f.file()));
|
||||
CPPUNIT_ASSERT(!f.isNull());
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5);
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020);
|
||||
f.tag()->setArtist("ttest artist");
|
||||
f.tag()->setTitle("ytest title");
|
||||
f.tag()->setGenre("uTest!");
|
||||
f.tag()->setAlbum("ialbummmm");
|
||||
f.tag()->setTrack(7);
|
||||
f.tag()->setYear(2080);
|
||||
f.save();
|
||||
|
||||
fileContent = *bs.data();
|
||||
}
|
||||
{
|
||||
ByteVectorStream bs(fileContent);
|
||||
FileRef f(&bs);
|
||||
CPPUNIT_ASSERT(dynamic_cast<T*>(f.file()));
|
||||
CPPUNIT_ASSERT(!f.isNull());
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("ttest artist"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm"));
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7);
|
||||
CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080);
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,6 +298,19 @@ public:
|
||||
CPPUNIT_ASSERT(f2.isNull());
|
||||
}
|
||||
|
||||
void testCreate()
|
||||
{
|
||||
// This is depricated. But worth it to test.
|
||||
|
||||
File *f = FileRef::create(TEST_FILE_PATH_C("empty_vorbis.oga"));
|
||||
CPPUNIT_ASSERT(dynamic_cast<Ogg::Vorbis::File*>(f));
|
||||
delete f;
|
||||
|
||||
f = FileRef::create(TEST_FILE_PATH_C("xing.mp3"));
|
||||
CPPUNIT_ASSERT(dynamic_cast<MPEG::File*>(f));
|
||||
delete f;
|
||||
}
|
||||
|
||||
void testFileResolver()
|
||||
{
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user