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