changeset 518:087a88d25ba2

new program gsmhr-dec-craft
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 01 Sep 2024 18:58:02 +0000
parents 2d703e1e9107
children 6f8abfe253a4
files .hgignore hrutil/Makefile hrutil/dec-craft.c
diffstat 3 files changed, 341 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Fri Aug 30 08:27:05 2024 +0000
+++ b/.hgignore	Sun Sep 01 18:58:02 2024 +0000
@@ -73,6 +73,7 @@
 ^frtest/gsmfr-preproc$
 
 ^hrutil/gsmhr-cod-parse$
+^hrutil/gsmhr-dec-craft$
 ^hrutil/gsmhr-dec-parse$
 
 ^libgsmhr1/dhf_packed\.c$
--- a/hrutil/Makefile	Fri Aug 30 08:27:05 2024 +0000
+++ b/hrutil/Makefile	Sun Sep 01 18:58:02 2024 +0000
@@ -1,4 +1,4 @@
-PROGS=	gsmhr-cod-parse gsmhr-dec-parse
+PROGS=	gsmhr-cod-parse gsmhr-dec-craft gsmhr-dec-parse
 LIBHR1=	../libgsmhr1/libgsmhr1.a
 
 include ../config.defs
@@ -8,6 +8,9 @@
 gsmhr-cod-parse:	cod-parse.o print-frame.o read-cod.o ${LIBHR1}
 	${CC} ${CFLAGS} -o $@ $^
 
+gsmhr-dec-craft:	dec-craft.o ${LIBHR1}
+	${CC} ${CFLAGS} -o $@ $^
+
 gsmhr-dec-parse:	dec-parse.o print-frame.o read-dec.o ${LIBHR1}
 	${CC} ${CFLAGS} -o $@ $^
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hrutil/dec-craft.c	Sun Sep 01 18:58:02 2024 +0000
@@ -0,0 +1,336 @@
+/*
+ * 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()
+{
+	/* Int_LPC and Mode */
+	dec_frame[4] = 1;
+	dec_frame[5] = 3;
+	/* subframe 1 */
+	dec_frame[6] = 0xFF;
+	dec_frame[7] = 0x1FF;
+	dec_frame[8] = 0x1F;
+	/* subframe 2 */
+	dec_frame[9] = 0xF;
+	dec_frame[10] = 0x1FF;
+	dec_frame[11] = 0x1F;
+	/* subframe 3 */
+	dec_frame[12] = 0xF;
+	dec_frame[13] = 0x1FF;
+	dec_frame[14] = 0x1F;
+	/* subframe 4 */
+	dec_frame[15] = 0xF;
+	dec_frame[16] = 0x1FF;
+	dec_frame[17] = 0x1F;
+}
+
+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] - '0';
+	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_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], "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);
+}