1 /* libSoX MP3 utilities Copyright (c) 2007-9 SoX contributors
3 * This library is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation; either version 2.1 of the License, or (at
6 * your option) any later version.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11 * General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 static char const * id3tagmap
[][2] =
31 {"TRCK", "Tracknumber"},
35 {"TPOS", "Discnumber"},
39 static id3_utf8_t
* utf8_id3tag_findframe(
40 struct id3_tag
* tag
, const char * const frameid
, unsigned index
)
42 struct id3_frame
const * frame
= id3_tag_findframe(tag
, frameid
, index
);
44 unsigned nfields
= frame
->nfields
;
47 union id3_field
const *field
= id3_frame_field(frame
, nfields
);
48 int ftype
= id3_field_type(field
);
49 const id3_ucs4_t
*ucs4
= NULL
;
53 case ID3_FIELD_TYPE_STRING
:
54 ucs4
= id3_field_getstring(field
);
57 case ID3_FIELD_TYPE_STRINGFULL
:
58 ucs4
= id3_field_getfullstring(field
);
61 case ID3_FIELD_TYPE_STRINGLIST
:
62 nstrings
= id3_field_getnstrings(field
);
64 ucs4
= id3_field_getstrings(field
, nstrings
);
72 return id3_ucs4_utf8duplicate(ucs4
); /* Must call free() on this */
80 struct tag_info_node
* next
;
87 struct tag_info_node
* head
;
91 static int add_tag(struct tag_info
* info
)
93 struct tag_info_node
* current
;
95 id3_byte_t query
[ID3_TAG_QUERYSIZE
];
100 /* Ensure we're at the start of a valid tag and get its size. */
101 if (ID3_TAG_QUERYSIZE
!= lsx_readbuf(info
->ft
, query
, ID3_TAG_QUERYSIZE
) ||
102 !(size
= id3_tag_query(query
, ID3_TAG_QUERYSIZE
))) {
106 if (0 != lsx_seeki(info
->ft
, size
, SEEK_CUR
) ||
107 ID3_TAG_QUERYSIZE
!= lsx_readbuf(info
->ft
, query
, ID3_TAG_QUERYSIZE
) ||
108 (size
= id3_tag_query(query
, ID3_TAG_QUERYSIZE
)) <= 0) {
113 /* Don't read a tag more than once. */
114 start
= lsx_tell(info
->ft
);
116 for (current
= info
->head
; current
; current
= current
->next
) {
117 if (start
== current
->start
&& end
== current
->end
) {
119 } else if (start
< current
->end
&& current
->start
< end
) {
124 buffer
= lsx_malloc((size_t)size
);
128 memcpy(buffer
, query
, ID3_TAG_QUERYSIZE
);
129 if ((unsigned long)size
- ID3_TAG_QUERYSIZE
==
130 lsx_readbuf(info
->ft
, buffer
+ ID3_TAG_QUERYSIZE
, (size_t)size
- ID3_TAG_QUERYSIZE
)) {
131 struct id3_tag
* tag
= id3_tag_parse(buffer
, (size_t)size
);
133 current
= lsx_malloc(sizeof(struct tag_info_node
));
135 current
->next
= info
->head
;
136 current
->start
= start
;
138 info
->head
= current
;
139 if (info
->tag
&& (info
->tag
->extendedflags
& ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE
)) {
140 struct id3_frame
* frame
;
142 for (i
= 0; (frame
= id3_tag_findframe(tag
, NULL
, i
)); i
++) {
143 id3_tag_attachframe(info
->tag
, frame
);
148 id3_tag_delete(info
->tag
);
159 void lsx_id3_read_tag(sox_format_t
* ft
, sox_bool search
)
161 struct tag_info info
;
172 ID3v1 at end (EOF - 128).
174 ID3v2 at end (but before ID3v1 from end if there was one).
178 if (0 == lsx_seeki(ft
, -128, SEEK_END
)) {
181 1 == ID3_TAG_VERSION_MAJOR(id3_tag_version(info
.tag
));
183 if (0 == lsx_seeki(ft
, 0, SEEK_SET
)) {
186 if (0 == lsx_seeki(ft
, has_id3v1
? -138 : -10, SEEK_END
)) {
193 if (info
.tag
&& info
.tag
->frames
) {
194 for (i
= 0; id3tagmap
[i
][0]; ++i
) {
195 if ((utf8
= utf8_id3tag_findframe(info
.tag
, id3tagmap
[i
][0], 0))) {
196 char * comment
= lsx_malloc(strlen(id3tagmap
[i
][1]) + 1 + strlen((char *)utf8
) + 1);
197 sprintf(comment
, "%s=%s", id3tagmap
[i
][1], utf8
);
198 sox_append_comment(&ft
->oob
.comments
, comment
);
203 if ((utf8
= utf8_id3tag_findframe(info
.tag
, "TLEN", 0))) {
204 unsigned long tlen
= strtoul((char *)utf8
, NULL
, 10);
205 if (tlen
> 0 && tlen
< ULONG_MAX
) {
206 ft
->signal
.length
= tlen
; /* In ms; convert to samples later */
207 lsx_debug("got exact duration from ID3 TLEN");
213 struct tag_info_node
* head
= info
.head
;
214 info
.head
= head
->next
;
218 id3_tag_delete(info
.tag
);
224 /* Stub for format modules */
225 void lsx_id3_read_tag(sox_format_t
*ft
, sox_bool search
) { }