Pages: [1] 2   Go Down
Author Topic: One-line algorithmic music  (Read 5782 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
God Member
*****
Karma: 32
Posts: 506
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

http://countercomplex.blogspot.com/2011/10/algorithmic-symphonies-from-one-line-of.html



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.

Code:
// 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
  
  );
  
}
« Last Edit: October 02, 2011, 05:29:32 pm by stimmer » Logged


Nowhere
Offline Offline
God Member
*****
Karma: 3
Posts: 852
|-\ |\|\
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

MIND BLOWN


Sounds awesome, I'll have to have a go at it sometime
Logged

Soundcloud page: http://soundcloud.com/beefinator-2
Youtube channel: http://www.youtube.com/user/beefinator14
Old soundcloud page (ran out o

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 170
Posts: 12451
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Did you try the Arduino random generator allready?
Or an PI in 1 million decimals generator?
...
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks this great job.
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Beautifull!

I dont get the code at all but amazing how such low volume code can make those sounds.

Thanks!
Logged

Ottawa, ON, Canada
Offline Offline
Newbie
*
Karma: 0
Posts: 1
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello world!

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.

Thanks for the inspiration.
Logged

Offline Offline
God Member
*****
Karma: 32
Posts: 506
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Code:
// 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);
    }
  }
}
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I just did a similar thing (see http://arduino.cc/forum/index.php/topic,77884.0.html ). You can't write entirely new algorithms, but you can adjust the parameters of whatever ones you upload while they play.
Logged

Tokyo
Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thats great, I used to do something similar for pc graphics.

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.

Duane B

rcarduino.blogspot.com

 
Logged


Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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 -

http://www.youtube.com/watch?feature=player_detailpage&v=LWMdtStvpMg

Code:
// 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);
}

There are some little bugs that I will sort out

1) Leds do not turn off when there is no output
2) Two of the tunes do not play

Duane B

rcarduino.blogspot.com
« Last Edit: September 21, 2012, 08:44:32 am by DuaneB » Logged


Tokyo
Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is my code (with serial, not a keyboard as in the video):

Code:
// 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.
Logged

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

That looks very interesting, do you have any examples using your notation that could be pasted into the serial monitor ?

Duane B

rcarduino.blogspot.com
Logged


Tokyo
Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

A simple example: ((t>3)*t)E

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|*

Stephane
Logged

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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 ?

Duane B
Logged


Pages: [1] 2   Go Up
Jump to: