Monday, April 8, 2013

Arduino Therimin - One Resistor, Photocell, And Spare Headphones Or Speaker

Disclaimer: The code contained here is refactored code from user gwarbeh at Instructables.com. I'm posting this because I wanted to share the refactored code and a schematic that was missing on the original post. I encourage anyone interested in photos of the implentation and steps on how to hack some headphones to visit the original tutorial.

The Schematic





The Refactored Code


[sourcecode language="c"]
// Optical Theramin

// pin definitions
#define PHONES 9 // headphones at digital pin 9
#define PHOTOCELL 0 // photocell at analog pin 0

// variable definitions
long val = 0; // stores raw value from photocell
long maxread = 0; // maximum value reset for calibration phase
long minread = 1000; // minimum value reset for calibration phase
double f = 0; // tone frequency
double logf = 0; // logarithm of normalized frequency
double normf = 0; // normalized frequency
int i = 0; // loop incrementer
int ilogf = 0; // rounded logarithm
int length = 0; // length of tone
double shift = 0; // shift for calibration
double factor = 0; // scaling factor 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... sort of
double gap = 1.148698355; // ratio of consecutive notes (pentatonic)
// it's the 5th root of 2
// or as an alternative:
// double gap = 1.059463094; // ratio of consecutive notes (chromatic)
// its the 12th root of 2

void setup()
{
pinMode(PHONES, OUTPUT);

// calibration loop to determine a reasonable range of light levels (minread to maxread)
// then 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) maxread = val; // as the values climb, store the largest
if (val < minread) minread = val; // as the values drop, store the smallest
delay(10);
}

// now we use the calibration to calculate scale and shift parameters
// scale parameter: it's like a slope
factor = (double)(maxfreq - minfreq) / (double)(maxread - minread);

// shift parameter: it's like an offset
shift = factor * minread - minfreq;
}

void loop()
{
val = analogRead(PHOTOCELL); // read photocell
length = random(10, 400); // generate random tone length

// this linearly maps the frequency to
// a value between minfreq and maxfreq
// according to the calibration result
f = factor * val - shift;

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, length);
}
[/sourcecode]

2 comments:

  1. [...] Disclaimer: The code contained here is refactored code from user gwarbeh at Instructables.com. I'm posting this because I wanted to share the refactored code and a schematic that was missing on the...  [...]

    ReplyDelete
  2. [...] Disclaimer: The code contained here is refactored code from user gwarbeh at Instructables.com. I'm posting this because I wanted to share the refactored code and a schematic that was missing on the...  [...]

    ReplyDelete