User:Fako Berkers/clapping

From XPUB & Lens-Based wiki

Dynamic clapping music

My idea was to make a small interface to the clapping music program that enables people to change the pattern manually. I wanted to have four buttons:

  • button 1 would shift a note/rest from the end to the beginning with the part of performer 1
  • button 2 would shift a note/rest from the beginning to the end with the part of performer 1
  • button 3 would shift a note/rest from the end to the beginning with the part of performer 2
  • button 4 would shift a note/rest from the beginning to the end with the part of performer 2

This allows you to explore all possibilities of the clapping music principle.

While building this Stock mentioned some possible improvements and I went along learning some new things. This also means that I do not entirely understand the code yet, but I'll try to explain as much as possible so I can grasp it better myself. I also want to mention Laura for her great idea to double the pattern (it makes coding Clapping Music so much easier, thnx!)

Hardware

  • Pin 13 is connected (together with ground) to the speaker as output.
  • Pin 8 to 11 are output and set to high one at a time using a timer (internal) interrupt
  • Pin 2 is used as input and is set to high
  • The four wires coming from 8 through 11 are going to one end of the switches
  • The other end of these switches are connected through diodes with ground (in such a way that no current can go from ground to the switches)
  • Ground is connected to pin 2

Code

// Include libraries
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>

// Declare variables
// The pattern
char pat[] = "xxx xx x xx ";
char patd[25];
int patlen = strlen(pat);
// Indices for pattern changing
int per1 = 0;
int per2 = 0;
// per12 and 22 will change when buttons are pressed and per1 and 2 will synchronize with them at the end of the loop.
int per12 = per1;
int per22 = per2;
// Make button vars.
//bool pressed = false;
int button = 0;
// Define Pins
int outPin = 13;
 
int butt_state[4] = {HIGH, HIGH, HIGH, HIGH};

 
// define constatnts for configuring timer1

#define TIMER1_MAX               1250
#define TIMER1_INTERRUPT_MASK    _BV(OCIE1A)
#define TIMER1_MODE_A            0
#define TIMER1_MODE_B            (_BV(WGM12) | _BV(CS11) | _BV(CS10)) // CTC-mode, prescaler Fclk/64
 
void setup() {
  
  // Make the pattern to work with double size.
  strcat(patd,pat);  
  strcat(patd,pat);
  
  // Set Ardiuno in right mode.
  pinMode(8,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  digitalWrite(8,HIGH);
  digitalWrite(9,HIGH);
  digitalWrite(10,HIGH);
  digitalWrite(11,HIGH);
  pinMode(2,INPUT);
  digitalWrite(2,HIGH); // Activate internal pull up resistor.
  
  TCCR1A = TIMER1_MODE_A;
  TCCR1B = TIMER1_MODE_B;
  OCR1A = TIMER1_MAX;
  TIMSK1 = TIMER1_INTERRUPT_MASK;

  sei();  // enable interrupts globally 
 
  Serial.begin(115200); 
}
 
void loop () {
 
  for (int p=0; p<patlen; p++) {
    
    // Make the tones.
    if (patd[p+per1] == 'x') {
      tone(outPin, 500, 25);
    }
    delay(25);
    if (patd[p+per2] == 'x') {
      tone(outPin, 1000, 25);
    }
    delay(300);
    
    per1 = per12;
    per2 = per22;
    
    Serial.print(per1);
    Serial.print(" ");
    Serial.print(per2);
    Serial.print(" Butt_State:");
    for (int i = 0; i < 4; i++)
    {
      Serial.print(butt_state[i]);
      Serial.print(" ");    
    }
    Serial.println();
    
  }
  
}

// Called during timer interrupt
void read_buttons() {
  int input_pin = digitalRead(2);
  
  if (input_pin != butt_state[button])
  {
    butt_state[button] = input_pin;
    if (input_pin == LOW)    // button pressed 
    {
      switch (button)
      {
        case 0:
          per22++;
          break;
        case 1:
          per22--;
          break;
        case 2:
          per12++;
          break;
        case 3:
          per12--;
          break;
      }
      if (per22 > patlen) per22 = 0;
      if (per22 < 0) per22 = patlen;
      if (per12 > patlen) per12 = 0;
      if (per12 < 0) per12 = patlen;
    }
  }  
}

// Timer1 Output Compare A Interrupt
ISR(TIMER1_COMPA_vect)
{
    digitalWrite(button+8,HIGH);
    button++;
    button %= 4; // This makes a value of 4 going to 0 (making possible values 0, 1, 2, 3)
    digitalWrite(button+8,LOW);
    read_buttons();
}

The timer interruption is a build in possibility of the AVR chip used on the Arduino. The chip is reprogrammed to activate the timer by using some constants and two AVR libraries. The timer counts to 1250, starts over and calls the code in the ISR block (interrupt system respons). This code sets an output (8 through 11) HIGH and the output that was HIGH before to low. The interruption also calls the read_buttons function.

This function reads the input from pin 2. It monitors in which "mode" each button is and only reacts if the "mode" changes. Furthermore it only executes the buttons code if the signal at pin 2 is low. When a button is pushed the voltage on pin 2 drops to 0, because the voltage is 0 at the end of a closed circuit, so read_buttons only responses when a button is pressed. Which button is pressed is saved in the button variable. This way the code knows how the user wants to shift the pattern of which "performer".

Next step

I would like to insert a latin rhythm in the same interface instead of what Steve Reich used.