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
20 #if defined(HAVE_LAME)
22 static void write_comments(sox_format_t
* ft
)
24 priv_t
*p
= (priv_t
*) ft
->priv
;
27 p
->id3tag_init(p
->gfp
);
28 p
->id3tag_set_pad(p
->gfp
, (size_t)ID3PADDING
);
30 /* Note: id3tag_set_fieldvalue is not present in LAME 3.97, so we're using
31 the 3.97-compatible methods for all of the tags that 3.97 supported. */
32 /* FIXME: This is no more necessary, since support for LAME 3.97 has ended. */
33 if ((comment
= sox_find_comment(ft
->oob
.comments
, "Title")))
34 p
->id3tag_set_title(p
->gfp
, comment
);
35 if ((comment
= sox_find_comment(ft
->oob
.comments
, "Artist")))
36 p
->id3tag_set_artist(p
->gfp
, comment
);
37 if ((comment
= sox_find_comment(ft
->oob
.comments
, "Album")))
38 p
->id3tag_set_album(p
->gfp
, comment
);
39 if ((comment
= sox_find_comment(ft
->oob
.comments
, "Tracknumber")))
40 p
->id3tag_set_track(p
->gfp
, comment
);
41 if ((comment
= sox_find_comment(ft
->oob
.comments
, "Year")))
42 p
->id3tag_set_year(p
->gfp
, comment
);
43 if ((comment
= sox_find_comment(ft
->oob
.comments
, "Comment")))
44 p
->id3tag_set_comment(p
->gfp
, comment
);
45 if ((comment
= sox_find_comment(ft
->oob
.comments
, "Genre")))
47 if (p
->id3tag_set_genre(p
->gfp
, comment
))
48 lsx_warn("\"%s\" is not a recognized ID3v1 genre.", comment
);
51 if ((comment
= sox_find_comment(ft
->oob
.comments
, "Discnumber")))
53 char* id3tag_buf
= lsx_malloc(strlen(comment
) + 6);
56 sprintf(id3tag_buf
, "TPOS=%s", comment
);
57 p
->id3tag_set_fieldvalue(p
->gfp
, id3tag_buf
);
63 #endif /* HAVE_LAME */
67 static unsigned long xing_frames(priv_t
* p
, struct mad_bitptr ptr
, unsigned bitlen
)
69 #define XING_MAGIC ( ('X' << 24) | ('i' << 16) | ('n' << 8) | 'g' )
70 if (bitlen
>= 96 && p
->mad_bit_read(&ptr
, 32) == XING_MAGIC
&&
71 (p
->mad_bit_read(&ptr
, 32) & 1 )) /* XING_FRAMES */
72 return p
->mad_bit_read(&ptr
, 32);
76 static size_t mp3_duration(sox_format_t
* ft
)
78 priv_t
* p
= (priv_t
*) ft
->priv
;
79 struct mad_stream mad_stream
;
80 struct mad_header mad_header
;
81 struct mad_frame mad_frame
;
82 size_t initial_bitrate
= 0; /* Initialised to prevent warning */
83 size_t tagsize
= 0, consumed
= 0, frames
= 0;
84 sox_bool vbr
= sox_false
, depadded
= sox_false
;
85 size_t num_samples
= 0;
87 p
->mad_stream_init(&mad_stream
);
88 p
->mad_header_init(&mad_header
);
89 p
->mad_frame_init(&mad_frame
);
91 do { /* Read data from the MP3 file */
92 int read
, padding
= 0;
93 size_t leftover
= mad_stream
.bufend
- mad_stream
.next_frame
;
95 memmove(p
->mp3_buffer
, mad_stream
.this_frame
, leftover
);
96 read
= lsx_readbuf(ft
, p
->mp3_buffer
+ leftover
, p
->mp3_buffer_size
- leftover
);
98 lsx_debug("got exact duration by scan to EOF (frames=%" PRIuPTR
" leftover=%" PRIuPTR
")", frames
, leftover
);
101 for (; !depadded
&& padding
< read
&& !p
->mp3_buffer
[padding
]; ++padding
);
103 p
->mad_stream_buffer(&mad_stream
, p
->mp3_buffer
+ padding
, leftover
+ read
- padding
);
105 while (sox_true
) { /* Decode frame headers */
106 mad_stream
.error
= MAD_ERROR_NONE
;
107 if (p
->mad_header_decode(&mad_header
, &mad_stream
) == -1) {
108 if (mad_stream
.error
== MAD_ERROR_BUFLEN
)
109 break; /* Normal behaviour; get some more data from the file */
110 if (!MAD_RECOVERABLE(mad_stream
.error
)) {
111 lsx_warn("unrecoverable MAD error");
114 if (mad_stream
.error
== MAD_ERROR_LOSTSYNC
) {
115 unsigned available
= (mad_stream
.bufend
- mad_stream
.this_frame
);
116 tagsize
= tagtype(mad_stream
.this_frame
, (size_t) available
);
117 if (tagsize
) { /* It's some ID3 tags, so just skip */
118 if (tagsize
>= available
) {
119 lsx_seeki(ft
, (off_t
)(tagsize
- available
), SEEK_CUR
);
120 depadded
= sox_false
;
122 p
->mad_stream_skip(&mad_stream
, min(tagsize
, available
));
124 else lsx_warn("MAD lost sync");
126 else lsx_warn("recoverable MAD error");
127 continue; /* Not an audio frame */
130 num_samples
+= MAD_NSBSAMPLES(&mad_header
) * 32;
131 consumed
+= mad_stream
.next_frame
- mad_stream
.this_frame
;
133 lsx_debug_more("bitrate=%lu", mad_header
.bitrate
);
135 initial_bitrate
= mad_header
.bitrate
;
137 /* Get the precise frame count from the XING header if present */
138 mad_frame
.header
= mad_header
;
139 if (p
->mad_frame_decode(&mad_frame
, &mad_stream
) == -1)
140 if (!MAD_RECOVERABLE(mad_stream
.error
)) {
141 lsx_warn("unrecoverable MAD error");
144 if ((frames
= xing_frames(p
, mad_stream
.anc_ptr
, mad_stream
.anc_bitlen
))) {
145 num_samples
*= frames
;
146 lsx_debug("got exact duration from XING frame count (%" PRIuPTR
")", frames
);
150 else vbr
|= mad_header
.bitrate
!= initial_bitrate
;
152 /* If not VBR, we can time just a few frames then extrapolate */
153 if (++frames
== 25 && !vbr
) {
154 double frame_size
= (double) consumed
/ frames
;
155 size_t num_frames
= (lsx_filelength(ft
) - tagsize
) / frame_size
;
156 num_samples
= num_samples
/ frames
* num_frames
;
157 lsx_debug("got approx. duration by CBR extrapolation");
161 } while (mad_stream
.error
== MAD_ERROR_BUFLEN
);
163 p
->mad_frame_finish(&mad_frame
);
164 mad_header_finish(&mad_header
);
165 p
->mad_stream_finish(&mad_stream
);
171 #endif /* HAVE_MAD_H */