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; -} | 
