From 45b0279b411958d8fcc1428dc38084f351ae4b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sun, 11 Nov 2012 16:43:36 +0100 Subject: [PATCH] Add a tool to inspect audio files, only MP4 is implemented for now --- examples/CMakeLists.txt | 8 +++++ examples/inspect.cpp | 61 ++++++++++++++++++++++++++++++++++++ taglib/audioproperties.cpp | 11 +++++++ taglib/audioproperties.h | 3 ++ taglib/mp4/mp4file.cpp | 13 ++++++++ taglib/mp4/mp4file.h | 5 +++ taglib/mp4/mp4item.cpp | 56 ++++++++++++++++++++++++++++++++- taglib/mp4/mp4item.h | 17 ++++++++++ taglib/mp4/mp4properties.cpp | 31 +++++++++++++++++- taglib/mp4/mp4properties.h | 2 ++ taglib/mp4/mp4tag.cpp | 10 ++++++ taglib/mp4/mp4tag.h | 2 ++ taglib/tag.cpp | 14 +++++++++ taglib/tag.h | 2 ++ taglib/toolkit/tfile.cpp | 14 +++++++++ taglib/toolkit/tfile.h | 5 +++ 16 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 examples/inspect.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 17d4bd6b..7812d2e9 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,6 +5,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1 ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2 + ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mp4 ${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/ ) if(ENABLE_STATIC) @@ -46,5 +47,12 @@ ADD_EXECUTABLE(strip-id3v1 strip-id3v1.cpp) TARGET_LINK_LIBRARIES(strip-id3v1 tag ) +########### next target ############### + +ADD_EXECUTABLE(inspect inspect.cpp) + +TARGET_LINK_LIBRARIES(inspect tag ) + + endif(BUILD_EXAMPLES) diff --git a/examples/inspect.cpp b/examples/inspect.cpp new file mode 100644 index 00000000..90a94dbb --- /dev/null +++ b/examples/inspect.cpp @@ -0,0 +1,61 @@ +/* Copyright (C) 2012 Lukas Lalinsky + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +using namespace std; +using namespace TagLib; + +#define MAYBE_PRINT_DESC(_Type) \ + if(dynamic_cast<_Type *>(f.file())) { \ + cout << dynamic_cast<_Type *>(f.file())->toString().to8Bit(true) << endl; \ + found = 1; \ + } + +int main(int argc, char *argv[]) +{ + // process the command line args + + for(int i = 1; i < argc; i++) { + + cout << "******************** \"" << argv[i] << "\"********************" << endl; + + FileRef f(argv[i]); + + bool found = 0; + if(!f.isNull() && f.file()) { + MAYBE_PRINT_DESC(MP4::File); + MAYBE_PRINT_DESC(File); + } + + if(!found) { + cout << "could not find any information about the file" << endl; + } + + } +} diff --git a/taglib/audioproperties.cpp b/taglib/audioproperties.cpp index 298b97da..5b25867b 100644 --- a/taglib/audioproperties.cpp +++ b/taglib/audioproperties.cpp @@ -24,6 +24,7 @@ ***************************************************************************/ #include "audioproperties.h" +#include "tstringlist.h" using namespace TagLib; @@ -49,3 +50,13 @@ AudioProperties::AudioProperties(ReadStyle) { } + +String +AudioProperties::toString() const +{ + StringList desc; + desc.append("Audio"); + desc.append(String::number(length()) + " seconds"); + desc.append(String::number(bitrate()) + " kbps"); + return desc.toString(", "); +} diff --git a/taglib/audioproperties.h b/taglib/audioproperties.h index e9844fa0..12400b9f 100644 --- a/taglib/audioproperties.h +++ b/taglib/audioproperties.h @@ -27,6 +27,7 @@ #define TAGLIB_AUDIOPROPERTIES_H #include "taglib_export.h" +#include "tstring.h" namespace TagLib { @@ -86,6 +87,8 @@ namespace TagLib { */ virtual int channels() const = 0; + String toString() const; + protected: /*! diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp index a9543211..8d6a99a3 100644 --- a/taglib/mp4/mp4file.cpp +++ b/taglib/mp4/mp4file.cpp @@ -149,3 +149,16 @@ MP4::File::save() return d->tag->save(); } +String +MP4::File::toString() const +{ + StringList desc; + if(d->properties) { + desc.append(d->properties->toString()); + } + if(d->tag) { + desc.append(d->tag->toString()); + } + return desc.toString("\n"); +} + diff --git a/taglib/mp4/mp4file.h b/taglib/mp4/mp4file.h index 2ed3bea5..d8468490 100644 --- a/taglib/mp4/mp4file.h +++ b/taglib/mp4/mp4file.h @@ -100,6 +100,11 @@ namespace TagLib { */ bool save(); + /*! + * Description of the file. + */ + String toString() const; + private: void read(bool readProperties, Properties::ReadStyle audioPropertiesStyle); diff --git a/taglib/mp4/mp4item.cpp b/taglib/mp4/mp4item.cpp index af2cc65c..4ed11a27 100644 --- a/taglib/mp4/mp4item.cpp +++ b/taglib/mp4/mp4item.cpp @@ -26,6 +26,7 @@ #ifdef HAVE_CONFIG_H #include #endif +#include #include #include @@ -36,10 +37,11 @@ using namespace TagLib; class MP4::Item::ItemPrivate : public RefCounter { public: - ItemPrivate() : RefCounter(), valid(true), atomDataType(TypeUndefined) {} + ItemPrivate() : RefCounter(), valid(true), atomDataType(MP4::TypeUndefined), type(MP4::Item::TypeUndefined) {} bool valid; AtomDataType atomDataType; + ItemType type; union { bool m_bool; int m_int; @@ -86,30 +88,35 @@ MP4::Item::Item(bool value) { d = new ItemPrivate; d->m_bool = value; + d->type = TypeBool; } MP4::Item::Item(int value) { d = new ItemPrivate; d->m_int = value; + d->type = TypeInt; } MP4::Item::Item(uchar value) { d = new ItemPrivate; d->m_byte = value; + d->type = TypeByte; } MP4::Item::Item(uint value) { d = new ItemPrivate; d->m_uint = value; + d->type = TypeUInt; } MP4::Item::Item(long long value) { d = new ItemPrivate; d->m_longlong = value; + d->type = TypeLongLong; } MP4::Item::Item(int value1, int value2) @@ -117,24 +124,28 @@ MP4::Item::Item(int value1, int value2) d = new ItemPrivate; d->m_intPair.first = value1; d->m_intPair.second = value2; + d->type = TypeIntPair; } MP4::Item::Item(const ByteVectorList &value) { d = new ItemPrivate; d->m_byteVectorList = value; + d->type = TypeByteVectorList; } MP4::Item::Item(const StringList &value) { d = new ItemPrivate; d->m_stringList = value; + d->type = TypeStringList; } MP4::Item::Item(const MP4::CoverArtList &value) { d = new ItemPrivate; d->m_coverArtList = value; + d->type = TypeCoverArtList; } void MP4::Item::setAtomDataType(MP4::AtomDataType type) @@ -207,3 +218,46 @@ MP4::Item::isValid() const return d->valid; } +String +MP4::Item::toString() const +{ + StringList desc; + char tmp[256]; + switch (d->type) { + case TypeBool: + return d->m_bool ? "true" : "false"; + case TypeInt: + sprintf(tmp, "%d", d->m_int); + return tmp; + case TypeIntPair: + sprintf(tmp, "%d/%d", d->m_intPair.first, d->m_intPair.second); + return tmp; + case TypeByte: + sprintf(tmp, "%d", d->m_byte); + return tmp; + case TypeUInt: + sprintf(tmp, "%u", d->m_uint); + return tmp; + case TypeLongLong: + sprintf(tmp, "%lld", d->m_longlong); + return tmp; + case TypeStringList: + return d->m_stringList.toString(" / "); + case TypeByteVectorList: + for(int i = 0; i < d->m_byteVectorList.size(); i++) { + sprintf(tmp, "[%d bytes of data]", d->m_byteVectorList[i].size()); + desc.append(tmp); + } + return desc.toString(", "); + case TypeCoverArtList: + for(int i = 0; i < d->m_coverArtList.size(); i++) { + sprintf(tmp, "[%d bytes of data]", d->m_coverArtList[i].data().size()); + desc.append(tmp); + } + return desc.toString(", "); + case TypeUndefined: + return "[unknown]"; + } + return String(); +} + diff --git a/taglib/mp4/mp4item.h b/taglib/mp4/mp4item.h index be7aa1a1..d97c9834 100644 --- a/taglib/mp4/mp4item.h +++ b/taglib/mp4/mp4item.h @@ -41,6 +41,19 @@ namespace TagLib { int first, second; }; + enum ItemType { + TypeUndefined = 0, + TypeBool, + TypeInt, + TypeIntPair, + TypeByte, + TypeUInt, + TypeLongLong, + TypeStringList, + TypeByteVectorList, + TypeCoverArtList, + }; + Item(); Item(const Item &item); Item &operator=(const Item &item); @@ -69,8 +82,12 @@ namespace TagLib { ByteVectorList toByteVectorList() const; CoverArtList toCoverArtList() const; + ItemType type() const; + bool isValid() const; + String toString() const; + private: class ItemPrivate; ItemPrivate *d; diff --git a/taglib/mp4/mp4properties.cpp b/taglib/mp4/mp4properties.cpp index d2d04167..35830bb9 100644 --- a/taglib/mp4/mp4properties.cpp +++ b/taglib/mp4/mp4properties.cpp @@ -38,7 +38,13 @@ using namespace TagLib; class MP4::Properties::PropertiesPrivate { public: - PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0), encrypted(false) {} + PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0), encrypted(false), format(Unknown) {} + + enum Format { + Unknown = 0, + AAC = 1, + ALAC = 2, + }; int length; int bitrate; @@ -46,6 +52,7 @@ public: int channels; int bitsPerSample; bool encrypted; + Format format; }; MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) @@ -118,6 +125,7 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) file->seek(atom->offset); data = file->readBlock(atom->length); if(data.mid(20, 4) == "mp4a") { + d->format = PropertiesPrivate::AAC; d->channels = data.mid(40, 2).toShort(); d->bitsPerSample = data.mid(42, 2).toShort(); d->sampleRate = data.mid(46, 4).toUInt(); @@ -138,6 +146,7 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) } } else if (data.mid(20, 4) == "alac") { + d->format = PropertiesPrivate::ALAC; if (atom->length == 88 && data.mid(56, 4) == "alac") { d->bitsPerSample = data.at(69); d->channels = data.at(73); @@ -193,3 +202,23 @@ MP4::Properties::isEncrypted() const return d->encrypted; } +String +MP4::Properties::toString() const +{ + String format; + if(d->format == PropertiesPrivate::AAC) { + format = "AAC"; + } + else if(d->format == PropertiesPrivate::ALAC) { + format = "ALAC"; + } + else { + format = "Unknown"; + } + StringList desc; + desc.append("MPEG-4 audio (" + format + ")"); + desc.append(String::number(length()) + " seconds"); + desc.append(String::number(bitrate()) + " kbps"); + return desc.toString(", "); +} + diff --git a/taglib/mp4/mp4properties.h b/taglib/mp4/mp4properties.h index 7906824d..0f12f934 100644 --- a/taglib/mp4/mp4properties.h +++ b/taglib/mp4/mp4properties.h @@ -50,6 +50,8 @@ namespace TagLib { virtual int bitsPerSample() const; bool isEncrypted() const; + String toString() const; + private: class PropertiesPrivate; PropertiesPrivate *d; diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp index afecf98a..24693fe0 100644 --- a/taglib/mp4/mp4tag.cpp +++ b/taglib/mp4/mp4tag.cpp @@ -759,3 +759,13 @@ MP4::Tag::itemListMap() return d->items; } +String +MP4::Tag::toString() const +{ + StringList desc; + for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) { + desc.append(i->first + "=" + i->second.toString()); + } + return desc.toString("\n"); +} + diff --git a/taglib/mp4/mp4tag.h b/taglib/mp4/mp4tag.h index b5ea6ebb..2270432c 100644 --- a/taglib/mp4/mp4tag.h +++ b/taglib/mp4/mp4tag.h @@ -67,6 +67,8 @@ namespace TagLib { ItemListMap &itemListMap(); + String toString() const; + private: AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); diff --git a/taglib/tag.cpp b/taglib/tag.cpp index 67634081..17dcca5c 100644 --- a/taglib/tag.cpp +++ b/taglib/tag.cpp @@ -178,3 +178,17 @@ void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static target->setTrack(source->track()); } } + +String Tag::toString() const +{ + StringList desc; + desc.append("title=" + title()); + desc.append("artist=" + artist()); + desc.append("album=" + album()); + desc.append("comment=" + comment()); + desc.append("genre=" + genre()); + desc.append("year=" + String::number(year())); + desc.append("track=" + String::number(track())); + return desc.toString("\n"); +} + diff --git a/taglib/tag.h b/taglib/tag.h index 76c9a82a..43337a74 100644 --- a/taglib/tag.h +++ b/taglib/tag.h @@ -182,6 +182,8 @@ namespace TagLib { */ static void duplicate(const Tag *source, Tag *target, bool overwrite = true); + String toString() const; + protected: /*! * Construct a Tag. This is protected since tags should only be instantiated diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index 8d7ccdc9..3d7c1885 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -489,6 +489,20 @@ bool File::isWritable(const char *file) } +String File::toString() const +{ + StringList desc; + AudioProperties *properties = audioProperties(); + if(properties) { + desc.append(properties->toString()); + } + Tag *t = tag(); + if(t) { + desc.append(t->toString()); + } + return desc.toString("\n"); +} + //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/toolkit/tfile.h b/taglib/toolkit/tfile.h index 7e6f2b93..a7cd2d09 100644 --- a/taglib/toolkit/tfile.h +++ b/taglib/toolkit/tfile.h @@ -247,6 +247,11 @@ namespace TagLib { */ static bool isWritable(const char *name); + /*! + * Description of the file. + */ + String toString() const; + protected: /*! * Construct a File object and opens the \a file. \a file should be a