view ffstools/tiaud/compile.c @ 964:a96cb97b66a2

ringtools/imy: fix duplicate definition of tdma_durations[] The bug was reported by Vadim Yanitskiy <fixeria@osmocom.org>, although the present fix is slightly different from the contributed patch: because main.c doesn't need this tdma_durations[] array at all, let's simply remove the reference to this array from main.c rather than turn it into an extern. I no longer remember my original thought flow that resulted (by mistake) in tdma_durations[] being multiply defined in main.c and durations.c. My intent might have been to define all globals in main.c and have the reference in durations.c be an extern - and I missed that extern - but without clear memory, I have no certainty. In any case, having this data array defined in the same module that fills it (durations.c) is sensible, so let's make it the new way.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 31 Aug 2023 19:38:18 +0000
parents f7e946389f8b
children
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;
int is_new_aec;

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(name, nwords, args)
	char *name, *args;
{
	char *cp, *np;
	int n;

	cp = args;
	for (n = 0; n < nwords; n++) {
		while (isspace(*cp))
			cp++;
		if (*cp == '\0' || *cp == '#') {
argcount_err:		fprintf(stderr,
				"%s line %d: %s setting takes %d arguments\n",
				infname, lineno, name, nwords);
			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")) {
			is_new_aec = 0;
			aec_setting("aec", 5, cp);
		} else if (!strcmp(np, "aec-new")) {
			is_new_aec = 1;
			aec_setting("aec-new", 12, 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,
	      is_new_aec ? MODE_FILE_SIZE_NEWAEC : MODE_FILE_SIZE_OLDAEC);
	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);
}