1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <errno.h>
8 #include <stdint.h>
9 #include <assert.h>
10 #include <string.h>
11
12 #include <sys/ioctl.h>
13
14 #define FAILIF(x, ...) do if (x) { \
15 fprintf(stderr, __VA_ARGS__); \
16 exit(EXIT_FAILURE); \
17 } while (0)
18
19 static char buffer[4096];
20
21 struct wav_header {
22 char riff[4];
23 uint32_t chunk_size;
24 char format[4];
25
26 char subchunk1_id[4];
27 uint32_t subchunk1_size;
28 uint16_t audio_format;
29 uint16_t num_channels;
30 uint32_t sample_rate;
31 uint32_t byte_rate;
32 uint16_t block_align;
33 uint16_t bits_per_sample;
34
35 char subchunk2_id[4];
36 uint32_t subchunk2_size;
37 } __attribute__((packed));
38
init_wav_header(struct wav_header * hdr,uint32_t num_samples,uint16_t bits_per_sample,int channels,uint32_t sample_rate)39 static void init_wav_header(struct wav_header *hdr,
40 uint32_t num_samples,
41 uint16_t bits_per_sample,
42 int channels,
43 uint32_t sample_rate)
44 {
45 hdr->riff[0] = 'R';
46 hdr->riff[1] = 'I';
47 hdr->riff[2] = 'F';
48 hdr->riff[3] = 'F';
49
50 hdr->subchunk2_size = num_samples * channels * bits_per_sample / 8;
51
52 hdr->chunk_size = 36 + hdr->subchunk2_size;
53 hdr->format[0] = 'W';
54 hdr->format[1] = 'A';
55 hdr->format[2] = 'V';
56 hdr->format[3] = 'E';
57
58 hdr->subchunk1_id[0] = 'f';
59 hdr->subchunk1_id[1] = 'm';
60 hdr->subchunk1_id[2] = 't';
61 hdr->subchunk1_id[3] = ' ';
62
63 hdr->subchunk1_size = 16;
64 hdr->audio_format = 1; /* PCM */
65 hdr->num_channels = channels;
66 hdr->sample_rate = sample_rate;
67 hdr->byte_rate = sample_rate * channels * bits_per_sample / 8;
68 hdr->block_align = channels * bits_per_sample / 8;
69 hdr->bits_per_sample = bits_per_sample;
70
71 hdr->subchunk2_id[0] = 'd';
72 hdr->subchunk2_id[1] = 'a';
73 hdr->subchunk2_id[2] = 't';
74 hdr->subchunk2_id[3] = 'a';
75 }
76
77 int
main(int argc,char * argv[])78 main(int argc, char *argv[])
79 {
80 int ifd, ofd;
81 int nr, nw = 0, total = 0;
82 struct wav_header hdr;
83
84 int opt;
85 char * output = NULL;
86 char * input = NULL;
87 int bits_per_sample = 16;
88 int sampling_rate = -1;
89 int num_channels = -1;
90
91 while ((opt = getopt(argc, argv, "o:c:b:s:")) != -1) {
92 switch (opt) {
93 case 'o':
94 output = strdup(optarg);
95 break;
96 case 'c':
97 num_channels = atoi(optarg);
98 assert(num_channels == 1 || num_channels == 2);
99 break;
100 case 'b':
101 bits_per_sample = atoi(optarg);
102 break;
103 case 's':
104 sampling_rate = atoi(optarg);
105 break;
106 default: /* '?' */
107 fprintf(stderr, "Usage: %s [-ooutfile] -c2 -b16 -s44100 infile\n",
108 argv[0]);
109 exit(EXIT_FAILURE);
110 }
111 }
112
113 assert(sampling_rate >= 0);
114 assert(num_channels >= 0);
115
116 FAILIF(!output, "Expecting an output file name\n");
117 FAILIF(optind >= argc, "Expecting an input file name\n");
118
119 input = argv[optind];
120
121 printf("> input %s\n", input);
122 printf("> output %s\n", output);
123 printf("> bits per sample %d\n", bits_per_sample);
124 printf("> sampling rate %d\n", sampling_rate);
125 printf("> channels %d\n", num_channels);
126
127 ofd = open(output, O_WRONLY | O_CREAT, 0777);
128 FAILIF(ofd < 0, "could not open %s: %s\n", output, strerror(errno));
129
130 ifd = open(input, O_RDONLY);
131 FAILIF(ifd < 0, "could not open %s: %s\n", input, strerror(errno));
132
133 lseek(ofd, sizeof(struct wav_header), SEEK_SET);
134
135 do {
136 nr = read(ifd, buffer, sizeof(buffer));
137 FAILIF(nr < 0, "Could not read from input: %s\n", strerror(errno));
138 if (!nr) {
139 printf("done recording\n");
140 break;
141 }
142 nw = write(ofd, buffer, nr);
143 FAILIF(nw < 0, "Could not copy to output: %s\n", strerror(errno));
144 FAILIF(nw != nr, "Mismatch nw = %d nr = %d\n", nw, nr);
145 total += nw;
146 } while (1);
147
148 printf("writing WAV header\n");
149 lseek(ofd, 0, SEEK_SET);
150 init_wav_header(&hdr,
151 total * 8 / (num_channels * bits_per_sample),
152 bits_per_sample,
153 num_channels,
154 sampling_rate);
155
156 FAILIF(write(ofd, &hdr, sizeof(hdr)) != sizeof(hdr),
157 "Could not write WAV header: %s\n", strerror(errno));
158
159 printf("done\n");
160 return 0;
161 }
162