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 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 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