Vagy legalábbis valami hozzá hasonlót! A Theremin talán a legelső elektronikus hangszer. Úgy működik, hogy van két antennája, és ezekkel érzékeli, hogy milyen közel tesszük a kezünket. A jobb kezünkkel a hangmagasságot tudjuk megváltoztatni, míg a bal kezünkkel a hangerősséget. Ha a bal kezét közel teszi, úgy lehet "szünetet" játszani, míg egyre távolabb téve egyre hangosabb a hang.
Elég nehéz játszani rajta, több okból:
Egy nagyon okos kéztartás módszert dolgoztak ki, a jobb kezet figyeld, amit igyekszik nem elmozdítani. A hangok egyik felét csak az ujjakat hirtelen kinyújtva találja el, a másik feléhez a csuklóját fordítja kicsit előrébb, így próbálja meg eltalálni a megfelelő hangot.
Amikor "integet" az ujjaival, abból lesz ez a kísértetiesen hullámzó hang - mivel változik a hangmagasság az integetés közben.
Rendes Theremint még nem tudunk csinálni, de a fényérzékelő nagyjából használható arra, hogy távolságot érzékeljünk vele. No ezt meg hogy? Egyszerűen! Általában világos van, és ahogy a kezünket közelítjük a fényérzékelőhöz, a fényérzékelő egyre kevesebb fényt kap.
Tehát a fényérzékelő által érzékelt szám nagyjából arányos a kezünk távolságával.
Az analogRead(6) visszaad egy számot: minél sötétebb van (azaz minél közelebb a kezünk) annál nagyobb számot. Nagyjából 100-at ad vissza akkor, amikor világos van, és maximum 1023-at amikor a legsötétebb sötétet érzékeli.
Tehát:
és:
Az előző oldalon elkészített villanytücsök remek lesz erre is, hiszen van rajta fényérzékelő és piezo hangszóró is.
A progi meg valahogy így nézhet ki:
const int PIEZO=0;
const int SENSOR=4;
void setup() {
pinMode(PIEZO,OUTPUT);
pinMode(SENSOR,INPUT_PULLUP); // fényérzékelő
}
void loop() {
if (analogRead(6)>80) {
tone(PIEZO,(analogRead(6)-80)*30+100);
delay(1);
}
else {
noTone(PIEZO);
}
}
Hú, mi az a (analogRead(6)-80) * 30 itt kérem szépen? Hát izé, csak ált. iskolás matek. Ha 80-at mond az érzékelő, akkor 80-80 * 30 az pont 100 lesz. Ez jó mély hang.
Ha meg mondjuk sötétedik, és úgy 200-at mond az érzékelő, akkor 200-80 * 30 + 100 az 3700 lesz, ez egy jó magas hang.
Lehet, hogy Nálad variálni kell kicsit a számokkal - hiszen ez az érzékelés attól is függ, hogy épp mennyi a külső fény.
Ha akkor is zenél, amikor nincs előtte a kezed, akkor növeld a fényt (pld. kapcsold fel a lámpát), vagy változtasd meg a 80-at mindenhol.
Ha zavar, hogy könnyen nagyon mély és nagyon magas hangok is vannak, csökkentsd a * 30 értékét. Ha kisebb számmal szorzod, akkor kisebb lesz az eltérés a sötét és a világosabb állapotoz tartozó hangok közt.
Ha minden jó, csak az egésznek magasabban vagy mélyebben kellene szólnia, változtasd meg a + 100 értékét. Ha nagyobb ez a szám, akkor magasabb, ha kisebb akkor meg mélyebb lesz minden.
Variálj a delay(1) értékével - próbáld ki a delay(20), delay(100)-at is. Ettől egy-egy hang tovább szól, és érdekesen követi a kezed.
Lehet, hogy ezzel a majdnem-Thereminnel sikerült zenét csinálnod - de szerintem elég nehéz egy Boci-boci-t lenyomni vele. Az igazi Thereminnel is nagyon nehéz, már próbáltam :)
Mi lenne, ha tennénk egy kis csalást, és csinálnánk Boci-boci Theremint? Ugyanúgy a kezeddel kell játszani, de a csalás az, hogy minden kézmozdulatra automatikusan a következő hangot játssza le, a megfelelő hosszúságban.
Mit kell ehhez tenni? Vedd elő a Boci-boci programot. Ne feledd átírni, mert most a 0-ás porton van a piezo.
const int PIEZO=0;
void setup() {
pinMode(PIEZO,OUTPUT);
}
void note(int freq,int duration) {
tone(PIEZO,freq);
delay(duration);
noTone(PIEZO);
delay(50);
}
void loop() {
note(261,200);
note(392,200);
note(261,200);
note(392,200);
note(493,400);
note(493,400);
}
A következő lépés pedig az, hogy beletesszük a fényérzékelő kezelését, és a hangok lejászása előtt megállunk, amíg világost érzékel a fényérzékelő. Ha a kezünkkel belenyúlunk, akkor picit sötét lesz, és elindul a hang.
const int PIEZO=0;
const int SENSOR=4;
void setup() {
pinMode(PIEZO,OUTPUT);
pinMode(SENSOR,INPUT_PULLUP); // fényérzékelő
}
void note(int freq,int duration) {
while(analogRead(6)<100) {
// világos van, nem csinálunk semmit
}
tone(PIEZO,freq);
delay(duration);
noTone(PIEZO);
delay(50);
}
void loop() {
note(261,200);
note(392,200);
note(261,200);
note(392,200);
note(493,400);
note(493,400);
}
Próbáld csak ki! Szerintem majdnem jó. Az a while ciklus megy körbe-körbe és csinálja a nagy semmit, amíg világos van. Azaz, amíg a szenzor 100-tól kisebb értéket ad, ismétli a ciklus magját ami a bajszos zárójelek közti kód - ami most pont egy nagy semmi. Szóval semmi sem történik, amíg világos van. De mihelyst sötét lett, a ciklusnak vége lesz, és megy tovább a hangot lejátszani.
Mivel ezt a note függvénybe tettük, ezért minden hang lejátszása előtt várni fog, amíg az érzékelő fölé tesszük a kezünket, emiatt a Boci-boci dallamot nem is kellett módosítani.
Az az egyetlen baja, hogyha folyamatosan az érzékelő fölött van a kezed, akkor lejátsza az egész Boci-boci-t, pedig csak egy hangot kellene neki. Igaziból ha egy hanggal készen vagyunk, és még mindig fölötte van a kezünk, akkor meg kellene várni, hogy elvegyük a kezünket. Mivel is? Hát egy másik while ciklussal, ami pont addig vár, amíg sötét van a szenzor előtt, azaz amíg a kiolvasott érték nagyobb, mint 100.
const int PIEZO=0;
const int SENSOR=4;
void setup() {
pinMode(PIEZO,OUTPUT);
pinMode(SENSOR,INPUT_PULLUP); // fényérzékelő
}
void note(int freq,int duration) {
while(analogRead(6)<100) {
// világos van, nem csinálunk semmit
}
tone(PIEZO,freq);
delay(duration);
noTone(PIEZO);
delay(50);
while(analogRead(6)<100) {
// még mindig előtte van a kéz, nem csinálunk semmit
}
}
void loop() {
note(261,200);
note(392,200);
note(261,200);
note(392,200);
note(493,400);
note(493,400);
}
No most már tökéletes. Bár az igazi Theremin hangja nem ilyen csipogós, sokkal inkább hullámzós.
A vibrato azt jelenti, hogy egy hang magasságát szándékosan hullámoztatjuk a kívánt hangmagasság körül. Azaz olyan, mint egy gyors sziréna, picit feljebb-lejjebb mászkálva.
const int PIEZO=0;
const int SENSOR=4;
void setup() {
pinMode(PIEZO,OUTPUT);
pinMode(SENSOR,INPUT_PULLUP); // fényérzékelő
}
void note(int freq,int duration) {
while(analogRead(6)<100) {
// világos van, nem csinálunk semmit
}
// mágikus vibrato effekt
for (int i=0;i<duration;i+=40) {
// kicsit magasabbra
for (int vibrato=-10;vibrato<10;vibrato++) {
tone(PIEZO,freq+vibrato);
delay(2);
}
// kicsit mélyebbre
for (int vibrato=10;vibrato>-10;vibrato--) {
tone(PIEZO,freq+vibrato);
delay(2);
}
}
noTone(PIEZO);
delay(50);
while(analogRead(6)<100) {
// még mindig előtte van a kéz, nem csinálunk semmit
}
}
void loop() {
note(261,200);
note(392,200);
note(261,200);
note(392,200);
note(493,400);
note(493,400);
}
Wow, ez már aztán jobban szól! De mi az a rengeteg for ciklus a note-ban?
A belseje:
// kicsit magasabbra
for (int vibrato=-10;vibrato<10;vibrato++) {
tone(PIEZO,freq+vibrato);
delay(2);
}
// kicsit mélyebbre
for (int vibrato=10;vibrato>-10;vibrato--) {
tone(PIEZO,freq+vibrato);
delay(2);
}
Olyan, mint a sima vijjogós sziréna kód. Épp csak nem 220 és 440 között vijjog, hanem -10..10 közötti számot fog hozzáadni az eredeti kívánt frekvenciájoz, és ezért fogjuk hallani ezt a finom kis vibrato effektet.
Viszont itt vannak fix delay-ek, mégpedig 2 ms. A vibrato sebességének fixnek kell lennie, nem lehet túl lassú, de túl gyors sem. Viszont, ha itt 2 ms van, és a két ciklusban -10..9, aztán meg 10..-9 között tekerjük a vibrato értékét, akkor az azt jelenti, hogy pontosan 40ms eltelik, mire egy vibrato körrel végzünk.
Hogyan fogjuk addig kitartani a hangot, amíg kellene? Erre való a külső for ciklus.
for (int i=0;i<duration;i+=40) {
// itt van a vibrato ami 40ms-ig tart
}
A külső for ciklussal annyiszor ismételjük a vibrato kódot, hogy leteljen a teljes hang kívánt ideje. Az i lesz az eltellt idő, és mivel minden vibrato 40ms-ig tart, ezért 40-esével növeljük az i-t. Addig kell a vibrato-kat csinálni ebben a ciklusban, amíg el nem érte az i a hang kívánt teljes idejét (duration).
Fúú... Na most szaladj neki mégegyszer, ha nem érted :) a dolog logikus, csak picit nyakatekert.