diff ringtools/fc-pwt-comp.c @ 867:dfd98dd46068

ringtools: fc-pwt-comp utility written
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 28 Mar 2022 05:45:31 +0000
parents
children 8eb1f60f6676
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ringtools/fc-pwt-comp.c	Mon Mar 28 05:45:31 2022 +0000
@@ -0,0 +1,203 @@
+/*
+ * This program compiles a FreeCalypso PWT melody from our ASCII source
+ * format into the compact binary format that will be uploaded into
+ * FC device FFS for use by our BUZM melody player engine.
+ *
+ * The binary format going into FFS consists of 4-byte records;
+ * each record has the following format:
+ *
+ * 1 byte: PWT note code [0,47] going directly into hw register
+ * 1 byte: tone volume in [1,64] range, or 0 means rest period
+ * 2 bytes LE: tone or rest duration in TDMA frames
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+static struct pwt_note {
+	char	*name;
+	int	code;
+} pwt_notes_table[] = {
+	{"f4",	47},	/* 349 Hz */
+	{"fs4",	43},	/* 370 Hz */
+	{"g4",	39},	/* 392 Hz */
+	{"gs4",	35},	/* 415 Hz */
+	{"a4",	31},	/* 440 Hz */
+	{"as4",	27},	/* 466 Hz */
+	{"b4",	23},	/* 494 Hz */
+	{"c5",	19},	/* 523 Hz */
+	{"cs5",	15},	/* 554 Hz */
+	{"d5",	11},	/* 587 Hz */
+	{"ds5",	7},	/* 622 Hz */
+	{"e5",	3},	/* 659 Hz */
+	{"f5",	46},	/* 698 Hz */
+	{"fs5",	42},	/* 740 Hz */
+	{"g5",	38},	/* 784 Hz */
+	{"gs5",	34},	/* 831 Hz */
+	{"a5",	30},	/* 880 Hz */
+	{"as5",	26},	/* 932 Hz */
+	{"b5",	22},	/* 988 Hz */
+	{"c6",	18},	/* 1047 Hz */
+	{"cs6",	14},	/* 1109 Hz */
+	{"d6",	10},	/* 1175 Hz */
+	{"ds6",	6},	/* 1245 Hz */
+	{"e6",	2},	/* 1319 Hz */
+	{"f6",	45},	/* 1397 Hz */
+	{"fs6",	41},	/* 1480 Hz */
+	{"g6",	37},	/* 1568 Hz */
+	{"gs6",	33},	/* 1661 Hz */
+	{"a6",	29},	/* 1760 Hz */
+	{"as6",	25},	/* 1865 Hz */
+	{"b6",	21},	/* 1976 Hz */
+	{"c7",	17},	/* 2093 Hz */
+	{"cs7",	13},	/* 2217 Hz */
+	{"d7",	9},	/* 2349 Hz */
+	{"ds7",	5},	/* 2489 Hz */
+	{"e7",	1},	/* 2637 Hz */
+	{"f7",	44},	/* 2794 Hz */
+	{"fs7",	40},	/* 2960 Hz */
+	{"g7",	36},	/* 3136 Hz */
+	{"gs7",	32},	/* 3322 Hz */
+	{"a7",	28},	/* 3520 Hz */
+	{"as7",	24},	/* 3729 Hz */
+	{"b7",	20},	/* 3951 Hz */
+	{"c8",	16},	/* 4186 Hz */
+	{"cs8",	12},	/* 4435 Hz */
+	{"d8",	8},	/* 4699 Hz */
+	{"ds8",	4},	/* 4978 Hz */
+	{"e8",	0},	/* 5274 Hz */
+	/* table search terminator */
+	{0,	-1}
+};
+
+char *infname, *outfname;
+FILE *inf, *outf;
+char linebuf[256], *fields[3];
+int lineno, nfields;
+
+parse_line_into_fields()
+{
+	char *cp;
+
+	cp = index(linebuf, '\n');
+	if (!cp) {
+		fprintf(stderr, "%s line %d: missing newline\n",
+			infname, lineno);
+		exit(1);
+	}
+	cp = linebuf;
+	nfields = 0;
+	for (;;) {
+		while (isspace(*cp))
+			cp++;
+		if (*cp == '\0' || *cp == '#')
+			break;
+		if (nfields >= 3) {
+			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';
+	}
+}
+
+emit_record(freq, volume, duration)
+	unsigned freq, volume, duration;
+{
+	putc(freq, outf);
+	putc(volume, outf);
+	putc(duration & 0xFF, outf);
+	putc(duration >> 8, outf);
+}
+
+process_tone_entry()
+{
+	struct pwt_note *tp;
+	unsigned note_vol, duration;
+
+	for (tp = pwt_notes_table; tp->name; tp++)
+		if (!strcmp(tp->name, fields[0]))
+			break;
+	if (tp->code < 0) {
+		fprintf(stderr, "%s line %d: invalid note name\n",
+			infname, lineno);
+		exit(1);
+	}
+	note_vol = strtoul(fields[1], 0, 0);
+	if (note_vol < 1 || note_vol > 64) {
+		fprintf(stderr, "%s line %d: invalid note volume\n",
+			infname, lineno);
+		exit(1);
+	}
+	duration = strtoul(fields[2], 0, 0);
+	if (duration < 1 || duration > 0xFFFF) {
+		fprintf(stderr,
+			"%s line %d: the duration number is out of range\n",
+			infname, lineno);
+		exit(1);
+	}
+	emit_record(tp->code, note_vol, duration);
+}
+
+process_rest_entry()
+{
+	unsigned duration;
+
+	duration = strtoul(fields[1], 0, 0);
+	if (duration < 1 || duration > 0xFFFF) {
+		fprintf(stderr,
+			"%s line %d: the duration number is out of range\n",
+			infname, lineno);
+		exit(1);
+	}
+	emit_record(0, 0, (unsigned) duration);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s src-file bin-file\n", argv[0]);
+		exit(1);
+	}
+	if (strcmp(argv[1], "-")) {
+		infname = argv[1];
+		inf = fopen(infname, "r");
+		if (!inf) {
+			perror(infname);
+			exit(1);
+		}
+	} else {
+		infname = "stdin";
+		inf = stdin;
+	}
+	outfname = argv[2];
+	outf = fopen(outfname, "w");
+	if (!outf) {
+		perror(outfname);
+		exit(1);
+	}
+
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
+		parse_line_into_fields();
+		if (!nfields)
+			continue;
+		if (nfields == 3)
+			process_tone_entry();
+		else if (nfields == 2 && !strcmp(fields[0], "rest"))
+			process_rest_entry();
+		else {
+			fprintf(stderr, "%s line %d: invalid syntax\n",
+				infname, lineno);
+			exit(1);
+		}
+	}
+	exit(0);
+}