diff options
-rw-r--r-- | bytetunes.cpp | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/bytetunes.cpp b/bytetunes.cpp new file mode 100644 index 0000000..09d12f1 --- /dev/null +++ b/bytetunes.cpp @@ -0,0 +1,405 @@ + +#include "wirish.h" +//#include <stdio.h> +#include <string.h> +//#include <stdlib.h> + +#define DEFAULT "(& (>> t 6) (& (* 2 t) (>> t 1)))" +#define NUMNODE 160 +#define PWM_OUT_PIN 33 + +struct node { + char type; + char cval; + unsigned int ival; + struct node *lval; + struct node *rval; +}; + +int isdigit(char); +void print_sexpr(struct node *sexpr); +unsigned int execute(struct node *sexpr, unsigned int t); +int find_split(char* s, int start, int end); +struct node *parse(char *s, int start, int end); +struct node *new_node(char type, char cval, unsigned int ival, + struct node *lval, struct node *rval); + +static struct node active_table[NUMNODE]; +static struct node node_table[NUMNODE]; +static int newest_node = 0; +node *machine; +node *new_machine; +void handler_sample(void); + +unsigned char sin_8bit(int counter, int period); +char inbuffer[256]; + +int sstrlen(char *s, int max); + +HardwareTimer gen(1); +HardwareTimer pwm(3); + +int counter = 0; + +int isdigit(char c) { + return (c >= '0' and c <= '9'); +} + +/* +from math import sin +count = 64 +print [int(127+127*sin(3.14159268*2*i/count)) for i in range(count)] +*/ +unsigned char sine_lookup[] __FLASH__ = {127, 139, 151, 163, 175, 186, 197, 207, 216, + 225, 232, 239, 244, 248, 251, 253, 254, 253, 251, 248, 244, 239, 232, 225, + 216, 207, 197, 186, 175, 163, 151, 139, 126, 114, 102, 90, 78, 67, 56, 46, + 37, 28, 21, 14, 9, 5, 2, 0, 0, 0, 2, 5, 9, 14, 21, 28, 37, 46, 56, 67, 78, + 90, 102, 114}; + +void setup() { + int i; + pinMode(PWM_OUT_PIN, OUTPUT); + pinMode(33, OUTPUT); + pinMode(3, PWM); + pinMode(4, OUTPUT); + digitalWrite(1, 1); + + // initialize with DEFAULT machines + machine = parse(DEFAULT, 0, strlen((char*)DEFAULT)-1); + for (i=0;i<NUMNODE;i++) { + active_table[i] = node_table[i]; + } + + // configure PWM output + pinMode(PWM_OUT_PIN, OUTPUT); + pwm.setChannel3Mode(TIMER_PWM); + pwm.setPrescaleFactor(1); + pwm.setOverflow(255); // 8-bit resolution + pwm.setCompare(3, 128); // initialize to "zero" + + + // configure 8KHz ticker and interrupt handler + gen.pause(); + gen.setPeriod(125); // 8Khz + gen.setCompare(1, 1); + gen.setMode(1, TIMER_OUTPUT_COMPARE); + gen.attachInterrupt(1, handler_sample); + gen.refresh(); + + // get things started! + gen.resume(); +} + +int inbuffer_index; + +void loop() { + int len, i; + SerialUSB.println(); + SerialUSB.println("Input a tune in exact s-expr syntax:"); + SerialUSB.print("> "); + inbuffer_index = 0; + while (1) { + inbuffer[inbuffer_index] = SerialUSB.read(); + SerialUSB.print(inbuffer[inbuffer_index]); + if (inbuffer[inbuffer_index] == 8) { + if (inbuffer_index > 0) { + inbuffer_index--; + } + } else if (inbuffer[inbuffer_index] == '\n' || + inbuffer[inbuffer_index] == '\r') { + inbuffer[inbuffer_index] = '\0'; + SerialUSB.println(); + break; + } else { + inbuffer_index++; + } + if (inbuffer_index == 256) { + SerialUSB.println("\n\rInput too long!"); + return; + } + } + len = sstrlen(inbuffer, 256); + if (len == 256 || len < 1) { + SerialUSB.println("Invalid input!"); + return; + } + newest_node = 0; + new_machine = parse(inbuffer, 0, len-2); + if (new_machine == NULL) { + return; + } + // swap in new machine + gen.pause(); + for (i=0;i<NUMNODE;i++) { + active_table[i] = node_table[i]; + } + machine = new_machine; + SerialUSB.println("Playing new tune:"); + print_sexpr(machine); + SerialUSB.println(); + SerialUSB.println(); + gen.resume(); +} + +void handler_sample(void) { + digitalWrite(4, 1); + //pwm.setCompare(4, sin_8bit(counter, 800)); // 10Hz sine wave + pwmWrite(3, sin_8bit(counter, 800)); + // pwm.setCompare(1, execute(counter); + counter++; + //SerialUSB.print('.'); + digitalWrite(4, 0); +} + +unsigned char sin_8bit(int counter, int period) { + int high, low; + float t = (counter % period) / (float)period; + float weight = t - (int)t; + low = sine_lookup[(int)(63*t)]; + if (63*t >= 62) + high = sine_lookup[0]; + else + high = sine_lookup[1+(int)(63*t)]; + + return (int)(high * weight + low * (1.0 - weight)); +} + +int sstrlen(char *s, int max) { + int i; + for (i=0; i<max; i++) { + if (s[i] == '\n' || s[i] == '\0') { + return i+1; + } + } + return max; +} + +int digtoi(char *s, int start, int end) { + int num = 0; + for(;start<=end;start++) { + if (! isdigit(s[start])) { + SerialUSB.print("parse: not a digit: "); + SerialUSB.println(s[start]); + return -1; + } + num = num*10 + (s[start] - '0'); + } + return num; +} + +struct node *parse(char *s, int start, int end) { + struct node *lval, *rval; + char cval; + int offset, split; + + // number + if (isdigit(s[start])) { + return new_node('n', '_', digtoi(s, start, end), NULL, NULL); + } + // variable + if ((s[start] == 't') && (end-start == 0)) { + return new_node('v', '_', 0, NULL, NULL); + } + + // must be an s-exp + if ( !(s[start] == '(' && s[end] == ')') || (end-start < 4)) { + SerialUSB.print("parse: unparsable: "); + for (;start<=end;start++) { + SerialUSB.print(s[start]); + } + SerialUSB.print("\n"); + return NULL; + } + + // unitary operator + if (s[start+1] == '~') { + rval = parse(s, start+3, end-1); + return new_node('u', '~', 0, NULL, rval); + } + + // various binary operators + if ( strchr("*/%+-&^|<>", s[start+1]) != NULL) { + cval = s[start+1]; + offset = 3; + if (cval == '<' && s[start+2] == '<' && s[start+3] == ' ') { + cval = 'l'; + offset = 4; + } else if (cval == '>' && s[start+2] == '>' && s[start+3] == ' ') { + cval = 'r'; + offset = 4; + } else if (s[start+2] != ' ') { + SerialUSB.print("parse: invalid operator: "); + SerialUSB.println(cval); + return NULL; + } + split = find_split(s, start+offset, end-1); + lval = parse(s, start+offset, split-1); + rval = parse(s, split+1, end-1); + return new_node('b', cval, 0, lval, rval); + } + + // should not get here + SerialUSB.print("parse: feel through\n"); + return NULL; +} + +unsigned int execute(struct node *sexpr, unsigned int t) { + switch (sexpr->type) { + // atom + case 'v': + return t; + case 'n': + return sexpr->ival; + // unary + case 'u': + if (sexpr->cval == '~') { + return (~ execute(sexpr->rval, t)); + } else { + SerialUSB.print("unexpected unary oper: "); + SerialUSB.println(sexpr->cval); + } + // binary + case 'b': + switch (sexpr->cval) { + case '+': + return execute(sexpr->lval, t) + execute(sexpr->rval, t); + case '-': + return execute(sexpr->lval, t) - execute(sexpr->rval, t); + case '*': + return execute(sexpr->lval, t) * execute(sexpr->rval, t); + case '/': + return execute(sexpr->lval, t) / execute(sexpr->rval, t); + case '^': + return execute(sexpr->lval, t) ^ execute(sexpr->rval, t); + case '&': + return execute(sexpr->lval, t) & execute(sexpr->rval, t); + case '|': + return execute(sexpr->lval, t) | execute(sexpr->rval, t); + case '%': + return execute(sexpr->lval, t) % execute(sexpr->rval, t); + case 'r': + return execute(sexpr->lval, t) >> execute(sexpr->rval, t); + case 'l': + return execute(sexpr->lval, t) << execute(sexpr->rval, t); + default: + SerialUSB.print("unexpected binary oper: "); + SerialUSB.print(sexpr->cval); + return NULL; + // XXX: halt + } + default: + SerialUSB.print("execute: unknown type: "); + SerialUSB.print(sexpr->type); + return NULL; + // XXX: halt + } +} + +int find_split(char *s, int start, int end) { + int depth = 0; + int i; + for (i=start; i <= end; i++) { + if ((depth == 0) && (s[i] == ' ')) + return i; + if (s[i] == '(') + depth++; + if (s[i] == ')') + depth--; + if (depth < 0) { + SerialUSB.print("parse: unmatched ')'\n"); + return -1; + // XXX: fail + } + } + if (depth > 0) { + SerialUSB.print("parse: unmatched '('\n"); + return -1; + // XXX: fail + } + SerialUSB.print("parse: could not find split\n"); + return -1; + // XXX: fail +} + +void print_sexpr(struct node *sexpr) { + char oper = '_'; + char twice = 0; + switch (sexpr->type) { + // atoms + case 'v': + SerialUSB.print('t'); + break; + case 'n': + SerialUSB.print(sexpr->ival); + break; + // unary operators + case 'u': + if (sexpr->cval == '~') { + SerialUSB.print("(~ "); + print_sexpr(sexpr->rval); + SerialUSB.print(")"); + } else { + SerialUSB.print("unexpected unary: "); + SerialUSB.println(sexpr->cval); + } + // binary operators + case 'b': + if ((sexpr->cval == 'l') || (sexpr->cval == 'r')) { + twice = 1; + oper = '<'; + if (sexpr->cval == 'r') + oper = '>'; + } else { + oper = sexpr->cval; + } + if (strchr("+-*/^|&<>%", oper) != NULL) { + SerialUSB.print('('); + SerialUSB.print(oper); + SerialUSB.print(' '); + if (twice) + SerialUSB.print(oper); + print_sexpr(sexpr->lval); + SerialUSB.print(' '); + print_sexpr(sexpr->rval); + SerialUSB.print(')'); + } else { + SerialUSB.print("unexpected binary: "); + SerialUSB.println(sexpr->cval); + // XXX: + } + + } +} + +struct node *new_node(char type, char cval, unsigned int ival, + struct node *lval, struct node *rval) { + struct node *n; + newest_node++; + if (newest_node >= NUMNODE) { + SerialUSB.print("node table overrun\n"); + // XXX: + } + n = &node_table[newest_node]; + n->type = type; + n->cval = cval; + n->ival = ival; + n->lval = lval; + n->rval = rval; + return n; +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + + return 0; +} |