view ringtools/fc-pwt-comp.c @ 893:85091e14be9c

fc-buzplay: PWT refactoring, first step
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 03 Apr 2022 08:00:50 +0000
parents 16e259bff02b
children
line wrap: on
line source

/*
 * 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, duration);
}

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

	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);
	}

	nonempty = 0;
	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);
		}
		nonempty = 1;
	}
	if (nonempty)
		exit(0);
	else {
		fprintf(stderr, "error: %s is empty\n", infname);
		exit(1);
	}
}