view hrutil/dec-craft.c @ 585:3c6bf0d26ee7 default tip

TW-TS-005 reader: fix maximum line length bug TW-TS-005 section 4.1 states: The maximum allowed length of each line is 80 characters, not including the OS-specific newline encoding. The implementation of this line length limit in the TW-TS-005 hex file reader function in the present suite was wrong, such that lines of the full maximum length could not be read. Fix it. Note that this bug affects comment lines too, not just actual RTP payloads. Neither Annex A nor Annex B features an RTP payload format that goes to the maximum of 40 bytes, but if a comment line goes to the maximum allowed length of 80 characters not including the terminating newline, the bug will be triggered, necessitating the present fix.
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 25 Feb 2025 07:49:28 +0000
parents 94f0cc85ad50
children
line wrap: on
line source

/*
 * This program facilitates hand-crafting HRv1 decoder input files (*.dec).
 * It reads a line-based ASCII source and compiles it into binary *.dec format.
 */

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "../libgsmhr1/tw_gsmhr.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 int16_t dec_frame[GSMHR_NUM_PARAMS_DEC];
static unsigned taf_count;

static void
set_all_0()
{
	bzero(dec_frame, sizeof(int16_t) * GSMHR_NUM_PARAMS);
}

static void
set_dhf()
{
	bcopy(gsmhr_dhf_params, dec_frame, sizeof(int16_t) * GSMHR_NUM_PARAMS);
}

static void
set_sid_cw()
{
	gsmhr_set_sid_cw_params(dec_frame);
}

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
set_speech_params(start_param, start_arg)
	unsigned start_param, start_arg;
{
	unsigned ni, no;
	u_long val;
	char *cp;

	no = start_param;
	for (ni = start_arg; ni < nfields; ni++) {
		if (!isxdigit(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, 16);
		if (*cp)
			goto inv_number;
		if (val > 0x7FF) {
			fprintf(stderr,
	"%s line %d: number \"%s\" is too large for a speech parameter\n",
				infname, lineno, fields[ni]);
			exit(1);
		}
		dec_frame[no++] = val;
	}
}

static void
handle_r0()
{
	if (nfields != 2) {
		fprintf(stderr, "%s line %d: R0 takes one argument\n",
			infname, lineno);
		exit(1);
	}
	set_speech_params(0, 1);
}

static void
handle_lpc()
{
	if (nfields != 5) {
		fprintf(stderr, "%s line %d: LPC takes 4 arguments\n",
			infname, lineno);
		exit(1);
	}
	set_speech_params(1, 1);
}

static void
handle_mode()
{
	if (nfields != 2) {
		fprintf(stderr, "%s line %d: Mode takes one argument\n",
			infname, lineno);
		exit(1);
	}
	set_speech_params(5, 1);
}

static void
handle_sf()
{
	unsigned sf_0_based;

	if (nfields != 5) {
		fprintf(stderr, "%s line %d: sf takes 4 arguments\n",
			infname, lineno);
		exit(1);
	}
	if (fields[1][0] < '1' || fields[1][0] > '4' || fields[1][1]) {
		fprintf(stderr,
		"%s line %d: first argument to sf must be in range [1,4]\n",
			infname, lineno);
		exit(1);
	}
	sf_0_based = fields[1][0] - '1';
	set_speech_params(6 + sf_0_based * 3, 2);
}

static void
handle_bfi()
{
	if (nfields != 2) {
		fprintf(stderr, "%s line %d: BFI takes one argument\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);
	}
	dec_frame[18] = fields[1][0] - '0';
}

static void
handle_bfi_nodata()
{
	dec_frame[18] = 2;
}

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

static void
handle_sid()
{
	if (nfields != 2) {
		fprintf(stderr, "%s line %d: SID takes one argument\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);
	}
	dec_frame[20] = fields[1][0] - '0';
}

static void
set_taf_count()
{
	if (nfields != 2) {
		fprintf(stderr, "%s line %d: taf-count takes one argument\n",
			infname, lineno);
		exit(1);
	}
	taf_count = atoi(fields[1]);
	if (taf_count > 11) {
		fprintf(stderr,
			"%s line %d: taf-count argument \"%s\" is invalid\n",
			infname, lineno, fields[1]);
		exit(1);
	}
}

static void
emit_frame()
{
	if (gsmhr_check_common_params(dec_frame) < 0) {
		fprintf(stderr,
	"%s line %d: one of the preceding speech parameters is out of range\n",
			infname, lineno);
		exit(1);
	}
	dec_frame[21] = (taf_count == 11);
	fwrite(dec_frame, 2, GSMHR_NUM_PARAMS_DEC, outf);
	taf_count++;
	if (taf_count >= 12)
		taf_count = 0;
}

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

	if (argc != 3) {
		fprintf(stderr, "usage: %s input.src output.dec\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], "all-0"))
			set_all_0();
		else if (!strcasecmp(fields[0], "dhf"))
			set_dhf();
		else if (!strcasecmp(fields[0], "r0"))
			handle_r0();
		else if (!strcasecmp(fields[0], "lpc"))
			handle_lpc();
		else if (!strcasecmp(fields[0], "mode"))
			handle_mode();
		else if (!strcasecmp(fields[0], "sf"))
			handle_sf();
		else if (!strcasecmp(fields[0], "bfi"))
			handle_bfi();
		else if (!strcasecmp(fields[0], "bfi-nodata"))
			handle_bfi_nodata();
		else if (!strcasecmp(fields[0], "ufi"))
			handle_ufi();
		else if (!strcasecmp(fields[0], "sid"))
			handle_sid();
		else if (!strcasecmp(fields[0], "sid-cw"))
			set_sid_cw();
		else if (!strcasecmp(fields[0], "taf-count"))
			set_taf_count();
		else if (!strcasecmp(fields[0], "emit-frame"))
			emit_frame();
		else {
			fprintf(stderr,
				"%s line %d: non-understood keyword \"%s\"\n",
				infname, lineno, fields[0]);
			exit(1);
		}
	}
	exit(0);
}