<> Navigation: * [[AJMPublic/teaching/arduino-pi/projects/arduino/dac-audio|DAC Audio]] * [[AJMPublic/teaching/arduino-pi/projects/arduino/dac-audio/light-theremin|Light Theremin]] [[attachment:Light Theremin code]] {{{ /* Light Theremin A. J. Misquitta (2013) based on code by Amanda Ghassaei Use the sine wave generated by an 8bit DAC as the foundation for a light theremin. The sketch first performs a callibration step with beeps to help the user perform the necessary actions. With the callibration, the sketch determines the light and dark levels registered by the phototransistor. This makes the range of tones generated by the light theremin fairly stable (though in low light the *number* of tones available will be limited). Thereafter the pitch can be controlled by varying the light incident on the phototransistor - presumably using your hand as a mask. There is no software volume control possible. Instead, it is expected that the theremin will contain a hardware volume control. */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * */ byte sine[] = { 127, 134, 142, 150, 158, 166, 173, 181, 188, 195, 201, 207, 213, 219, 224, 229, 234, 238, 241, 245, 247, 250, 251, 252, 253, 254, 253, 252, 251, 250, 247, 245, 241, 238, 234, 229, 224, 219, 213, 207, 201, 195, 188, 181, 173, 166, 158, 150, 142, 134, 127, 119, 111, 103, 95, 87, 80, 72, 65, 58, 52, 46, 40, 34, 29, 24, 19, 15, 12, 8, 6, 3, 2, 1, 0, 0, 0, 1, 2, 3, 6, 8, 12, 15, 19, 24, 29, 34, 40, 46, 52, 58, 65, 72, 80, 87, 95, 103, 111, 119, }; // Control parameters: These can be altered. int num_waves = 50; // number of sine waves between phototransistor readings int max_delay = 30; // maximum delay (the larger the deeper the pitch can go) int min_delay = 4; // minimum delay (the smaller the higher the pitch can go) //Sketch variables and parameters. Do not alter these. int samples = 100; // number of samples in sine[] int bright = 0; // bright reading int dark = 0; // dark reading int range_of_delays = (max_delay - min_delay); int range_of_readings = 0; void setup(){ //set digital pins 0-7 as outputs for (int i=0;i<8;i++){ pinMode(i,OUTPUT); } // The pitch phototransistor will be read at analogue pin A2: pinMode(A2,INPUT); //Start the serial monitor: Serial.begin(115200); /* Bright calibration. Remove your hands from the phototransistors. The calibration uses the average of 10 readings spread over around 5 secs. A beep will sound when the calibration is over. */ Serial.println("Calibration: BRIGHT : No hands!!!"); beep(12,500); delay(2000); bright = 0; for (int i = 1; i<10; i++){ bright = bright + analogRead(A2); delay(500); } bright = bright/10; Serial.print("Bright level = "); Serial.println(bright); /* Dark calibration. Place your hand in the lowest position comfortably possible over the phototransistor. As with the bright calibration, we use the average of 10 readings performed over 5 secs. A beep will sound when this is done. */ Serial.println("Calibration: DARK : Cover it!!!"); beep(12,500); delay(2000); dark = 0; for (int i = 1; i<10; i++){ dark = dark + analogRead(A2); delay(500); } dark = dark/10; Serial.print("Dark level = "); Serial.println(dark); Serial.println("Calibration complete."); delay(2000); beep(8,500); //This is the range of expected readings: range_of_readings = bright - dark; } void loop(){ //Read in the phototransistor output: int photores = analogRead(A2); /* Calculate the pitch-delay. A simple linear interpolation is used to map the range of phototransistor readings to the range of expected values of the pitch-delay. The callibration step doesn't ensure we won't see a negative pitch-delay. This could happen if the light levels changed after the callibration was performed. So we use the abs() function. This calculation needs to be make faster. Divisions are expensive, so we should find a way to get rid of the division. */ int pitch_delay = (range_of_delays*(photores-dark))/range_of_readings; pitch_delay = abs(pitch_delay) + min_delay; /* Another way of performing the mapping is to use the map() function. This is faster than the above and should be attempted. It will not check for negative values so some kind of offset may be needed. The map() function maps the value of the first entry (variable photores) from the range [light,dark] to the range [max_delay,min_delay] With the exception of the abs() step, this is exactly what the above statements perform. But, this function is much better optimized so is substatntially faster than the above. */ //int pitch_delay = map(photores,light,dark,max_delay,min_delay); // Uncomment the following lines if you wish to see the readings on the // Serial monitor: /* Serial.print(photores); Serial.print(" "); Serial.println(pitch_delay); delay(500); */ //Generate a specified number of sine waves. for (int n=0; n