Tag Parser  9.2.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  { title(), KnownField::Title },
110  { genre(), KnownField::Genre },
113  { partNumber(), KnownField::PartNumber },
116  { encoderSettings(), KnownField::EncoderSettings },
122  { lyrics(), KnownField::Lyrics },
124  });
125  // clang-format on
126  const auto knownField(fieldMap.find(id));
127  return knownField != fieldMap.cend() ? knownField->second : KnownField::Invalid;
128 }
129 
133 template <class StreamType> void VorbisComment::internalParse(StreamType &stream, std::uint64_t maxSize, VorbisCommentFlags flags, Diagnostics &diag)
134 {
135  // prepare parsing
136  static const string context("parsing Vorbis comment");
137  std::uint64_t startOffset = static_cast<std::uint64_t>(stream.tellg());
138  try {
139  // read signature: 0x3 + "vorbis"
140  char sig[8];
141  bool skipSignature = flags & VorbisCommentFlags::NoSignature;
142  if (!skipSignature) {
143  CHECK_MAX_SIZE(7)
144  stream.read(sig, 7);
145  skipSignature = (BE::toUInt64(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
146  }
147  if (skipSignature) {
148  // read vendor (length prefixed string)
149  {
150  CHECK_MAX_SIZE(4)
151  stream.read(sig, 4);
152  const auto vendorSize = LE::toUInt32(sig);
153  if (vendorSize <= maxSize) {
154  auto buff = make_unique<char[]>(vendorSize);
155  stream.read(buff.get(), vendorSize);
156  m_vendor.assignData(move(buff), vendorSize, TagDataType::Text, TagTextEncoding::Utf8);
157  // TODO: Is the vendor string actually UTF-8 (like the field values)?
158  } else {
159  diag.emplace_back(DiagLevel::Critical, "Vendor information is truncated.", context);
160  throw TruncatedDataException();
161  }
162  maxSize -= vendorSize;
163  }
164  // read field count
165  CHECK_MAX_SIZE(4)
166  stream.read(sig, 4);
167  std::uint32_t fieldCount = LE::toUInt32(sig);
168  for (std::uint32_t i = 0; i < fieldCount; ++i) {
169  // read fields
170  VorbisCommentField field;
171  try {
172  field.parse(stream, maxSize, diag);
173  fields().emplace(field.id(), move(field));
174  } catch (const TruncatedDataException &) {
175  throw;
176  } catch (const Failure &) {
177  // nothing to do here since notifications will be added anyways
178  }
179  }
180  if (!(flags & VorbisCommentFlags::NoFramingByte)) {
181  stream.ignore(); // skip framing byte
182  }
183  m_size = static_cast<std::uint32_t>(static_cast<std::uint64_t>(stream.tellg()) - startOffset);
184  } else {
185  diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
186  throw InvalidDataException();
187  }
188  } catch (const TruncatedDataException &) {
189  m_size = static_cast<std::uint32_t>(static_cast<std::uint64_t>(stream.tellg()) - startOffset);
190  diag.emplace_back(DiagLevel::Critical, "Vorbis comment is truncated.", context);
191  throw;
192  }
193 }
194 
202 void VorbisComment::parse(OggIterator &iterator, VorbisCommentFlags flags, Diagnostics &diag)
203 {
204  internalParse(iterator, iterator.streamSize(), flags, diag);
205 }
206 
214 void VorbisComment::parse(istream &stream, std::uint64_t maxSize, VorbisCommentFlags flags, Diagnostics &diag)
215 {
216  internalParse(stream, maxSize, flags, diag);
217 }
218 
226 void VorbisComment::make(std::ostream &stream, VorbisCommentFlags flags, Diagnostics &diag)
227 {
228  // prepare making
229  static const string context("making Vorbis comment");
230  string vendor;
231  try {
232  m_vendor.toString(vendor);
233  } catch (const ConversionException &) {
234  diag.emplace_back(DiagLevel::Warning, "Can not convert the assigned vendor to string.", context);
235  }
236  BinaryWriter writer(&stream);
237  if (!(flags & VorbisCommentFlags::NoSignature)) {
238  // write signature
239  static const char sig[7] = { 0x03, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73 };
240  stream.write(sig, sizeof(sig));
241  }
242  // write vendor
243  writer.writeUInt32LE(vendor.size());
244  writer.writeString(vendor);
245  // write field count later
246  const auto fieldCountOffset = stream.tellp();
247  writer.writeUInt32LE(0);
248  // write fields
249  std::uint32_t fieldsWritten = 0;
250  for (auto i : fields()) {
251  VorbisCommentField &field = i.second;
252  if (!field.value().isEmpty()) {
253  try {
254  if (field.make(writer, flags, diag)) {
255  ++fieldsWritten;
256  }
257  } catch (const Failure &) {
258  }
259  }
260  }
261  // write field count
262  const auto framingByteOffset = stream.tellp();
263  stream.seekp(fieldCountOffset);
264  writer.writeUInt32LE(fieldsWritten);
265  stream.seekp(framingByteOffset);
266  // write framing byte
267  if (!(flags & VorbisCommentFlags::NoFramingByte)) {
268  stream.put(0x01);
269  }
270 }
271 
272 } // 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:166
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. It is meant to be assigned to a tag field.
Definition: tagvalue.h:75
TagParser::VorbisCommentField
The VorbisCommentField class is used by VorbisComment to store the fields.
Definition: vorbiscommentfield.h:47
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