• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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