Some demoscene guys have been experimenting with generating loops from short C programs and some of them sound pretty cool! Take a look at this:
The algorithms looked simple enough to run on an Arduino so I wrote a sketch to try them out. Audio out is on pin 10. Have a go at altering the formulas and 'composing' your own, it is great fun.
// one-line algorithmic music
// see viznut's blog http://countercomplex.blogspot.com/2011/10/algorithmic-symphonies-from-one-line-of.html
// and http://www.youtube.com/watch?v=GtQdIYUtAHg&feature=related
// ported to arduino by stimmer
// Audio out on pin 10
void setup() {
TCCR1B = (TCCR1B & 0xf8) | 1;
analogWrite(10,1);
}
void putb(byte b)
{
static long m;
long t;
while((t=micros())-m < 125);
m=t;
OCR1B=b;
}
void loop() {
long v;
for(long t=0;;t++)putb(
// uncomment ONE line below
// or try making one of your own...
t*((t>>12|t>>8)&63&t>>4) // by viznut
// ((-t&4095)*(255&t*(t&t>>13))>>12)+(127&t*(234&t>>8&t>>3)>>(3&t>>14)) // by tejeez
// t*(t>>11&t>>8&123&t>>3) // by tejeez
// t*((t>>9|t>>13)&25&t>>6) // by visy
// (t*(t>>5|t>>8))>>(t>>16) // by tejeez
// ((t*(t>>8|t>>9)&46&t>>8))^(t&t>>13|t>>6) // by xpansive
// ((t&4096)?((t*(t^t%255)|(t>>4))>>1):(t>>3)|((t&8192)?t<<2:t)) // by skurk (raer's version)
// (t>>7|t|t>>6)*10+4*(t&t>>13|t>>6) // by viznut, xpansive, varjohukka
// t*5&(t>>7)|t*3&(t*4>>10) // by miiro
// (t|(t>>9|t>>7))*t&(t>>11|t>>9) // by red
// v=(v>>1)+(v>>4)+t*(((t>>16)|(t>>6))&(69&(t>>9))) // by pyryp
// (t>>6|t|t>>(t>>16))*10+((t>>11)&7) //by viznut
// (t*(4|7&t>>13)>>((~t>>11)&1)&128) + ((t)*(t>>11&t>>13)*((~t>>9)&3)&127) // by stimmer
);
}
This is the thread that has just inspired me to get an arduino and get into electronics. So awesome! My starter kit is in the mail.
I wonder if it's feasible to create a standalone interface for this that would allow you to write new algorithms using only the arduino itself.
I'm thinking there would be an LCD screen and at least 4 buttons to move left and right through the line of code and "up and down" to alter the selected character.
Any ideas on how feasible/difficult this would be to create? I don't have much electronics experience and only some experience with BASIC. But the arduino code I've seen so far is pretty easy to understand.
It would be pretty difficult to do - Arduino doesn't have an equivalent to the Eval function in BASIC and that's what you'd need to be able to completely change the algorithm at runtime.
It is possible to make slight changes to an algorithm such as changing numeric constants by turning them into variables. I've been experimenting with this myself - try this sketch, use a button from GND to pin 2 to decrease one of the variables and pin3 to increase it.
// interactive one-line algorithmic music by stimmer
void setup() {
TCCR1B = (TCCR1B & 0xf8) | 1;
analogWrite(10,1);
pinMode(2,INPUT);digitalWrite(2,HIGH);
pinMode(3,INPUT);digitalWrite(3,HIGH);
pinMode(13,OUTPUT);
}
void putb(byte b)
{
static long m;
long t;
while((t=micros())-m < 125);
m=t;
OCR1B=b;
}
void loop() {
long v;
static int debounce=0;
static int var=25;
for(long t=0;;t++){putb(
t*((t>>9|t>>13)&var&t>>6) // original by visy was t*((t>>9|t>>13)&25&t>>6)
);
if(debounce==0){
digitalWrite(13,LOW);
if(digitalRead(2)==0){var--;debounce=2000;}
if(digitalRead(3)==0){var++;debounce=2000;}
}else{
debounce--;digitalWrite(13,debounce>300);
}
}
}
No eval on the arduino but you can program a parser or just type directly in Reverse Polish Notation (RPN). Then live coding of algorithm music on the microcontroller is possible.
I would add a potentiometer to select which algorithm runs at run time and a simple LED Visualizer that would make a really neat project.
Actually I would not do that at all, instead I would convert it to run using interrupts and read four buttons to decide which of upto 16 algorithms plays and then add a four bit LED Visualiser.
I will put up a video in a little while, in the meantime, heres the code -
EDIT: Video Added -
// one-line algorithmic music
// see viznut's blog http://countercomplex.blogspot.com/2011/10/algorithmic-symphonies-from-one-line-of.html
// and http://www.youtube.com/watch?v=GtQdIYUtAHg&feature=related
// ported to arduino by stimmer
// Audio out on pin 10
// Further ported to Interrupt based music generation with 4 button selection of upto 16 algorithms (or 15 + silence on no buttons pressed)
// by Duane B aka RCArduino
//
// Buttons are digital 8,9,10,11, music output is on digital 6
//
// Also added a four LED Visualiser on digital pins 2,3,4,5
//
#define SAMPLE_MAX (65535.0)
#define SAMPLE_FREQUENCY (8000.0)
#define TIMER1_FREQUENCY 2000000
#define UPDATE_RATE 8000
// by keeping t global we can use it to drive the visualiser as well
long t;
// iterate the grains and LFO
SIGNAL (TIMER1_COMPA_vect)
{
OCR1A += (TIMER1_FREQUENCY/UPDATE_RATE);
t++;
switch(PINB&15)
{
default:
case 0:
OCR0A = 0;
break;
case 1:
OCR0A = ((-t&4095)*(255&t*(t&t>>13))>>12)+(127&t*(234&t>>8&t>>3)>>(3&t>>14)); // by tejeez
break;
case 2:
OCR0A = t*(t>>11&t>>8&123&t>>3); // by tejeez
break;
case 3:
OCR0A = t*((t>>9|t>>13)&25&t>>6); // by visy
break;
case 4:
OCR0A = (t*(t>>5|t>>8))>>(t>>16); // by tejeez - nothing
break;
case 5:
OCR0A = ((t*(t>>8|t>>9)&46&t>>8))^(t&t>>13|t>>6); // by xpansive
break;
case 6:
OCR0A = ((t&4096)?((t*(t^t%255)|(t>>4))>>1):(t>>3)|((t&8192)?t<<2:t)); // by skurk (raer's version)
break;
case 7:
OCR0A = (t>>7|t|t>>6)*10+4*(t&t>>13|t>>6); // by viznut, xpansive, varjohukka
break;
case 8:
OCR0A = t*5&(t>>7)|t*3&(t*4>>10); // by miiro - nothing
break;
case 9:
OCR0A = (t|(t>>9|t>>7))*t&(t>>11|t>>9); // by red
break;
case 10:
long v;
OCR0A = v=(v>>1)+(v>>4)+t*(((t>>16)|(t>>6))&(69&(t>>9))); // by pyryp
break;
case 11:
OCR0A = (t>>6|t|t>>(t>>16))*10+((t>>11)&7); //by viznut
break;
case 12:
OCR0A = (t*(4|7&t>>13)>>((~t>>11)&1)&128) + ((t)*(t>>11&t>>13)*((~t>>9)&3)&127); // by stimmer - wow!
break;
case 13:
// free to use
break;
case 14:
// free to use
break;
case 15:
// free to use
break;
// any more and we need another bit from PORTB
}
}
void setup()
{
TCCR1A=0x0; // set the timer prescaler to 8 = 16/8 = 2MHz
TCCR1B=0x02; // set the timer prescaler to 8 = 16/8 = 2MHz
TIMSK1 |= (1<<OCIE1A); // Enable output compare match interrupt on OCR1A
//TCCR0A=0B10110011; //-8 bit audio PWM
TCCR0A=0B10000011; //-8 bit audio PWM
//TCCR0A=0x83; // Set timer waveform generation mode to FAST PWM, clear OC0A On match, set at bottom - OC0A = digital pin 6.
TCCR0B=0x01; // Set to clock frequency, no prescaler
OCR0A=127; // set in the middle - do we need this ? probably not.
DDRD|=1<<6; // Set digital pin 6 to output - channels 2 and 3
DDRB &= (~15); // set digital pins 8,9,10,11 as inputs
DDRD |= ((1<<2) | (1<<3) | (1<<4) | (1<<5)); // set digital pins 2,3,4,5 as outputs for the visualiser
}
void loop()
{
unsigned char output = OCR0A;
// dvidide by 16 to get a four bit power level
output >> 4;
// clear visualiser bits on portD
PORTD &= 0B000011;
// set the visualiser bits
PORTD |= (output<<2);
}
Here is my code (with serial, not a keyboard as in the video):
// Parser for algorithmic music (serial version)
// This version:
// * no limit on number of digits for constants
// * No error checking
// * No precedence (of operators) checking
// * Each individual operation MUST BE between parentheses
// * Algos must end with the character 'E'
// by 23N! 2012.09.15
const byte MAX_CHAR=50; // max number of characters in formula
char pile[MAX_CHAR],sortie[MAX_CHAR]; // stacks for conversion to RPN (shunting-yard algorithm)
unsigned long pileRPN[MAX_CHAR],t; // stack for RPN computation / time variable
byte p,s; // counters for pile and sortie
boolean firstChar,multipleDigit; // for reinitialization when new algo is received / multiple digit detection
void setup() {
// This works on an arduino MEGA.
// Just modify the registers and pins for other boards.
// A prescaler of 8 with a 8 or 9 bits fast PWM might me more appropriate but lower sampling rate.
// In the current situation, long algos might cause problems/crashes.
// If sound seems too slow, just increase the increment of t.
// PWM out pin
DDRB = (1 << PB5); // Pin 11 on MEGA
// fast-PWM 10-bit / compare match on OCR1A / output on OC1A=PB5 / prescaler=1
TCCR1A = (1 << COM1A1)|(1<<WGM10)|(1<<WGM11);
TCCR1B = (1 << CS10)|(1<<WGM12);
// Initial values
t=0;
p=0;
s=0;
firstChar=true;
multipleDigit=false;
// Serial
Serial.begin(57600);
Serial.println("Ready!");
}
ISR(TIMER1_OVF_vect) // Interrupt Service Routine (sampling rate of sound)
{
// computation of the algo based on its RPN representation
p=0;
for (byte i=0;i<s;i++) { // Loop on the RPN algo as an array of char (=sortie)
switch(sortie[i]) {
case 't': // variable=> just add to pile
pileRPN[p++]=t;
multipleDigit=false; // not a digit
break;
case '+': // perform ADDITION, de-pile
p--; //de-pile
pileRPN[p-1]+=pileRPN[p]; // perform operation
multipleDigit=false; // not a digit
break;
case '|': // perform OR, de-pile
p--;
pileRPN[p-1]|=pileRPN[p];
multipleDigit=false;
break;
case '&': // perform AND, de-pile
p--;
pileRPN[p-1]&=pileRPN[p];
multipleDigit=false;
break;
case '^': // perform XOR, de-pile
p--;
pileRPN[p-1]^=pileRPN[p];
multipleDigit=false;
break;
case '-': // perform SUBSTRACTION, de-pile
p--;
pileRPN[p-1]-=pileRPN[p];
multipleDigit=false;
break;
case '*': // perform MULTIPLICATION, de-pile
p--;
pileRPN[p-1]*=pileRPN[p];
multipleDigit=false;
break;
case '/': // perform DIVISION, de-pile
p--;
pileRPN[p-1]/=pileRPN[p];
multipleDigit=false;
break;
case '>': // perform BIT SHIFT RIGHT, de-pile
p--;
pileRPN[p-1]=pileRPN[p-1]>>pileRPN[p];
multipleDigit=false;
break;
case '<': // perform BIT SHIFT LEFT, de-pile
p--;
pileRPN[p-1]=pileRPN[p-1]<<pileRPN[p];
multipleDigit=false;
break;
case '%': // perform MODULO, de-pile
p--;
pileRPN[p-1]=pileRPN[p-1]%pileRPN[p];
multipleDigit=false;
break;
default: // digit, add to pile except if multiple digits (in this case process the digits)
if (multipleDigit) {
pileRPN[p-1]*=10;
pileRPN[p-1]+=sortie[i]-48;
}
else {
pileRPN[p++]=sortie[i]-48;
multipleDigit=true;
}
break;
}
}
OCR1A=pileRPN[0]; // PWM output (the final result is the first element of the stack)
t++; // variable inc.
}
void loop() {
// read the serial input and parse it (don't forget to type "E" at the end of the algo)
// example (copy-paste in serial monitor): ((t>5)*t)E (">" is actually ">>" not "greater than")
// each individual operation MUST BE between parentheses.
while (Serial.available() > 0) {
if(firstChar) { // Disable interrupt and initialize variables
TIMSK1 = (0 << TOIE1);
firstChar=false;
p=0;
s=0;
}
char inChar = Serial.read(); // current char
char symbol=inChar; // meta-char
if ((inChar>=48)&(inChar<58)) symbol='D'; // Digit
if ((inChar!='(')&(inChar!=')')&(inChar!='t')&(inChar!='E')&(symbol!='D')) symbol='O'; // Operator
// Shunting-yard algorithm
switch(symbol) {
case 't':
case 'D':
sortie[s++]=inChar; // if variable or digit, add to sortie
break;
case 'O':
case '(':
pile[p++]=inChar; // if operator or left parenthesis, add to pile
break;
case ')':
sortie[s++]=pile[p-1]; // if right parenthesis, transfer from pile to sortie, remove left parenthesis
// In our case there is always a single operation between parentheses. No need to check if there is
// a left parenthesis in the pile, we can just move the index p back and overwrite the left parenthesis.
p-=2;
break;
case 'E': // End char => send the new algo to the interrupt for sound generation.
// Not necessary to have an end char, end can be detected by matching parentheses.
// But an End char makes things clearer and easier.
Serial.println("Uploaded!");
firstChar=true;
t=0;
p=0;
TIMSK1 = (1 << TOIE1); // enable interrupt
break;
}
}
}
If the algorithm is entered in RPN then it's even easier as there is almost nothing in the loop.
In the case of a keyboard and an LCD screen, then it's getting difficult to type anything if the algo is a bit long (the program rarely enters the loop and then misread the keyboard most of the time). What I do in this case is using an arduino for the interface/parsing (just a loop, no interrupt) and an attiny for sound (computation of the algo). Those two are connected through serial.
Another example from your code: (t|(t>>9|t>>7))t&(t>>11|t>>9). This becomes ((t|((t>9)|(t>7)))(t&((t>11)|(t>9))))E
Parentheses make thing quite complicated and long. That's why it might be better to just write directly in RPN or to use operators precedence.
In RPN, it becomes (if no mistake): t11>>t9>>|t&t9>>t7>>|t|*
Stimmer,
Since I have put a video on youtube which is essentially a video of your original sketch in action with a half and hours worth of additions, what can I add to the description to credit you ? do you have a web page or just a link to this post which I have already included ?
DuaneB: Linking back here is fine, I don't have a website (yet). I just had a go at building your version using an RGB LED, it looks cool 8) Try wiring up one of the tune select inputs to an LED output - it usually sounds terrible but sometimes you get funky mashups of two of the tunes (All the tunes are working, although tune 4 needs you to press reset to get it to start.)
Hi,
I saw that I had my buttons connected out of order so yes, your right they do all work.
As for pressing reset, I should really add a reset of the counter when ever the tune number changes.
I will update my blog post and comments over the weekend, I will also add support for Leonardo's the timer output is connected to a different pin than it is on UNOs.