Computing the Interval in the Optical Theremin

I wanted to briefly explain the interval scaling that happens in the sketch for the optical theremin since I did not get a chance to talk about this during the workshop.

So, the idea was to generate sounds between 20Hz and 200Hz (I will explain later why I picked this interval). In order to compute the period for one sound wave of each of these frequencies we take the reciprocal:

Tlo = 1/(20Hz) = 1/(20 1/s) = 1/20 s = 0.05s = 50 msec 
Thi = 1/(200Hz) = 1/200 s = 5 msec
This means the code
loop(){ 
	digitalWrite(soundPin, HIGH); 
	delay(25); 
	digitalWrite(soundPin, LOW);
	delay(25); 
}
will generate a sound with the frequency 20Hz, and the code
loop(){ 
	digitalWrite(soundPin, HIGH); 
	delay(2);  // had to approximate...only int values allowed 
	digitalWrite(soundPin, LOW); 
	delay(2); 
}
will generate a sound with the frequency 200Hz (actually slightly higher because we delaying by 2msec instead of 2.5msec). Here we ignore any delays due to the commands themselves. As you might have gathered, the function 'delay' delays execution in milliseconds. Also notice that we had to split the periods in half; one half for the positive part of the sound wave and one half for the negative part of the sound wave.

Now, in order to let the theremin to sweep from 20Hz to 200Hz we want to convert the analog signal digitized into values between 0 and 1023 into delays between 5 and 50 milliseconds. Once we have done that we then want to divide the resulting delay by two in order to compute one half of the appropriate period of the sound waves. This is exactly what the statement

interval = (analogRead(freqPin)/25 + 5)/2;
accomplishes in the sketch. At the low end of the analog signal we have
(0/25 + 5)/2 = 5/2 = 2
The result is 2 rather than 2.5 because integer division truncates the result. At the high end we have
(1023/25 + 5)/2 = 45/2 = 22
Which is close enough to 25. Turns out that there is a very elegant way to accomplish the same scaling with the Arduino 'map' function:
interval = map(analogRead(freqPin),0,1023,5,50)/2
or even more elegantly
interval = map(analogRead(freqPin),0,1023,2,25)
Now, why did I choose the range 20-200Hz? First, 20Hz is the lowest frequency we can typically hear. Second, because the 'delay' function limits the frequency of generated tones with periods T = 2msec = 2 10^-3 sec,
maxfreq = 1/T = 1/(2 10^-3 sec) = .5 10^3 Hz = 500 Hz
(ok, so I could have gone to 500Hz, but I liked the range 20-200Hz). If you want to generate higher pitched tones than 500Hz you will have to use a different delay function, namely 'delayMicroseconds'. So what kind of delay would we need in order to generate a 20,000Hz tone.
T = 1/(20,000 Hz) = 1/20,000 s = 5 10^-5 s = 50 microseconds = 50 usec
so our code would look something like this,
loop(){ 
	digitalWrite(soundPin, HIGH); 
	delayMicroseconds(25);
	digitalWrite(soundPin, LOW); 
	delayMicroseconds(25); 
}
I hope this shed some light on the interval scaling.