Feladat: Csináljunk Theremint!

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.

A feltaláló, Leon Theremin játszik a hangszeren

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.

Csak a kezét nézd!

Majdnem-Theremin

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.

Csalás-Theremin

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.

Bónusz: vibrato effekt

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.

Mit tanultunk ebben a részben?

A C nyelv csak pár utasítás (if, else, for, while), adattípus (int, boolean) és minden más függvény. A függvények egy részét az Arduino készítőinek köszönhetjük, de mi is könnyen készíthetünk függvényeket. A függvények a programok építőelemei: kis programdarabkák, amikből építkezni lehet.