1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 *
8 * Copyright (C) 2011 by
9 * Nicholai Main
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24 * 02111-1307, USA.
25 *
26 * DESCRIPTION:
27 *
28 *---------------------------------------------------------------------
29 */
30
31
32
33 #include "config.h"
34
35 #include "musicplayer.h"
36
37 #ifndef HAVE_LIBMAD
38 #include <string.h>
39
mp_name(void)40 static const char *mp_name (void)
41 {
42 return "mad mp3 player (DISABLED)";
43 }
44
45
mp_init(int samplerate)46 static int mp_init (int samplerate)
47 {
48 return 0;
49 }
50
51 const music_player_t mp_player =
52 {
53 mp_name,
54 mp_init,
55 NULL,
56 NULL,
57 NULL,
58 NULL,
59 NULL,
60 NULL,
61 NULL,
62 NULL,
63 NULL
64 };
65
66 #else // HAVE_LIBMAD
67
68
69 #include <stdlib.h>
70 #include <string.h>
71 #include "lprintf.h"
72
73 #include "../libmad/mad.h"
74
75 #include "i_sound.h"
76
77 static struct mad_stream Stream;
78 static struct mad_frame Frame;
79 static struct mad_synth Synth;
80 static struct mad_header Header;
81
82
83 static int mp_looping = 0;
84 static int mp_volume = 0; // 0-15
85 static int mp_samplerate_target = 0;
86 static int mp_paused = 0;
87 static int mp_playing = 0;
88
89 static const void *mp_data;
90 static int mp_len;
91
92
93 static int mp_leftoversamps = 0; // number of extra samples
94 // left over in mad decoder
95 static int mp_leftoversamppos = 0;
96
97
mp_name(void)98 static const char *mp_name (void)
99 {
100 return "mad mp3 player";
101 }
102
103
mp_init(int samplerate)104 static int mp_init (int samplerate)
105 {
106 mad_stream_init (&Stream);
107 mad_frame_init (&Frame);
108 mad_synth_init (&Synth);
109 mad_header_init (&Header);
110 mp_samplerate_target = samplerate;
111 return 1;
112 }
113
mp_shutdown(void)114 static void mp_shutdown (void)
115 {
116
117 mad_synth_finish (&Synth);
118 mad_frame_finish (&Frame);
119 mad_stream_finish (&Stream);
120 mad_header_finish (&Header);
121 }
122
mp_registersong(const void * data,unsigned len)123 static const void *mp_registersong (const void *data, unsigned len)
124 {
125 int i;
126 int maxtry;
127 int success = 0;
128
129 // the MP3 standard doesn't include any global file header. the only way to tell filetype
130 // is to start decoding stuff. you can't be too strict however because MP3 is resilient to
131 // crap in the stream.
132
133 // this routine is a bit slower than it could be, but apparently there are lots of files out
134 // there with some dodgy stuff at the beginning.
135
136 // if the stream begins with an ID3v2 magic, search hard and long for our first valid header
137 if (memcmp (data, "ID3", 3) == 0)
138 maxtry = 100;
139 // otherwise, search for not so long
140 else
141 maxtry = 20;
142
143 mad_stream_buffer (&Stream, data, len);
144
145 for (i = 0; i < maxtry; i++)
146 {
147 if (mad_header_decode (&Header, &Stream) != 0)
148 {
149 if (!MAD_RECOVERABLE (Stream.error))
150 {
151 lprintf (LO_WARN, "mad_registersong failed: %s\n", mad_stream_errorstr (&Stream));
152 return NULL;
153 }
154 }
155 else
156 {
157 success++;
158 }
159 }
160
161 // 80% to pass
162 if (success < maxtry * 8 / 10)
163 {
164 lprintf (LO_WARN, "mad_registersong failed\n");
165 return NULL;
166 }
167
168 lprintf (LO_INFO, "mad_registersong succeed. bitrate %lu samplerate %d\n", Header.bitrate, Header.samplerate);
169
170 mp_data = data;
171 mp_len = len;
172 // handle not used
173 return data;
174 }
175
mp_setvolume(int v)176 static void mp_setvolume (int v)
177 {
178 mp_volume = v;
179 }
180
mp_pause(void)181 static void mp_pause (void)
182 {
183 mp_paused = 1;
184 }
185
mp_resume(void)186 static void mp_resume (void)
187 {
188 mp_paused = 0;
189 }
190
mp_unregistersong(const void * handle)191 static void mp_unregistersong (const void *handle)
192 { // nothing to do
193 mp_data = NULL;
194 mp_playing = 0;
195 }
196
mp_play(const void * handle,int looping)197 static void mp_play (const void *handle, int looping)
198 {
199 mad_stream_buffer (&Stream, mp_data, mp_len);
200
201 mp_playing = 1;
202 mp_looping = looping;
203 mp_leftoversamps = 0;
204 mp_leftoversamppos = 0;
205 }
206
mp_stop(void)207 static void mp_stop (void)
208 {
209 mp_playing = 0;
210 }
211
212 // convert from mad's internal fixed point representation
mp_fixtoshort(mad_fixed_t f)213 static INLINE short mp_fixtoshort (mad_fixed_t f)
214 {
215 // clip
216 if (f < -MAD_F_ONE)
217 f = -MAD_F_ONE;
218 if (f > MAD_F_ONE)
219 f = MAD_F_ONE;
220 // apply volume before conversion to 16bit
221 f /= 15;
222 f *= mp_volume;
223 f >>= (MAD_F_FRACBITS - 15);
224
225 return (short) f;
226 }
227
mp_render_ex(void * dest,unsigned nsamp)228 static void mp_render_ex (void *dest, unsigned nsamp)
229 {
230 short *sout = (short *) dest;
231
232 int localerrors = 0;
233
234 if (!mp_playing || mp_paused)
235 {
236 memset (dest, 0, nsamp * 4);
237 return;
238 }
239
240 while (1)
241 {
242 // write any leftover data from last MP3 frame
243 while (mp_leftoversamps > 0 && nsamp > 0)
244 {
245 short s = mp_fixtoshort (Synth.pcm.samples[0][mp_leftoversamppos]);
246 *sout++ = s;
247 if (Synth.pcm.channels == 2)
248 s = mp_fixtoshort (Synth.pcm.samples[1][mp_leftoversamppos]);
249 // if mono, just duplicate the first channel again
250 *sout++ = s;
251
252 mp_leftoversamps -= 1;
253 mp_leftoversamppos += 1;
254 nsamp -= 1;
255 }
256 if (nsamp == 0)
257 return; // done
258
259 // decode next valid MP3 frame
260 while (mad_frame_decode (&Frame, &Stream) != 0)
261 {
262 if (MAD_RECOVERABLE (Stream.error))
263 { // unspecified problem with one frame.
264 // try the next frame, but bail if we get a bunch of crap in a row;
265 // likely indicates a larger problem (and if we don't bail, we could
266 // spend arbitrarily long amounts of time looking for the next good
267 // packet)
268 localerrors++;
269 if (localerrors == 10)
270 {
271 lprintf (LO_WARN, "mad_frame_decode: Lots of errors. Most recent %s\n", mad_stream_errorstr (&Stream));
272 mp_playing = 0;
273 memset (sout, 0, nsamp * 4);
274 return;
275 }
276 }
277 else if (Stream.error == MAD_ERROR_BUFLEN)
278 { // EOF
279 // FIXME: in order to not drop the last frame, there must be at least MAD_BUFFER_GUARD
280 // of extra bytes (with value 0) at the end of the file. current implementation
281 // drops last frame
282 if (mp_looping)
283 { // rewind, then go again
284 mad_stream_buffer (&Stream, mp_data, mp_len);
285 continue;
286 }
287 else
288 { // stop
289 mp_playing = 0;
290 memset (sout, 0, nsamp * 4);
291 return;
292 }
293 }
294 else
295 { // oh well.
296 lprintf (LO_WARN, "mad_frame_decode: Unrecoverable error %s\n", mad_stream_errorstr (&Stream));
297 mp_playing = 0;
298 memset (sout, 0, nsamp * 4);
299 return;
300 }
301 }
302
303 // got a good frame, so synth it and dispatch it.
304 mad_synth_frame (&Synth, &Frame);
305 mp_leftoversamps = Synth.pcm.length;
306 mp_leftoversamppos = 0;
307
308 }
309 // NOT REACHED
310 }
311
312 void I_ResampleStream (void *dest, unsigned nsamp, void (*proc)(void *dest, unsigned nsamp),
313 unsigned sratein, unsigned srateout);
314
mp_render(void * dest,unsigned nsamp)315 static void mp_render (void *dest, unsigned nsamp)
316 {
317 I_ResampleStream (dest, nsamp, mp_render_ex, Header.samplerate, mp_samplerate_target);
318 }
319
320
321 const music_player_t mp_player =
322 {
323 mp_name,
324 mp_init,
325 mp_shutdown,
326 mp_setvolume,
327 mp_pause,
328 mp_resume,
329 mp_registersong,
330 mp_unregistersong,
331 mp_play,
332 mp_stop,
333 mp_render
334 };
335
336 #endif // HAVE_LIBMAD
337