view trau-ul-compile/trau-ul-compile.c @ 42:61181373875d

trau-hr-dump: off-by-one error in file end limit
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 12 Sep 2024 21:58:26 +0000
parents 8e9bbb83bd16
children
line wrap: on
line source

/*
 * This program compile *.tsrc source format into *.tul binary format,
 * see ../doc/TRAU-UL-testing article.
 */

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <tw_gsmfr.h>
#include <gsm_efr.h>

#define	MAX_FIELDS	18

static char *infname, *outfname;
static FILE *inf, *outf;

static int lineno;
static char linebuf[256];
static char *fields[MAX_FIELDS];
static unsigned nfields;

static int is_efr;
static int16_t speech_params[GSMFR_NUM_PARAMS];
static int global_params_set, subframe_count;
static int bfi, bfi_set, sid, sid_set;
static uint8_t crc_inv, crc_inv_set;

static int
get_line()
{
	char *cp;

	if (!fgets(linebuf, sizeof linebuf, inf))
		return 1;
	lineno++;
	if (!index(linebuf, '\n')) {
		fprintf(stderr, "%s line %d: too long or missing newline\n",
			infname, lineno);
		exit(1);
	}
	nfields = 0;
	for (cp = linebuf; ; ) {
		while (isspace(*cp))
			cp++;
		if (*cp == '\0' || *cp == '#')
			break;
		if (nfields >= MAX_FIELDS) {
			fprintf(stderr, "%s line %d: too many fields\n",
				infname, lineno);
			exit(1);
		}
		fields[nfields++] = cp;
		while (*cp && !isspace(*cp))
			cp++;
		if (*cp)
			*cp++ = '\0';
	}
	return 0;
}

static int
get_line_nonempty()
{
	int rc;

	for (;;) {
		rc = get_line();
		if (rc)
			return rc;
		if (nfields)
			return 0;
	}
}

static void
start_new_frame()
{
	if (nfields != 2 || strcmp(fields[1], "{")) {
		fprintf(stderr, "%s line %d: invalid frame opening line\n",
			infname, lineno);
		exit(1);
	}
	global_params_set = 0;
	subframe_count = 0;
	bfi = 0;
	bfi_set = 0;
	sid_set = 0;
	crc_inv = 0;
	crc_inv_set = 0;
}

static void
set_speech_params(start_param)
	unsigned start_param;
{
	unsigned ni, no;
	u_long val;
	char *cp;

	no = start_param;
	for (ni = 1; ni < nfields; ni++) {
		if (!isdigit(fields[ni][0])) {
inv_number:		fprintf(stderr,
			"%s line %d: field \"%s\" is not a valid number\n",
				infname, lineno, fields[ni]);
			exit(1);
		}
		val = strtoul(fields[ni], &cp, 0);
		if (*cp)
			goto inv_number;
		if (val > 0x1FF) {
			fprintf(stderr,
	"%s line %d: number \"%s\" is too large for a speech parameter\n",
				infname, lineno, fields[ni]);
			exit(1);
		}
		speech_params[no++] = val;
	}
}

static void
handle_larc()
{
	if (is_efr) {
		fprintf(stderr, "%s line %d: LARc not valid for EFR\n",
			infname, lineno);
		exit(1);
	}
	if (nfields != 9) {
		fprintf(stderr,
			"%s line %d: wrong number of arguments for LARc\n",
			infname, lineno);
		exit(1);
	}
	if (global_params_set) {
		fprintf(stderr, "%s line %d: LARc already set\n",
			infname, lineno);
		exit(1);
	}
	set_speech_params(0);
	global_params_set = 1;
}

static void
handle_lpc()
{
	if (!is_efr) {
		fprintf(stderr, "%s line %d: LPC not valid for FR\n",
			infname, lineno);
		exit(1);
	}
	if (nfields != 6) {
		fprintf(stderr,
			"%s line %d: wrong number of arguments for LPC\n",
			infname, lineno);
		exit(1);
	}
	if (global_params_set) {
		fprintf(stderr, "%s line %d: LPC already set\n",
			infname, lineno);
		exit(1);
	}
	set_speech_params(0);
	global_params_set = 1;
}

static void
handle_sf()
{
	if (is_efr) {
		if (nfields != 14) {
			fprintf(stderr,
			"%s line %d: wrong number of arguments for EFR sf\n",
				infname, lineno);
			exit(1);
		}
	} else {
		if (nfields != 18) {
			fprintf(stderr,
			"%s line %d: wrong number of arguments for FR sf\n",
				infname, lineno);
			exit(1);
		}
	}
	if (subframe_count >= 4) {
		fprintf(stderr, "%s line %d: too many subframes\n",
			infname, lineno);
		exit(1);
	}
	if (is_efr)
		set_speech_params(5 + subframe_count * 13);
	else
		set_speech_params(8 + subframe_count * 17);
	subframe_count++;
}

