Home | History | Annotate | Download | only in taudio
      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 
     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 
     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 
    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