From 0daa0360d510e2a1b32eb55f856933aadf69061f Mon Sep 17 00:00:00 2001 From: bnewbold Date: Thu, 13 Dec 2012 22:43:14 +0100 Subject: initial commit --- bin/bytebeat.sh | 11 +++ bytetunes.py | 17 ++++ expr.py | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ notes.txt | 63 +++++++++++++++ 4 files changed, 326 insertions(+) create mode 100755 bin/bytebeat.sh create mode 100644 bytetunes.py create mode 100644 expr.py create mode 100644 notes.txt diff --git a/bin/bytebeat.sh b/bin/bytebeat.sh new file mode 100755 index 0000000..71c614b --- /dev/null +++ b/bin/bytebeat.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env sh + +FORMULA="(t>>6)&(2*t)&(t>>1)" + +if [ $1 ]; then + FORMULA=$1 +fi + + +echo "$FORMULA" +echo "main(t){for(t=0;;t++)putchar($FORMULA);}" | gcc -x c -o /tmp/bytebeat - && /tmp/bytebeat | aplay -f U8 -r 8000 -c 1 -q diff --git a/bytetunes.py b/bytetunes.py new file mode 100644 index 0000000..1d2a8a7 --- /dev/null +++ b/bytetunes.py @@ -0,0 +1,17 @@ + +from expr import * + +import sys + +def play(s): + machine = parse(preparse(tokenize(s))[0]) + t = 0 + while True: + t += 1 + sys.stdout.write(chr(execute(machine, t) & 0x000000FF)) + +def main(): + play("(t>>6)&(2*t)&(t>>1)") + +if __name__=='__main__': + main() diff --git a/expr.py b/expr.py new file mode 100644 index 0000000..00435e9 --- /dev/null +++ b/expr.py @@ -0,0 +1,235 @@ + +""" +PARENS: '(' (EXPR | ATOM) ')' +EXPR: (BINARY | PARENS | NOT) +BINARY: (ATOM | EXPR) BOPER (ATOM | EXPR) +UNITARY: UOPER (EXPR | ATOM) +ATOM: (VAR | NUM) +VAR: 't' +NUM: + + +Order of ops: + () + ~ + * / % + + - + << >> + & + ^ + | + +TODO: allow negative numbers? +""" + +hexchr = "0123456789ABCDEFabcdef" +raw_opers = "+ - * / % << >> & | ^ ~".split() +binary_opers = list("+-*/%lr&|^") +unitary_opers = list("~+-") + +binaryoper_priority = ( + tuple("|"), + tuple("^"), + tuple("&"), + tuple("rl"), + tuple("+-"), + tuple("*/%"), +) + +def cons(a, l): + return [a, ] + l + +def car(l): + return l[0] + +def cdr(l): + return l[1:] + +def preparse(l, depth=0): + """ + match parens and set types + """ + ret = [] + for i in range(len(l)): + if l[i] == '(': + #print "%sleft: %s" % (" "*depth, ret) + middle, remains = preparse(l[i+1:], depth+1) + #print "%smiddle: %s" % (" "*depth, middle) + right, moreright = preparse(remains, depth) + #print "%sright: %s" % (" "*depth, right) + return ret + [tuple(cons('PARENS', middle))] + right, moreright + elif l[i] == ')': + if depth == 0: + Exception("preparse: unexpected parens") + return ret, l[i+1:] + else: + if type(l[i]) == int: + ret.append(('NUM', l[i])) + elif l[i] == '~': + ret.append(('NOTOPER',)) + elif l[i] in binary_opers: + ret.append(('OPER', l[i])) + elif l[i] == 't': + ret.append(('VAR',)) + else: + raise Exception("prepase: unexpected token: %s" % l[i]) + if depth > 0: + raise Exception("preparse: unmatched parens") + return ret, [] + +def parse(l): + #print l + if not l or len(l) == 0: + return [] + if len(l) == 1: + if car(car(l)) in ('VAR', 'NUM'): + return ('ATOM', car(l)) + elif car(car(l)) == 'PARENS': + return parse(cdr(car(l))) + else: + raise Exception("parse error: %s" % l) + # ~ has highest priority + if ('NOTOPER',) in l: + i = l.index(('NOTOPER',)) + if i == len(l)-1: + raise Exception("parse error: trailing ~") + return parse(l[:i] + ('NOT', parse(l[i+1])) + l[i+2:]) + # helper function + def hunt_opers(l, operlist): + for i in range(1, len(l) - 1): + if car(l[i]) == 'OPER' and l[i][1] in operlist: + left = parse(l[:i]) + right = parse(l[i+1:]) + if not car(left) in ['ATOM', 'BINARY', 'NOT']: + raise Exception("parse error: not a binary operand: %s" % + left) + if not car(right) in ['ATOM', 'BINARY', 'NOT']: + raise Exception("parse error: not a binary operand: %s" % + right) + return ('BINARY', l[i][1], left, right) + for operlist in binaryoper_priority: + match = hunt_opers(l, operlist) + if match: + return match + raise Exception("parse error: %s" % [l]) + +def schemify(ast): + if car(ast) == 'BINARY': + return "(%s %s %s)" % (ast[1], schemify(ast[2]), schemify(ast[3])) + elif car(ast) == 'NOT': + return "(~ %s)" % schemify(ast[1]) + elif car(ast) == 'ATOM': + return "%s" % ast[1][1] + else: + raise Exception("schemify: bad type: %s" % ast) + +def test_parse(): + """ + () + ~~1 + """ + +def tokenize(s): + s = s.strip() + if not s or len(s) is 0: + return [] + if car(s) in ['t', 'i']: + # accept i, but tokenize as t + return cons('t', tokenize(cdr(s))) + if car(s) in raw_opers or car(s) in ['(', ')']: + return cons(car(s), tokenize(cdr(s))) + if len(s) >= 2 and s[:2] == '>>': + return cons('r', tokenize(s[2:])) + if len(s) >= 2 and s[:2] == '<<': + return cons('l', tokenize(s[2:])) + if len(s) >= 3 and s[:2] == "0x" and s[2] in hexchr: + num = s[:3] + for c in s[3:]: + if c in hexchr: + num += c + else: + break + return cons(int(num, 16), tokenize(s[len(num):])) + if car(s).isdigit(): + num = car(s) + for c in s[1:]: + if c.isdigit(): + num += c + else: + break + return cons(int(num), tokenize(s[len(num):])) + # default + raise Exception("tokenization error: %s" % s) + +def execute(ast, t): + if car(ast) == 'ATOM': + if car(ast[1]) == 'VAR': + return t + if car(ast[1]) == 'NUM': + return ast[1][1] + elif car(ast) == 'NOT': + return ~ execute(ast[1], t) + elif car(ast) == 'BINARY': + # UGH + oper, left, right = ast[1:4] + left = execute(ast[2], t) + right = execute(ast[3], t) + if oper == '+': + return left + right + elif oper == '-': + return left - right + elif oper == '*': + return left * right + elif oper == '/': + return left / right + elif oper == '%': + return left % right + elif oper == 'r': + return left >> right + elif oper == 'l': + return left << right + elif oper == '|': + return left | right + elif oper == '^': + return left ^ right + elif oper == '&': + return left & right + raise Exception("execute: unexpected node: %s" % car(ast)) + + +# ============================== +def test_tokenize(): + assert tokenize("(t>>4)") == ['(','t','r',4,')'] + assert tokenize("t") == ['t'] + assert tokenize("i") == ['t'] + assert tokenize("tt123i") == ['t','t',123,'t'] + assert tokenize("12345") == [12345] + assert tokenize("0xF") == [15] + assert tokenize("<<") == ['l'] + assert tokenize(">>") == ['r'] + assert tokenize("~") == ['~'] + assert tokenize("444 444") == [444,444] + assert tokenize("t*(((t>>12)|(t>>8))&(63&(t>>4)))") == \ + ['t','*','(','(','(','t','r',12,')','|','(','t','r',8,')',')', + '&','(',63,'&','(','t','r',4,')',')',')'] + try: + assert tokenize("1.0") is "TOKENIZE ERROR" + except: + pass + try: + assert tokenize("t,(") is "TOKENIZE ERROR" + except: + pass + try: + assert tokenize("0x") is "TOKENIZE ERROR" + except: + pass + try: + assert tokenize("1230x123") is "TOKENIZE ERROR" + except: + pass + try: + assert tokenize("x123") is "TOKENIZE ERROR" + except: + pass + diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000..80773b8 --- /dev/null +++ b/notes.txt @@ -0,0 +1,63 @@ + +opcodes + note: no unary, increment, decrement + arthithmetic: + - * / % + bitwise: l r & | ^ ~ + "<<" is l, ">>" is r + all are atoms or binary operators, so recursive compilation to a forth-like? + order of ops: + ~ + * / % + + - + << >> + & + ^ + | + +final console interface: + #hashtag (exact) -> searches identica for tunes + @username (exact) -> parses identica feed for tunes + _1234... -> plays track from memory + expression -> compiles and plays expression + [else] -> searches greedily for a valid expression; if not found, ignores + +called "bytebeat". related to softsynths, demoscene. other buzzwords: Algostep, +googlecore, "shortcode compos", + +bytetros? + +original (to me): +echo "main(i){for(i=0;;i++)putchar((i*(i>>8|i>>9)&46&i>>8)^(i&i>>13|i>>6));}" | gcc -x c - && ./a.out | aplay + +from http://countercomplex.blogspot.de/2011/10/algorithmic-symphonies-from-one-line-of.html + + main(t){for(t=0;;t++)putchar(t*(((t>>12)|(t>>8))&(63&(t>>4))));} + +projects: + http://wurstcaptures.untergrund.net/music/ + http://shackspace.de/?p=2509 (The Noiseplug) + http://hackaday.com/2011/11/01/annoy-your-sound-guy-even-more/ (Noiseplug HaD) + https://github.com/erlehmann/libglitch + opcodes: http://paste.ubuntu.com/733764/ + http://0xa.kuri.mu/2011/10/09/bitop-videos/ + GlitchMachine: http://www.umlautllama.com/w2/?action=view&page=GlitchMachine + +listings: + http://shackspace.de/wiki/doku.php?id=project:algorithmicsoundscapes + http://pelulamu.net/countercomplex/music_formula_collection.txt + +commentary, other: + http://countercomplex.blogspot.de/2011/10/algorithmic-symphonies-from-one-line-of.html + http://countercomplex.blogspot.de/2011/10/some-deep-analysis-of-one-line-music.html + http://countercomplex.blogspot.de/2011/06/16-byte-frontier-extreme-results-from.html + http://canonical.org/~kragen/bytebeat/ + http://pouet.net/topic.php?which=8357 + "Discovering novel computer music techniques by exploring the space of short computer programs" + http://arxiv.org/abs/1112.1368 + http://royal-paw.com/2012/01/bytebeats-in-c-and-python-generative-symphonies-from-extremely-small-programs/ + http://www.metafilter.com/111959/Todays-formulaic-music + +similar: + http://yaxu.org/haskell-hack/ + http://sunsite.univie.ac.at/Mozart/dice/ + http://music.columbia.edu/~douglas/strange_things/?p=78 -- cgit v1.2.3