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.

// 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 }

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