2 |
- |
1 |
/*
|
|
|
2 |
* id3tag.c -- Write ID3 version 1 and 2 tags.
|
|
|
3 |
*
|
|
|
4 |
* Copyright (C) 2000 Don Melton.
|
|
|
5 |
*
|
|
|
6 |
* This library is free software; you can redistribute it and/or
|
|
|
7 |
* modify it under the terms of the GNU Library General Public
|
|
|
8 |
* License as published by the Free Software Foundation; either
|
|
|
9 |
* version 2 of the License, or (at your option) any later version.
|
|
|
10 |
*
|
|
|
11 |
* This library is distributed in the hope that it will be useful,
|
|
|
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
14 |
* Library General Public License for more details.
|
|
|
15 |
*
|
|
|
16 |
* You should have received a copy of the GNU Library General Public
|
|
|
17 |
* License along with this library; if not, write to the Free Software
|
|
|
18 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
|
19 |
*/
|
|
|
20 |
|
|
|
21 |
/*
|
|
|
22 |
* HISTORY: This source file is part of LAME (see http://www.mp3dev.org/mp3/)
|
|
|
23 |
* and was originally adapted by Conrad Sanderson <c.sanderson@me.gu.edu.au>
|
|
|
24 |
* from mp3info by Ricardo Cerqueira <rmc@rccn.net> to write only ID3 version 1
|
|
|
25 |
* tags. Don Melton <don@blivet.com> COMPLETELY rewrote it to support version
|
|
|
26 |
* 2 tags and be more conformant to other standards while remaining flexible.
|
|
|
27 |
*
|
|
|
28 |
* NOTE: See http://id3.org/ for more information about ID3 tag formats.
|
|
|
29 |
*/
|
|
|
30 |
|
|
|
31 |
/* $Id: id3tag.c,v 1.18 2001/01/15 15:16:09 aleidinger Exp $ */
|
|
|
32 |
|
|
|
33 |
#ifdef HAVE_CONFIG_H
|
|
|
34 |
#include <config.h>
|
|
|
35 |
#endif
|
|
|
36 |
|
|
|
37 |
#ifdef STDC_HEADERS
|
|
|
38 |
# include <stddef.h>
|
|
|
39 |
# include <stdlib.h>
|
|
|
40 |
# include <string.h>
|
|
|
41 |
#else
|
|
|
42 |
# ifndef HAVE_STRCHR
|
|
|
43 |
# define strchr index
|
|
|
44 |
# define strrchr rindex
|
|
|
45 |
# endif
|
|
|
46 |
char *strchr (), *strrchr ();
|
|
|
47 |
# ifndef HAVE_MEMCPY
|
|
|
48 |
# define memcpy(d, s, n) bcopy ((s), (d), (n))
|
|
|
49 |
# define memmove(d, s, n) bcopy ((s), (d), (n))
|
|
|
50 |
# endif
|
|
|
51 |
#endif
|
|
|
52 |
|
|
|
53 |
#include "lame.h"
|
|
|
54 |
#include "id3tag.h"
|
|
|
55 |
#include "util.h"
|
|
|
56 |
#include "bitstream.h"
|
|
|
57 |
|
|
|
58 |
#ifdef WITH_DMALLOC
|
|
|
59 |
#include <dmalloc.h>
|
|
|
60 |
#endif
|
|
|
61 |
|
|
|
62 |
static const char *const genre_names[] =
|
|
|
63 |
{
|
|
|
64 |
/*
|
|
|
65 |
* NOTE: The spelling of these genre names is identical to those found in
|
|
|
66 |
* Winamp and mp3info.
|
|
|
67 |
*/
|
|
|
68 |
"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge",
|
|
|
69 |
"Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B",
|
|
|
70 |
"Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska",
|
|
|
71 |
"Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
|
|
|
72 |
"Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental",
|
|
|
73 |
"Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alt. Rock",
|
|
|
74 |
"Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
|
|
|
75 |
"Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
|
|
|
76 |
"Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
|
|
|
77 |
"Cult", "Gangsta Rap", "Top 40", "Christian Rap", "Pop/Funk", "Jungle",
|
|
|
78 |
"Native American", "Cabaret", "New Wave", "Psychedelic", "Rave",
|
|
|
79 |
"Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz",
|
|
|
80 |
"Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk",
|
|
|
81 |
"Folk/Rock", "National Folk", "Swing", "Fast-Fusion", "Bebob", "Latin",
|
|
|
82 |
"Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
|
|
|
83 |
"Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
|
|
|
84 |
"Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech",
|
|
|
85 |
"Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass",
|
|
|
86 |
"Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
|
|
|
87 |
"Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
|
|
|
88 |
"Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall",
|
|
|
89 |
"Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie",
|
|
|
90 |
"BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap",
|
|
|
91 |
"Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian",
|
|
|
92 |
"Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
|
|
|
93 |
"Synthpop"
|
|
|
94 |
};
|
|
|
95 |
|
|
|
96 |
#define GENRE_NAME_COUNT \
|
|
|
97 |
((int)(sizeof genre_names / sizeof (const char *const)))
|
|
|
98 |
|
|
|
99 |
static const int genre_alpha_map [] = {
|
|
|
100 |
123, 34, 74, 73, 99, 20, 40, 26, 145, 90, 116, 41, 135, 85, 96, 138, 89, 0,
|
|
|
101 |
107, 132, 65, 88, 104, 102, 97, 136, 61, 141, 32, 1, 112, 128, 57, 140, 2,
|
|
|
102 |
139, 58, 3, 125, 50, 22, 4, 55, 127, 122, 120, 98, 52, 48, 54, 124, 25, 84,
|
|
|
103 |
80, 115, 81, 119, 5, 30, 36, 59, 126, 38, 49, 91, 6, 129, 79, 137, 7, 35,
|
|
|
104 |
100, 131, 19, 33, 46, 47, 8, 29, 146, 63, 86, 71, 45, 142, 9, 77, 82, 64,
|
|
|
105 |
133, 10, 66, 39, 11, 103, 12, 75, 134, 13, 53, 62, 109, 117, 23, 108, 92,
|
|
|
106 |
67, 93, 43, 121, 15, 68, 14, 16, 76, 87, 118, 17, 78, 143, 114, 110, 69, 21,
|
|
|
107 |
111, 95, 105, 42, 37, 24, 56, 44, 101, 83, 94, 106, 147, 113, 18, 51, 130,
|
|
|
108 |
144, 60, 70, 31, 72, 27, 28
|
|
|
109 |
};
|
|
|
110 |
|
|
|
111 |
#define GENRE_ALPHA_COUNT ((int)(sizeof genre_alpha_map / sizeof (int)))
|
|
|
112 |
|
|
|
113 |
void
|
|
|
114 |
id3tag_genre_list(void (*handler)(int, const char *, void *), void *cookie)
|
|
|
115 |
{
|
|
|
116 |
if (handler) {
|
|
|
117 |
int i;
|
|
|
118 |
for (i = 0; i < GENRE_NAME_COUNT; ++i) {
|
|
|
119 |
if (i < GENRE_ALPHA_COUNT) {
|
|
|
120 |
int j = genre_alpha_map[i];
|
|
|
121 |
handler(j, genre_names[j], cookie);
|
|
|
122 |
}
|
|
|
123 |
}
|
|
|
124 |
}
|
|
|
125 |
}
|
|
|
126 |
|
|
|
127 |
#define GENRE_NUM_UNKNOWN 255
|
|
|
128 |
|
|
|
129 |
void
|
|
|
130 |
id3tag_init(lame_global_flags *gfp)
|
|
|
131 |
{
|
|
|
132 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
133 |
memset(&gfc->tag_spec, 0, sizeof gfc->tag_spec);
|
|
|
134 |
gfc->tag_spec.genre = GENRE_NUM_UNKNOWN;
|
|
|
135 |
}
|
|
|
136 |
|
|
|
137 |
#define CHANGED_FLAG (1U << 0)
|
|
|
138 |
#define ADD_V2_FLAG (1U << 1)
|
|
|
139 |
#define V1_ONLY_FLAG (1U << 2)
|
|
|
140 |
#define V2_ONLY_FLAG (1U << 3)
|
|
|
141 |
#define SPACE_V1_FLAG (1U << 4)
|
|
|
142 |
#define PAD_V2_FLAG (1U << 5)
|
|
|
143 |
|
|
|
144 |
void
|
|
|
145 |
id3tag_add_v2(lame_global_flags *gfp)
|
|
|
146 |
{
|
|
|
147 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
148 |
gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
|
|
|
149 |
gfc->tag_spec.flags |= ADD_V2_FLAG;
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
void
|
|
|
153 |
id3tag_v1_only(lame_global_flags *gfp)
|
|
|
154 |
{
|
|
|
155 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
156 |
gfc->tag_spec.flags &= ~(ADD_V2_FLAG | V2_ONLY_FLAG);
|
|
|
157 |
gfc->tag_spec.flags |= V1_ONLY_FLAG;
|
|
|
158 |
}
|
|
|
159 |
|
|
|
160 |
void
|
|
|
161 |
id3tag_v2_only(lame_global_flags *gfp)
|
|
|
162 |
{
|
|
|
163 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
164 |
gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
|
|
|
165 |
gfc->tag_spec.flags |= V2_ONLY_FLAG;
|
|
|
166 |
}
|
|
|
167 |
|
|
|
168 |
void
|
|
|
169 |
id3tag_space_v1(lame_global_flags *gfp)
|
|
|
170 |
{
|
|
|
171 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
172 |
gfc->tag_spec.flags &= ~V2_ONLY_FLAG;
|
|
|
173 |
gfc->tag_spec.flags |= SPACE_V1_FLAG;
|
|
|
174 |
}
|
|
|
175 |
|
|
|
176 |
void
|
|
|
177 |
id3tag_pad_v2(lame_global_flags *gfp)
|
|
|
178 |
{
|
|
|
179 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
180 |
gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
|
|
|
181 |
gfc->tag_spec.flags |= PAD_V2_FLAG;
|
|
|
182 |
}
|
|
|
183 |
|
|
|
184 |
void
|
|
|
185 |
id3tag_set_title(lame_global_flags *gfp, const char *title)
|
|
|
186 |
{
|
|
|
187 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
188 |
if (title && *title) {
|
|
|
189 |
gfc->tag_spec.title = title;
|
|
|
190 |
gfc->tag_spec.flags |= CHANGED_FLAG;
|
|
|
191 |
}
|
|
|
192 |
}
|
|
|
193 |
|
|
|
194 |
void
|
|
|
195 |
id3tag_set_artist(lame_global_flags *gfp, const char *artist)
|
|
|
196 |
{
|
|
|
197 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
198 |
if (artist && *artist) {
|
|
|
199 |
gfc->tag_spec.artist = artist;
|
|
|
200 |
gfc->tag_spec.flags |= CHANGED_FLAG;
|
|
|
201 |
}
|
|
|
202 |
}
|
|
|
203 |
|
|
|
204 |
void
|
|
|
205 |
id3tag_set_album(lame_global_flags *gfp, const char *album)
|
|
|
206 |
{
|
|
|
207 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
208 |
if (album && *album) {
|
|
|
209 |
gfc->tag_spec.album = album;
|
|
|
210 |
gfc->tag_spec.flags |= CHANGED_FLAG;
|
|
|
211 |
}
|
|
|
212 |
}
|
|
|
213 |
|
|
|
214 |
void
|
|
|
215 |
id3tag_set_year(lame_global_flags *gfp, const char *year)
|
|
|
216 |
{
|
|
|
217 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
218 |
if (year && *year) {
|
|
|
219 |
int num = atoi(year);
|
|
|
220 |
if (num < 0) {
|
|
|
221 |
num = 0;
|
|
|
222 |
}
|
|
|
223 |
/* limit a year to 4 digits so it fits in a version 1 tag */
|
|
|
224 |
if (num > 9999) {
|
|
|
225 |
num = 9999;
|
|
|
226 |
}
|
|
|
227 |
if (num) {
|
|
|
228 |
gfc->tag_spec.year = num;
|
|
|
229 |
gfc->tag_spec.flags |= CHANGED_FLAG;
|
|
|
230 |
}
|
|
|
231 |
}
|
|
|
232 |
}
|
|
|
233 |
|
|
|
234 |
void
|
|
|
235 |
id3tag_set_comment(lame_global_flags *gfp, const char *comment)
|
|
|
236 |
{
|
|
|
237 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
238 |
if (comment && *comment) {
|
|
|
239 |
gfc->tag_spec.comment = comment;
|
|
|
240 |
gfc->tag_spec.flags |= CHANGED_FLAG;
|
|
|
241 |
}
|
|
|
242 |
}
|
|
|
243 |
|
|
|
244 |
void
|
|
|
245 |
id3tag_set_track(lame_global_flags *gfp, const char *track)
|
|
|
246 |
{
|
|
|
247 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
248 |
if (track && *track) {
|
|
|
249 |
int num = atoi(track);
|
|
|
250 |
if (num < 0) {
|
|
|
251 |
num = 0;
|
|
|
252 |
}
|
|
|
253 |
/* limit a track to 255 so it fits in a version 1 tag even though CD
|
|
|
254 |
* audio doesn't allow more than 99 tracks */
|
|
|
255 |
if (num > 255) {
|
|
|
256 |
num = 255;
|
|
|
257 |
}
|
|
|
258 |
if (num) {
|
|
|
259 |
gfc->tag_spec.track = num;
|
|
|
260 |
gfc->tag_spec.flags |= CHANGED_FLAG;
|
|
|
261 |
}
|
|
|
262 |
}
|
|
|
263 |
}
|
|
|
264 |
|
|
|
265 |
/* would use real "strcasecmp" but it isn't portable */
|
|
|
266 |
static int
|
|
|
267 |
local_strcasecmp(const char *s1, const char *s2)
|
|
|
268 |
{
|
|
|
269 |
unsigned char c1;
|
|
|
270 |
unsigned char c2;
|
|
|
271 |
do {
|
|
|
272 |
c1 = tolower(*s1);
|
|
|
273 |
c2 = tolower(*s2);
|
|
|
274 |
if (!c1) {
|
|
|
275 |
break;
|
|
|
276 |
}
|
|
|
277 |
++s1;
|
|
|
278 |
++s2;
|
|
|
279 |
} while (c1 == c2);
|
|
|
280 |
return c1 - c2;
|
|
|
281 |
}
|
|
|
282 |
|
|
|
283 |
int
|
|
|
284 |
id3tag_set_genre(lame_global_flags *gfp, const char *genre)
|
|
|
285 |
{
|
|
|
286 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
287 |
if (genre && *genre) {
|
|
|
288 |
char *str;
|
|
|
289 |
int num = strtol(genre, &str, 10);
|
|
|
290 |
/* is the input a string or a valid number? */
|
|
|
291 |
if (*str) {
|
|
|
292 |
int i;
|
|
|
293 |
for (i = 0; i < GENRE_NAME_COUNT; ++i) {
|
|
|
294 |
if (!local_strcasecmp(genre, genre_names[i])) {
|
|
|
295 |
num = i;
|
|
|
296 |
break;
|
|
|
297 |
}
|
|
|
298 |
}
|
|
|
299 |
if (i == GENRE_NAME_COUNT) {
|
|
|
300 |
return -1;
|
|
|
301 |
}
|
|
|
302 |
} else if ((num < 0) || (num >= GENRE_NAME_COUNT)) {
|
|
|
303 |
return -1;
|
|
|
304 |
}
|
|
|
305 |
gfc->tag_spec.genre = num;
|
|
|
306 |
gfc->tag_spec.flags |= CHANGED_FLAG;
|
|
|
307 |
}
|
|
|
308 |
return 0;
|
|
|
309 |
}
|
|
|
310 |
|
|
|
311 |
static unsigned char *
|
|
|
312 |
set_4_byte_value(unsigned char *bytes, unsigned long value)
|
|
|
313 |
{
|
|
|
314 |
int index;
|
|
|
315 |
for (index = 3; index >= 0; --index) {
|
|
|
316 |
bytes[index] = value & 0xfful;
|
|
|
317 |
value >>= 8;
|
|
|
318 |
}
|
|
|
319 |
return bytes + 4;
|
|
|
320 |
}
|
|
|
321 |
|
|
|
322 |
#define FRAME_ID(a, b, c, d) \
|
|
|
323 |
( ((unsigned long)(a) << 24) \
|
|
|
324 |
| ((unsigned long)(b) << 16) \
|
|
|
325 |
| ((unsigned long)(c) << 8) \
|
|
|
326 |
| ((unsigned long)(d) << 0) )
|
|
|
327 |
#define TITLE_FRAME_ID FRAME_ID('T', 'I', 'T', '2')
|
|
|
328 |
#define ARTIST_FRAME_ID FRAME_ID('T', 'P', 'E', '1')
|
|
|
329 |
#define ALBUM_FRAME_ID FRAME_ID('T', 'A', 'L', 'B')
|
|
|
330 |
#define YEAR_FRAME_ID FRAME_ID('T', 'Y', 'E', 'R')
|
|
|
331 |
#define COMMENT_FRAME_ID FRAME_ID('C', 'O', 'M', 'M')
|
|
|
332 |
#define TRACK_FRAME_ID FRAME_ID('T', 'R', 'C', 'K')
|
|
|
333 |
#define GENRE_FRAME_ID FRAME_ID('T', 'C', 'O', 'N')
|
|
|
334 |
|
|
|
335 |
static unsigned char *
|
|
|
336 |
set_frame(unsigned char *frame, unsigned long id, const char *text,
|
|
|
337 |
size_t length)
|
|
|
338 |
{
|
|
|
339 |
if (length) {
|
|
|
340 |
frame = set_4_byte_value(frame, id);
|
|
|
341 |
/* Set frame size = total size - header size. Frame header and field
|
|
|
342 |
* bytes include 2-byte header flags, 1 encoding descriptor byte, and
|
|
|
343 |
* for comment frames: 3-byte language descriptor and 1 content
|
|
|
344 |
* descriptor byte */
|
|
|
345 |
frame = set_4_byte_value(frame, ((id == COMMENT_FRAME_ID) ? 5 : 1)
|
|
|
346 |
+ length);
|
|
|
347 |
/* clear 2-byte header flags */
|
|
|
348 |
*frame++ = 0;
|
|
|
349 |
*frame++ = 0;
|
|
|
350 |
/* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
|
|
|
351 |
*frame++ = 0;
|
|
|
352 |
if (id == COMMENT_FRAME_ID) {
|
|
|
353 |
/* use id3lib-compatible bogus language descriptor */
|
|
|
354 |
*frame++ = 'X';
|
|
|
355 |
*frame++ = 'X';
|
|
|
356 |
*frame++ = 'X';
|
|
|
357 |
/* clear 1 byte to make content descriptor empty string */
|
|
|
358 |
*frame++ = 0;
|
|
|
359 |
}
|
|
|
360 |
while (length--) {
|
|
|
361 |
*frame++ = *text++;
|
|
|
362 |
}
|
|
|
363 |
}
|
|
|
364 |
return frame;
|
|
|
365 |
}
|
|
|
366 |
|
|
|
367 |
int
|
|
|
368 |
id3tag_write_v2(lame_global_flags *gfp)
|
|
|
369 |
{
|
|
|
370 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
371 |
if ((gfc->tag_spec.flags & CHANGED_FLAG)
|
|
|
372 |
&& !(gfc->tag_spec.flags & V1_ONLY_FLAG)) {
|
|
|
373 |
/* calculate length of four fields which may not fit in verion 1 tag */
|
|
|
374 |
size_t title_length = gfc->tag_spec.title
|
|
|
375 |
? strlen(gfc->tag_spec.title) : 0;
|
|
|
376 |
size_t artist_length = gfc->tag_spec.artist
|
|
|
377 |
? strlen(gfc->tag_spec.artist) : 0;
|
|
|
378 |
size_t album_length = gfc->tag_spec.album
|
|
|
379 |
? strlen(gfc->tag_spec.album) : 0;
|
|
|
380 |
size_t comment_length = gfc->tag_spec.comment
|
|
|
381 |
? strlen(gfc->tag_spec.comment) : 0;
|
|
|
382 |
/* write tag if explicitly requested or if fields overflow */
|
|
|
383 |
if ((gfc->tag_spec.flags & (ADD_V2_FLAG | V2_ONLY_FLAG))
|
|
|
384 |
|| (title_length > 30)
|
|
|
385 |
|| (artist_length > 30) || (album_length > 30)
|
|
|
386 |
|| (comment_length > 30)
|
|
|
387 |
|| (gfc->tag_spec.track && (comment_length > 28))) {
|
|
|
388 |
size_t tag_size;
|
|
|
389 |
char year[5];
|
|
|
390 |
size_t year_length;
|
|
|
391 |
char track[3];
|
|
|
392 |
size_t track_length;
|
|
|
393 |
char genre[6];
|
|
|
394 |
size_t genre_length;
|
|
|
395 |
unsigned char *tag;
|
|
|
396 |
unsigned char *p;
|
|
|
397 |
size_t adjusted_tag_size;
|
|
|
398 |
unsigned int index;
|
|
|
399 |
/* calulate size of tag starting with 10-byte tag header */
|
|
|
400 |
tag_size = 10;
|
|
|
401 |
if (title_length) {
|
|
|
402 |
/* add 10-byte frame header, 1 encoding descriptor byte ... */
|
|
|
403 |
tag_size += 11 + title_length;
|
|
|
404 |
}
|
|
|
405 |
if (artist_length) {
|
|
|
406 |
tag_size += 11 + artist_length;
|
|
|
407 |
}
|
|
|
408 |
if (album_length) {
|
|
|
409 |
tag_size += 11 + album_length;
|
|
|
410 |
}
|
|
|
411 |
if (gfc->tag_spec.year) {
|
|
|
412 |
year_length = sprintf(year, "%d", gfc->tag_spec.year);
|
|
|
413 |
tag_size += 11 + year_length;
|
|
|
414 |
} else {
|
|
|
415 |
year_length = 0;
|
|
|
416 |
}
|
|
|
417 |
if (comment_length) {
|
|
|
418 |
/* add 10-byte frame header, 1 encoding descriptor byte,
|
|
|
419 |
* 3-byte language descriptor, 1 content descriptor byte ... */
|
|
|
420 |
tag_size += 15 + comment_length;
|
|
|
421 |
}
|
|
|
422 |
if (gfc->tag_spec.track) {
|
|
|
423 |
track_length = sprintf(track, "%d", gfc->tag_spec.track);
|
|
|
424 |
tag_size += 11 + track_length;
|
|
|
425 |
} else {
|
|
|
426 |
track_length = 0;
|
|
|
427 |
}
|
|
|
428 |
if (gfc->tag_spec.genre != GENRE_NUM_UNKNOWN) {
|
|
|
429 |
genre_length = sprintf(genre, "(%d)", gfc->tag_spec.genre);
|
|
|
430 |
tag_size += 11 + genre_length;
|
|
|
431 |
} else {
|
|
|
432 |
genre_length = 0;
|
|
|
433 |
}
|
|
|
434 |
if (gfc->tag_spec.flags & PAD_V2_FLAG) {
|
|
|
435 |
/* add 128 bytes of padding */
|
|
|
436 |
tag_size += 128;
|
|
|
437 |
}
|
|
|
438 |
tag = (unsigned char *)malloc(tag_size);
|
|
|
439 |
if (!tag) {
|
|
|
440 |
return -1;
|
|
|
441 |
}
|
|
|
442 |
p = tag;
|
|
|
443 |
/* set tag header starting with file identifier */
|
|
|
444 |
*p++ = 'I'; *p++ = 'D'; *p++ = '3';
|
|
|
445 |
/* set version number word */
|
|
|
446 |
*p++ = 3; *p++ = 0;
|
|
|
447 |
/* clear flags byte */
|
|
|
448 |
*p++ = 0;
|
|
|
449 |
/* calculate and set tag size = total size - header size */
|
|
|
450 |
adjusted_tag_size = tag_size - 10;
|
|
|
451 |
/* encode adjusted size into four bytes where most significant
|
|
|
452 |
* bit is clear in each byte, for 28-bit total */
|
|
|
453 |
*p++ = (adjusted_tag_size >> 21) & 0x7fu;
|
|
|
454 |
*p++ = (adjusted_tag_size >> 14) & 0x7fu;
|
|
|
455 |
*p++ = (adjusted_tag_size >> 7) & 0x7fu;
|
|
|
456 |
*p++ = adjusted_tag_size & 0x7fu;
|
|
|
457 |
|
|
|
458 |
/*
|
|
|
459 |
* NOTE: The remainder of the tag (frames and padding, if any)
|
|
|
460 |
* are not "unsynchronized" to prevent false MPEG audio headers
|
|
|
461 |
* from appearing in the bitstream. Why? Well, most players
|
|
|
462 |
* and utilities know how to skip the ID3 version 2 tag by now
|
|
|
463 |
* even if they don't read its contents, and it's actually
|
|
|
464 |
* very unlikely that such a false "sync" pattern would occur
|
|
|
465 |
* in just the simple text frames added here.
|
|
|
466 |
*/
|
|
|
467 |
|
|
|
468 |
/* set each frame in tag */
|
|
|
469 |
p = set_frame(p, TITLE_FRAME_ID, gfc->tag_spec.title, title_length);
|
|
|
470 |
p = set_frame(p, ARTIST_FRAME_ID, gfc->tag_spec.artist,
|
|
|
471 |
artist_length);
|
|
|
472 |
p = set_frame(p, ALBUM_FRAME_ID, gfc->tag_spec.album, album_length);
|
|
|
473 |
p = set_frame(p, YEAR_FRAME_ID, year, year_length);
|
|
|
474 |
p = set_frame(p, COMMENT_FRAME_ID, gfc->tag_spec.comment,
|
|
|
475 |
comment_length);
|
|
|
476 |
p = set_frame(p, TRACK_FRAME_ID, track, track_length);
|
|
|
477 |
p = set_frame(p, GENRE_FRAME_ID, genre, genre_length);
|
|
|
478 |
/* clear any padding bytes */
|
|
|
479 |
memset(p, 0, tag_size - (p - tag));
|
|
|
480 |
/* write tag directly into bitstream at current position */
|
|
|
481 |
for (index = 0; index < tag_size; ++index) {
|
|
|
482 |
add_dummy_byte(gfp, tag[index]);
|
|
|
483 |
}
|
|
|
484 |
free(tag);
|
|
|
485 |
return tag_size;
|
|
|
486 |
}
|
|
|
487 |
}
|
|
|
488 |
return 0;
|
|
|
489 |
}
|
|
|
490 |
|
|
|
491 |
static unsigned char *
|
|
|
492 |
set_text_field(unsigned char *field, const char *text, size_t size, int pad)
|
|
|
493 |
{
|
|
|
494 |
while (size--) {
|
|
|
495 |
if (text && *text) {
|
|
|
496 |
*field++ = *text++;
|
|
|
497 |
} else {
|
|
|
498 |
*field++ = pad;
|
|
|
499 |
}
|
|
|
500 |
}
|
|
|
501 |
return field;
|
|
|
502 |
}
|
|
|
503 |
|
|
|
504 |
int
|
|
|
505 |
id3tag_write_v1(lame_global_flags *gfp)
|
|
|
506 |
{
|
|
|
507 |
lame_internal_flags *gfc = gfp->internal_flags;
|
|
|
508 |
if ((gfc->tag_spec.flags & CHANGED_FLAG)
|
|
|
509 |
&& !(gfc->tag_spec.flags & V2_ONLY_FLAG)) {
|
|
|
510 |
unsigned char tag[128];
|
|
|
511 |
unsigned char *p = tag;
|
|
|
512 |
int pad = (gfc->tag_spec.flags & SPACE_V1_FLAG) ? ' ' : 0;
|
|
|
513 |
char year[5];
|
|
|
514 |
unsigned int index;
|
|
|
515 |
/* set tag identifier */
|
|
|
516 |
*p++ = 'T'; *p++ = 'A'; *p++ = 'G';
|
|
|
517 |
/* set each field in tag */
|
|
|
518 |
p = set_text_field(p, gfc->tag_spec.title, 30, pad);
|
|
|
519 |
p = set_text_field(p, gfc->tag_spec.artist, 30, pad);
|
|
|
520 |
p = set_text_field(p, gfc->tag_spec.album, 30, pad);
|
|
|
521 |
sprintf(year, "%d", gfc->tag_spec.year);
|
|
|
522 |
p = set_text_field(p, gfc->tag_spec.year ? year : NULL, 4, pad);
|
|
|
523 |
/* limit comment field to 28 bytes if a track is specified */
|
|
|
524 |
p = set_text_field(p, gfc->tag_spec.comment, gfc->tag_spec.track
|
|
|
525 |
? 28 : 30, pad);
|
|
|
526 |
if (gfc->tag_spec.track) {
|
|
|
527 |
/* clear the next byte to indicate a version 1.1 tag */
|
|
|
528 |
*p++ = 0;
|
|
|
529 |
*p++ = gfc->tag_spec.track;
|
|
|
530 |
}
|
|
|
531 |
*p++ = gfc->tag_spec.genre;
|
|
|
532 |
/* write tag directly into bitstream at current position */
|
|
|
533 |
for (index = 0; index < 128; ++index) {
|
|
|
534 |
add_dummy_byte(gfp, tag[index]);
|
|
|
535 |
}
|
|
|
536 |
return 128;
|
|
|
537 |
}
|
|
|
538 |
return 0;
|
|
|
539 |
}
|