aboutsummaryrefslogtreecommitdiffstats
path: root/bytetunes.cpp
diff options
context:
space:
mode:
authorbnewbold <bnewbold@robocracy.org>2012-12-24 15:39:35 +0100
committerbnewbold <bnewbold@robocracy.org>2012-12-24 15:39:35 +0100
commit2403b07ec0a7586108798271fa04eb034445f51d (patch)
tree805f1ac5b46b484799c7c585f46b626da890dd96 /bytetunes.cpp
parent4818443a6a7abb9fe3976dd5846d42816e9d2328 (diff)
downloadbytetunes-2403b07ec0a7586108798271fa04eb034445f51d.tar.gz
bytetunes-2403b07ec0a7586108798271fa04eb034445f51d.zip
updates to documentation, code cleanup, comments
Diffstat (limited to 'bytetunes.cpp')
-rw-r--r--bytetunes.cpp249
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;
-}