view ffstools/tiaud/compile.c @ 246:2abc086b13ed

tiaud-compile: max FIR coefficient number is 30, not 31
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 26 Aug 2017 05:17:49 +0000
parents 2568a2a8a453
children c458e33060bf
line wrap: on
line source

/*
 * This utility compiles a TI audio mode config file from our ASCII source
 * format into the binary for uploading into Calypso device FFS.
 */

#include <sys/types.h>
#include <sys/file.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdint.h>
#include <endian.h>
#include <stdlib.h>
#include <unistd.h>
#include "binstruct.h"

struct audio_cfg_bin bin;

char *infname;
FILE *inf;
int lineno;

init_nonzero_defaults()
{
	bin.mic_bytes[0] = 3;
	bin.mic_fir[0] = 0x4000;
	bin.speaker_fir[0] = 0x4000;
	bin.sidetone_gain = -5;
}

static long
strtol_wrap(arg)
	char *arg;
{
	char *endptr;
	long ret;

	ret = strtol(arg, &endptr, 0);
	if (*endptr) {
		fprintf(stderr, "%s line %d: \"%s\" is not a valid number\n",
			infname, lineno, arg);
		exit(1);
	}
	return(ret);
}

static void
mic_gain_setting(args)
	char *args;
{
	char *cp, *np;

	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
		fprintf(stderr,
			"%s line %d: mic gain setting requires an argument\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#') {
		fprintf(stderr,
		"%s line %d: mic gain setting takes only one argument\n",
			infname, lineno);
		exit(1);
	}
	bin.mic_bytes[0] = strtol_wrap(np);
}

static void
mic_extra_gain_setting(args)
	char *args;
{
	char *cp, *np;

	if (bin.mic_mode != AUDIO_MICROPHONE_HANDFREE) {
		fprintf(stderr,
	"%s line %d: mic extra-gain setting is only valid in aux mode\n",
			infname, lineno);
		exit(1);
	}
	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
		fprintf(stderr,
		"%s line %d: mic extra-gain setting requires an argument\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#') {
		fprintf(stderr,
		"%s line %d: mic extra-gain setting takes only one argument\n",
			infname, lineno);
		exit(1);
	}
	bin.mic_bytes[1] = strtol_wrap(np);
}

static void
mic_output_bias_setting(args)
	char *args;
{
	char *cp, *np;
	int idx;

	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
		fprintf(stderr,
		"%s line %d: mic output-bias setting requires an argument\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#') {
		fprintf(stderr,
		"%s line %d: mic output-bias setting takes only one argument\n",
			infname, lineno);
		exit(1);
	}
	if (bin.mic_mode != AUDIO_MICROPHONE_HANDFREE)
		idx = 1;
	else
		idx = 2;
	bin.mic_bytes[idx] = strtol_wrap(np);
}

