1#include <unistd.h>
2#include <stdlib.h>
3#include <stdio.h>
4#include <fcntl.h>
5#include <errno.h>
6#include <stdint.h>
7#include <assert.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <sys/ioctl.h>
11#include <linux/cpcap_audio.h>
12#include <linux/tegra_audio.h>
13
14#define FAILIF(x, ...) do if (x) { \
15    fprintf(stderr, __VA_ARGS__);  \
16    exit(EXIT_FAILURE);            \
17} while (0)
18
19static char buffer[4096];
20
21struct 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
39static 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
77int
78main(int argc, char *argv[])
79{
80    int ifd, ifd_c, ofd, opt, cfd;
81    const char *name;
82    int nr, nw = 0, total = 0;
83    int wave = 0;
84    const int bits_per_sample = 16;
85    int sampling_rate = -1;
86    int num_channels = -1;
87
88    struct tegra_audio_in_config cfg;
89    struct wav_header hdr;
90
91    while ((opt = getopt(argc, argv, "wc:s:")) != -1) {
92        switch (opt) {
93        case 'w':
94            wave = 1;
95            break;
96        case 'c':
97            num_channels = atoi(optarg);
98            assert(num_channels == 1 || num_channels == 2);
99            break;
100        case 's':
101            sampling_rate = atoi(optarg);
102            break;
103        default: /* '?' */
104            fprintf(stderr,
105                    "usage: %s [-w] [-s<rate>] [-c<chans>] <destfile>\n",
106                    *argv);
107            exit(EXIT_FAILURE);
108        }
109    }
110
111    FAILIF(optind >= argc,
112                    "usage: %s [-w] [-s<rate>] [-c<chans>] <destfile>\n",
113                    *argv);
114
115    name = argv[optind];
116
117    printf("> recording into %s\n", name);
118    printf("> sampling rate %d\n", sampling_rate);
119    printf("> channels %d\n", num_channels);
120
121    cfd = open("/dev/audio_ctl", O_RDWR);
122    FAILIF(cfd < 0, "could not open control: %s\n", strerror(errno));
123    if(sampling_rate > 0) {
124        FAILIF(ioctl(cfd, CPCAP_AUDIO_IN_SET_RATE, sampling_rate),
125            "Could not set input sampling rate: %s\n", strerror(errno));
126    }
127
128    ifd = open("/dev/audio1_in", O_RDWR);
129    FAILIF(ifd < 0, "could not open input: %s\n", strerror(errno));
130
131    ifd_c = open("/dev/audio1_in_ctl", O_RDWR);
132    FAILIF(ifd < 0, "could not open input: %s\n", strerror(errno));
133
134    printf("getting audio-input config\n");
135    FAILIF(ioctl(ifd_c, TEGRA_AUDIO_IN_GET_CONFIG, &cfg) < 0,
136           "could not get input config: %s\n", strerror(errno));
137
138    if (num_channels >= 0 || sampling_rate >= 0) {
139        if (num_channels >= 0)
140            cfg.stereo = num_channels == 2;
141//        if (sampling_rate >= 0)
142//            cfg.rate = sampling_rate;
143//  No sample rate conversion in driver
144        cfg.rate = 44100;
145        printf("setting audio-input config (stereo %d, rate %d)\n",
146               cfg.stereo, cfg.rate);
147        FAILIF(ioctl(ifd_c, TEGRA_AUDIO_IN_SET_CONFIG, &cfg) < 0,
148               "could not set input config: %s\n", strerror(errno));
149    }
150
151    if (num_channels < 0) {
152        num_channels = cfg.stereo ? 2 : 1;
153        printf("> channels %d (from config)\n", num_channels);
154    }
155
156    if (sampling_rate < 0) {
157        sampling_rate = cfg.rate;
158        printf("> sampling rate %d (from config)\n", sampling_rate);
159    }
160
161    ofd = open(name, O_WRONLY | O_TRUNC | O_CREAT, 0666);
162    FAILIF(ofd < 0, "could not open %s: %s\n", name, strerror(errno));
163
164    if (wave)
165        FAILIF(lseek(ofd, sizeof(struct wav_header), SEEK_SET) < 0,
166               "seek error: %s\n", strerror(errno));
167
168    do {
169        errno = 0;
170        nr = read(ifd, buffer, sizeof(buffer));
171        FAILIF(nr < 0, "input read error: %s\n", strerror(errno));
172
173        if (!nr) {
174            printf("done recording\n");
175            break;
176        }
177
178        printf("in %d\n", nr);
179
180        nw = write(ofd, buffer, nr);
181        FAILIF(nw < 0, "Could not copy to output: %s\n", strerror(errno));
182        FAILIF(nw != nr, "Mismatch nw = %d nr = %d\n", nw, nr);
183        total += nw;
184    } while (1);
185
186    if (wave) {
187        printf("writing WAV header\n");
188        lseek(ofd, 0, SEEK_SET);
189        init_wav_header(&hdr,
190                        total * 8 / (num_channels * bits_per_sample),
191                        bits_per_sample,
192                        num_channels,
193                        sampling_rate);
194        FAILIF(write(ofd, &hdr, sizeof(hdr)) != sizeof(hdr),
195               "Could not write WAV header: %s\n", strerror(errno));
196    }
197
198    printf("done\n");
199    return 0;
200}
201