1 /* libSoX MP3 utilities  Copyright (c) 2007-9 SoX contributors
2  *
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.
7  *
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.
12  *
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
16  */
17 
18 #include <sys/stat.h>
19 
20 #if defined(HAVE_LAME)
21 
write_comments(sox_format_t * ft)22 static void write_comments(sox_format_t * ft)
23 {
24   priv_t *p = (priv_t *) ft->priv;
25   const char* comment;
26 
27   p->id3tag_init(p->gfp);
28   p->id3tag_set_pad(p->gfp, (size_t)ID3PADDING);
29 
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")))
46   {
47     if (p->id3tag_set_genre(p->gfp, comment))
48       lsx_warn("\"%s\" is not a recognized ID3v1 genre.", comment);
49   }
50 
51   if ((comment = sox_find_comment(ft->oob.comments, "Discnumber")))
52   {
53     char* id3tag_buf = lsx_malloc(strlen(comment) + 6);
54     if (id3tag_buf)
55     {
56       sprintf(id3tag_buf, "TPOS=%s", comment);
57       p->id3tag_set_fieldvalue(p->gfp, id3tag_buf);
58       free(id3tag_buf);
59     }
60   }
61 }
62 
63 #endif /* HAVE_LAME */
64 
65 #ifdef HAVE_MAD_H
66 
xing_frames(priv_t * p,struct mad_bitptr ptr,unsigned bitlen)67 static unsigned long xing_frames(priv_t * p, struct mad_bitptr ptr, unsigned bitlen)
68 {
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);
73   return 0;
74 }
75 
mp3_duration(sox_format_t * ft)76 static size_t mp3_duration(sox_format_t * ft)
77 {
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;
86 
87   p->mad_stream_init(&mad_stream);
88   p->mad_header_init(&mad_header);
89   p->mad_frame_init(&mad_frame);
90 
91   do {  /* Read data from the MP3 file */
92     int read, padding = 0;
93     size_t leftover = mad_stream.bufend - mad_stream.next_frame;
94 
95     memmove(p->mp3_buffer, mad_stream.this_frame, leftover);
96     read = lsx_readbuf(ft, p->mp3_buffer + leftover, p->mp3_buffer_size - leftover);
97     if (read <= 0) {
98       lsx_debug("got exact duration by scan to EOF (frames=%" PRIuPTR " leftover=%" PRIuPTR ")", frames, leftover);
99       break;
100     }
101     for (; !depadded && padding < read && !p->mp3_buffer[padding]; ++padding);
102     depadded = sox_true;
103     p->mad_stream_buffer(&mad_stream, p->mp3_buffer + padding, leftover + read - padding);
104 
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");
112           break;
113         }
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;
121             }
122             p->mad_stream_skip(&mad_stream, min(tagsize, available));
123           }
124           else lsx_warn("MAD lost sync");
125         }
126         else lsx_warn("recoverable MAD error");
127         continue; /* Not an audio frame */
128       }
129 
130       num_samples += MAD_NSBSAMPLES(&mad_header) * 32;
131       consumed += mad_stream.next_frame - mad_stream.this_frame;
132 
133       lsx_debug_more("bitrate=%lu", mad_header.bitrate);
134       if (!frames) {
135         initial_bitrate = mad_header.bitrate;
136 
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");
142             break;
143           }
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);
147           break;
148         }
149       }
150       else vbr |= mad_header.bitrate != initial_bitrate;
151 
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");
158         break;
159       }
160     }
161   } while (mad_stream.error == MAD_ERROR_BUFLEN);
162 
163   p->mad_frame_finish(&mad_frame);
164   mad_header_finish(&mad_header);
165   p->mad_stream_finish(&mad_stream);
166   lsx_rewind(ft);
167 
168   return num_samples;
169 }
170 
171 #endif /* HAVE_MAD_H */
172