LEGO Optical Theremin

It should be pretty straightforward to use one or more light sensors with a LEGO Mindstorms controller to build a theremin.

Someone did it with an Arduino, for example.

here's the link:

http://www.instructables.com/id/Arduino-Optical-Theremin/?ALLSTEPS

here's the code:

// Optical Theramin

//pin definitions
#define PHONES 9   // headphones connected to digital pin 9
#define PHOTOCELL 0 //photocell analog in pin 0

//variable definitions
long val = 0;        //stores raw value from photocell
long maxread = 0;    //maximum value from calibration phase
long minread = 1000;  // minimum value from calibration phase
double f = 0;         // frequency of sound
double normf = 0;    // normalized frequency 
double logf = 0;      // logarithm of normalized frequency
int ilogf = 0;        // rounded logarithm
int i = 0;            // loop dummy variable
double factor = 0;    // scaling factor for calibration
double shift = 0;     // shift for calibration
long maxfreq = 1048;  // maximum desired frequency after calibration
long minfreq = 131;   // minimum desired frequency after calibration


//magic numbers that make the intervals sound pleasing
double gap = 1.148698355;  //ratio of consecutive notes (pentatonic)
                             // it's the 5th root of 2
//double gap = 1.059463094;  //ratio of consecutive notes (chromatic)
                              // its the 12th root of 2

                              
void setup()
{
  pinMode(PHONES, OUTPUT);    // sets the digital pin as output

// calibration loop to determine a rasonable range of light levels (minread to maxread)
// and map that to frequencies between minfreq and maxfreq
  for (i = 0; i< 500; i++) {  // calibration loop runs for 5 seconds
    val = analogRead(PHOTOCELL);   // read photocell 
    tone(PHONES, val);          // play raw tone to guide calibration
    if (val > maxread) {        // as the values climb, store the largest
      maxread = val;
    }
    if (val < minread) {        // as the values drop, store the smallest
      minread = val;
    }  
    delay(10);                  // reasonable delay
  } 
  
  //Now we use the calibration to calculate scale and shift parameters
  factor = (double)(maxfreq - minfreq) / (double)(maxread - minread); // scale parameter
  //it's like a slope
  
  shift = factor * minread - minfreq;  //shift parameter: it's like an offset
}

void loop()
{
  val = analogRead(PHOTOCELL);   // read photocell 
  f = factor * val - shift;     // this linearly maps the frequency to
                                // a value between minfreq and maxfreq
                                // according to the calibration result
  
  normf = f / (double) minfreq;  // Dividing an exponential function by the min value
  logf = log(normf) / log(gap); // allows us to take the log (base gap) and the result 
  ilogf = round(logf);           // is the number of notes above the lowest, once we round it.
  f = minfreq * pow(gap,ilogf);  // we better "unlog" it.
  
  
  tone(PHONES, f);              // this produces the tone signal
  
}

next steps

That gives us enough to work with as a general idea.

First, when you run the program, it would have a "calibration" phase. You would cover the light sensor(s) to make them receive as little light as possible for a second, then expose them to as much light as they'll be getting at "max".

* The calibration loop will remember a minimum and a maximum. * Then calculate "factor": (highest-desired-frequency - lowest-desired-frequency) / (maximum - minimum) * then likewise calculate "shift".

For "runtime" we make a loop that gets a reading from the light sensor, processes it, and use that as an input for the speaker virtual instrument.

Sounds straightforward!

I'll post details as I implement this. (note: it's not on the top of my list right now)

Topic revision: r3 - 2012.10.02 - PaulReiber
 
Copyright © is by author. All material on this collaboration platform is the property of its contributing author.