changeset 882:fd4c9bc7835d

fc-imy2pwt program written, compiles
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 03 Apr 2022 03:30:27 +0000
parents bb8ad7c0cee8
children 34f0b7eb4b75
files .hgignore ringtools/imy/Makefile ringtools/imy/convert.c ringtools/imy/durations.c ringtools/imy/firstpass.c ringtools/imy/main.c ringtools/imy/sizelimits.h
diffstat 7 files changed, 488 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Sat Apr 02 23:48:53 2022 +0000
+++ b/.hgignore	Sun Apr 03 03:30:27 2022 +0000
@@ -52,6 +52,7 @@
 ^ringtools/fc-e1gen$
 ^ringtools/fc-pwt-comp$
 ^ringtools/fc-ringlist-comp$
+^ringtools/imy/fc-imy2pwt$
 
 ^rvinterf/asyncshell/fc-shell$
 ^rvinterf/ctracedec/ctracedec$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ringtools/imy/Makefile	Sun Apr 03 03:30:27 2022 +0000
@@ -0,0 +1,23 @@
+CC=	gcc
+CFLAGS=	-O2
+PROG=	fc-imy2pwt
+OBJS=	convert.o durations.o firstpass.o main.o
+HDRS=	sizelimits.h
+
+INSTALL_PREFIX=	/opt/freecalypso
+
+INSTBIN=${INSTALL_PREFIX}/bin
+
+all:	${PROG}
+
+${PROG}:	${OBJS}
+	${CC} -o $@ ${OBJS}
+
+${OBJS}:	${HDRS}
+
+install:	${PROG}
+	mkdir -p ${INSTBIN}
+	install -c ${PROG} ${INSTBIN}
+
+clean:
+	rm -f ${PROG} *.o *.out *errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ringtools/imy/convert.c	Sun Apr 03 03:30:27 2022 +0000
@@ -0,0 +1,276 @@
+/*
+ * This module implements the second pass of fc-imy2pwt processing:
+ * stepping through the captured melody and converting it to PWT.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+extern char melody_str_buf[];
+extern unsigned tdma_durations[6][4];
+extern FILE *outF;
+
+static int cur_octave = 4;
+
+static char *pwt_note_names[12] = {"c", "cs", "d", "ds", "e", "f", "fs",
+				   "g", "gs", "a", "as", "b"};
+
+static void
+process_octave_cmd(octchar)
+{
+	if (!isdigit(octchar)) {
+		fprintf(stderr,
+		"melody error: '*' octave prefix not followed by digit\n");
+		exit(1);
+	}
+	cur_octave = octchar - '0';
+}
+
+static int
+process_note(str, type)
+	char *str;
+{
+	int note, dur_basic, dur_mod;
+
+	switch (*str) {
+	case 'c':
+		note = 0;
+		break;
+	case 'd':
+		note = 2;
+		break;
+	case 'e':
+		note = 4;
+		break;
+	case 'f':
+		note = 5;
+		break;
+	case 'g':
+		note = 7;
+		break;
+	case 'a':
+		note = 9;
+		break;
+	case 'b':
+		note = 11;
+		break;
+	default:
+		fprintf(stderr,
+		"melody error: note letter expected after '&' or '#'\n");
+		exit(1);
+	}
+	switch (type) {
+	case 1:
+		if (note == 0 || note == 5) {
+			fprintf(stderr, "melody error: invalid flat note\n");
+			exit(1);
+		}
+		note--;
+		break;
+	case 2:
+		if (note == 4 || note == 11) {
+			fprintf(stderr, "melody error: invalid sharp note\n");
+			exit(1);
+		}
+		note++;
+		break;
+	}
+	if (str[1] < '0' || str[1] > '5') {
+		fprintf(stderr,
+			"melody error: missing expected note duration digit\n");
+		exit(1);
+	}
+	dur_basic = str[1] - '0';
+	switch (str[2]) {
+	case '.':
+		dur_mod = 1;
+		break;
+	case ':':
+		dur_mod = 2;
+		break;
+	case ';':
+		dur_mod = 3;
+		break;
+	default:
+		dur_mod = 0;
+		break;
+	}
+	fprintf(outF, "%s%d\t64\t%u\n", pwt_note_names[note], cur_octave + 1,
+		tdma_durations[dur_basic][dur_mod]);
+	if (dur_mod)
+		return 3;
+	else
+		return 2;
+}
+
+static int
+process_rest(str)
+	char *str;
+{
+	int dur_basic, dur_mod;
+
+	if (str[1] < '0' || str[1] > '5') {
+		fprintf(stderr,
+			"melody error: missing expected rest duration digit\n");
+		exit(1);
+	}
+	dur_basic = str[1] - '0';
+	switch (str[2]) {
+	case '.':
+		dur_mod = 1;
+		break;
+	case ':':
+		dur_mod = 2;
+		break;
+	case ';':
+		dur_mod = 3;
+		break;
+	default:
+		dur_mod = 0;
+		break;
+	}
+	fprintf(outF, "rest\t\t%u\n", tdma_durations[dur_basic][dur_mod]);
+	if (dur_mod)
+		return 3;
+	else
+		return 2;
+}
+
+melody_convert_pass()
+{
+	char *cp, *repeat_start_ptr;
+	int repeat_start_octave, repeat_count, rpt_set;
+
+	repeat_start_ptr = 0;
+	for (cp = melody_str_buf; *cp; ) {
+		/* skip junk first */
+		if (!strncmp(cp, "vibeon", 6)) {
+			cp += 6;
+			continue;
+		}
+		if (!strncmp(cp, "vibeoff", 7)) {
+			cp += 7;
+			continue;
+		}
+		if (!strncmp(cp, "ledon", 5)) {
+			cp += 5;
+			continue;
+		}
+		if (!strncmp(cp, "ledoff", 6)) {
+			cp += 6;
+			continue;
+		}
+		if (!strncmp(cp, "backon", 6)) {
+			cp += 6;
+			continue;
+		}
+		if (!strncmp(cp, "backoff", 7)) {
+			cp += 7;
+			continue;
+		}
+		/* real stuff */
+		switch (*cp) {
+		case '*':
+			process_octave_cmd(cp[1]);
+			cp += 2;
+			continue;
+		case 'c':
+		case 'd':
+		case 'e':
+		case 'f':
+		case 'g':
+		case 'a':
+		case 'b':
+			cp += process_note(cp, 0);
+			continue;
+		case '&':
+			cp++;
+			cp += process_note(cp, 1);
+			continue;
+		case '#':
+			cp++;
+			cp += process_note(cp, 2);
+			continue;
+		case 'r':
+			cp += process_rest(cp);
+			continue;
+		case 'V':
+			/* skip unimplemented volume control */
+			cp++;
+			if (*cp == '+' || *cp == '-') {
+				cp++;
+				continue;
+			}
+			if (!isdigit(*cp)) {
+				fprintf(stderr,
+				"melody error: invalid character after 'V'\n");
+				exit(1);
+			}
+			if (*cp == '1' && cp[1] >= '0' && cp[1] <= '5')
+				cp += 2;
+			else
+				cp++;
+			continue;
+		case '(':
+			if (repeat_start_ptr) {
+				fprintf(stderr,
+					"melody error: nested repeat\n");
+				exit(1);
+			}
+			cp++;
+			repeat_start_ptr = cp;
+			repeat_start_octave = cur_octave;
+			repeat_count = 0;
+			continue;
+		case '@':
+			if (!repeat_start_ptr) {
+				fprintf(stderr,
+				"melody error: '@' not in repeat block\n");
+				exit(1);
+			}
+			cp++;
+			if (!isdigit(*cp)) {
+				fprintf(stderr,
+				"melody error: '@' not followed by digit\n");
+				exit(1);
+			}
+			rpt_set = *cp - '0';
+			if (!rpt_set) {
+				fprintf(stderr,
+			"melody error: infinite repeat not supported\n");
+				exit(1);
+			}
+			cp++;
+			if (!repeat_count)
+				repeat_count = rpt_set;
+			continue;
+		case ')':
+			if (!repeat_start_ptr) {
+				fprintf(stderr,
+				"melody error: ')' without opening '('\n");
+				exit(1);
+			}
+			if (!repeat_count) {
+				fprintf(stderr,
+				"melody error: repeat block without count\n");
+				exit(1);
+			}
+			repeat_count--;
+			if (repeat_count) {
+				cp = repeat_start_ptr;
+				cur_octave = repeat_start_octave;
+			} else {
+				cp++;
+				repeat_start_ptr = 0;
+			}
+			continue;
+		default:
+			fprintf(stderr,
+				"melody error: non-understood character\n");
+			exit(1);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ringtools/imy/durations.c	Sun Apr 03 03:30:27 2022 +0000
@@ -0,0 +1,31 @@
+/*
+ * This module implements the step of precomputing various note durations
+ * in TDMA frames.
+ */
+
+extern unsigned beats_per_min;
+unsigned tdma_durations[6][4];
+
+static float modifier_values[4] = {1.0f, 1.5f, 1.75f, 2.0f / 3.0f};
+
+compute_note_durations()
+{
+	float beat_ref, basic_ms[6], dur_ms;
+	unsigned dur_tdma;
+	int i, j;
+
+	beat_ref = 60000.0f / beats_per_min;
+	basic_ms[0] = beat_ref * 4.0f;
+	basic_ms[1] = beat_ref * 2.0f;
+	basic_ms[2] = beat_ref;
+	basic_ms[3] = beat_ref / 2.0f;
+	basic_ms[4] = beat_ref / 4.0f;
+	basic_ms[5] = beat_ref / 8.0f;
+	for (i = 0; i < 6; i++) {
+		for (j = 0; j < 4; j++) {
+			dur_ms = basic_ms[i] * modifier_values[j];
+			dur_tdma = dur_ms * 13.0f / 60.0f + 0.5f;
+			tdma_durations[i][j] = dur_tdma;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ringtools/imy/firstpass.c	Sun Apr 03 03:30:27 2022 +0000
@@ -0,0 +1,118 @@
+/*
+ * This module implements the first pass of fc-imy2pwt processing:
+ * reading and parsing the iMelody input file at the level of lines,
+ * storing the melody for subsequent processing and catching the
+ * optional BEAT: line, if present.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "sizelimits.h"
+
+extern char *imy_filename;
+extern char melody_str_buf[MELODY_BUF_SIZE];
+extern unsigned beats_per_min;
+
+static char *melody_write_ptr, *melody_write_endp;
+static int lineno;
+
+static void
+copy_melody_str(line)
+	char *line;
+{
+	char *cp, *np;
+
+	cp = line;
+	while (isspace(*cp))
+		cp++;
+	if (!*cp) {
+		fprintf(stderr, "%s line %d: empty melody line\n",
+			imy_filename, lineno);
+		exit(1);
+	}
+	for (np = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp) {
+		fprintf(stderr, "%s line %d: single melody string expected\n",
+			imy_filename, lineno);
+		exit(1);
+	}
+	if (strlen(np) > (melody_write_endp - melody_write_ptr)) {
+		fprintf(stderr, "%s line %d: melody buffer size exceeded\n",
+			imy_filename, lineno);
+		exit(1);
+	}
+	strcpy(melody_write_ptr, np);
+	melody_write_ptr += strlen(np);
+}
+
+static void
+process_beat_line(line)
+	char *line;
+{
+	if (!isdigit(*line)) {
+		fprintf(stderr, "%s line %d: number expected on BEAT: line\n",
+			imy_filename, lineno);
+		exit(1);
+	}
+	beats_per_min = atoi(line);
+	if (beats_per_min < 25 || beats_per_min > 900) {
+		fprintf(stderr, "%s line %d: bpm number is out of range\n",
+			imy_filename, lineno);
+		exit(1);
+	}
+}
+
+read_imy_firstpass()
+{
+	FILE *inf;
+	char linebuf[LINE_BUF_SIZE];
+	int prev_line_is_melody;
+
+	inf = fopen(imy_filename, "r");
+	if (!inf) {
+		perror(imy_filename);
+		exit(1);
+	}
+	melody_write_ptr = melody_str_buf;
+	melody_write_endp = melody_str_buf + MELODY_BUF_SIZE - 1;
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
+		if (!index(linebuf, '\n')) {
+			fprintf(stderr,
+				"%s line %d: too long or unterminated\n",
+				imy_filename, lineno);
+			exit(1);
+		}
+		if (linebuf[0] == ' ' || linebuf[0] == '\t') {
+			if (lineno == 1) {
+				fprintf(stderr,
+					"%s line 1: invalid continuation\n",
+					imy_filename);
+				exit(1);
+			}
+			if (prev_line_is_melody)
+				copy_melody_str(linebuf);
+			continue;
+		}
+		if (!strncasecmp(linebuf, "MELODY:", 7)) {
+			copy_melody_str(linebuf + 7);
+			prev_line_is_melody = 1;
+		} else if (!strncasecmp(linebuf, "BEAT:", 5)) {
+			process_beat_line(linebuf + 5);
+			prev_line_is_melody = 0;
+		} else
+			prev_line_is_melody = 0;
+	}
+	fclose(inf);
+	if (!melody_str_buf[0]) {
+		fprintf(stderr, "error: no melody found in %s\n", imy_filename);
+		exit(1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ringtools/imy/main.c	Sun Apr 03 03:30:27 2022 +0000
@@ -0,0 +1,35 @@
+/*
+ * This file contains the top-level code for fc-imy2pwt.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "sizelimits.h"
+
+char *imy_filename;
+char melody_str_buf[MELODY_BUF_SIZE];
+unsigned beats_per_min = 120;
+unsigned tdma_durations[6][4];
+FILE *outF;
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc < 2 || argc > 3) {
+		fprintf(stderr, "usage: %s imy-file [outfile]\n", argv[0]);
+		exit(1);
+	}
+	imy_filename = argv[1];
+	read_imy_firstpass();
+	compute_note_durations();
+	if (argc > 2) {
+		outF = fopen(argv[2], "w");
+		if (!outF) {
+			perror(argv[2]);
+			exit(1);
+		}
+	} else
+		outF = stdout;
+	melody_convert_pass();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ringtools/imy/sizelimits.h	Sun Apr 03 03:30:27 2022 +0000
@@ -0,0 +1,4 @@
+/* size limit definitions for fc-imy2pwt */
+
+#define	LINE_BUF_SIZE	1024
+#define	MELODY_BUF_SIZE	4096