static void
handle_bfi()
{
	if (nfields != 2) {
		fprintf(stderr, "%s line %d: BFI takes one argument\n",
			infname, lineno);
		exit(1);
	}
	if (bfi_set) {
		fprintf(stderr, "%s line %d: BFI already set\n",
			infname, lineno);
		exit(1);
	}
	if (fields[1][0] < '0' || fields[1][0] > '1' || fields[1][1]) {
		fprintf(stderr,
		"%s line %d: invalid BFI argument \"%s\" (must be 0 or 1)\n",
			infname, lineno, fields[1]);
		exit(1);
	}
	bfi = fields[1][0] - '0';
	bfi_set = 1;
}

static void
handle_sid()
{
	if (nfields != 2) {
		fprintf(stderr, "%s line %d: SID takes one argument\n",
			infname, lineno);
		exit(1);
	}
	if (sid_set) {
		fprintf(stderr, "%s line %d: SID already set\n",
			infname, lineno);
		exit(1);
	}
	if (fields[1][0] < '0' || fields[1][0] > '2' || fields[1][1]) {
		fprintf(stderr,
		"%s line %d: invalid SID argument \"%s\" (must be 0, 1 or 2)\n",
			infname, lineno, fields[1]);
		exit(1);
	}
	sid = fields[1][0] - '0';
	sid_set = 1;
}

static void
handle_crc_inv()
{
	unsigned nf;

	if (!is_efr) {
		fprintf(stderr, "%s line %d: crc-inv not valid for FR\n",
			infname, lineno);
		exit(1);
	}
	if (nfields < 2) {
		fprintf(stderr,
			"%s line %d: crc-inv requires at least 1 argument\n",
			infname, lineno);
		exit(1);
	}
	if (crc_inv_set) {
		fprintf(stderr, "%s line %d: crc-inv already set\n",
			infname, lineno);
		exit(1);
	}
	for (nf = 1; nf < nfields; nf++) {
		if (!strcasecmp(fields[nf], "all"))
			crc_inv = 0xF8;
		else if (fields[nf][0] >= '0' && fields[nf][0] <= '4' &&
			 !fields[nf][1])
			crc_inv |= 0x80 >> (fields[nf][0] - '0');
		else {
			fprintf(stderr,
		"%s line %d: \"%s\" is not a valid argument to crc-inv\n",
				infname, lineno, fields[nf]);
			exit(1);
		}
	}
	crc_inv_set = 1;
}

static void
process_frame_interior()
{
	int rc;

	for (;;) {
		rc = get_line_nonempty();
		if (rc) {
			fprintf(stderr, "bad input: EOF inside frame block\n");
			exit(1);
		}
		if (!strcasecmp(fields[0], "larc"))
			handle_larc();
		else if (!strcasecmp(fields[0], "lpc"))
			handle_lpc();
		else if (!strcasecmp(fields[0], "sf"))
			handle_sf();
		else if (!strcasecmp(fields[0], "bfi"))
			handle_bfi();
		else if (!strcasecmp(fields[0], "sid"))
			handle_sid();
		else if (!strcasecmp(fields[0], "crc-inv"))
			handle_crc_inv();
		else if (!strcmp(fields[0], "}")) {
			if (nfields != 1) {
				fprintf(stderr,
				"%s line %d: stuff after } not permitted\n",
					infname, lineno);
				exit(1);
			}
			return;
		} else {
			fprintf(stderr,
				"%s line %d: non-understood keyword \"%s\"\n",
				infname, lineno, fields[0]);
			exit(1);
		}
	}
}

static void
finish_frame()
{
	uint8_t bin[34];

	if (!global_params_set) {
		fprintf(stderr, "%s line %d: LARc/LPC parameters not set\n",
			infname, lineno);
		exit(1);
	}
	if (subframe_count != 4) {
		fprintf(stderr, "%s line %d: subframe parameters not set\n",
			infname, lineno);
		exit(1);
	}
	if (is_efr) {
		EFR_params2frame(speech_params, bin);
		if (!sid_set)
			sid = EFR_sid_classify(bin);
		bin[31] = crc_inv;
		bin[32] = 0;
	} else {
		gsmfr_pack_from_array(speech_params, bin);
		if (!sid_set)
			sid = gsmfr_preproc_sid_classify(bin);
	}
	bin[33] = (bfi << 7) | sid;
	fwrite(bin, 1, 34, outf);
}

main(argc, argv)
	char **argv;
{
	int rc;

	if (argc != 3) {
		fprintf(stderr, "usage: %s input.tsrc output.tul\n", argv[0]);
		exit(1);
	}
	infname = argv[1];
	outfname = argv[2];

	inf = fopen(infname, "r");
	if (!inf) {
		perror(infname);
		exit(1);
	}
	outf = fopen(outfname, "w");
	if (!outf) {
		perror(outfname);
		exit(1);
	}

	for (;;) {
		rc = get_line_nonempty();
		if (rc)
			break;
		if (!strcasecmp(fields[0], "frame_fr")) {
			is_efr = 0;
			start_new_frame();
		} else if (!strcasecmp(fields[0], "frame_efr")) {
			is_efr = 1;
			start_new_frame();
		} else {
			fprintf(stderr,
		"%s line %d: expected frame opening line, got something else\n",
				infname, lineno);
			exit(1);
		}
		process_frame_interior();
		finish_frame();
	}
	exit(0);
}