Commit | Line | Data |
---|---|---|
f8f8039f RK |
1 | /* |
2 | * This file is part of DisOrder | |
8d399b30 | 3 | * Copyright (C) 2007-2010 Richard Kettlewell |
f8f8039f | 4 | * |
e7eb3a27 | 5 | * This program is free software: you can redistribute it and/or modify |
f8f8039f | 6 | * it under the terms of the GNU General Public License as published by |
e7eb3a27 | 7 | * the Free Software Foundation, either version 3 of the License, or |
f8f8039f RK |
8 | * (at your option) any later version. |
9 | * | |
e7eb3a27 RK |
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 | * | |
f8f8039f | 15 | * You should have received a copy of the GNU General Public License |
e7eb3a27 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
f8f8039f RK |
17 | */ |
18 | /** @file server/decode.c | |
19 | * @brief General-purpose decoder for use by speaker process | |
20 | */ | |
8d399b30 | 21 | #include "decode.h" |
f8f8039f | 22 | |
f8f8039f | 23 | #include <mad.h> |
572c2899 | 24 | #include <vorbis/vorbisfile.h> |
762806f1 | 25 | |
7b203d74 | 26 | #include <FLAC/stream_decoder.h> |
f8f8039f | 27 | |
ce6c36be | 28 | #include "wav.h" |
f8f8039f | 29 | |
e7eb3a27 | 30 | |
f8f8039f RK |
31 | /** @brief Encoding lookup table type */ |
32 | struct decoder { | |
33 | /** @brief Glob pattern matching file */ | |
34 | const char *pattern; | |
35 | /** @brief Decoder function */ | |
36 | void (*decode)(void); | |
37 | }; | |
38 | ||
8d399b30 RK |
39 | FILE *outputfp; |
40 | const char *path; | |
41 | char input_buffer[INPUT_BUFFER_SIZE]; | |
42 | int input_count; | |
75db8354 | 43 | |
44 | /** @brief Write a block header | |
45 | * @param rate Sample rate in Hz | |
46 | * @param channels Channel count (currently only 1 or 2 supported) | |
47 | * @param bits Bits per sample (must be a multiple of 8, no more than 64) | |
48 | * @param nbytes Total number of data bytes | |
49 | * @param endian @ref ENDIAN_BIG or @ref ENDIAN_LITTLE | |
50 | * | |
51 | * Checks that the sample format is a supported one (so other calls do not have | |
2e9ba080 | 52 | * to) and calls disorder_fatal() on error. |
f8f8039f | 53 | */ |
8d399b30 RK |
54 | void output_header(int rate, |
55 | int channels, | |
56 | int bits, | |
57 | int nbytes, | |
58 | int endian) { | |
39492555 | 59 | struct stream_header header; |
60 | ||
75db8354 | 61 | if(bits <= 0 || bits % 8 || bits > 64) |
2e9ba080 RK |
62 | disorder_fatal(0, "decoding %s: unsupported sample size %d bits", |
63 | path, bits); | |
75db8354 | 64 | if(channels <= 0 || channels > 2) |
2e9ba080 RK |
65 | disorder_fatal(0, "decoding %s: unsupported channel count %d", |
66 | path, channels); | |
75db8354 | 67 | if(rate <= 0) |
2e9ba080 | 68 | disorder_fatal(0, "decoding %s: nonsensical sample rate %dHz", path, rate); |
39492555 | 69 | header.rate = rate; |
70 | header.bits = bits; | |
71 | header.channels = channels; | |
ce6c36be | 72 | header.endian = endian; |
39492555 | 73 | header.nbytes = nbytes; |
74 | if(fwrite(&header, sizeof header, 1, outputfp) < 1) | |
2e9ba080 | 75 | disorder_fatal(errno, "decoding %s: writing format header", path); |
f8f8039f RK |
76 | } |
77 | ||
f8f8039f RK |
78 | /** @brief Lookup table of decoders */ |
79 | static const struct decoder decoders[] = { | |
80 | { "*.mp3", decode_mp3 }, | |
81 | { "*.MP3", decode_mp3 }, | |
572c2899 | 82 | { "*.ogg", decode_ogg }, |
83 | { "*.OGG", decode_ogg }, | |
75db8354 | 84 | { "*.flac", decode_flac }, |
85 | { "*.FLAC", decode_flac }, | |
ce6c36be | 86 | { "*.wav", decode_wav }, |
87 | { "*.WAV", decode_wav }, | |
f8f8039f RK |
88 | { 0, 0 } |
89 | }; | |
90 | ||
91 | static const struct option options[] = { | |
92 | { "help", no_argument, 0, 'h' }, | |
93 | { "version", no_argument, 0, 'V' }, | |
94 | { 0, 0, 0, 0 } | |
95 | }; | |
96 | ||
97 | /* Display usage message and terminate. */ | |
98 | static void help(void) { | |
99 | xprintf("Usage:\n" | |
100 | " disorder-decode [OPTIONS] PATH\n" | |
101 | "Options:\n" | |
102 | " --help, -h Display usage message\n" | |
103 | " --version, -V Display version number\n" | |
104 | "\n" | |
105 | "Audio decoder for DisOrder. Only intended to be used by speaker\n" | |
106 | "process, not for normal users.\n"); | |
107 | xfclose(stdout); | |
108 | exit(0); | |
109 | } | |
110 | ||
f8f8039f RK |
111 | int main(int argc, char **argv) { |
112 | int n; | |
113 | const char *e; | |
114 | ||
115 | set_progname(argv); | |
2e9ba080 | 116 | if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "calling setlocale"); |
f8f8039f RK |
117 | while((n = getopt_long(argc, argv, "hV", options, 0)) >= 0) { |
118 | switch(n) { | |
119 | case 'h': help(); | |
3fbdc96d | 120 | case 'V': version("disorder-decode"); |
2e9ba080 | 121 | default: disorder_fatal(0, "invalid option"); |
f8f8039f RK |
122 | } |
123 | } | |
124 | if(optind >= argc) | |
2e9ba080 | 125 | disorder_fatal(0, "missing filename"); |
f8f8039f | 126 | if(optind + 1 < argc) |
2e9ba080 | 127 | disorder_fatal(0, "excess arguments"); |
f8f8039f RK |
128 | if((e = getenv("DISORDER_RAW_FD"))) { |
129 | if(!(outputfp = fdopen(atoi(e), "wb"))) | |
2e9ba080 | 130 | disorder_fatal(errno, "fdopen"); |
f8f8039f RK |
131 | } else |
132 | outputfp = stdout; | |
133 | path = argv[optind]; | |
134 | for(n = 0; | |
135 | decoders[n].pattern | |
136 | && fnmatch(decoders[n].pattern, path, 0) != 0; | |
137 | ++n) | |
138 | ; | |
139 | if(!decoders[n].pattern) | |
2e9ba080 | 140 | disorder_fatal(0, "cannot determine file type for %s", path); |
f8f8039f RK |
141 | decoders[n].decode(); |
142 | xfclose(outputfp); | |
143 | return 0; | |
144 | } | |
145 | ||
146 | /* | |
147 | Local Variables: | |
148 | c-basic-offset:2 | |
149 | comment-column:40 | |
150 | fill-column:79 | |
151 | indent-tabs-mode:nil | |
152 | End: | |
153 | */ |