diff ringtools/imy/convert.c @ 882:fd4c9bc7835d

fc-imy2pwt program written, compiles
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 03 Apr 2022 03:30:27 +0000
parents
children 3e398f9c31a0
line wrap: on
line diff
--- /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);
+		}
+	}
+}