static void
mic_fir_setting(args)
	char *args;
{
	char *cp, *np;
	int idx, start_idx;

	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
missing_arg:	fprintf(stderr,
		"%s line %d: mic fir setting requires two or more arguments\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	start_idx = strtol_wrap(np);
	if (start_idx < 0 || start_idx > 30) {
		fprintf(stderr,
		"%s line %d: FIR coefficient number must be between 0 and 30\n",
			infname, lineno);
		exit(1);
	}
	for (idx = start_idx; ; idx++) {
		while (isspace(*cp))
			cp++;
		if (*cp == '\0' || *cp == '#') {
			if (idx == start_idx)
				goto missing_arg;
			break;
		}
		if (idx > 30) {
			fprintf(stderr,
				"%s line %d: spill past FIR coefficient 30\n",
				infname, lineno);
			exit(1);
		}
		for (np = cp; *cp && !isspace(*cp); cp++)
			;
		if (*cp)
			*cp++ = '\0';
		bin.mic_fir[idx] = htole16(strtol_wrap(np));
	}
}

static void
mic_head_line(args)
	char *args;
{
	char linebuf[512];
	char *cp, *np;

	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
		fprintf(stderr,
			"%s line %d: mic setting requires a mode argument\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	if (!strcmp(np, "default"))
		bin.mic_mode = AUDIO_MICROPHONE_HANDHELD;
	else if (!strcmp(np, "aux"))
		bin.mic_mode = AUDIO_MICROPHONE_HANDFREE;
	else if (!strcmp(np, "headset"))
		bin.mic_mode = AUDIO_MICROPHONE_HEADSET;
	else {
		fprintf(stderr, "%s line %d: unknown mic mode \"%s\"\n",
			infname, lineno, np);
		exit(1);
	}
	while (isspace(*cp))
		cp++;
	if (*cp == '\0' || *cp == '#')
		return;
	if (*cp != '{') {
		fprintf(stderr,
			"%s line %d: invalid syntax on the mic head line\n",
			infname, lineno);
		exit(1);
	}
	for (;;) {
		if (!fgets(linebuf, sizeof linebuf, inf)) {
			fprintf(stderr, "%s: unexpected EOF in mic section\n",
				infname);
			exit(1);
		}
		lineno++;
		for (cp = linebuf; isspace(*cp); cp++)
			;
		if (*cp == '\0' || *cp == '#')
			continue;
		for (np = cp; *cp && !isspace(*cp); cp++)
			;
		if (*cp)
			*cp++ = '\0';
		if (!strcmp(np, "gain"))
			mic_gain_setting(cp);
		else if (!strcmp(np, "extra-gain"))
			mic_extra_gain_setting(cp);
		else if (!strcmp(np, "output-bias"))
			mic_output_bias_setting(cp);
		else if (!strcmp(np, "fir"))
			mic_fir_setting(cp);
		else if (!strcmp(np, "}"))
			break;
		else {
			fprintf(stderr,
				"%s line %d: unknown mic setting \"%s\"\n",
				infname, lineno, np);
			exit(1);
		}
	}
}

static void
speaker_gain_setting(args)
	char *args;
{
	char *cp, *np;

	if (bin.speaker_mode == AUDIO_SPEAKER_BUZZER) {
		fprintf(stderr,
	"%s line %d: speaker gain setting is not valid in buzzer mode\n",
			infname, lineno);
		exit(1);
	}
	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
		fprintf(stderr,
		"%s line %d: speaker gain setting requires an argument\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#') {
		fprintf(stderr,
		"%s line %d: speaker gain setting takes only one argument\n",
			infname, lineno);
		exit(1);
	}
	bin.speaker_bytes[0] = strtol_wrap(np);
}

static void
speaker_audio_filter_setting(args)
	char *args;
{
	char *cp, *np;

	if (bin.speaker_mode == AUDIO_SPEAKER_BUZZER) {
		fprintf(stderr,
      "%s line %d: speaker audio-filter setting is not valid in buzzer mode\n",
			infname, lineno);
		exit(1);
	}
	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
		fprintf(stderr,
	"%s line %d: speaker audio-filter setting requires an argument\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#') {
		fprintf(stderr,
	"%s line %d: speaker audio-filter setting takes only one argument\n",
			infname, lineno);
		exit(1);
	}
	bin.speaker_bytes[1] = strtol_wrap(np);
}

static void
speaker_fir_setting(args)
	char *args;
{
	char *cp, *np;
	int idx, start_idx;

	if (bin.speaker_mode == AUDIO_SPEAKER_BUZZER) {
		fprintf(stderr,
		"%s line %d: speaker fir setting is not valid in buzzer mode\n",
			infname, lineno);
		exit(1);
	}
	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
missing_arg:	fprintf(stderr,
	"%s line %d: speaker fir setting requires two or more arguments\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	start_idx = strtol_wrap(np);
	if (start_idx < 0 || start_idx > 30) {
		fprintf(stderr,
		"%s line %d: FIR coefficient number must be between 0 and 30\n",
			infname, lineno);
		exit(1);
	}
	for (idx = start_idx; ; idx++) {
		while (isspace(*cp))
			cp++;
		if (*cp == '\0' || *cp == '#') {
			if (idx == start_idx)
				goto missing_arg;
			break;
		}
		if (idx > 30) {
			fprintf(stderr,
				"%s line %d: spill past FIR coefficient 30\n",
				infname, lineno);
			exit(1);
		}
		for (np = cp; *cp && !isspace(*cp); cp++)
			;
		if (*cp)
			*cp++ = '\0';
		bin.speaker_fir[idx] = htole16(strtol_wrap(np));
	}
}

static void
speaker_activate_setting(args)
	char *args;
{
	char *cp, *np;

	if (bin.speaker_mode != AUDIO_SPEAKER_BUZZER) {
		fprintf(stderr,
	"%s line %d: speaker activate setting is only valid in buzzer mode\n",
			infname, lineno);
		exit(1);
	}
	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
		fprintf(stderr,
		"%s line %d: speaker activate setting requires an argument\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#') {
		fprintf(stderr,
	"%s line %d: speaker activate setting takes only one argument\n",
			infname, lineno);
		exit(1);
	}
	bin.speaker_bytes[0] = strtol_wrap(np);
}

static void
speaker_head_line(args)
	char *args;
{
	char linebuf[512];
	char *cp, *np;

	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
		fprintf(stderr,
		"%s line %d: speaker setting requires a mode argument\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	if (!strcmp(np, "ear"))
		bin.speaker_mode = AUDIO_SPEAKER_HANDHELD;
	else if (!strcmp(np, "aux"))
		bin.speaker_mode = AUDIO_SPEAKER_HANDFREE;
	else if (!strcmp(np, "headset"))
		bin.speaker_mode = AUDIO_SPEAKER_HEADSET;
	else if (!strcmp(np, "buzzer"))
		bin.speaker_mode = AUDIO_SPEAKER_BUZZER;
	else if (!strcmp(np, "ear+aux"))
		bin.speaker_mode = AUDIO_SPEAKER_HANDHELD_HANDFREE;
	else {
		fprintf(stderr, "%s line %d: unknown speaker mode \"%s\"\n",
			infname, lineno, np);
		exit(1);
	}
	while (isspace(*cp))
		cp++;
	if (*cp == '\0' || *cp == '#')
		return;
	if (*cp != '{') {
		fprintf(stderr,
			"%s line %d: invalid syntax on the speaker head line\n",
			infname, lineno);
		exit(1);
	}
	for (;;) {
		if (!fgets(linebuf, sizeof linebuf, inf)) {
			fprintf(stderr,
				"%s: unexpected EOF in speaker section\n",
				infname);
			exit(1);
		}
		lineno++;
		for (cp = linebuf; isspace(*cp); cp++)
			;
		if (*cp == '\0' || *cp == '#')
			continue;
		for (np = cp; *cp && !isspace(*cp); cp++)
			;
		if (*cp)
			*cp++ = '\0';
		if (!strcmp(np, "gain"))
			speaker_gain_setting(cp);
		else if (!strcmp(np, "audio-filter"))
			speaker_audio_filter_setting(cp);
		else if (!strcmp(np, "fir"))
			speaker_fir_setting(cp);
		else if (!strcmp(np, "activate"))
			speaker_activate_setting(cp);
		else if (!strcmp(np, "}"))
			break;
		else {
			fprintf(stderr,
				"%s line %d: unknown speaker setting \"%s\"\n",
				infname, lineno, np);
			exit(1);
		}
	}
}

static void
voice_path_setting(args)
	char *args;
{
	char *cp, *np;

	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
		fprintf(stderr,
			"%s line %d: voice-path setting requires an argument\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#') {
		fprintf(stderr,
		"%s line %d: voice-path setting takes only one argument\n",
			infname, lineno);
		exit(1);
	}
	bin.voice_path = strtol_wrap(np);
}

static void
sidetone_setting(args)
	char *args;
{
	char *cp, *np;

	for (cp = args; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#') {
		fprintf(stderr,
			"%s line %d: sidetone setting requires an argument\n",
			infname, lineno);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#') {
		fprintf(stderr,
		"%s line %d: sidetone setting takes only one argument\n",
			infname, lineno);
		exit(1);
	}
	bin.sidetone_gain = strtol_wrap(np);
}

static void
aec_setting(args)
	char *args;
{
	char *cp, *np;
	int n;

	cp = args;
	for (n = 0; n < 5; n++) {
		while (isspace(*cp))
			cp++;
		if (*cp == '\0' || *cp == '#') {
argcount_err:		fprintf(stderr,
				"%s line %d: aec setting takes 5 arguments\n",
				infname, lineno);
			exit(1);
		}
		for (np = cp; *cp && !isspace(*cp); cp++)
			;
		if (*cp)
			*cp++ = '\0';
		bin.aec_words[n] = htole16(strtol_wrap(np));
	}
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#')
		goto argcount_err;
}

read_input_main()
{
	char linebuf[512];
	char *cp, *np;

	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
		for (cp = linebuf; isspace(*cp); cp++)
			;
		if (*cp == '\0' || *cp == '#')
			continue;
		for (np = cp; *cp && !isspace(*cp); cp++)
			;
		if (*cp)
			*cp++ = '\0';
		if (!strcmp(np, "voice-path"))
			voice_path_setting(cp);
		else if (!strcmp(np, "mic"))
			mic_head_line(cp);
		else if (!strcmp(np, "speaker"))
			speaker_head_line(cp);
		else if (!strcmp(np, "sidetone"))
			sidetone_setting(cp);
		else if (!strcmp(np, "aec"))
			aec_setting(cp);
		else {
			fprintf(stderr,
			"%s line %d: unknown top-level setting \"%s\"\n",
				infname, lineno, np);
			exit(1);
		}
	}
}

write_bin_output(filename)
	char *filename;
{
	int fd;

	fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
	if (fd < 0) {
		perror(filename);
		exit(1);
	}
	write(fd, &bin, sizeof bin);
	close(fd);
}

main(argc, argv)
	char **argv;
{
	if (argc != 3) {
		fprintf(stderr, "usage: %s infile outfile\n", argv[0]);
		exit(1);
	}
	if (strcmp(argv[1], "-")) {
		infname = argv[1];
		inf = fopen(infname, "r");
		if (!inf) {
			perror(infname);
			exit(1);
		}
	} else {
		inf = stdin;
		infname = "stdin";
	}
	init_nonzero_defaults();
	read_input_main();
	write_bin_output(argv[2]);
	exit(0);
}