2 * id3tag.c -- Write ID3 version 1 and 2 tags.
4 * Copyright (C) 2000 Don Melton
5 * Copyright (C) 2011 Robert Hegemann
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
23 * HISTORY: This source file is part of LAME (see http://www.mp3dev.org)
24 * and was originally adapted by Conrad Sanderson <c.sanderson@me.gu.edu.au>
25 * from mp3info by Ricardo Cerqueira <rmc@rccn.net> to write only ID3 version 1
26 * tags. Don Melton <don@blivet.com> COMPLETELY rewrote it to support version
27 * 2 tags and be more conformant to other standards while remaining flexible.
29 * NOTE: See http://id3.org/ for more information about ID3 tag formats.
32 /* $Id: id3tag.c,v 1.75.2.1 2011/11/26 18:15:27 robert Exp $ */
46 # define strrchr rindex
48 char *strchr(), *strrchr();
50 # define memcpy(d, s, n) bcopy ((s), (d), (n))
59 #include "lame_global_flags.h"
61 #include "bitstream.h"
64 static const char *const genre_names[] = {
66 * NOTE: The spelling of these genre names is identical to those found in
69 "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge",
70 "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B",
71 "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska",
72 "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
73 "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental",
74 "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock",
75 "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
76 "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
77 "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
78 "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle",
79 "Native US", "Cabaret", "New Wave", "Psychedelic", "Rave",
80 "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz",
81 "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk",
82 "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin",
83 "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
84 "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
85 "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech",
86 "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass",
87 "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
88 "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
89 "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall",
90 "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie",
91 "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta",
92 "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian",
93 "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
97 #define GENRE_NAME_COUNT \
98 ((int)(sizeof genre_names / sizeof (const char *const)))
100 static const int genre_alpha_map[] = {
101 123, 34, 74, 73, 99, 20, 40, 26, 145, 90, 116, 41, 135, 85, 96, 138, 89, 0,
102 107, 132, 65, 88, 104, 102, 97, 136, 61, 141, 32, 1, 112, 128, 57, 140, 2,
103 139, 58, 3, 125, 50, 22, 4, 55, 127, 122, 120, 98, 52, 48, 54, 124, 25, 84,
104 80, 115, 81, 119, 5, 30, 36, 59, 126, 38, 49, 91, 6, 129, 79, 137, 7, 35,
105 100, 131, 19, 33, 46, 47, 8, 29, 146, 63, 86, 71, 45, 142, 9, 77, 82, 64,
106 133, 10, 66, 39, 11, 103, 12, 75, 134, 13, 53, 62, 109, 117, 23, 108, 92,
107 67, 93, 43, 121, 15, 68, 14, 16, 76, 87, 118, 17, 78, 143, 114, 110, 69, 21,
108 111, 95, 105, 42, 37, 24, 56, 44, 101, 83, 94, 106, 147, 113, 18, 51, 130,
109 144, 60, 70, 31, 72, 27, 28
112 #define GENRE_ALPHA_COUNT ((int)(sizeof genre_alpha_map / sizeof (int)))
114 #define GENRE_INDEX_OTHER 12
117 #define FRAME_ID(a, b, c, d) \
118 ( ((unsigned long)(a) << 24) \
119 | ((unsigned long)(b) << 16) \
120 | ((unsigned long)(c) << 8) \
121 | ((unsigned long)(d) << 0) )
123 typedef enum UsualStringIDs { ID_TITLE = FRAME_ID('T', 'I', 'T', '2')
124 , ID_ARTIST = FRAME_ID('T', 'P', 'E', '1')
125 , ID_ALBUM = FRAME_ID('T', 'A', 'L', 'B')
126 , ID_GENRE = FRAME_ID('T', 'C', 'O', 'N')
127 , ID_ENCODER = FRAME_ID('T', 'S', 'S', 'E')
128 , ID_PLAYLENGTH = FRAME_ID('T', 'L', 'E', 'N')
129 , ID_COMMENT = FRAME_ID('C', 'O', 'M', 'M') /* full text string */
132 typedef enum NumericStringIDs { ID_DATE = FRAME_ID('T', 'D', 'A', 'T') /* "ddMM" */
133 , ID_TIME = FRAME_ID('T', 'I', 'M', 'E') /* "hhmm" */
134 , ID_TPOS = FRAME_ID('T', 'P', 'O', 'S') /* '0'-'9' and '/' allowed */
135 , ID_TRACK = FRAME_ID('T', 'R', 'C', 'K') /* '0'-'9' and '/' allowed */
136 , ID_YEAR = FRAME_ID('T', 'Y', 'E', 'R') /* "yyyy" */
139 typedef enum MiscIDs { ID_TXXX = FRAME_ID('T', 'X', 'X', 'X')
140 , ID_WXXX = FRAME_ID('W', 'X', 'X', 'X')
141 , ID_SYLT = FRAME_ID('S', 'Y', 'L', 'T')
142 , ID_APIC = FRAME_ID('A', 'P', 'I', 'C')
143 , ID_GEOB = FRAME_ID('G', 'E', 'O', 'B')
144 , ID_PCNT = FRAME_ID('P', 'C', 'N', 'T')
145 , ID_AENC = FRAME_ID('A', 'E', 'N', 'C')
146 , ID_LINK = FRAME_ID('L', 'I', 'N', 'K')
147 , ID_ENCR = FRAME_ID('E', 'N', 'C', 'R')
148 , ID_GRID = FRAME_ID('G', 'R', 'I', 'D')
149 , ID_PRIV = FRAME_ID('P', 'R', 'I', 'V')
150 , ID_VSLT = FRAME_ID('V', 'S', 'L', 'T') /* full text string */
155 frame_id_matches(int id, int mask)
157 int result = 0, i, window = 0xff;
158 for (i = 0; i < 4; ++i, window <<= 8) {
159 int const mw = (mask & window);
160 int const iw = (id & window);
161 if (mw != 0 && mw != iw) {
169 isFrameIdMatching(int id, int mask)
171 return frame_id_matches(id, mask) == 0 ? 1 : 0;
175 test_tag_spec_flags(lame_internal_flags const *gfc, unsigned int tst)
177 return (gfc->tag_spec.flags & tst) != 0u ? 1 : 0;
182 debug_tag_spec_flags(lame_internal_flags * gfc, const char* info)
184 MSGF(gfc, "%s\n", info);
185 MSGF(gfc, "CHANGED_FLAG : %d\n", test_tag_spec_flags(gfc, CHANGED_FLAG ));
186 MSGF(gfc, "ADD_V2_FLAG : %d\n", test_tag_spec_flags(gfc, ADD_V2_FLAG ));
187 MSGF(gfc, "V1_ONLY_FLAG : %d\n", test_tag_spec_flags(gfc, V1_ONLY_FLAG ));
188 MSGF(gfc, "V2_ONLY_FLAG : %d\n", test_tag_spec_flags(gfc, V2_ONLY_FLAG ));
189 MSGF(gfc, "SPACE_V1_FLAG : %d\n", test_tag_spec_flags(gfc, SPACE_V1_FLAG));
190 MSGF(gfc, "PAD_V2_FLAG : %d\n", test_tag_spec_flags(gfc, PAD_V2_FLAG ));
197 id3v2_add_ucs2(lame_internal_flags * gfc, uint32_t frame_id, char const *lang,
198 unsigned short const *desc, unsigned short const *text);
200 id3v2_add_latin1(lame_internal_flags * gfc, uint32_t frame_id, char const *lang, char const *desc,
204 copyV1ToV2(lame_internal_flags * gfc, int frame_id, char const *s)
206 unsigned int flags = gfc->tag_spec.flags;
207 id3v2_add_latin1(gfc, frame_id, 0, 0, s);
208 gfc->tag_spec.flags = flags;
210 debug_tag_spec_flags(gfc, "copyV1ToV2");
216 id3v2AddLameVersion(lame_internal_flags * gfc)
219 const char *b = get_lame_os_bitness();
220 const char *v = get_lame_version();
221 const char *u = get_lame_url();
222 const size_t lenb = strlen(b);
225 sprintf(buffer, "LAME %s version %s (%s)", b, v, u);
228 sprintf(buffer, "LAME version %s (%s)", v, u);
230 copyV1ToV2(gfc, ID_ENCODER, buffer);
234 id3v2AddAudioDuration(lame_internal_flags * gfc, double ms)
236 SessionConfig_t const *const cfg = &gfc->cfg;
238 double const max_ulong = MAX_U_32_NUM;
239 unsigned long playlength_ms;
242 ms /= cfg->samplerate_in;
243 if (ms > max_ulong) {
244 playlength_ms = max_ulong;
252 sprintf(buffer, "%lu", playlength_ms);
253 copyV1ToV2(gfc, ID_PLAYLENGTH, buffer);
257 id3tag_genre_list(void (*handler) (int, const char *, void *), void *cookie)
261 for (i = 0; i < GENRE_NAME_COUNT; ++i) {
262 if (i < GENRE_ALPHA_COUNT) {
263 int j = genre_alpha_map[i];
264 handler(j, genre_names[j], cookie);
270 #define GENRE_NUM_UNKNOWN 255
275 id3tag_init(lame_global_flags * gfp)
277 lame_internal_flags *gfc = gfp->internal_flags;
279 memset(&gfc->tag_spec, 0, sizeof gfc->tag_spec);
280 gfc->tag_spec.genre_id3v1 = GENRE_NUM_UNKNOWN;
281 gfc->tag_spec.padding_size = 128;
282 id3v2AddLameVersion(gfc);
288 id3tag_add_v2(lame_global_flags * gfp)
290 lame_internal_flags *gfc = gfp->internal_flags;
291 gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
292 gfc->tag_spec.flags |= ADD_V2_FLAG;
296 id3tag_v1_only(lame_global_flags * gfp)
298 lame_internal_flags *gfc = gfp->internal_flags;
299 gfc->tag_spec.flags &= ~(ADD_V2_FLAG | V2_ONLY_FLAG);
300 gfc->tag_spec.flags |= V1_ONLY_FLAG;
304 id3tag_v2_only(lame_global_flags * gfp)
306 lame_internal_flags *gfc = gfp->internal_flags;
307 gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
308 gfc->tag_spec.flags |= V2_ONLY_FLAG;
312 id3tag_space_v1(lame_global_flags * gfp)
314 lame_internal_flags *gfc = gfp->internal_flags;
315 gfc->tag_spec.flags &= ~V2_ONLY_FLAG;
316 gfc->tag_spec.flags |= SPACE_V1_FLAG;
320 id3tag_pad_v2(lame_global_flags * gfp)
322 id3tag_set_pad(gfp, 128);
326 id3tag_set_pad(lame_global_flags * gfp, size_t n)
328 lame_internal_flags *gfc = gfp->internal_flags;
329 gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
330 gfc->tag_spec.flags |= PAD_V2_FLAG;
331 gfc->tag_spec.flags |= ADD_V2_FLAG;
332 gfc->tag_spec.padding_size = n;
336 hasUcs2ByteOrderMarker(unsigned short bom)
338 if (bom == 0xFFFEu || bom == 0xFEFFu) {
345 static unsigned short
346 swap_bytes(unsigned short w)
348 return (0xff00u & (w << 8)) | (0x00ffu & (w >> 8));
352 static unsigned short
353 toLittleEndian(unsigned short bom, unsigned short c)
355 if (bom == 0xFFFEu) {
356 return swap_bytes(c);
361 static unsigned short
362 fromLatin1Char(const unsigned short* s, unsigned short c)
364 if (s[0] == 0xFFFEu) {
365 return swap_bytes(c);
372 local_strdup(char **dst, const char *src)
381 for (n = 0; src[n] != 0; ++n) { /* calc src string length */
383 if (n > 0) { /* string length without zero termination */
384 assert(sizeof(*src) == sizeof(**dst));
385 *dst = malloc((n + 1) * sizeof(**dst));
387 memcpy(*dst, src, n * sizeof(**dst));
397 local_ucs2_strdup(unsigned short **dst, unsigned short const *src)
402 free(*dst); /* free old string pointer */
406 for (n = 0; src[n] != 0; ++n) { /* calc src string length */
408 if (n > 0) { /* string length without zero termination */
409 assert(sizeof(*src) >= 2);
410 assert(sizeof(*src) == sizeof(**dst));
411 *dst = malloc((n + 1) * sizeof(**dst));
413 memcpy(*dst, src, n * sizeof(**dst));
424 local_ucs2_strlen(unsigned short const *s)
437 local_ucs2_substr(unsigned short** dst, unsigned short const* src, size_t start, size_t end)
439 size_t const len = 1 + 1 + ((start < end) ? (end - start) : 0);
441 unsigned short *ptr = calloc(len, sizeof(ptr[0]));
443 if (ptr == 0 || src == 0) {
446 if (hasUcs2ByteOrderMarker(src[0])) {
452 while (start < end) {
453 ptr[n++] = src[start++];
460 local_ucs2_pos(unsigned short const* str, unsigned short c)
463 for (i = 0; str != 0 && str[i] != 0; ++i) {
472 maybeLatin1(unsigned short const* text)
475 unsigned short bom = *text++;
477 unsigned short c = toLittleEndian(bom, *text++);
478 if (c > 0x00fe) return 0;
484 static int searchGenre(char const* genre);
485 static int sloppySearchGenre(char const* genre);
488 lookupGenre(char const* genre)
491 int num = strtol(genre, &str, 10);
492 /* is the input a string or a valid number? */
494 num = searchGenre(genre);
495 if (num == GENRE_NAME_COUNT) {
496 num = sloppySearchGenre(genre);
498 if (num == GENRE_NAME_COUNT) {
499 return -2; /* no common genre text found */
503 if ((num < 0) || (num >= GENRE_NAME_COUNT)) {
504 return -1; /* number unknown */
510 static unsigned char *
511 writeLoBytes(unsigned char *frame, unsigned short const *str, size_t n);
514 local_strdup_utf16_to_latin1(unsigned short const* utf16)
516 size_t len = local_ucs2_strlen(utf16);
517 unsigned char* latin1 = calloc(len+1, 1);
518 writeLoBytes(latin1, utf16, len);
519 return (char*)latin1;
524 id3tag_set_genre_utf16(lame_t gfp, unsigned short const* text)
526 lame_internal_flags* gfc = gfp->internal_flags;
531 if (!hasUcs2ByteOrderMarker(text[0])) {
534 if (maybeLatin1(text)) {
535 char* latin1 = local_strdup_utf16_to_latin1(text);
536 int num = lookupGenre(latin1);
538 if (num == -1) return -1; /* number out of range */
539 if (num >= 0) { /* common genre found */
540 gfc->tag_spec.flags |= CHANGED_FLAG;
541 gfc->tag_spec.genre_id3v1 = num;
542 copyV1ToV2(gfc, ID_GENRE, genre_names[num]);
546 ret = id3v2_add_ucs2(gfp->internal_flags, ID_GENRE, 0, 0, text);
548 gfc->tag_spec.flags |= CHANGED_FLAG;
549 gfc->tag_spec.genre_id3v1 = GENRE_INDEX_OTHER;
555 Some existing options for ID3 tag can be specified by --tv option
557 --tt <value>, --tv TIT2=value
558 --ta <value>, --tv TPE1=value
559 --tl <value>, --tv TALB=value
560 --ty <value>, --tv TYER=value
561 --tn <value>, --tv TRCK=value
562 --tg <value>, --tv TCON=value
563 (although some are not exactly same)*/
566 id3tag_set_albumart(lame_global_flags * gfp, const char *image, size_t size)
569 unsigned char const *data = (unsigned char const *) image;
570 lame_internal_flags *gfc = gfp->internal_flags;
572 /* determine MIME type from the actual image data */
573 if (2 < size && data[0] == 0xFF && data[1] == 0xD8) {
574 mimetype = MIMETYPE_JPEG;
576 else if (4 < size && data[0] == 0x89 && strncmp((const char *) &data[1], "PNG", 3) == 0) {
577 mimetype = MIMETYPE_PNG;
579 else if (4 < size && strncmp((const char *) data, "GIF8", 4) == 0) {
580 mimetype = MIMETYPE_GIF;
585 if (gfc->tag_spec.albumart != 0) {
586 free(gfc->tag_spec.albumart);
587 gfc->tag_spec.albumart = 0;
588 gfc->tag_spec.albumart_size = 0;
589 gfc->tag_spec.albumart_mimetype = MIMETYPE_NONE;
594 gfc->tag_spec.albumart = malloc(size);
595 if (gfc->tag_spec.albumart != 0) {
596 memcpy(gfc->tag_spec.albumart, image, size);
597 gfc->tag_spec.albumart_size = size;
598 gfc->tag_spec.albumart_mimetype = mimetype;
599 gfc->tag_spec.flags |= CHANGED_FLAG;
605 static unsigned char *
606 set_4_byte_value(unsigned char *bytes, uint32_t value)
609 for (i = 3; i >= 0; --i) {
610 bytes[i] = value & 0xffUL;
617 toID3v2TagId(char const *s)
619 unsigned int i, x = 0;
623 for (i = 0; i < 4 && s[i] != 0; ++i) {
625 unsigned int const u = 0x0ff & c;
628 if (c < 'A' || 'Z' < c) {
629 if (c < '0' || '9' < c) {
638 toID3v2TagId_ucs2(unsigned short const *s)
640 unsigned int i, x = 0;
641 unsigned short bom = 0;
646 if (hasUcs2ByteOrderMarker(bom)) {
649 for (i = 0; i < 4 && s[i] != 0; ++i) {
650 unsigned short const c = toLittleEndian(bom, s[i]);
651 if (c < 'A' || 'Z' < c) {
652 if (c < '0' || '9' < c) {
664 isNumericString(uint32_t frame_id)
679 isMultiFrame(uint32_t frame_id)
701 isFullTextString(int frame_id)
712 static FrameDataNode *
713 findNode(id3tag_spec const *tag, uint32_t frame_id, FrameDataNode const *last)
715 FrameDataNode *node = last ? last->nxt : tag->v2_head;
717 if (node->fid == frame_id) {
726 appendNode(id3tag_spec * tag, FrameDataNode * node)
728 if (tag->v2_tail == 0 || tag->v2_head == 0) {
733 tag->v2_tail->nxt = node;
739 setLang(char *dst, char const *src)
742 if (src == 0 || src[0] == 0) {
748 for (i = 0; i < 3 && src && *src; ++i) {
758 isSameLang(char const *l1, char const *l2)
763 for (i = 0; i < 3; ++i) {
764 char a = tolower(l1[i]);
765 char b = tolower(d[i]);
778 isSameDescriptor(FrameDataNode const *node, char const *dsc)
781 if (node->dsc.enc == 1 && node->dsc.dim > 0) {
784 for (i = 0; i < node->dsc.dim; ++i) {
785 if (!dsc || node->dsc.ptr.l[i] != dsc[i]) {
793 isSameDescriptorUcs2(FrameDataNode const *node, unsigned short const *dsc)
796 if (node->dsc.enc != 1 && node->dsc.dim > 0) {
799 for (i = 0; i < node->dsc.dim; ++i) {
800 if (!dsc || node->dsc.ptr.u[i] != dsc[i]) {
808 id3v2_add_ucs2(lame_internal_flags * gfc, uint32_t frame_id, char const *lang,
809 unsigned short const *desc, unsigned short const *text)
812 FrameDataNode *node = 0;
813 node = findNode(&gfc->tag_spec, frame_id, 0);
814 if (isMultiFrame(frame_id)) {
816 if (isSameLang(node->lng, lang)) {
817 if (isSameDescriptorUcs2(node, desc)) {
821 node = findNode(&gfc->tag_spec, frame_id, node);
825 node = calloc(1, sizeof(FrameDataNode));
827 return -254; /* memory problem */
829 appendNode(&gfc->tag_spec, node);
831 node->fid = frame_id;
832 setLang(node->lng, lang);
833 node->dsc.dim = local_ucs2_strdup(&node->dsc.ptr.u, desc);
835 node->txt.dim = local_ucs2_strdup(&node->txt.ptr.u, text);
837 gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
843 id3v2_add_latin1(lame_internal_flags * gfc, uint32_t frame_id, char const *lang, char const *desc,
847 FrameDataNode *node = 0;
848 node = findNode(&gfc->tag_spec, frame_id, 0);
849 if (isMultiFrame(frame_id)) {
851 if (isSameLang(node->lng, lang)) {
852 if (isSameDescriptor(node, desc)) {
856 node = findNode(&gfc->tag_spec, frame_id, node);
860 node = calloc(1, sizeof(FrameDataNode));
862 return -254; /* memory problem */
864 appendNode(&gfc->tag_spec, node);
866 node->fid = frame_id;
867 setLang(node->lng, lang);
868 node->dsc.dim = local_strdup(&node->dsc.ptr.l, desc);
870 node->txt.dim = local_strdup(&node->txt.ptr.l, text);
872 gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
879 id3tag_set_userinfo_latin1(lame_internal_flags* gfc, uint32_t id, char const *fieldvalue)
883 local_strdup(&dsc, fieldvalue);
892 rc = id3v2_add_latin1(gfc, id, "XXX", dsc, val);
898 id3tag_set_userinfo_ucs2(lame_internal_flags* gfc, uint32_t id, unsigned short const *fieldvalue)
901 unsigned short const separator = fromLatin1Char(fieldvalue,'=');
902 b = local_ucs2_strlen(fieldvalue);
903 a = local_ucs2_pos(fieldvalue, separator);
904 if (a >= 0 && a <= b) {
905 unsigned short* dsc = 0, *val = 0;
906 local_ucs2_substr(&dsc, fieldvalue, 0, a);
907 local_ucs2_substr(&val, fieldvalue, a+1, b);
908 rc = id3v2_add_ucs2(gfc, id, "XXX", dsc, val);
916 id3tag_set_textinfo_utf16(lame_global_flags * gfp, char const *id, unsigned short const *text)
918 uint32_t const frame_id = toID3v2TagId(id);
922 if (isFrameIdMatching(frame_id, FRAME_ID('T', 0, 0, 0))
923 ||isFrameIdMatching(frame_id, FRAME_ID('W', 0, 0, 0))) {
925 if (isNumericString(frame_id)) {
926 return -2; /* must be Latin-1 encoded */
932 if (!hasUcs2ByteOrderMarker(text[0])) {
933 return -3; /* BOM missing */
936 if (frame_id == ID_TXXX || frame_id == ID_WXXX) {
937 return id3tag_set_userinfo_ucs2(gfp->internal_flags, frame_id, text);
939 if (frame_id == ID_GENRE) {
940 return id3tag_set_genre_utf16(gfp, text);
942 return id3v2_add_ucs2(gfp->internal_flags, frame_id, 0, 0, text);
945 return -255; /* not supported by now */
949 id3tag_set_textinfo_ucs2(lame_global_flags * gfp, char const *id, unsigned short const *text);
952 id3tag_set_textinfo_ucs2(lame_global_flags * gfp, char const *id, unsigned short const *text)
954 return id3tag_set_textinfo_utf16(gfp, id, text);
958 id3tag_set_textinfo_latin1(lame_global_flags * gfp, char const *id, char const *text)
960 uint32_t const frame_id = toID3v2TagId(id);
964 if (isFrameIdMatching(frame_id, FRAME_ID('T', 0, 0, 0))
965 ||isFrameIdMatching(frame_id, FRAME_ID('W', 0, 0, 0))) {
970 if (frame_id == ID_TXXX || frame_id == ID_WXXX) {
971 return id3tag_set_userinfo_latin1(gfp->internal_flags, frame_id, text);
973 return id3v2_add_latin1(gfp->internal_flags, frame_id, 0, 0, text);
976 return -255; /* not supported by now */
981 id3tag_set_comment_latin1(lame_global_flags * gfp, char const *lang, char const *desc,
985 return id3v2_add_latin1(gfp->internal_flags, ID_COMMENT, lang, desc, text);
992 id3tag_set_comment_utf16(lame_global_flags * gfp, char const *lang, unsigned short const *desc,
993 unsigned short const *text)
996 return id3v2_add_ucs2(gfp->internal_flags, ID_COMMENT, lang, desc, text);
1002 id3tag_set_comment_ucs2(lame_global_flags * gfp, char const *lang, unsigned short const *desc,
1003 unsigned short const *text);
1007 id3tag_set_comment_ucs2(lame_global_flags * gfp, char const *lang, unsigned short const *desc,
1008 unsigned short const *text)
1010 return id3tag_set_comment_utf16(gfp, lang, desc, text);
1015 id3tag_set_title(lame_global_flags * gfp, const char *title)
1017 lame_internal_flags *gfc = gfp->internal_flags;
1018 if (title && *title) {
1019 local_strdup(&gfc->tag_spec.title, title);
1020 gfc->tag_spec.flags |= CHANGED_FLAG;
1021 copyV1ToV2(gfc, ID_TITLE, title);
1026 id3tag_set_artist(lame_global_flags * gfp, const char *artist)
1028 lame_internal_flags *gfc = gfp->internal_flags;
1029 if (artist && *artist) {
1030 local_strdup(&gfc->tag_spec.artist, artist);
1031 gfc->tag_spec.flags |= CHANGED_FLAG;
1032 copyV1ToV2(gfc, ID_ARTIST, artist);
1037 id3tag_set_album(lame_global_flags * gfp, const char *album)
1039 lame_internal_flags *gfc = gfp->internal_flags;
1040 if (album && *album) {
1041 local_strdup(&gfc->tag_spec.album, album);
1042 gfc->tag_spec.flags |= CHANGED_FLAG;
1043 copyV1ToV2(gfc, ID_ALBUM, album);
1048 id3tag_set_year(lame_global_flags * gfp, const char *year)
1050 lame_internal_flags *gfc = gfp->internal_flags;
1051 if (year && *year) {
1052 int num = atoi(year);
1056 /* limit a year to 4 digits so it fits in a version 1 tag */
1061 gfc->tag_spec.year = num;
1062 gfc->tag_spec.flags |= CHANGED_FLAG;
1064 copyV1ToV2(gfc, ID_YEAR, year);
1069 id3tag_set_comment(lame_global_flags * gfp, const char *comment)
1071 lame_internal_flags *gfc = gfp->internal_flags;
1072 if (comment && *comment) {
1073 local_strdup(&gfc->tag_spec.comment, comment);
1074 gfc->tag_spec.flags |= CHANGED_FLAG;
1076 uint32_t const flags = gfc->tag_spec.flags;
1077 id3v2_add_latin1(gfc, ID_COMMENT, "XXX", "", comment);
1078 gfc->tag_spec.flags = flags;
1084 id3tag_set_track(lame_global_flags * gfp, const char *track)
1086 char const *trackcount;
1087 lame_internal_flags *gfc = gfp->internal_flags;
1090 if (track && *track) {
1091 int num = atoi(track);
1092 /* check for valid ID3v1 track number range */
1093 if (num < 1 || num > 255) {
1095 ret = -1; /* track number out of ID3v1 range, ignored for ID3v1 */
1096 gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
1099 gfc->tag_spec.track_id3v1 = num;
1100 gfc->tag_spec.flags |= CHANGED_FLAG;
1102 /* Look for the total track count after a "/", same restrictions */
1103 trackcount = strchr(track, '/');
1104 if (trackcount && *trackcount) {
1105 gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
1107 copyV1ToV2(gfc, ID_TRACK, track);
1112 /* would use real "strcasecmp" but it isn't portable */
1114 local_strcasecmp(const char *s1, const char *s2)
1132 const char* nextUpperAlpha(const char* p, char x)
1135 for(c = toupper(*p); *p != 0; c = toupper(*++p)) {
1136 if ('A' <= c && c <= 'Z') {
1147 sloppyCompared(const char* p, const char* q)
1150 p = nextUpperAlpha(p, 0);
1151 q = nextUpperAlpha(q, 0);
1158 if (p[1] == '.') { /* some abbrevation */
1159 while (*q && *q++ != ' ') {
1162 p = nextUpperAlpha(p, cp);
1163 q = nextUpperAlpha(q, cq);
1172 sloppySearchGenre(const char *genre)
1175 for (i = 0; i < GENRE_NAME_COUNT; ++i) {
1176 if (sloppyCompared(genre, genre_names[i])) {
1180 return GENRE_NAME_COUNT;
1185 searchGenre(const char* genre)
1188 for (i = 0; i < GENRE_NAME_COUNT; ++i) {
1189 if (!local_strcasecmp(genre, genre_names[i])) {
1193 return GENRE_NAME_COUNT;
1198 id3tag_set_genre(lame_global_flags * gfp, const char *genre)
1200 lame_internal_flags *gfc = gfp->internal_flags;
1202 if (genre && *genre) {
1203 int const num = lookupGenre(genre);
1204 if (num == -1) return num;
1205 gfc->tag_spec.flags |= CHANGED_FLAG;
1207 gfc->tag_spec.genre_id3v1 = num;
1208 genre = genre_names[num];
1211 gfc->tag_spec.genre_id3v1 = GENRE_INDEX_OTHER;
1212 gfc->tag_spec.flags |= ADD_V2_FLAG;
1214 copyV1ToV2(gfc, ID_GENRE, genre);
1220 static unsigned char *
1221 set_frame_custom(unsigned char *frame, const char *fieldvalue)
1223 if (fieldvalue && *fieldvalue) {
1224 const char *value = fieldvalue + 5;
1225 size_t length = strlen(value);
1226 *frame++ = *fieldvalue++;
1227 *frame++ = *fieldvalue++;
1228 *frame++ = *fieldvalue++;
1229 *frame++ = *fieldvalue++;
1230 frame = set_4_byte_value(frame, (unsigned long) (strlen(value) + 1));
1231 /* clear 2-byte header flags */
1234 /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
1237 *frame++ = *value++;
1244 sizeOfNode(FrameDataNode const *node)
1248 n = 10; /* header size */
1249 n += 1; /* text encoding flag */
1250 switch (node->txt.enc) {
1253 if (node->dsc.dim > 0) {
1254 n += node->dsc.dim + 1;
1259 if (node->dsc.dim > 0) {
1260 n += (node->dsc.dim+1) * 2;
1262 n += node->txt.dim * 2;
1270 sizeOfCommentNode(FrameDataNode const *node)
1274 n = 10; /* header size */
1275 n += 1; /* text encoding flag */
1276 n += 3; /* language */
1277 switch (node->dsc.enc) {
1280 n += 1 + node->dsc.dim;
1283 n += 2 + node->dsc.dim * 2;
1286 switch (node->txt.enc) {
1292 n += node->txt.dim * 2;
1300 sizeOfWxxxNode(FrameDataNode const *node)
1304 n = 10; /* header size */
1305 if (node->dsc.dim > 0) {
1306 n += 1; /* text encoding flag */
1307 switch (node->dsc.enc) {
1310 n += 1 + node->dsc.dim;
1313 n += 2 + node->dsc.dim * 2;
1317 switch (node->txt.enc) {
1323 n += node->txt.dim - 1; /* UCS2 -> Latin1, skip BOM */
1330 static unsigned char *
1331 writeChars(unsigned char *frame, char const *str, size_t n)
1339 static unsigned char *
1340 writeUcs2s(unsigned char *frame, unsigned short const *str, size_t n)
1343 unsigned short const bom = *str;
1345 unsigned short const c = toLittleEndian(bom, *str++);
1346 *frame++ = 0x00ffu & c;
1347 *frame++ = 0x00ffu & (c >> 8);
1353 static unsigned char *
1354 writeLoBytes(unsigned char *frame, unsigned short const *str, size_t n)
1357 unsigned short const bom = *str;
1358 if (hasUcs2ByteOrderMarker(bom)) {
1359 str++; n--; /* skip BOM */
1362 unsigned short const c = toLittleEndian(bom, *str++);
1363 if (c < 0x0020u || 0x00ffu < c) {
1364 *frame++ = 0x0020; /* blank */
1374 static unsigned char *
1375 set_frame_comment(unsigned char *frame, FrameDataNode const *node)
1377 size_t const n = sizeOfCommentNode(node);
1379 frame = set_4_byte_value(frame, node->fid);
1380 frame = set_4_byte_value(frame, (uint32_t) (n - 10));
1381 /* clear 2-byte header flags */
1384 /* encoding descriptor byte */
1385 *frame++ = node->txt.enc == 1 ? 1 : 0;
1386 /* 3 bytes language */
1387 *frame++ = node->lng[0];
1388 *frame++ = node->lng[1];
1389 *frame++ = node->lng[2];
1390 /* descriptor with zero byte(s) separator */
1391 if (node->dsc.enc != 1) {
1392 frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
1396 frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
1400 /* comment full text */
1401 if (node->txt.enc != 1) {
1402 frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
1405 frame = writeUcs2s(frame, node->txt.ptr.u, node->txt.dim);
1411 static unsigned char *
1412 set_frame_custom2(unsigned char *frame, FrameDataNode const *node)
1414 size_t const n = sizeOfNode(node);
1416 frame = set_4_byte_value(frame, node->fid);
1417 frame = set_4_byte_value(frame, (unsigned long) (n - 10));
1418 /* clear 2-byte header flags */
1421 /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
1422 *frame++ = node->txt.enc == 1 ? 1 : 0;
1423 if (node->dsc.dim > 0) {
1424 if (node->dsc.enc != 1) {
1425 frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
1429 frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
1434 if (node->txt.enc != 1) {
1435 frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
1438 frame = writeUcs2s(frame, node->txt.ptr.u, node->txt.dim);
1444 static unsigned char *
1445 set_frame_wxxx(unsigned char *frame, FrameDataNode const *node)
1447 size_t const n = sizeOfWxxxNode(node);
1449 frame = set_4_byte_value(frame, node->fid);
1450 frame = set_4_byte_value(frame, (unsigned long) (n - 10));
1451 /* clear 2-byte header flags */
1454 if (node->dsc.dim > 0) {
1455 /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
1456 *frame++ = node->txt.enc == 1 ? 1 : 0;
1457 if (node->dsc.enc != 1) {
1458 frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
1462 frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
1467 if (node->txt.enc != 1) {
1468 frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
1471 frame = writeLoBytes(frame, node->txt.ptr.u, node->txt.dim);
1477 static unsigned char *
1478 set_frame_apic(unsigned char *frame, const char *mimetype, const unsigned char *data, size_t size)
1480 /* ID3v2.3 standard APIC frame:
1481 * <Header for 'Attached picture', ID: "APIC">
1483 * MIME type <text string> $00
1485 * Description <text string according to encoding> $00 (00)
1486 * Picture data <binary data>
1488 if (mimetype && data && size) {
1489 frame = set_4_byte_value(frame, FRAME_ID('A', 'P', 'I', 'C'));
1490 frame = set_4_byte_value(frame, (unsigned long) (4 + strlen(mimetype) + size));
1491 /* clear 2-byte header flags */
1494 /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
1496 /* copy mime_type */
1498 *frame++ = *mimetype++;
1501 /* set picture type to 0 */
1503 /* empty description field */
1505 /* copy the image data */
1514 id3tag_set_fieldvalue(lame_global_flags * gfp, const char *fieldvalue)
1516 lame_internal_flags *gfc = gfp->internal_flags;
1517 if (fieldvalue && *fieldvalue) {
1518 uint32_t const frame_id = toID3v2TagId(fieldvalue);
1520 if (strlen(fieldvalue) < 5 || fieldvalue[4] != '=') {
1523 if (frame_id != 0) {
1524 if (id3tag_set_textinfo_latin1(gfp, fieldvalue, &fieldvalue[5])) {
1525 p = (char **) realloc(gfc->tag_spec.values,
1526 sizeof(char *) * (gfc->tag_spec.num_values + 1));
1530 gfc->tag_spec.values = (char **) p;
1531 local_strdup(&gfc->tag_spec.values[gfc->tag_spec.num_values++], fieldvalue);
1534 gfc->tag_spec.flags |= CHANGED_FLAG;
1541 id3tag_set_fieldvalue_utf16(lame_global_flags * gfp, const unsigned short *fieldvalue)
1543 if (fieldvalue && *fieldvalue) {
1544 size_t dx = hasUcs2ByteOrderMarker(fieldvalue[0]);
1545 unsigned short const separator = fromLatin1Char(fieldvalue, '=');
1546 char fid[5] = {0,0,0,0,0};
1547 uint32_t const frame_id = toID3v2TagId_ucs2(fieldvalue);
1548 if (local_ucs2_strlen(fieldvalue) < (5+dx) || fieldvalue[4+dx] != separator) {
1551 fid[0] = (frame_id >> 24) & 0x0ff;
1552 fid[1] = (frame_id >> 16) & 0x0ff;
1553 fid[2] = (frame_id >> 8) & 0x0ff;
1554 fid[3] = frame_id & 0x0ff;
1555 if (frame_id != 0) {
1556 unsigned short* txt = 0;
1558 local_ucs2_substr(&txt, fieldvalue, dx+5, local_ucs2_strlen(fieldvalue));
1559 rc = id3tag_set_textinfo_ucs2(gfp, fid, txt);
1568 id3tag_set_fieldvalue_ucs2(lame_global_flags * gfp, const unsigned short *fieldvalue);
1571 id3tag_set_fieldvalue_ucs2(lame_global_flags * gfp, const unsigned short *fieldvalue)
1573 return id3tag_set_fieldvalue_utf16(gfp, fieldvalue);
1577 lame_get_id3v2_tag(lame_global_flags * gfp, unsigned char *buffer, size_t size)
1579 lame_internal_flags *gfc;
1583 gfc = gfp->internal_flags;
1587 if (test_tag_spec_flags(gfc, V1_ONLY_FLAG)) {
1591 debug_tag_spec_flags(gfc, "lame_get_id3v2_tag");
1594 int usev2 = test_tag_spec_flags(gfc, ADD_V2_FLAG | V2_ONLY_FLAG);
1595 /* calculate length of four fields which may not fit in verion 1 tag */
1596 size_t title_length = gfc->tag_spec.title ? strlen(gfc->tag_spec.title) : 0;
1597 size_t artist_length = gfc->tag_spec.artist ? strlen(gfc->tag_spec.artist) : 0;
1598 size_t album_length = gfc->tag_spec.album ? strlen(gfc->tag_spec.album) : 0;
1599 size_t comment_length = gfc->tag_spec.comment ? strlen(gfc->tag_spec.comment) : 0;
1600 /* write tag if explicitly requested or if fields overflow */
1601 if ((title_length > 30)
1602 || (artist_length > 30)
1603 || (album_length > 30)
1604 || (comment_length > 30)
1605 || (gfc->tag_spec.track_id3v1 && (comment_length > 28))) {
1611 size_t adjusted_tag_size;
1613 const char *albumart_mime = NULL;
1614 static const char *mime_jpeg = "image/jpeg";
1615 static const char *mime_png = "image/png";
1616 static const char *mime_gif = "image/gif";
1618 if (gfp->num_samples != MAX_U_32_NUM) {
1619 id3v2AddAudioDuration(gfc, gfp->num_samples);
1622 /* calulate size of tag starting with 10-byte tag header */
1624 for (i = 0; i < gfc->tag_spec.num_values; ++i) {
1625 tag_size += 6 + strlen(gfc->tag_spec.values[i]);
1627 if (gfc->tag_spec.albumart && gfc->tag_spec.albumart_size) {
1628 switch (gfc->tag_spec.albumart_mimetype) {
1630 albumart_mime = mime_jpeg;
1633 albumart_mime = mime_png;
1636 albumart_mime = mime_gif;
1639 if (albumart_mime) {
1640 tag_size += 10 + 4 + strlen(albumart_mime) + gfc->tag_spec.albumart_size;
1644 id3tag_spec *tag = &gfc->tag_spec;
1645 if (tag->v2_head != 0) {
1646 FrameDataNode *node;
1647 for (node = tag->v2_head; node != 0; node = node->nxt) {
1648 if (node->fid == ID_COMMENT) {
1649 tag_size += sizeOfCommentNode(node);
1651 else if (isFrameIdMatching(node->fid, FRAME_ID('W',0,0,0))) {
1652 tag_size += sizeOfWxxxNode(node);
1655 tag_size += sizeOfNode(node);
1660 if (test_tag_spec_flags(gfc, PAD_V2_FLAG)) {
1661 /* add some bytes of padding */
1662 tag_size += gfc->tag_spec.padding_size;
1664 if (size < tag_size) {
1671 /* set tag header starting with file identifier */
1675 /* set version number word */
1678 /* clear flags byte */
1680 /* calculate and set tag size = total size - header size */
1681 adjusted_tag_size = tag_size - 10;
1682 /* encode adjusted size into four bytes where most significant
1683 * bit is clear in each byte, for 28-bit total */
1684 *p++ = (unsigned char) ((adjusted_tag_size >> 21) & 0x7fu);
1685 *p++ = (unsigned char) ((adjusted_tag_size >> 14) & 0x7fu);
1686 *p++ = (unsigned char) ((adjusted_tag_size >> 7) & 0x7fu);
1687 *p++ = (unsigned char) (adjusted_tag_size & 0x7fu);
1690 * NOTE: The remainder of the tag (frames and padding, if any)
1691 * are not "unsynchronized" to prevent false MPEG audio headers
1692 * from appearing in the bitstream. Why? Well, most players
1693 * and utilities know how to skip the ID3 version 2 tag by now
1694 * even if they don't read its contents, and it's actually
1695 * very unlikely that such a false "sync" pattern would occur
1696 * in just the simple text frames added here.
1699 /* set each frame in tag */
1701 id3tag_spec *tag = &gfc->tag_spec;
1702 if (tag->v2_head != 0) {
1703 FrameDataNode *node;
1704 for (node = tag->v2_head; node != 0; node = node->nxt) {
1705 if (node->fid == ID_COMMENT) {
1706 p = set_frame_comment(p, node);
1708 else if (isFrameIdMatching(node->fid,FRAME_ID('W',0,0,0))) {
1709 p = set_frame_wxxx(p, node);
1712 p = set_frame_custom2(p, node);
1717 for (i = 0; i < gfc->tag_spec.num_values; ++i) {
1718 p = set_frame_custom(p, gfc->tag_spec.values[i]);
1720 if (albumart_mime) {
1721 p = set_frame_apic(p, albumart_mime, gfc->tag_spec.albumart,
1722 gfc->tag_spec.albumart_size);
1724 /* clear any padding bytes */
1725 memset(p, 0, tag_size - (p - buffer));
1733 id3tag_write_v2(lame_global_flags * gfp)
1735 lame_internal_flags *gfc = gfp->internal_flags;
1737 debug_tag_spec_flags(gfc, "write v2");
1739 if (test_tag_spec_flags(gfc, V1_ONLY_FLAG)) {
1742 if (test_tag_spec_flags(gfc, CHANGED_FLAG)) {
1743 unsigned char *tag = 0;
1746 n = lame_get_id3v2_tag(gfp, 0, 0);
1751 tag_size = lame_get_id3v2_tag(gfp, tag, n);
1758 /* write tag directly into bitstream at current position */
1759 for (i = 0; i < tag_size; ++i) {
1760 add_dummy_byte(gfc, tag[i], 1);
1764 return (int) tag_size; /* ok, tag should not exceed 2GB */
1769 static unsigned char *
1770 set_text_field(unsigned char *field, const char *text, size_t size, int pad)
1773 if (text && *text) {
1784 lame_get_id3v1_tag(lame_global_flags * gfp, unsigned char *buffer, size_t size)
1786 size_t const tag_size = 128;
1787 lame_internal_flags *gfc;
1792 if (size < tag_size) {
1795 gfc = gfp->internal_flags;
1802 if (test_tag_spec_flags(gfc, V2_ONLY_FLAG)) {
1805 if (test_tag_spec_flags(gfc, CHANGED_FLAG)) {
1806 unsigned char *p = buffer;
1807 int pad = test_tag_spec_flags(gfc, SPACE_V1_FLAG) ? ' ' : 0;
1810 /* set tag identifier */
1814 /* set each field in tag */
1815 p = set_text_field(p, gfc->tag_spec.title, 30, pad);
1816 p = set_text_field(p, gfc->tag_spec.artist, 30, pad);
1817 p = set_text_field(p, gfc->tag_spec.album, 30, pad);
1818 sprintf(year, "%d", gfc->tag_spec.year);
1819 p = set_text_field(p, gfc->tag_spec.year ? year : NULL, 4, pad);
1820 /* limit comment field to 28 bytes if a track is specified */
1821 p = set_text_field(p, gfc->tag_spec.comment, gfc->tag_spec.track_id3v1 ? 28 : 30, pad);
1822 if (gfc->tag_spec.track_id3v1) {
1823 /* clear the next byte to indicate a version 1.1 tag */
1825 *p++ = gfc->tag_spec.track_id3v1;
1827 *p++ = gfc->tag_spec.genre_id3v1;
1834 id3tag_write_v1(lame_global_flags * gfp)
1836 lame_internal_flags *const gfc = gfp->internal_flags;
1838 unsigned char tag[128];
1841 n = lame_get_id3v1_tag(gfp, tag, m);
1845 /* write tag directly into bitstream at current position */
1846 for (i = 0; i < n; ++i) {
1847 add_dummy_byte(gfc, tag[i], 1);
1849 return (int) n; /* ok, tag has fixed size of 128 bytes, well below 2GB */