1 #include <stdio.h>
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <fcntl.h>
5 #include <errno.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10
11 struct wav_header {
12 char riff[4];
13 uint32_t chunk_size;
14 char format[4];
15
16 char subchunk1_id[4];
17 uint32_t subchunk1_size;
18 uint16_t audio_format;
19 uint16_t num_channels;
20 uint32_t sample_rate;
21 uint32_t byte_rate;
22 uint16_t block_align;
23 uint16_t bits_per_sample;
24
25 char subchunk2_id[4];
26 uint32_t subchunk2_size;
27 } __attribute__((packed));
28
init_wav_header(struct wav_header * hdr,uint32_t num_samples,uint16_t bits_per_sample,int channels,uint32_t sample_rate)29 static void init_wav_header(struct wav_header *hdr,
30 uint32_t num_samples,
31 uint16_t bits_per_sample,
32 int channels,
33 uint32_t sample_rate)
34 {
35 hdr->riff[0] = 'R';
36 hdr->riff[1] = 'I';
37 hdr->riff[2] = 'F';
38 hdr->riff[3] = 'F';
39
40 hdr->subchunk2_size = num_samples * channels * bits_per_sample / 8;
41
42 hdr->chunk_size = 36 + hdr->subchunk2_size;
43 hdr->format[0] = 'W';
44 hdr->format[1] = 'A';
45 hdr->format[2] = 'V';
46 hdr->format[3] = 'E';
47
48 hdr->subchunk1_id[0] = 'f';
49 hdr->subchunk1_id[1] = 'm';
50 hdr->subchunk1_id[2] = 't';
51 hdr->subchunk1_id[3] = ' ';
52
53 hdr->subchunk1_size = 16;
54 hdr->audio_format = 1; /* PCM */
55 hdr->num_channels = channels;
56 hdr->sample_rate = sample_rate;
57 hdr->byte_rate = sample_rate * channels * bits_per_sample / 8;
58 hdr->block_align = channels * bits_per_sample / 8;
59 hdr->bits_per_sample = bits_per_sample;
60
61 hdr->subchunk2_id[0] = 'd';
62 hdr->subchunk2_id[1] = 'a';
63 hdr->subchunk2_id[2] = 't';
64 hdr->subchunk2_id[3] = 'a';
65 }
66
67 static const int divs_8000[] = { 5, 6, 6, 5 };
68 static const int divs_11025[] = { 4 };
69 static const int divs_22050[] = { 2 };
70 static const int divs_44100[] = { 1 };
71
downsample(const int16_t * in,int16_t * out,int len,int * consumed,const int * divs,int divs_len,int out_stereo)72 int downsample(const int16_t *in, int16_t *out, int len, int *consumed,
73 const int *divs, int divs_len,
74 int out_stereo)
75 {
76 int i, j, lsum, rsum;
77 int di, div;
78 int oi;
79
80 i = 0;
81 oi = 0;
82 di = 0;
83 div = divs[0];
84 while (i + div * 2 <= len) {
85 // printf("div %d, i %d, oi %d\n", div, i, oi);
86 for (j = 0, lsum = 0, rsum = 0; j < div; j++) {
87 lsum += in[i + j * 2];
88 rsum += in[i + j * 2 + 1];
89 }
90 if (!out_stereo)
91 out[oi] = (lsum + rsum) / (div * 2);
92 else {
93 out[oi] = lsum / div;
94 out[oi + 1] = rsum / div;
95 }
96
97 oi += out_stereo + 1;
98 i += div * 2;
99 div = divs[++di % divs_len];
100 }
101
102 // printf("done: i %d, len %d, oi %d\n", i, len, oi);
103 *consumed = i;
104 return oi;
105 }
106
107 #define FAILIF(x, ...) do if (x) { \
108 fprintf(stderr, __VA_ARGS__); \
109 exit(EXIT_FAILURE); \
110 } while (0)
111
main(int argc,char ** argv)112 int main(int argc, char **argv)
113 {
114 int opt, ifd, ofd;
115 int new_rate = -1;
116 const int *divs;
117 int divs_len;
118 int consumed;
119 int channels = -1;
120 char *input = NULL;
121 char *output = NULL;
122 int nr, nr_out, nw;
123 int put_header = 0;
124 int total = 0;
125 const int bits_per_sample = 16;
126
127 struct wav_header src_hdr, dst_hdr;
128
129 int16_t buf[2048];
130
131 while ((opt = getopt(argc, argv, "o:s:c:w")) != -1) {
132 switch (opt) {
133 case 'o':
134 FAILIF(output != NULL, "Multiple output files not supported\n");
135 output = strdup(optarg);
136 break;
137 case 's':
138 new_rate = atoi(optarg);
139 break;
140 case 'c':
141 channels = atoi(optarg);
142 break;
143 case 'w':
144 put_header = 1;
145 break;
146 default: /* '?' */
147 fprintf(stderr, "usage: %s -o<outfile> -s<sampling> -c<channels>\n",
148 *argv);
149 fprintf(stderr, "usage: %s -o<outfile> -s<sampling> -c<channels>\n",
150 *argv);
151 exit(EXIT_FAILURE);
152 }
153 }
154
155 FAILIF(channels != 1 && channels != 2, "-c value must be 1 or 2\n");
156
157 switch(new_rate) {
158 case 8000:
159 divs = divs_8000;
160 divs_len = 4;
161 break;
162 case 11025:
163 divs = divs_11025;
164 divs_len = 1;
165 break;
166 case 22050:
167 divs = divs_22050;
168 divs_len = 1;
169 break;
170 case 44100:
171 divs = divs_44100;
172 divs_len = 1;
173 break;
174 default:
175 FAILIF(1, "rate %d is not supported\n", new_rate);
176 }
177
178 FAILIF(!output, "Expecting an output file name\n");
179 FAILIF(optind >= argc, "Expecting an input file name\n");
180
181 input = argv[optind];
182
183 printf("input file [%s]\n", input);
184 printf("output file [%s]\n", output);
185 printf("new rate: [%d]\n", new_rate);
186
187 ifd = open(input, O_RDONLY);
188 FAILIF(ifd < 0, "Could not open %s: %s\n", input, strerror(errno));
189 ofd = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0666);
190 FAILIF(ofd < 0, "Could not open %s: %s\n", output, strerror(errno));
191
192 if (strstr(input, ".wav")) {
193 FAILIF(read(ifd, &src_hdr, sizeof(src_hdr)) != sizeof(src_hdr),
194 "Failed to read input WAV header: %s\n",
195 strerror(errno));
196 FAILIF(src_hdr.audio_format != 1,
197 "Expecting PCM encoding\n");
198 FAILIF(src_hdr.sample_rate != 44100,
199 "Expecting 44.kHz files\n");
200 FAILIF(src_hdr.num_channels != 2,
201 "Expecting 2-channel files\n");
202 FAILIF(src_hdr.bits_per_sample != bits_per_sample,
203 "Expecting 16-bit PCM files\n");
204 }
205
206 if (put_header)
207 FAILIF(lseek(ofd, sizeof(struct wav_header), SEEK_SET) < 0,
208 "seek error in %s: %s\n", output, strerror(errno));
209
210 consumed = 0;
211 while (1) {
212 nr = read(ifd, buf + consumed, sizeof(buf) - consumed);
213 FAILIF(nr < 0, "could not read from %s: %s\n", input, strerror(errno));
214 if (!nr) {
215 printf("done\n");
216 break;
217 }
218 nr += consumed;
219
220 // printf("resampling %d samples\n", nr / 2);
221 nr_out = downsample(buf, buf, nr / 2, &consumed, divs, divs_len, channels == 2);
222 consumed *= 2;
223 // printf("done: %d samples were generated (consumed %d out of %d bytes)\n", nr_out, consumed, nr);
224
225 if (consumed < nr) {
226 memcpy(buf, buf + consumed, nr - consumed);
227 consumed = nr - consumed;
228 // printf("copied %d bytes to front\n", consumed);
229 }
230 else consumed = 0;
231
232 nr_out *= 2;
233 nw = write(ofd, buf, nr_out);
234 FAILIF(nw < 0, "could not write to %s: %s\n", output, strerror(errno));
235 FAILIF(nw != nr_out, "mismatch, generated %d, wrote %d bytes\n", nr_out, nw);
236 total += nw;
237 }
238
239 if (put_header) {
240 printf("writing WAV header\n");
241 lseek(ofd, 0, SEEK_SET);
242 init_wav_header(&dst_hdr,
243 total * 8 / (channels * bits_per_sample),
244 bits_per_sample,
245 channels,
246 new_rate);
247 FAILIF(write(ofd, &dst_hdr, sizeof(dst_hdr)) != sizeof(dst_hdr),
248 "Could not write WAV header: %s\n", strerror(errno));
249 }
250
251 close(ifd);
252 close(ofd);
253
254 return 0;
255 }
256