usynth

simple midi synth for sndio
git clone git://nsmpr.xyz/usynth.git
Log | Files | Refs

commit 9e45f3b6099022e4be1da1ac19abac92f5a73c38
parent eadf2ce1fa6978a1d9e201d9b4235fa3f5bd33a3
Author: Pavel Renev <rpa@traveller.nsmpr.xyz>
Date:   Thu, 12 Aug 2021 03:27:30 +0300

miditest, buildtest, program assemly, example program

miditest will send a note for one second on midithru/0
buildtest will run program from stdin and print some machine stats
usynth will accept program either as arg or from stdin
example program is replica of previously hardcoded program

Diffstat:
MMakefile | 14++++++++++----
Abuild.c | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abuild.h | 19+++++++++++++++++++
Abuildtest.c | 30++++++++++++++++++++++++++++++
Aexample.prg | 6++++++
Mmachine.c | 35++++-------------------------------
Mmachine.h | 1-
Amiditest.c | 32++++++++++++++++++++++++++++++++
Musynth.c | 27++++++++++++++++++++-------
Autil.c | 34++++++++++++++++++++++++++++++++++
Autil.h | 11+++++++++++
11 files changed, 368 insertions(+), 43 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ include config.mk # SRC = usynth.c fm.c machine.c midi.c operator.c wavetable.c -SRC = usynth.c midi.c machine.c wavetable.c +SRC = midi.c machine.c wavetable.c build.c util.c OBJ = ${SRC:.c=.o} @@ -18,8 +18,14 @@ all: usynth ${OBJ}: -usynth: ${OBJ} - ${CC} -o $@ ${OBJ} ${LDFLAGS} +usynth: $@.o ${OBJ} + ${CC} -o $@ $@.o ${OBJ} ${LDFLAGS} + +miditest: miditest.c + ${CC} miditest.c -o $@ -lsndio + +buildtest: buildtest.c ${OBJ} + ${CC} -o $@ buildtest.c ${OBJ} ${LDFLAGS} wavetable.o: wavetable.h @@ -38,6 +44,6 @@ uninstall: ${DESTDIR}${MANDIR}/usynth.1 clean: - rm -f usynth ${OBJ} + rm -f usynth buildtest miditest ${OBJ} .PHONY: all clean instal uninstall diff --git a/build.c b/build.c @@ -0,0 +1,202 @@ +#include <stdio.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" +#include "machine.h" +#include "build.h" + +const Label builtin[] = { + /* memory mapping */ + {"VM_PC", VM_PC}, + {"VM_ACTIVE", VM_ACTIVE}, + {"VM_HALTED", VM_HALTED}, + {"VM_KEY", VM_KEY}, + {"VM_KEY_ON", VM_KEY_ON}, + {"VM_OPERATORS", VM_OPERATORS}, + {"VM_SP", VM_SP}, + {"VM_STACK", VM_STACK}, + {"VM_PROG_START", VM_PROG_START}, + {"VM_MEM_END", VM_MEM_END}, + /* processor instructions */ + {"halt", I_HALT}, + {"lit", I_LIT}, + {"fetch", I_FETCH}, + {"op", I_OP}, + {"adsr", I_ADSR}, + {"wave", I_WAVE}, + {"2dt", I_2DT}, + {"dup", I_DUP}, + {"drop", I_DROP}, + {"swap", I_SWAP}, + {"add", I_ADD}, + {"comb", I_COMB}, +}; + + +void +splittokens(char *dat, Array *tokens) +{ + char *sp, *p; + sp = dat; + for (p = dat; *p != '\0'; p++) { + if (*p == '\n') { + Token *tok; + *p = '\0'; + p++; + tok = arrayinc(tokens); + *tok = (struct Token){*sp, sp+1}; + sp = p; + } + } +} + +void +addwords(Array *words, char *p) +{ + char *w; + while(p != NULL) { + w = strsep(&p, " \t"); + if (*w != '\0') { + char **n; + n = arrayinc(words); + *n = w; + } + } +} + +int +run1(Array *tokens, Array *labels, Array *words) +{ + struct Token *tok; + struct Label *l; + char *np, *vp, *ep; + int v, i; + for (i = 0; i < tokens->n; i++) { + tok = arrayget(tokens, i); + switch (tok->t) { + case '\t': + case ' ': + addwords(words, tok->p); + break; + case ':': + np = strsep(&tok->p, " \t"); + vp = strsep(&tok->p, " \t"); + if (vp != NULL) { + v = strtol(vp, &ep, 16); + if (vp == ep) { + fprintf(stderr, "%s?\n", vp); + return -1; + } + } else v = words->n + VM_PROG_START; + l = arrayinc(labels); + *l = (Label){np, v}; + break; + case '\0': + case '#': + break; + default: + fprintf(stderr, "%c%s?\n", + tok->t, tok->p); + } + } + return 0; +} + +Label * +getlabel(Array *labels, char *p) +{ + int i; + Label *l; + for (i = 0; i < labels->n; i++) { + l = arrayget(labels, i); + if (strcmp(l->p, p) == 0) return l; + } + return NULL; +} + + +int +run2(Array *labels, Array *words, int16_t *prog) +{ + int i; + if (words->n > VM_MEM_END - VM_PROG_START) { + fprintf(stderr, "not enough memory\n"); + return -1; + } + for (i = 0; i < words->n; i++) { + Label *l; + int16_t *p; + char **w; + w = arrayget(words, i); + p = &prog[i + VM_PROG_START]; + l = getlabel(labels, *w); + if (l != NULL) { + *p = l->n; + } else { + char *ep; + int16_t n; + n = strtol(*w, &ep, 16); + if ((*w == ep)||(*ep != '\0')) { + fprintf(stderr, "%s?\n", *w); + return -1; + } + *p = n; + } + } + return 0; +} + +int +build(char *dat, int16_t *prog) +{ + int r; + Array *tokens; + Array *labels; + Array *words; + + r = 0; + + tokens = newarray(sizeof(Token)); + labels = newarray(sizeof(Label)); + words = newarray(sizeof(char *)); + + labels->n = sizeof(builtin)/sizeof(*builtin); + labels->p = realloc(labels->p, sizeof(builtin)); + memcpy(labels->p, builtin, sizeof(builtin)); + + splittokens(dat, tokens); + if ((run1(tokens, labels, words) != 0) || + (run2(labels, words, prog) != 0)) { + fprintf(stderr, "build failed\n"); + r = -1; + } + freearray(tokens); + freearray(labels); + freearray(words); + return r; +} + +int +buildfile(FILE *fp, int16_t *prog) +{ + char *buf, *bp; + size_t n, s; + int r; + s = 1024; + buf = malloc(s); + bp = buf; + while ((n = fread(bp, 1024, 1, fp)) > 0) { + s += n; + buf = realloc(buf, s + 1024); + bp = buf + s; + } + if (n < 0) { + fprintf(stderr, "failed to read file\n"); + return -1; + } + r = build(buf, prog); + free(buf); + return r; +} diff --git a/build.h b/build.h @@ -0,0 +1,19 @@ +typedef struct Token Token; +struct Token { + int t; + char *p; +}; + +typedef struct Label Label; +struct Label { + char *p; + uint16_t n; +}; + +void splittokens(char *dat, Array *tokens); +void addwords(Array *words, char *p); +Label * getlabel(Array *labels, char *p); +int run1(Array *tokens, Array *labels, Array *words); +int run2(Array *labels, Array *words, int16_t *prog); +int build(char *dat, int16_t *prog); +int buildfile(FILE *fp, int16_t *prog); diff --git a/buildtest.c b/buildtest.c @@ -0,0 +1,30 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "util.h" +#include "wavetable.h" +#include "machine.h" +#include "build.h" + +int +main(void) +{ + int i; + int16_t *prog; + wt_init(); + prog = malloc(VM_MEM_END * sizeof(int16_t)); + buildfile(stdin, prog); + prog[VM_ACTIVE] = 1; + prog[VM_KEY] = 100; + vm_run(prog); + printf("---\n"); + for (i = 0; i < VM_KEY_ON+1; i++) { + printf("%04hx ", prog[i]); + }; + printf("\n%hx: ", prog[VM_SP]); + for (i = VM_STACK; i < VM_STACK+prog[VM_SP]; i++) { + printf("%04hx ", prog[i]); + }; + printf("\n"); + return 0; +} diff --git a/example.prg b/example.prg @@ -0,0 +1,6 @@ +:main + lit VM_KEY fetch + 2dt + op + wave + halt diff --git a/machine.c b/machine.c @@ -22,7 +22,6 @@ void i_fetch(int16_t*, uint8_t); void i_op(int16_t*, uint8_t); void i_adsr(int16_t*, uint8_t); void i_wave(int16_t*, uint8_t); -void i_note(int16_t*, uint8_t); void i_2dt(int16_t*, uint8_t); void i_dup(int16_t*, uint8_t); void i_drop(int16_t*, uint8_t); @@ -37,7 +36,6 @@ instruction instructions[128] = { [I_OP] = i_op, [I_ADSR] = i_adsr, [I_WAVE] = i_wave, - [I_NOTE] = i_note, [I_2DT] = i_2dt, [I_DUP] = i_dup, [I_DROP] = i_drop, @@ -46,28 +44,9 @@ instruction instructions[128] = { [I_COMB] = i_comb, }; -/*** simple hard-coded program ***/ - -int16_t sprog[1024*3] = { - I_NOTE, - I_2DT, - I_OP|0x0000, - I_WAVE|0x0000, - - //I_NOTE, - //I_2DT, - //I_OP|0x0000, - //I_WAVE|0x0000, - //I_COMB, - //I_ADSR|0x0000, - //I_COMB, - I_HALT -}; - void vm_set(int16_t *vm) { - memcpy(vm + VM_PROG_START, sprog, 1024); vm[VM_ACTIVE] = 0; } @@ -84,7 +63,7 @@ vm_run(int16_t *vm) inst = instructions[0xff & DATA(vm)]; if (inst == 0) vm_fatal(vm, "illegal instruction"); vm[VM_PC]++; - inst(vm, DATA(vm)>>8); + inst(vm, 0); } return TOS(vm); } @@ -102,7 +81,8 @@ void vm_fatal(int16_t* vm, char *errstr) { dprintf(2, "int16_t FATAL: %s\n", errstr); - dprintf(2, "pc = %d\n", vm[VM_PC]); + dprintf(2, "pc = %hd\n", vm[VM_PC]); + dprintf(2, "mem = %hd\n", vm[vm[VM_PC]]); exit(-1); } @@ -116,8 +96,8 @@ void i_lit(int16_t *vm, uint8_t arg) { vm[VM_SP]++; + TOS(vm) = vm[vm[VM_PC]]; vm[VM_PC]++; - TOS(vm) = DATA(vm); } void @@ -148,13 +128,6 @@ i_wave(int16_t *vm, uint8_t arg) } void -i_note(int16_t *vm, uint8_t arg) -{ - vm[VM_SP]++; - TOS(vm) = vm[VM_KEY]; -} - -void i_2dt(int16_t *vm, uint8_t arg) { uint8_t note = TOS(vm); diff --git a/machine.h b/machine.h @@ -7,7 +7,6 @@ enum { I_OP, I_ADSR, I_WAVE, - I_NOTE, I_2DT, /* * * * * */ I_DUP, diff --git a/miditest.c b/miditest.c @@ -0,0 +1,32 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#include <sndio.h> + +/* + * a simple program that sends midi NOTE ON + * and then NOTE OFF a second later + */ + +int +main(void) +{ + uint8_t msg[4]; + char *mdev; + msg[0] = 0x90; + msg[1] = 0x10; + msg[2] = 127; + mdev = "midithru/0"; + struct mio_hdl *mh; + mh = mio_open(mdev, MIO_OUT, 0); + if (mh == 0) { + fprintf(stderr, " can't open midi %s\n", mdev); + exit(-1); + } + mio_write(mh, msg, 3); + sleep(1); + msg[0] = 0x80; + mio_write(mh, msg, 3); + return 0; +} diff --git a/usynth.c b/usynth.c @@ -5,9 +5,12 @@ #include <sndio.h> #include <string.h> #include <assert.h> + +#include "util.h" #include "midi.h" #include "wavetable.h" #include "machine.h" +#include "build.h" struct sio_hdl *sh; size_t bs; @@ -40,17 +43,28 @@ s_init(char *sdev) } int -main(void) +main(int argc, char **argv) { + FILE *fp; + + fp = stdin; + if (argc == 2) { + fp = fopen(argv[1], "r"); + if (fp == NULL) { + fprintf(stderr, "failed to open file %s\n", argv[1]); + exit(-1); + } + } + s_init("default"); wt_init(); m_init("midithru/0"); m_vector.note_on = note_on; m_vector.note_off = note_off; - vm = malloc(4096); - + vm = malloc(VM_MEM_END); vm_set(vm); + buildfile(fp, vm); while (1){ size_t n; @@ -63,10 +77,9 @@ main(void) void note_on(uint8_t *m) { - int i; keystack[kp] = m[2]; kp++; - if (kp > 32) kp = 32; + if (kp > 31) kp = 31; vm[VM_ACTIVE] = 1; vm[VM_KEY] = m[2]; vm[VM_KEY_ON] = 1; @@ -76,9 +89,9 @@ void note_off(uint8_t *m) { int i; - for (i = 0; i < 32; i++) { + for (i = 0; i < kp; i++) { if (keystack[i] == m[2]) { - memcpy(keystack+i, keystack+i+1, 32 - i); + memcpy(keystack+i, keystack+i+1, kp - i); kp--; } } diff --git a/util.c b/util.c @@ -0,0 +1,34 @@ +#include <stdlib.h> + +#include "util.h" + +Array * +newarray(size_t s) +{ + Array *a; + a = malloc(sizeof(Array)); + *a = (Array){NULL, s, 0}; + return a; +} + +void +freearray(Array *a) +{ + free(a->p); + a->p = NULL; + free(a); +} + +void * +arrayinc(Array *v) +{ + v->n++; + v->p = realloc(v->p, v->n * v->s); + return v->p + (v->n - 1) * v->s; +} + +void * +arrayget(Array *v, int n) +{ + return v->p + n * v->s; +} diff --git a/util.h b/util.h @@ -0,0 +1,11 @@ +typedef struct Array Array; +struct Array { + char *p; + size_t s; + int n; +}; + +Array * newarray(size_t s); +void freearray(Array *); +void * arrayinc(Array *v); +void * arrayget(Array *v, int n);