Tag Parser  9.4.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
vorbiscomment.cpp
Go to the documentation of this file.
1 #include "./vorbiscomment.h"
2 #include "./vorbiscommentids.h"
3 
4 #include "../ogg/oggiterator.h"
5 
6 #include "../diagnostics.h"
7 #include "../exceptions.h"
8 
9 #include <c++utilities/io/binaryreader.h>
10 #include <c++utilities/io/binarywriter.h>
11 #include <c++utilities/io/copy.h>
12 
13 #include <map>
14 #include <memory>
15 
16 using namespace std;
17 using namespace CppUtilities;
18 
19 namespace TagParser {
20 
26 const TagValue &VorbisComment::value(KnownField field) const
27 {
28  switch (field) {
29  case KnownField::Vendor:
30  return vendor();
31  default:
33  }
34 }
35 
36 bool VorbisComment::setValue(KnownField field, const TagValue &value)
37 {
38  switch (field) {
39  case KnownField::Vendor:
40  setVendor(value);
41  return true;
42  default:
43  return FieldMapBasedTag<VorbisComment>::setValue(field, value);
44  }
45 }
46 
47 VorbisComment::IdentifierType VorbisComment::internallyGetFieldId(KnownField field) const
48 {
49  using namespace VorbisCommentIds;
50  switch (field) {
51  case KnownField::Album:
52  return album();
53  case KnownField::Artist:
54  return artist();
56  return comment();
57  case KnownField::Cover:
58  return cover();
59  case KnownField::RecordDate:
60  case KnownField::Year:
61  return date();
62  case KnownField::Title:
63  return title();
64  case KnownField::Genre:
65  return genre();
67  return trackNumber();
69  return diskNumber();
70  case KnownField::PartNumber:
71  return partNumber();
73  return composer();
75  return encoder();
76  case KnownField::EncoderSettings:
77  return encoderSettings();
79  return description();
81  return grouping();
83  return label();
85  return performer();
86  case KnownField::Language:
87  return language();
89  return lyricist();
90  case KnownField::Lyrics:
91  return lyrics();
93  return albumArtist();
94  default:
95  return string();
96  }
97 }
98 
99 KnownField VorbisComment::internallyGetKnownField(const IdentifierType &id) const
100 {
101  using namespace VorbisCommentIds;
102  // clang-format off
103  static const map<string, KnownField, CaseInsensitiveStringComparer> fieldMap({
104  { album(), KnownField::Album },
105  { artist(), KnownField::Artist },
107  { cover(), KnownField::Cover },
108  { date(), KnownField::RecordDate },
109  { year(), KnownField::RecordDate },
110  { title(), KnownField::Title },
111  { genre(), KnownField::Genre },
114  { partNumber(), KnownField::PartNumber },
117  { encoderSettings(), KnownField::EncoderSettings },
123  { lyrics(), KnownField::Lyrics },
125  });
126  // clang-format on
127  const auto knownField(fieldMap.find(id));
128  return knownField != fieldMap.cend() ? knownField->second : KnownField::Invalid;
129 }
130 
134 template <class StreamType> void VorbisComment::internalParse(StreamType &stream, std::uint64_t maxSize, VorbisCommentFlags flags, Diagnostics &diag)
135 {
136  // prepare parsing
137  static const string context("parsing Vorbis comment");
138  std::uint64_t startOffset = static_cast<std::uint64_t>(stream.tellg());
139  try {
140  // read signature: 0x3 + "vorbis"
141  char sig[8];
142  bool skipSignature = flags & VorbisCommentFlags::NoSignature;
143  if (!skipSignature) {
144  CHECK_MAX_SIZE(7)
145  stream.read(sig, 7);
146  skipSignature = (BE::toUInt64(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
147  }
148  if (skipSignature) {
149  // read vendor (length prefixed string)
150  {
151  CHECK_MAX_SIZE(4)
152  stream.read(sig, 4);
153  const auto vendorSize = LE::toUInt32(sig);
154  if (vendorSize <= maxSize) {
155  auto buff = make_unique<char[]>(vendorSize);
156  stream.read(buff.get(), vendorSize);
157  m_vendor.assignData(move(buff), vendorSize, TagDataType::Text, TagTextEncoding::Utf8);
158  // TODO: Is the vendor string actually UTF-8 (like the field values)?
159  } else {
160  diag.emplace_back(DiagLevel::Critical, "Vendor information is truncated.", context);
161  throw TruncatedDataException();
162  }
163  maxSize -= vendorSize;
164  }
165  // read field count
166  CHECK_MAX_SIZE(4)
167  stream.read(sig, 4);
168  std::uint32_t fieldCount = LE::toUInt32(sig);
169  for (std::uint32_t i = 0; i < fieldCount; ++i) {
170  // read fields
171  VorbisCommentField field;
172  try {
173  field.parse(stream, maxSize, diag);
174  fields().emplace(field.id(), move(field));
175  } catch (const TruncatedDataException &) {
176  throw;
177  } catch (const Failure &) {
178  // nothing to do here since notifications will be added anyways
179  }
180  }
181  if (!(flags & VorbisCommentFlags::NoFramingByte)) {
182  stream.ignore(); // skip framing byte
183  }
184  m_size = static_cast<std::uint32_t>(static_cast<std::uint64_t>(stream.tellg()) - startOffset);
185  // turn "YEAR" into "DATE" (unless "DATE" exists)
186  // note: "DATE" is an official field and "YEAR" only an inofficial one but present in some files. In consistency with
187  // MediaInfo and VLC player it is treated like "DATE" here.
188  if (fields().find(VorbisCommentIds::date()) == fields().end()) {
189  const auto [first, end] = fields().equal_range(VorbisCommentIds::year());
190  for (auto i = first; i != end; ++i) {
191  fields().insert(std::pair(VorbisCommentIds::date(), std::move(i->second)));
192  }
193  fields().erase(first, end);
194  }
195  } else {
196  diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
197  throw InvalidDataException();
198  }
199  } catch (const TruncatedDataException &) {
200  m_size = static_cast<std::uint32_t>(static_cast<std::uint64_t>(stream.tellg()) - startOffset);
201  diag.emplace_back(DiagLevel::Critical, "Vorbis comment is truncated.", context);
202  throw;
203  }
204 }
205 
213 void VorbisComment::parse(OggIterator &iterator, VorbisCommentFlags flags, Diagnostics &diag)
214 {
215  internalParse(iterator, iterator.streamSize(), flags, diag);
216 }
217 
225 void VorbisComment::parse(istream &stream, std::uint64_t maxSize, VorbisCommentFlags flags, Diagnostics &diag)
226 {
227  internalParse(stream, maxSize, flags, diag);
228 }
229 
237 void VorbisComment::make(std::ostream &stream, VorbisCommentFlags flags, Diagnostics &diag)
238 {
239  // prepare making
240  static const string context("making Vorbis comment");
241  string vendor;
242  try {
243  m_vendor.toString(vendor);
244  } catch (const ConversionException &) {
245  diag.emplace_back(DiagLevel::Warning, "Can not convert the assigned vendor to string.", context);
246  }
247  BinaryWriter writer(&stream);
248  if (!(flags & VorbisCommentFlags::NoSignature)) {
249  // write signature
250  static const char sig[7] = { 0x03, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73 };
251  stream.write(sig, sizeof(sig));
252  }
253  // write vendor
254  writer.writeUInt32LE(vendor.size());
255  writer.writeString(vendor);
256  // write field count later
257  const auto fieldCountOffset = stream.tellp();
258  writer.writeUInt32LE(0);
259  // write fields
260  std::uint32_t fieldsWritten = 0;
261  for (auto i : fields()) {
262  VorbisCommentField &field = i.second;
263  if (!field.value().isEmpty()) {
264  try {
265  if (field.make(writer, flags, diag)) {
266  ++fieldsWritten;
267  }
268  } catch (const Failure &) {
269  }
270  }
271  }
272  // write field count
273  const auto framingByteOffset = stream.tellp();
274  stream.seekp(fieldCountOffset);
275  writer.writeUInt32LE(fieldsWritten);
276  stream.seekp(framingByteOffset);
277  // write framing byte
278  if (!(flags & VorbisCommentFlags::NoFramingByte)) {
279  stream.put(0x01);
280  }
281 }
282 
283 } // namespace TagParser
TagParser::Mp4TagAtomIds::Album
@ Album
Definition: mp4ids.h:86
TagParser::VorbisCommentIds::performer
constexpr TAG_PARSER_EXPORT const char * performer()
Definition: vorbiscommentids.h:66
vorbiscommentids.h
TagParser::MatroskaTagIds::description
constexpr TAG_PARSER_EXPORT const char * description()
Definition: matroskatagid.h:231
TagParser::RawDataType::Utf8
@ Utf8
Definition: mp4tagfield.h:21
TagParser::VorbisCommentFlags
VorbisCommentFlags
The VorbisCommentFlags enum specifies flags which controls parsing and making of Vorbis comments.
Definition: vorbiscommentfield.h:16
TagParser::Mp4TagAtomIds::Lyrics
@ Lyrics
Definition: mp4ids.h:105
TagParser::VorbisCommentIds::grouping
constexpr TAG_PARSER_EXPORT const char * grouping()
Definition: vorbiscommentids.h:46
TagParser::Mp4TagAtomIds::Encoder
@ Encoder
Definition: mp4ids.h:97
CHECK_MAX_SIZE
#define CHECK_MAX_SIZE(sizeDenotation)
Throws TruncatedDataException() if the specified sizeDenotation exceeds maxSize; otherwise maxSize is...
Definition: exceptions.h:70
TagParser::FieldMapBasedTag< VorbisComment >::IdentifierType
typename FieldMapBasedTagTraits< VorbisComment >::FieldType::IdentifierType IdentifierType
Definition: fieldbasedtag.h:36
TagParser::OggIterator
The OggIterator class helps iterating through all segments of an OGG bitstream.
Definition: oggiterator.h:11
vorbiscomment.h
TagParser::Mp4TagAtomIds::Cover
@ Cover
Definition: mp4ids.h:94
TagParser::Mp4TagExtendedNameIds::label
const char * label
Definition: mp4ids.cpp:32
TagParser::MatroskaTagIds::lyrics
constexpr TAG_PARSER_EXPORT const char * lyrics()
Definition: matroskatagid.h:98
TagParser::OggIterator::streamSize
std::uint64_t streamSize() const
Returns the stream size (which has been specified when constructing the iterator).
Definition: oggiterator.h:114
TagParser::VorbisCommentField::make
bool make(CppUtilities::BinaryWriter &writer, VorbisCommentFlags flags, Diagnostics &diag)
Writes the field to a stream using the specified writer.
Definition: vorbiscommentfield.cpp:163
TagParser::MatroskaTagIds::lyricist
constexpr TAG_PARSER_EXPORT const char * lyricist()
Definition: matroskatagid.h:102
TagParser::Diagnostics
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
TagParser::Mp4TagAtomIds::TrackPosition
@ TrackPosition
Definition: mp4ids.h:116
TagParser
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
TagParser::VorbisCommentIds::albumArtist
constexpr TAG_PARSER_EXPORT const char * albumArtist()
Definition: vorbiscommentids.h:42
TagParser::MatroskaTagIds::artist
constexpr TAG_PARSER_EXPORT const char * artist()
Definition: matroskatagid.h:74
TagParser::MatroskaIds::Title
@ Title
Definition: matroskaid.h:54
TagParser::Mp4TagAtomIds::AlbumArtist
@ AlbumArtist
Definition: mp4ids.h:87
TagParser::Mp4TagAtomIds::Year
@ Year
Definition: mp4ids.h:122
TagParser::Failure
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
TagParser::TagValue::isEmpty
bool isEmpty() const
Returns whether no or an empty value is assigned.
Definition: tagvalue.h:464
TagParser::TagField::value
TagValue & value()
Returns the value of the current TagField.
Definition: generictagfield.h:144
TagParser::MatroskaTagIds::encoder
constexpr TAG_PARSER_EXPORT const char * encoder()
Definition: matroskatagid.h:319
TagParser::Mp4TagAtomIds::Lyricist
@ Lyricist
Definition: mp4ids.h:104
TagParser::MatroskaTagIds::partNumber
constexpr TAG_PARSER_EXPORT const char * partNumber()
Definition: matroskatagid.h:27
TagParser::Mp4TagAtomIds::Description
@ Description
Definition: mp4ids.h:95
TagParser::MatroskaTagIds::language
constexpr TAG_PARSER_EXPORT const char * language()
Definition: matroskatagid.h:343
CppUtilities
Definition: abstractcontainer.h:15
TagParser::VorbisCommentIds::trackNumber
constexpr TAG_PARSER_EXPORT const char * trackNumber()
Definition: vorbiscommentids.h:14
TagParser::Mp4TagAtomIds::Genre
@ Genre
Definition: mp4ids.h:101
TagParser::KnownField
KnownField
Specifies the field.
Definition: tag.h:42
TagParser::VorbisCommentIds::date
constexpr TAG_PARSER_EXPORT const char * date()
Definition: vorbiscommentids.h:142
TagParser::Mp4TagAtomIds::Grouping
@ Grouping
Definition: mp4ids.h:102
TagParser::MatroskaTagIds::comment
constexpr TAG_PARSER_EXPORT const char * comment()
Definition: matroskatagid.h:306
TagParser::VorbisCommentIds::cover
constexpr TAG_PARSER_EXPORT const char * cover()
Definition: vorbiscommentids.h:170
TagParser::FieldMapBasedTag
The FieldMapBasedTag provides a generic implementation of Tag which stores the tag fields using std::...
Definition: fieldbasedtag.h:31
TagParser::TagValue
The TagValue class wraps values of different types.
Definition: tagvalue.h:75
TagParser::VorbisCommentField
The VorbisCommentField class is used by VorbisComment to store the fields.
Definition: vorbiscommentfield.h:47
TagParser::VorbisCommentIds::year
constexpr TAG_PARSER_EXPORT const char * year()
Definition: vorbiscommentids.h:146
TagParser::MatroskaTagIds::album
constexpr TAG_PARSER_EXPORT const char * album()
Definition: matroskatagid.h:78
TagParser::Mp4TagAtomIds::Artist
@ Artist
Definition: mp4ids.h:88
TagParser::MatroskaTagIds::composer
constexpr TAG_PARSER_EXPORT const char * composer()
Definition: matroskatagid.h:90
TagParser::MatroskaTagIds::title
constexpr TAG_PARSER_EXPORT const char * title()
Definition: matroskatagid.h:36
TagParser::Mp4TagAtomIds::RecordLabel
@ RecordLabel
Definition: mp4ids.h:114
TagParser::MatroskaTagIds::encoderSettings
constexpr TAG_PARSER_EXPORT const char * encoderSettings()
Definition: matroskatagid.h:323
TagParser::MatroskaTagIds::genre
constexpr TAG_PARSER_EXPORT const char * genre()
Definition: matroskatagid.h:211
TagParser::Mp4TagAtomIds::DiskPosition
@ DiskPosition
Definition: mp4ids.h:96
TagParser::Mp4TagAtomIds::Composer
@ Composer
Definition: mp4ids.h:92
TagParser::Mp4TagAtomIds::Comment
@ Comment
Definition: mp4ids.h:91
TagParser::Mp4TagAtomIds::Performers
@ Performers
Definition: mp4ids.h:107
TagParser::VorbisCommentIds::diskNumber
constexpr TAG_PARSER_EXPORT const char * diskNumber()
Definition: vorbiscommentids.h:18