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