diff options
author | bnewbold <bnewbold@robocracy.org> | 2012-12-24 15:39:35 +0100 |
---|---|---|
committer | bnewbold <bnewbold@robocracy.org> | 2012-12-24 15:39:35 +0100 |
commit | 2403b07ec0a7586108798271fa04eb034445f51d (patch) | |
tree | 805f1ac5b46b484799c7c585f46b626da890dd96 /bytetunes.cpp | |
parent | 4818443a6a7abb9fe3976dd5846d42816e9d2328 (diff) | |
download | bytetunes-2403b07ec0a7586108798271fa04eb034445f51d.tar.gz bytetunes-2403b07ec0a7586108798271fa04eb034445f51d.zip |
updates to documentation, code cleanup, comments
Diffstat (limited to 'bytetunes.cpp')
-rw-r--r-- | bytetunes.cpp | 249 |
1 files changed, 132 insertions, 117 deletions
diff --git a/bytetunes.cpp b/bytetunes.cpp index 470d51f..ce9f557 100644 --- a/bytetunes.cpp +++ b/bytetunes.cpp @@ -1,13 +1,28 @@ +/* + * bytebeat.cpp - parse and play bytebeat songs + * Date: December 2012 + * Author: bnewbold@robocracy.org + * + * For use with libmaple, to run on ARM Cortex-M3 microcontrollers. + * + * This version only parses tunes in "perfect" S-EXPR format. + * + * Outputs PWM audio on pin 16 and listens on SerialUSB for new tunes. + * + * See TODO file for problems. + * + * This file is released under the General Public License version 3 (GPLv3). + */ #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 +#define DEFAULT_TUNE "(& (>> t 6) (& (* 2 t) (>> t 1)))" +#define NUMNODE 160 +#define LED_PIN 33 +#define MONITOR_PIN 31 +// S-EXPR data structure stuff struct node { char type; char cval; @@ -15,73 +30,59 @@ struct node { struct node *lval; struct node *rval; }; +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; +// S-EXPR parser stuff int isdigit(char); void print_sexpr(struct node *sexpr); -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); +int digtoi(char *s, int start, int end); -static struct node active_table[NUMNODE]; -static struct node node_table[NUMNODE]; -static int newest_node = 0; +// bytetune machine output stuff +int execute(struct node *sexpr, unsigned int t); 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(4); - int counter = 0; -int isdigit(char c) { - return (c >= '0' and c <= '9'); -} +// other stuff +char inbuffer[256]; +int inbuffer_index; +int sstrlen(char *s, int max); -/* -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}; +// ====================== primary control flow functions ====================== +// runs once at power up to configure hardware peripherals void setup() { int i; - pinMode(PWM_OUT_PIN, OUTPUT); - pinMode(31, OUTPUT); - pinMode(33, OUTPUT); - pinMode(16, PWM); - pinMode(4, OUTPUT); - digitalWrite(1, 1); - - // initialize with DEFAULT machines - machine = parse((char*)DEFAULT, 0, strlen((char*)DEFAULT)-1); + + // for monitoring interrupt loop length + pinMode(LED_PIN, OUTPUT); + pinMode(MONITOR_PIN, OUTPUT); + + // initialize with DEFAULT_TUNE + machine = parse((char*)DEFAULT_TUNE, 0, strlen((char*)DEFAULT_TUNE)-1); for (i=0;i<NUMNODE;i++) { active_table[i] = node_table[i]; } - // configure PWM output - pinMode(PWM_OUT_PIN, OUTPUT); + // configure PWM output on pin 16 (Timer 4, Channel 1) + pinMode(16, PWM); // primary PWM audio output pwm.setMode(1, TIMER_PWM); pwm.setPrescaleFactor(1); - pwm.setOverflow(255); // 8-bit resolution - pwm.setCompare(3, 128); // initialize to "zero" - + 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.setPeriod(125); // 8Khz gen.setCompare(1, 1); gen.setMode(1, TIMER_OUTPUT_COMPARE); gen.attachInterrupt(1, handler_sample); @@ -91,32 +92,34 @@ void setup() { gen.resume(); } -int inbuffer_index; - +// runs continuously, reading new machines from SerialUSB input void loop() { int len, i; + SerialUSB.println(); SerialUSB.println("Currently playing:"); print_sexpr(machine); SerialUSB.println(); SerialUSB.println("Input a tune in exact s-expr syntax:"); SerialUSB.print("> "); + inbuffer_index = 0; while (1) { + // read in characters one at a time inbuffer[inbuffer_index] = SerialUSB.read(); + if (inbuffer[inbuffer_index] > 127) { + // if not an ASCII character ignore it + continue; + } SerialUSB.print(inbuffer[inbuffer_index]); - if (inbuffer[inbuffer_index] == 8) { - if (inbuffer_index > 0) { - inbuffer_index--; - } - } else if (inbuffer[inbuffer_index] == '\n' || + if (inbuffer[inbuffer_index] == '\n' || inbuffer[inbuffer_index] == '\r') { + // on submit, zero terminate the string and break out to parse inbuffer[inbuffer_index] = '\0'; SerialUSB.println(); break; - } else { - inbuffer_index++; } + inbuffer_index++; if (inbuffer_index == 256) { SerialUSB.println("\n\rInput too long!"); return; @@ -124,15 +127,17 @@ void loop() { } len = sstrlen(inbuffer, 256); if (len == 256 || len < 1) { + // too long or short SerialUSB.println("Invalid input!"); return; } + // ok, we're going to try parsing newest_node = 0; new_machine = parse(inbuffer, 0, len-2); if (new_machine == NULL) { return; } - // swap in new machine + // if we got this far, swap in the new machine gen.pause(); for (i=0;i<NUMNODE;i++) { active_table[i] = node_table[i]; @@ -146,55 +151,32 @@ void loop() { gen.resume(); } +// gets called by 8KHz timer interrupt. pushes out the next audio sample to PWM +// output void handler_sample(void) { - digitalWrite(33, 1); - digitalWrite(31, 1); - //pwm.setCompare(1, sin_8bit(counter, 799)); // 10Hz sine wave + // set LED and monitor line high (so we can check with a 'scope how long + // this function call lasts) + digitalWrite(LED_PIN, 1); + digitalWrite(MONITOR_PIN, 1); + + // execute and write truncated result pwm.setCompare(1, (0x000000FF & execute(machine, counter))); - //pwmWrite(16, sin_8bit(counter, 800)); counter++; - //SerialUSB.print('.'); - digitalWrite(33, 0); - digitalWrite(31, 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]; - high = 118; - else - high = sine_lookup[1+(int)(63*t)]; - - return (int)(high * weight + low * (1.0 - weight)); + // set LED and monitor line low + digitalWrite(LED_PIN, 0); + digitalWrite(MONITOR_PIN, 0); } -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 main(void) { + setup(); + while (true) { loop(); } + return 0; } -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; -} +// ====================== tune parsing and playback functions ================= +// does what it says; returns NULL if there is a problem struct node *parse(char *s, int start, int end) { struct node *lval, *rval; char cval; @@ -251,6 +233,10 @@ struct node *parse(char *s, int start, int end) { return NULL; } +// calculates a single audio sample (index 't') from tune 'sexpr'. +// returns an int, which should be truncated to 8bit length for playback +// if there is a problem, tries to print out to SerialUSB and returns "zero" +// value int execute(struct node *sexpr, unsigned int t) { switch (sexpr->type) { // atom @@ -265,6 +251,7 @@ int execute(struct node *sexpr, unsigned int t) { } else { SerialUSB.print("unexpected unary oper: "); SerialUSB.println(sexpr->cval); + return 127; } // binary case 'b': @@ -292,17 +279,18 @@ int execute(struct node *sexpr, unsigned int t) { default: SerialUSB.print("unexpected binary oper: "); SerialUSB.print(sexpr->cval); - return NULL; - // XXX: halt + return 127; } default: SerialUSB.print("execute: unknown type: "); SerialUSB.print(sexpr->type); - return NULL; - // XXX: halt + return 127; } } +// finds the seperating whitespace character between the two arguments to a +// binary S-EXPR operator +// returns either the index of the seperator or -1 if there was a problem int find_split(char *s, int start, int end) { int depth = 0; int i; @@ -316,19 +304,17 @@ int find_split(char *s, int start, int end) { 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 } +// prints out the S-EXPR to SerialUSB void print_sexpr(struct node *sexpr) { char oper = '_'; char twice = 0; @@ -347,7 +333,8 @@ void print_sexpr(struct node *sexpr) { print_sexpr(sexpr->rval); SerialUSB.print(")"); } else { - SerialUSB.print("unexpected unary: "); + SerialUSB.println(); + SerialUSB.print("print_sexpr: unexpected unary: "); SerialUSB.println(sexpr->cval); } // binary operators @@ -371,21 +358,24 @@ void print_sexpr(struct node *sexpr) { print_sexpr(sexpr->rval); SerialUSB.print(')'); } else { - SerialUSB.print("unexpected binary: "); + SerialUSB.println(); + SerialUSB.print("print_sexpr: unexpected binary: "); SerialUSB.println(sexpr->cval); - // XXX: + return; } } } +// creates a new node struct in the node table 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: + SerialUSB.println(); + SerialUSB.println("node table overrun!"); + return NULL; } n = &node_table[newest_node]; n->type = type; @@ -396,18 +386,43 @@ struct node *new_node(char type, char cval, unsigned int ival, return n; } +// ====================== misc helper functions ====================== + +// "safe" string length. breaks on newline or NULL char, checks at most 'max' +// characters +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; +} + +// finds a (positive) integer. +// returns -1 if there is a problem. +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; +} + +int isdigit(char c) { + return (c >= '0' and c <= '9'); +} + +// libmaple-specific re-definition // 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; -} |