1 /*
2 * madplay - MPEG audio decoder and player
3 * Copyright (C) 2000-2004 Robert Leslie
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * $Id: madmix.c,v 1.24 2004/01/23 09:41:31 rob Exp $
20 */
21
22 # ifdef HAVE_CONFIG_H
23 # include "config.h"
24 # endif
25
26 # include "global.h"
27
28 # include <stdio.h>
29 # include <stdarg.h>
30 # include <stdlib.h>
31 # include <string.h>
32 # include <unistd.h>
33
34 # ifdef HAVE_ERRNO_H
35 # include <errno.h>
36 # endif
37
38 # include <mad.h>
39
40 # include "getopt.h"
41 # include "gettext.h"
42
43 # include "audio.h"
44
45 struct audio {
46 char const *fname;
47 FILE *file;
48 int active;
49 mad_fixed_t scale;
50
51 struct mad_frame frame;
52 };
53
54 char const *argv0;
55
56 /*
57 * NAME: error()
58 * DESCRIPTION: show a labeled error message
59 */
60 static
error(char const * id,char const * format,...)61 void error(char const *id, char const *format, ...)
62 {
63 int err;
64 va_list args;
65
66 err = errno;
67
68 if (id)
69 fprintf(stderr, "%s: ", id);
70
71 va_start(args, format);
72
73 if (*format == ':') {
74 if (format[1] == 0) {
75 format = va_arg(args, char const *);
76 errno = err;
77 perror(format);
78 }
79 else {
80 errno = err;
81 perror(format + 1);
82 }
83 }
84 else {
85 vfprintf(stderr, format, args);
86 fputc('\n', stderr);
87 }
88
89 va_end(args);
90 }
91
92 /*
93 * NAME: do_output()
94 * DESCRIPTION: play mixed output
95 */
96 static
do_output(int (* audio)(union audio_control *),struct mad_frame * frame,struct mad_synth * synth)97 int do_output(int (*audio)(union audio_control *),
98 struct mad_frame *frame, struct mad_synth *synth)
99 {
100 union audio_control control;
101 static unsigned int channels;
102 static unsigned long speed;
103
104 if (channels != synth->pcm.channels ||
105 speed != synth->pcm.samplerate) {
106 audio_control_init(&control, AUDIO_COMMAND_CONFIG);
107
108 control.config.channels = synth->pcm.channels;
109 control.config.speed = synth->pcm.samplerate;
110
111 if (audio(&control) == -1) {
112 error("output", audio_error);
113 return -1;
114 }
115
116 channels = synth->pcm.channels;
117 speed = synth->pcm.samplerate;
118 }
119
120 audio_control_init(&control, AUDIO_COMMAND_PLAY);
121
122 control.play.nsamples = synth->pcm.length;
123 control.play.samples[0] = synth->pcm.samples[0];
124 control.play.samples[1] = synth->pcm.samples[1];
125 control.play.mode = AUDIO_MODE_DITHER;
126
127 if (audio(&control) == -1) {
128 error("output", audio_error);
129 return -1;
130 }
131
132 return 0;
133 }
134
135 /*
136 * NAME: do_mix()
137 * DESCRIPTION: perform mixing and audio output
138 */
139 static
do_mix(struct audio * mix,int ninputs,int (* audio)(union audio_control *))140 int do_mix(struct audio *mix, int ninputs, int (*audio)(union audio_control *))
141 {
142 struct mad_frame frame;
143 struct mad_synth synth;
144 int i, count;
145
146 mad_frame_init(&frame);
147 mad_synth_init(&synth);
148
149 count = ninputs;
150
151 while (1) {
152 int ch, s, sb;
153
154 mad_frame_mute(&frame);
155
156 for (i = 0; i < ninputs; ++i) {
157 if (!mix[i].active)
158 continue;
159
160 if (fread(&mix[i].frame, sizeof(mix[i].frame), 1, mix[i].file) != 1) {
161 if (ferror(mix[i].file))
162 error("fread", ":", mix[i].fname);
163 mix[i].active = 0;
164 --count;
165 continue;
166 }
167
168 mix[i].frame.overlap = 0;
169
170 if (frame.header.layer == 0) {
171 frame.header.layer = mix[i].frame.header.layer;
172 frame.header.mode = mix[i].frame.header.mode;
173 frame.header.mode_extension = mix[i].frame.header.mode_extension;
174 frame.header.emphasis = mix[i].frame.header.emphasis;
175
176 frame.header.bitrate = mix[i].frame.header.bitrate;
177 frame.header.samplerate = mix[i].frame.header.samplerate;
178
179 frame.header.flags = mix[i].frame.header.flags;
180 frame.header.private_bits = mix[i].frame.header.private_bits;
181
182 frame.header.duration = mix[i].frame.header.duration;
183 }
184
185 for (ch = 0; ch < 2; ++ch) {
186 for (s = 0; s < 36; ++s) {
187 for (sb = 0; sb < 32; ++sb) {
188 frame.sbsample[ch][s][sb] +=
189 mad_f_mul(mix[i].frame.sbsample[ch][s][sb], mix[i].scale);
190 }
191 }
192 }
193 }
194
195 if (count == 0)
196 break;
197
198 mad_synth_frame(&synth, &frame);
199 do_output(audio, &frame, &synth);
200 }
201
202 mad_synth_finish(&synth);
203 mad_frame_finish(&frame);
204
205 return 0;
206 }
207
208 /*
209 * NAME: audio->init()
210 * DESCRIPTION: initialize the audio output module
211 */
212 static
audio_init(int (* audio)(union audio_control *),char const * path)213 int audio_init(int (*audio)(union audio_control *), char const *path)
214 {
215 union audio_control control;
216
217 audio_control_init(&control, AUDIO_COMMAND_INIT);
218 control.init.path = path;
219
220 if (audio(&control) == -1) {
221 error("audio", audio_error, control.init.path);
222 return -1;
223 }
224
225 return 0;
226 }
227
228 /*
229 * NAME: audio->finish()
230 * DESCRIPTION: terminate the audio output module
231 */
232 static
audio_finish(int (* audio)(union audio_control *))233 int audio_finish(int (*audio)(union audio_control *))
234 {
235 union audio_control control;
236
237 audio_control_init(&control, AUDIO_COMMAND_FINISH);
238
239 if (audio(&control) == -1) {
240 error("audio", audio_error);
241 return -1;
242 }
243
244 return 0;
245 }
246
247 /*
248 * NAME: usage()
249 * DESCRIPTION: display usage message and exit
250 */
251 static
usage(char const * argv0)252 void usage(char const *argv0)
253 {
254 fprintf(stderr, _("Usage: %s input1 [input2 ...]\n"), argv0);
255 }
256
257 /*
258 * NAME: main()
259 * DESCRIPTION: program entry point
260 */
main(int argc,char * argv[])261 int main(int argc, char *argv[])
262 {
263 int opt, ninputs, i, result = 0;
264 int (*output)(union audio_control *) = 0;
265 char const *fname, *opath = 0;
266 FILE *file;
267 struct audio *mix;
268
269 argv0 = argv[0];
270
271 if (argc > 1) {
272 if (strcmp(argv[1], "--version") == 0) {
273 printf("%s - %s\n", mad_version, mad_copyright);
274 printf(_("Build options: %s\n"), mad_build);
275 return 0;
276 }
277 if (strcmp(argv[1], "--help") == 0) {
278 usage(argv[0]);
279 return 0;
280 }
281 }
282
283 while ((opt = getopt(argc, argv, "o:")) != -1) {
284 switch (opt) {
285 case 'o':
286 opath = optarg;
287
288 output = audio_output(&opath);
289 if (output == 0) {
290 error(0, _("%s: unknown output format type"), opath);
291 return 2;
292 }
293 break;
294
295 default:
296 usage(argv[0]);
297 return 1;
298 }
299 }
300
301 if (optind == argc) {
302 usage(argv[0]);
303 return 1;
304 }
305
306 if (output == 0)
307 output = audio_output(0);
308
309 if (audio_init(output, opath) == -1)
310 return 2;
311
312 ninputs = argc - optind;
313
314 mix = malloc(ninputs * sizeof(*mix));
315 if (mix == 0) {
316 error(0, _("not enough memory to allocate mixing buffers"));
317 return 3;
318 }
319
320 printf(_("mixing %d streams\n"), ninputs);
321
322 for (i = 0; i < ninputs; ++i) {
323 if (strcmp(argv[optind + i], "-") == 0) {
324 fname = "stdin";
325 file = stdin;
326 }
327 else {
328 fname = argv[optind + i];
329 file = fopen(fname, "rb");
330 if (file == 0) {
331 error(0, ":", fname);
332 return 4;
333 }
334 }
335
336 mix[i].fname = fname;
337 mix[i].file = file;
338 mix[i].active = 1;
339 mix[i].scale = mad_f_tofixed(1.0); /* / ninputs); */
340 }
341
342 if (do_mix(mix, ninputs, output) == -1)
343 result = 5;
344
345 for (i = 0; i < ninputs; ++i) {
346 file = mix[i].file;
347
348 if (file != stdin) {
349 if (fclose(file) == EOF) {
350 error(0, ":", mix[i].fname);
351 result = 6;
352 }
353 }
354 }
355
356 free(mix);
357
358 if (audio_finish(output) == -1)
359 result = 7;
360
361 return result;
362 }
363