blob: 2ad506d2c1985e23d445f6e1e3d30e50cd61f7d8 [file] [log] [blame]
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/cpcap_audio.h>
#include <linux/tegra_audio.h>
#define FAILIF(x, ...) do if (x) { \
fprintf(stderr, __VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (0)
static char buffer[4096];
struct wav_header {
char riff[4];
uint32_t chunk_size;
char format[4];
char subchunk1_id[4];
uint32_t subchunk1_size;
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
char subchunk2_id[4];
uint32_t subchunk2_size;
} __attribute__((packed));
static void init_wav_header(struct wav_header *hdr,
uint32_t num_samples,
uint16_t bits_per_sample,
int channels,
uint32_t sample_rate)
{
hdr->riff[0] = 'R';
hdr->riff[1] = 'I';
hdr->riff[2] = 'F';
hdr->riff[3] = 'F';
hdr->subchunk2_size = num_samples * channels * bits_per_sample / 8;
hdr->chunk_size = 36 + hdr->subchunk2_size;
hdr->format[0] = 'W';
hdr->format[1] = 'A';
hdr->format[2] = 'V';
hdr->format[3] = 'E';
hdr->subchunk1_id[0] = 'f';
hdr->subchunk1_id[1] = 'm';
hdr->subchunk1_id[2] = 't';
hdr->subchunk1_id[3] = ' ';
hdr->subchunk1_size = 16;
hdr->audio_format = 1; /* PCM */
hdr->num_channels = channels;
hdr->sample_rate = sample_rate;
hdr->byte_rate = sample_rate * channels * bits_per_sample / 8;
hdr->block_align = channels * bits_per_sample / 8;
hdr->bits_per_sample = bits_per_sample;
hdr->subchunk2_id[0] = 'd';
hdr->subchunk2_id[1] = 'a';
hdr->subchunk2_id[2] = 't';
hdr->subchunk2_id[3] = 'a';
}
int
main(int argc, char *argv[])
{
int ifd, ifd_c, ofd, opt, cfd;
const char *name;
int nr, nw = 0, total = 0;
int wave = 0;
const int bits_per_sample = 16;
int sampling_rate = -1;
int num_channels = -1;
struct tegra_audio_in_config cfg;
struct wav_header hdr;
while ((opt = getopt(argc, argv, "wc:s:")) != -1) {
switch (opt) {
case 'w':
wave = 1;
break;
case 'c':
num_channels = atoi(optarg);
assert(num_channels == 1 || num_channels == 2);
break;
case 's':
sampling_rate = atoi(optarg);
break;
default: /* '?' */
fprintf(stderr,
"usage: %s [-w] [-s<rate>] [-c<chans>] <destfile>\n",
*argv);
exit(EXIT_FAILURE);
}
}
FAILIF(optind >= argc,
"usage: %s [-w] [-s<rate>] [-c<chans>] <destfile>\n",
*argv);
name = argv[optind];
printf("> recording into %s\n", name);
printf("> sampling rate %d\n", sampling_rate);
printf("> channels %d\n", num_channels);
cfd = open("/dev/audio_ctl", O_RDWR);
FAILIF(cfd < 0, "could not open control: %s\n", strerror(errno));
if(sampling_rate > 0) {
FAILIF(ioctl(cfd, CPCAP_AUDIO_IN_SET_RATE, sampling_rate),
"Could not set input sampling rate: %s\n", strerror(errno));
}
ifd = open("/dev/audio1_in", O_RDWR);
FAILIF(ifd < 0, "could not open input: %s\n", strerror(errno));
ifd_c = open("/dev/audio1_in_ctl", O_RDWR);
FAILIF(ifd < 0, "could not open input: %s\n", strerror(errno));
printf("getting audio-input config\n");
FAILIF(ioctl(ifd_c, TEGRA_AUDIO_IN_GET_CONFIG, &cfg) < 0,
"could not get input config: %s\n", strerror(errno));
if (num_channels >= 0 || sampling_rate >= 0) {
if (num_channels >= 0)
cfg.stereo = num_channels == 2;
// if (sampling_rate >= 0)
// cfg.rate = sampling_rate;
// No sample rate conversion in driver
cfg.rate = 44100;
printf("setting audio-input config (stereo %d, rate %d)\n",
cfg.stereo, cfg.rate);
FAILIF(ioctl(ifd_c, TEGRA_AUDIO_IN_SET_CONFIG, &cfg) < 0,
"could not set input config: %s\n", strerror(errno));
}
if (num_channels < 0) {
num_channels = cfg.stereo ? 2 : 1;
printf("> channels %d (from config)\n", num_channels);
}
if (sampling_rate < 0) {
sampling_rate = cfg.rate;
printf("> sampling rate %d (from config)\n", sampling_rate);
}
ofd = open(name, O_WRONLY | O_TRUNC | O_CREAT, 0666);
FAILIF(ofd < 0, "could not open %s: %s\n", name, strerror(errno));
if (wave)
FAILIF(lseek(ofd, sizeof(struct wav_header), SEEK_SET) < 0,
"seek error: %s\n", strerror(errno));
do {
errno = 0;
nr = read(ifd, buffer, sizeof(buffer));
FAILIF(nr < 0, "input read error: %s\n", strerror(errno));
if (!nr) {
printf("done recording\n");
break;
}
printf("in %d\n", nr);
nw = write(ofd, buffer, nr);
FAILIF(nw < 0, "Could not copy to output: %s\n", strerror(errno));
FAILIF(nw != nr, "Mismatch nw = %d nr = %d\n", nw, nr);
total += nw;
} while (1);
if (wave) {
printf("writing WAV header\n");
lseek(ofd, 0, SEEK_SET);
init_wav_header(&hdr,
total * 8 / (num_channels * bits_per_sample),
bits_per_sample,
num_channels,
sampling_rate);
FAILIF(write(ofd, &hdr, sizeof(hdr)) != sizeof(hdr),
"Could not write WAV header: %s\n", strerror(errno));
}
printf("done\n");
return 0;
}