Tag Parser  6.4.1
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
id3v1tag.cpp
Go to the documentation of this file.
1 #include "./id3v1tag.h"
2 #include "./id3genres.h"
3 
4 #include "../exceptions.h"
5 
6 #include <c++utilities/conversion/conversionexception.h>
7 
8 #include <cstring>
9 
10 using namespace std;
11 using namespace ConversionUtilities;
12 
13 namespace Media {
14 
23 Id3v1Tag::Id3v1Tag()
24 {}
25 
26 TagType Id3v1Tag::type() const
27 {
28  return TagType::Id3v1Tag;
29 }
30 
31 const char *Id3v1Tag::typeName() const
32 {
33  return "ID3v1 tag";
34 }
35 
36 bool Id3v1Tag::canEncodingBeUsed(TagTextEncoding encoding) const
37 {
38  return Tag::canEncodingBeUsed(encoding);
39 }
40 
49 void Id3v1Tag::parse(std::istream &stream, bool autoSeek)
50 {
51  invalidateStatus();
52  static const string context("parsing ID3v1 tag");
53  if(autoSeek) {
54  stream.seekg(-128, ios_base::end);
55  }
56  char buffer[128];
57  stream.read(buffer, 128);
58  if(buffer[0] == 0x54
59  && buffer[1] == 0x41
60  && buffer[2] == 0x47) {
61  m_size = 128;
62  readValue(m_title, 30, buffer + 3);
63  readValue(m_artist, 30, buffer + 33);
64  readValue(m_album, 30, buffer + 63);
65  readValue(m_year, 4, buffer + 93);
66  if(buffer[125] == 0) {
67  readValue(m_comment, 28, buffer + 97);
68  m_version = "1.1";
69  } else {
70  readValue(m_comment, 30, buffer + 97);
71  m_version = "1.0";
72  }
73  readValue(m_comment, buffer[125] == 0 ? 28 : 30, buffer + 97);
74  if(buffer[125] == 0) {
75  m_trackPos.assignPosition(PositionInSet(*reinterpret_cast<char *>(buffer + 126), 0));
76  }
77  m_genre.assignStandardGenreIndex(*reinterpret_cast<unsigned char *>(buffer + 127));
78  } else {
79  addNotification(NotificationType::Critical, "\"TAG\" identifier not found.", context);
80  throw NoDataFoundException();
81  }
82 }
83 
91 void Id3v1Tag::parse(std::iostream &stream)
92 {
93  return parse(stream, false);
94 }
95 
103 void Id3v1Tag::make(ostream &stream)
104 {
105  invalidateStatus();
106  static const string context("making ID3v1 tag");
107  char buffer[30];
108  buffer[0] = 0x54;
109  buffer[1] = 0x41;
110  buffer[2] = 0x47;
111  stream.write(buffer, 3);
112  // write text fields
113  writeValue(m_title, 30, buffer, stream);
114  writeValue(m_artist, 30, buffer, stream);
115  writeValue(m_album, 30, buffer, stream);
116  writeValue(m_year, 4, buffer, stream);
117  writeValue(m_comment, 28, buffer, stream);
118  // write numeric fields
119  buffer[0] = 0x0; // empty byte
120  buffer[1] = 0x0; // track nr
121  buffer[2] = 0x0; // genre
122  // track
123  try {
124  if(!m_trackPos.isEmpty() && m_trackPos.type() == TagDataType::PositionInSet)
125  buffer[1] = m_trackPos.toPositionInSet().position();
126  } catch(const ConversionException &) {
127  addNotification(NotificationType::Warning, "Track position field can not be set because given value can not be converted appropriately.", context);
128  }
129  // genre
130  try {
131  buffer[2] = m_genre.toStandardGenreIndex();
132  } catch(const ConversionException &) {
133  addNotification(NotificationType::Warning, "Genre field can not be set because given value can not be converted appropriately.", context);
134  }
135  stream.write(buffer, 3);
136  stream.flush();
137 }
138 
139 const TagValue &Id3v1Tag::value(KnownField field) const
140 {
141  switch(field) {
142  case KnownField::Title:
143  return m_title;
144  case KnownField::Artist:
145  return m_artist;
146  case KnownField::Album:
147  return m_album;
148  case KnownField::Year:
149  return m_year;
150  case KnownField::Comment:
151  return m_comment;
153  return m_trackPos;
154  case KnownField::Genre:
155  return m_genre;
156  default:
157  return TagValue::empty();
158  }
159 }
160 
161 bool Id3v1Tag::setValue(KnownField field, const TagValue &value)
162 {
163  switch(field) {
164  case KnownField::Title:
165  m_title = value;
166  break;
167  case KnownField::Artist:
168  m_artist = value;
169  break;
170  case KnownField::Album:
171  m_album = value;
172  break;
173  case KnownField::Year:
174  m_year = value;
175  break;
176  case KnownField::Comment:
177  m_comment = value;
178  break;
180  m_trackPos = value;
181  break;
182  case KnownField::Genre:
183  m_genre = value;
184  break;
185  default:
186  return false;
187  }
188  return true;
189 }
190 
191 bool Id3v1Tag::setValueConsideringTypeInfo(KnownField field, const TagValue &value, const string &)
192 {
193  return setValue(field, value);
194 }
195 
196 bool Id3v1Tag::hasField(KnownField field) const
197 {
198  switch(field) {
199  case KnownField::Title:
200  return !m_title.isEmpty();
201  case KnownField::Artist:
202  return !m_artist.isEmpty();
203  case KnownField::Album:
204  return !m_album.isEmpty();
205  case KnownField::Year:
206  return !m_year.isEmpty();
207  case KnownField::Comment:
208  return !m_comment.isEmpty();
210  return !m_trackPos.isEmpty();
211  case KnownField::Genre:
212  return !m_genre.isEmpty();
213  default:
214  return false;
215  }
216 }
217 
218 void Id3v1Tag::removeAllFields()
219 {
220  m_title.clearDataAndMetadata();
221  m_artist.clearDataAndMetadata();
222  m_album.clearDataAndMetadata();
223  m_year.clearDataAndMetadata();
224  m_comment.clearDataAndMetadata();
225  m_trackPos.clearDataAndMetadata();
226  m_genre.clearDataAndMetadata();
227 }
228 
229 unsigned int Id3v1Tag::fieldCount() const
230 {
231  int count = 0;
232  for(const auto &value : {m_title, m_artist, m_album,
233  m_year, m_comment, m_trackPos, m_genre}) {
234  if(!value.isEmpty()) {
235  ++count;
236  }
237  }
238  return count;
239 }
240 
241 bool Id3v1Tag::supportsField(KnownField field) const
242 {
243  switch(field) {
244  case KnownField::Title:
245  case KnownField::Artist:
246  case KnownField::Album:
247  case KnownField::Year:
248  case KnownField::Comment:
250  case KnownField::Genre:
251  return true;
252  default:
253  return false;
254  }
255 }
256 
257 void Id3v1Tag::ensureTextValuesAreProperlyEncoded()
258 {
259  m_title.convertDataEncodingForTag(this);
260  m_artist.convertDataEncodingForTag(this);
261  m_album.convertDataEncodingForTag(this);
262  m_year.convertDataEncodingForTag(this);
263  m_comment.convertDataEncodingForTag(this);
264  m_trackPos.convertDataEncodingForTag(this);
265  m_genre.convertDataEncodingForTag(this);
266 }
267 
271 void Id3v1Tag::readValue(TagValue &value, size_t maxLength, const char *buffer)
272 {
273  const char *end = buffer + maxLength - 1;
274  while((*end == 0x0 || *end == ' ') && end >= buffer) {
275  --end;
276  --maxLength;
277  }
278  value.assignData(buffer, maxLength, TagDataType::Text, TagTextEncoding::Latin1);
279 }
280 
281 
285 void Id3v1Tag::writeValue(const TagValue &value, size_t length, char *buffer, ostream &targetStream)
286 {
287  memset(buffer, 0, length);
288  try {
289  value.toString().copy(buffer, length);
290  } catch(const ConversionException &) {
291  addNotification(NotificationType::Warning, "Field can not be set because given value can not be converted appropriately.", "making ID3v1 tag field");
292  }
293  targetStream.write(buffer, length);
294 }
295 
296 }
The TagValue class wraps values of different types.
Definition: tagvalue.h:64
KnownField
Specifies the field.
Definition: tag.h:42
STL namespace.
TagTextEncoding
Specifies the text encoding.
Definition: tagvalue.h:22
The PositionInSet class describes the position of an element in a set which consists of a certain num...
Definition: positioninset.h:20
void assignData(const char *data, size_t length, TagDataType type=TagDataType::Binary, TagTextEncoding encoding=TagTextEncoding::Latin1)
Assigns a copy of the given data.
Definition: tagvalue.cpp:648
TagType
Specifies the tag type.
Definition: tag.h:21
The exception that is thrown when the data to be parsed holds no parsable information.
Definition: exceptions.h:19
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
std::string toString(TagTextEncoding encoding=TagTextEncoding::Unspecified) const
Converts the value of the current TagValue object to its equivalent std::string representation.
Definition: tagvalue.h:320