FreeCalypso > hg > freecalypso-tools
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); +}