view ringtools/imy/convert.c @ 886:0d6814238109

fc-imy2pwt: stop reading input on END:IMELODY
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 03 Apr 2022 04:00:09 +0000
parents fd4c9bc7835d
children 3e398f9c31a0
line wrap: on
line source

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