chiark / gitweb /
Use hands-off reader in MP3 decoding.
[disorder] / plugins / mad.c
1 /** @file plugins/mad.c
2  * @brief MP3 Length calculation
3  *
4  * This file is a subset of the debian source tarball of
5  * mpg321-0.2.10.3/mad.c - see http://mpg321.sourceforge.net/
6  */
7
8 /*
9     mpg321 - a fully free clone of mpg123.
10     Copyright (C) 2001 Joe Drew
11     
12     Originally based heavily upon:
13     plaympeg - Sample MPEG player using the SMPEG library
14     Copyright (C) 1999 Loki Entertainment Software
15     
16     Also uses some code from
17     mad - MPEG audio decoder
18     Copyright (C) 2000-2001 Robert Leslie
19     
20     Original playlist code contributed by Tobias Bengtsson <tobbe@tobbe.nu>
21
22     This program is free software; you can redistribute it and/or modify
23     it under the terms of the GNU General Public License as published by
24     the Free Software Foundation; either version 2 of the License, or
25     (at your option) any later version.
26
27     This program is distributed in the hope that it will be useful,
28     but WITHOUT ANY WARRANTY; without even the implied warranty of
29     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30     GNU General Public License for more details.
31
32     You should have received a copy of the GNU General Public License
33     along with this program; if not, write to the Free Software
34     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 */
36
37 #include <sys/types.h>
38 #include <string.h>
39
40 #include <mad.h>
41
42 #include "madshim.h"
43
44 /* XING parsing is from the MAD winamp input plugin */
45
46 struct xing {
47   int flags;
48   unsigned long frames;
49   unsigned long bytes;
50   unsigned char toc[100];
51   long scale;
52 };
53
54 enum {
55   XING_FRAMES = 0x0001,
56   XING_BYTES  = 0x0002,
57   XING_TOC    = 0x0004,
58   XING_SCALE  = 0x0008
59 };
60
61 # define XING_MAGIC     (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
62
63 static
64 int parse_xing(struct xing *xing, struct mad_bitptr ptr, unsigned int bitlen)
65 {
66   if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC)
67     goto fail;
68
69   xing->flags = mad_bit_read(&ptr, 32);
70   bitlen -= 64;
71
72   if (xing->flags & XING_FRAMES) {
73     if (bitlen < 32)
74       goto fail;
75
76     xing->frames = mad_bit_read(&ptr, 32);
77     bitlen -= 32;
78   }
79
80   if (xing->flags & XING_BYTES) {
81     if (bitlen < 32)
82       goto fail;
83
84     xing->bytes = mad_bit_read(&ptr, 32);
85     bitlen -= 32;
86   }
87
88   if (xing->flags & XING_TOC) {
89     int i;
90
91     if (bitlen < 800)
92       goto fail;
93
94     for (i = 0; i < 100; ++i)
95       xing->toc[i] = mad_bit_read(&ptr, 8);
96
97     bitlen -= 800;
98   }
99
100   if (xing->flags & XING_SCALE) {
101     if (bitlen < 32)
102       goto fail;
103
104     xing->scale = mad_bit_read(&ptr, 32);
105     bitlen -= 32;
106   }
107
108   return 1;
109
110  fail:
111   xing->flags = 0;
112   return 0;
113 }
114
115 /* Following two functions are adapted from mad_timer, from the 
116    libmad distribution */
117 void scan_mp3(void const *ptr, ssize_t len, buffer *buf)
118 {
119     struct mad_stream stream;
120     struct mad_header header;
121     struct xing xing;
122     
123     unsigned long bitrate = 0;
124     int has_xing = 0;
125     int is_vbr = 0;
126
127     memset(&xing, 0, sizeof xing);
128     
129     mad_stream_init(&stream);
130     mad_header_init(&header);
131
132     mad_stream_buffer(&stream, ptr, len);
133
134     buf->num_frames = 0;
135
136     /* There are three ways of calculating the length of an mp3:
137       1) Constant bitrate: One frame can provide the information
138          needed: # of frames and duration. Just see how long it
139          is and do the division.
140       2) Variable bitrate: Xing tag. It provides the number of 
141          frames. Each frame has the same number of samples, so
142          just use that.
143       3) All: Count up the frames and duration of each frames
144          by decoding each one. We do this if we've no other
145          choice, i.e. if it's a VBR file with no Xing tag.
146     */
147
148     while (1)
149     {
150         if (mad_header_decode(&header, &stream) == -1)
151         {
152             if (MAD_RECOVERABLE(stream.error))
153                 continue;
154             else
155                 break;
156         }
157
158         /* Limit xing testing to the first frame header */
159         if (!buf->num_frames++)
160         {
161             if(parse_xing(&xing, stream.anc_ptr, stream.anc_bitlen))
162             {
163                 is_vbr = 1;
164                 
165                 if (xing.flags & XING_FRAMES)
166                 {
167                     /* We use the Xing tag only for frames. If it doesn't have that
168                        information, it's useless to us and we have to treat it as a
169                        normal VBR file */
170                     has_xing = 1;
171                     buf->num_frames = xing.frames;
172                     break;
173                 }
174             }
175         }                
176
177         /* Test the first n frames to see if this is a VBR file */
178         if (!is_vbr && !(buf->num_frames > 20))
179         {
180             if (bitrate && header.bitrate != bitrate)
181             {
182                 is_vbr = 1;
183             }
184             
185             else
186             {
187                 bitrate = header.bitrate;
188             }
189         }
190         
191         /* We have to assume it's not a VBR file if it hasn't already been
192            marked as one and we've checked n frames for different bitrates */
193         else if (!is_vbr)
194         {
195             break;
196         }
197             
198         mad_timer_add(&buf->duration, header.duration);
199     }
200
201     if (!is_vbr)
202     {
203         double time = (len * 8.0) / (header.bitrate); /* time in seconds */
204         double timefrac = (double)time - ((long)(time));
205         long nsamples = 32 * MAD_NSBSAMPLES(&header); /* samples per frame */
206         
207         /* samplerate is a constant */
208         buf->num_frames = (long) (time * header.samplerate / nsamples);
209
210         mad_timer_set(&buf->duration, (long)time, (long)(timefrac*100), 100);
211     }
212         
213     else if (has_xing)
214     {
215         /* modify header.duration since we don't need it anymore */
216         mad_timer_multiply(&header.duration, buf->num_frames);
217         buf->duration = header.duration;
218     }
219
220     else
221     {
222         /* the durations have been added up, and the number of frames
223            counted. We do nothing here. */
224     }
225     
226     mad_header_finish(&header);
227     mad_stream_finish(&stream);
228 }
229
230 /*
231 Local Variables:
232 c-basic-offset:2
233 comment-column:40
234 End:
235 */