Multi-Serial PhotoCell Theremin

Started with the old Theremin #2, used a photocell instead of the rangefinder, added a second photocell and tweaked the pitch shift button to jump octaves (e.g. a D# is still a D# when button is pressed).

One photocell controls the note (takes five readings with an array and maps it between 100 & 500 if pitch button is pressed and 50 & 250 if else); the second photocell controls the duration of the note by dividing the reading by 20; and the switch, as previously mentioned, controls the octave jump. In Processing, the the note, duration and switch are collected as serial data and used to oscillate the colours on the sketch’s grid. The note controls the opacity, duration affects the movement and the button does two things: controls the green value in the fill() AND affects the note reading (opacity) via pitch shifting.

Arduino code:

int analogCell1 = 0;
int analogCell2 = 1;
int button = 2;
int analogOutPin = 8;
int inByte = 0;

void setup()
{
  Serial.begin(9600);
  while (!Serial) {
    ;
  }
  pinMode(button, INPUT);
  pinMode(analogCell1, INPUT);
  pinMode(analogCell2, INPUT);
  pinMode(analogOutPin, OUTPUT);
  establishContact();
}

void loop()
{
  if (Serial.available() > 0) {

    inByte = Serial.read();

    int note=0;
    int duration=0;
    int serialReading [5];
    for (int i=0;i<5;i++){    
        serialReading[i] = analogRead(analogCell1);     
    }   
    if(analogRead(button)>1020){   
      note = map (meanOfValues(serialReading[0],serialReading[1],serialReading[2],serialReading[3],serialReading[4]),40,288,100,500);
    }
    else {    
      note = map (meanOfValues(serialReading[0],serialReading[1],serialReading[2],serialReading[3],serialReading[4]),40,288,50,250);    
    }
    duration = analogRead(analogCell2)/20;        
    tone(8,note,duration);
    delay(1);

    Serial.write(note*2);
    Serial.write(duration);
    Serial.write(button);

  }
}

void establishContact() {
  while (Serial.available() <= 0) {
    Serial.print('A');
    delay(300);
  }
}

int meanOfValues (int value1,int value2,int value3,int value4,int value5) {      
    int mean = (value1+value2+value3+value4+value5)/5;
    return mean;
}

Processing code:

import processing.serial.*;
Serial myPort;  

Cell[][] grid;
int cols = 40;
int rows = 40;
int greenValue;
float alpha;
float angle;

int[] serialInArray = new int[3];
int serialCount = 0;
boolean firstContact = false;

void setup() {
  size(600,600);
  grid = new Cell[cols][rows];
  for (int i = 0; i < cols; i++) {
    for (int j = 0; j < rows; j++) {
      grid[i][j] = new Cell(i*20,j*20,20,20,i+j);
    }
  }
  println(Serial.list());
  String portName = Serial.list()[0];
  myPort = new Serial(this, portName, 9600);
}

void draw() {
  background(0);
  for (int i = 0; i < cols; i++) {
    for (int j = 0; j < rows; j++) {
      grid[i][j].oscillate();
      grid[i][j].display();
    }
  }
}

void serialEvent(Serial myPort) {
  int inByte = myPort.read();
  if (firstContact == false) {
    if (inByte == 'A') {
      myPort.clear();
      firstContact = true;
      myPort.write('A');
    }
  }
  else {
    serialInArray[serialCount] = inByte;
    serialCount++;
    if (serialCount > 2 ) {
      alpha = serialInArray[0];
      angle = serialInArray[1];
      greenValue = serialInArray[1];
      println(alpha + "\t" + angle + "\t" + greenValue);
      myPort.write('A');
      serialCount = 0;
    }
  }
}

//----------------------- CELL CLASS -----------------------//

class Cell {
  float x,y;
  float w,h;

  Cell(float tempX, float tempY, float tempW, float tempH, float tempAngle) {
    x = tempX;
    y = tempY;
    w = tempW;
    h = tempH;
    angle = tempAngle;
  }
  void oscillate() {
    angle += 0.02;
  }

  void display() {
    stroke(255);
    fill(55,greenValue,127+127*sin(angle),alpha);
    rect(x,y,w,h);
  }
}

Leave a Comment