diff options
author | Kyle McMartin <kyle@debian.org> | 2004-06-06 01:23:28 +0200 |
---|---|---|
committer | Kyle McMartin <kyle@debian.org> | 2004-06-06 01:23:28 +0200 |
commit | 194c0bbde98196ce6871bd21a9e3d52a771f8dfd (patch) | |
tree | f77af65ebcb4c1fa9d33dce45d206407a98c646d /madmix.c |
Import madplay_0.15.2b.orig.tar.gz
[dgit import orig madplay_0.15.2b.orig.tar.gz]
Diffstat (limited to 'madmix.c')
-rw-r--r-- | madmix.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/madmix.c b/madmix.c new file mode 100644 index 0000000..f6c5276 --- /dev/null +++ b/madmix.c @@ -0,0 +1,362 @@ +/* + * madplay - MPEG audio decoder and player + * Copyright (C) 2000-2004 Robert Leslie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: madmix.c,v 1.24 2004/01/23 09:41:31 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include "global.h" + +# include <stdio.h> +# include <stdarg.h> +# include <stdlib.h> +# include <string.h> +# include <unistd.h> + +# ifdef HAVE_ERRNO_H +# include <errno.h> +# endif + +# include <mad.h> + +# include "getopt.h" +# include "gettext.h" + +# include "audio.h" + +struct audio { + char const *fname; + FILE *file; + int active; + mad_fixed_t scale; + + struct mad_frame frame; +}; + +char const *argv0; + +/* + * NAME: error() + * DESCRIPTION: show a labeled error message + */ +static +void error(char const *id, char const *format, ...) +{ + int err; + va_list args; + + err = errno; + + if (id) + fprintf(stderr, "%s: ", id); + + va_start(args, format); + + if (*format == ':') { + if (format[1] == 0) { + format = va_arg(args, char const *); + errno = err; + perror(format); + } + else { + errno = err; + perror(format + 1); + } + } + else { + vfprintf(stderr, format, args); + fputc('\n', stderr); + } + + va_end(args); +} + +/* + * NAME: do_output() + * DESCRIPTION: play mixed output + */ +static +int do_output(int (*audio)(union audio_control *), + struct mad_frame *frame, struct mad_synth *synth) +{ + union audio_control control; + static unsigned int channels; + static unsigned long speed; + + if (channels != synth->pcm.channels || + speed != synth->pcm.samplerate) { + audio_control_init(&control, AUDIO_COMMAND_CONFIG); + + control.config.channels = synth->pcm.channels; + control.config.speed = synth->pcm.samplerate; + + if (audio(&control) == -1) { + error("output", audio_error); + return -1; + } + + channels = synth->pcm.channels; + speed = synth->pcm.samplerate; + } + + audio_control_init(&control, AUDIO_COMMAND_PLAY); + + control.play.nsamples = synth->pcm.length; + control.play.samples[0] = synth->pcm.samples[0]; + control.play.samples[1] = synth->pcm.samples[1]; + control.play.mode = AUDIO_MODE_DITHER; + + if (audio(&control) == -1) { + error("output", audio_error); + return -1; + } + + return 0; +} + +/* + * NAME: do_mix() + * DESCRIPTION: perform mixing and audio output + */ +static +int do_mix(struct audio *mix, int ninputs, int (*audio)(union audio_control *)) +{ + struct mad_frame frame; + struct mad_synth synth; + int i, count; + + mad_frame_init(&frame); + mad_synth_init(&synth); + + count = ninputs; + + while (1) { + int ch, s, sb; + + mad_frame_mute(&frame); + + for (i = 0; i < ninputs; ++i) { + if (!mix[i].active) + continue; + + if (fread(&mix[i].frame, sizeof(mix[i].frame), 1, mix[i].file) != 1) { + if (ferror(mix[i].file)) + error("fread", ":", mix[i].fname); + mix[i].active = 0; + --count; + continue; + } + + mix[i].frame.overlap = 0; + + if (frame.header.layer == 0) { + frame.header.layer = mix[i].frame.header.layer; + frame.header.mode = mix[i].frame.header.mode; + frame.header.mode_extension = mix[i].frame.header.mode_extension; + frame.header.emphasis = mix[i].frame.header.emphasis; + + frame.header.bitrate = mix[i].frame.header.bitrate; + frame.header.samplerate = mix[i].frame.header.samplerate; + + frame.header.flags = mix[i].frame.header.flags; + frame.header.private_bits = mix[i].frame.header.private_bits; + + frame.header.duration = mix[i].frame.header.duration; + } + + for (ch = 0; ch < 2; ++ch) { + for (s = 0; s < 36; ++s) { + for (sb = 0; sb < 32; ++sb) { + frame.sbsample[ch][s][sb] += + mad_f_mul(mix[i].frame.sbsample[ch][s][sb], mix[i].scale); + } + } + } + } + + if (count == 0) + break; + + mad_synth_frame(&synth, &frame); + do_output(audio, &frame, &synth); + } + + mad_synth_finish(&synth); + mad_frame_finish(&frame); + + return 0; +} + +/* + * NAME: audio->init() + * DESCRIPTION: initialize the audio output module + */ +static +int audio_init(int (*audio)(union audio_control *), char const *path) +{ + union audio_control control; + + audio_control_init(&control, AUDIO_COMMAND_INIT); + control.init.path = path; + + if (audio(&control) == -1) { + error("audio", audio_error, control.init.path); + return -1; + } + + return 0; +} + +/* + * NAME: audio->finish() + * DESCRIPTION: terminate the audio output module + */ +static +int audio_finish(int (*audio)(union audio_control *)) +{ + union audio_control control; + + audio_control_init(&control, AUDIO_COMMAND_FINISH); + + if (audio(&control) == -1) { + error("audio", audio_error); + return -1; + } + + return 0; +} + +/* + * NAME: usage() + * DESCRIPTION: display usage message and exit + */ +static +void usage(char const *argv0) +{ + fprintf(stderr, _("Usage: %s input1 [input2 ...]\n"), argv0); +} + +/* + * NAME: main() + * DESCRIPTION: program entry point + */ +int main(int argc, char *argv[]) +{ + int opt, ninputs, i, result = 0; + int (*output)(union audio_control *) = 0; + char const *fname, *opath = 0; + FILE *file; + struct audio *mix; + + argv0 = argv[0]; + + if (argc > 1) { + if (strcmp(argv[1], "--version") == 0) { + printf("%s - %s\n", mad_version, mad_copyright); + printf(_("Build options: %s\n"), mad_build); + return 0; + } + if (strcmp(argv[1], "--help") == 0) { + usage(argv[0]); + return 0; + } + } + + while ((opt = getopt(argc, argv, "o:")) != -1) { + switch (opt) { + case 'o': + opath = optarg; + + output = audio_output(&opath); + if (output == 0) { + error(0, _("%s: unknown output format type"), opath); + return 2; + } + break; + + default: + usage(argv[0]); + return 1; + } + } + + if (optind == argc) { + usage(argv[0]); + return 1; + } + + if (output == 0) + output = audio_output(0); + + if (audio_init(output, opath) == -1) + return 2; + + ninputs = argc - optind; + + mix = malloc(ninputs * sizeof(*mix)); + if (mix == 0) { + error(0, _("not enough memory to allocate mixing buffers")); + return 3; + } + + printf(_("mixing %d streams\n"), ninputs); + + for (i = 0; i < ninputs; ++i) { + if (strcmp(argv[optind + i], "-") == 0) { + fname = "stdin"; + file = stdin; + } + else { + fname = argv[optind + i]; + file = fopen(fname, "rb"); + if (file == 0) { + error(0, ":", fname); + return 4; + } + } + + mix[i].fname = fname; + mix[i].file = file; + mix[i].active = 1; + mix[i].scale = mad_f_tofixed(1.0); /* / ninputs); */ + } + + if (do_mix(mix, ninputs, output) == -1) + result = 5; + + for (i = 0; i < ninputs; ++i) { + file = mix[i].file; + + if (file != stdin) { + if (fclose(file) == EOF) { + error(0, ":", mix[i].fname); + result = 6; + } + } + } + + free(mix); + + if (audio_finish(output) == -1) + result = 7; + + return result; +} |