Projekt pokazujący jak stosunki częstotliwości są odbierane przez ludzki mózg. Rytmy, które dobrze się „czuje”, po zwiększeniu częstotliwości zamieniają się w interwał, który jest „przyjemny” do słuchania. Realizacja na buzzerach, potencjometrach oraz przycisku- bardzo podstawowe elementy. Możliwość samodzielnej zmiany częstotliwości, a także wyboru interwału (od sekundy małej do oktawy).
[przykładowe - prosimy o edycję]
1. Płytka ESP-WROOM 32
2. 2 buzzery
3. 2 potencjometry liniowe
4. Przycisk
5. Kable męsko-męskie
6. Płytka stykowa
Komputer ze środowiskiem Thonny
Celem projektu jest pokazanie, w jaki sposób stosunki częstotliwości są odbierane przez ludzki mózg. Przy niskich częstotliwościach odbierane jest to jako rytm, natomiast przy wyższych staje się to interwałem. Rytmy, które brzmią nienaturalnie, sprawiają, że przy po podwyższeniu częstotliwości odbieramy ich współgranie jako dysonans. W kodzie można zobaczyć, że oktawa będąca najmniej dysonansowym interwałem, to stosunek 2 do 1. Natomiast w przypadku trytonu i sekundy małej wygląda to bardziej skomplikowanie; są to odpowiednio 45 do 32 oraz 16 do 15. Dokładne wyjaśnienie znajduje się w załączonym filmiku YouTube.
Zasada działania: program zadaje jednemu i drugiemu buzzerowi stosunek częstotliwości, odpowiadający danemu interwałowi. Interwały można wybrać prawym potencjometrem, przekręcając w lewo można zmienić od sekundy małej do oktawy. Po wybraniu program oblicza częstotliwość maksymalną- czyli tę, którą osiągnie buzzer o wyższym dźwięku. W kodzie jest to f_limit.
Sterowanie częstotliwością odbywa się za pomocą lewego potencjometru. Jest on przeskalowany logarytmicznie, tak, aby zwiększanie częstotliwości nastąpiło wolniej przy niskich wartościach. Dzięki temu, łatwiej jest uchwycić moment, kiedy złożenie rytmicznych pyknięć zamienia się w dwa dźwięki, a tym samym- określony interwał.
Po osiągnięciu przez użytkownika częstotliwości maksymalnej, program odgrywa interwał najpierw harmonicznie, a później melodycznie. Dźwięki buzzera zbyt się ze sobą zlewają, więc jest to uwydatnienie, w jakim stosunku są one do siebie. Pętla powtarza proces grania harmonicznego, a później melodycznego aż użytkownik zmieni częstotliwość z maksymalnej.
Buzzery mają problemy ze zmianą z częstotliwości „wysokie” (powiedzmy ponad 40[Hz]- nie jest to istotne) na częstotliwość przy dolnej granicy pracy- czyli w okolicach 1[Hz]. Ten problem występuje przy kilku interwałach, które mają mniej skomplikowane stosunki. W takim wypadku należy kliknąć i przytrzymać na chwilę przycisk, aby buzzery przestały próbować cokolwiek odtwarzać i miały chwilę, by „odetchnąć”. Po puszczeniu z powrotem prawidłowo działają.
Opóźnienia w działaniu projektu (czyli pojawiające się w wielu miejscach kodu time.sleep()) wynika głównie z tego, że buzzery przy bardzo niskich częstotliwościach wymagają użycia tej funkcji, aby poprawnie działać. Ma to wpływ na całą petlę- przez to reakcje użytkownika z interfejsem uzyskują efekt z delikatnym opóźnieniem. Warto wziąć to pod uwagę w trakcie użytkowania.
Dla osób zainteresowanych w jakiś sposób muzyką, warto jeszcze wspomnieć o jednym fakcie. W rzeczywistości, w kanonie muzyki zachodniej interwały są zbliżone do tych odgrywanych przez buzzery, ale nie są one takie same. Wynika to z tego, że opisane zjawisko, jest można powiedzieć, w stroju pitagorejskim. Opiera się on na naturalnie występujących harmonicznych i był używany aż do połowy ubiegłego tysiąclecia. Dziś jednak używa się systemu 12TET, który równo dzieli dźwięki będące od siebie w stosunku pół tonu (czyli na przykład obok siebie na pianinie).
Jedno z załączonych zdjęć przedstawia całą idęę, która stoi za projektem. W tabeli opisane są konkretne stosunki częstotliwości, i którym interwałom to odpowiada.
import machine, time, math
from machine import Pin, PWM
interwaly = [
[16,15],[9,8],[6,5],[5,4],[4,3],[45,32],[3,2],[8,5],[5,3],[9,5],[15,8],[2,1]
]
adc_f = machine.ADC(machine.Pin(34)) #do zmiany czestotliwosci
adc_f.atten(machine.ADC.ATTN_11DB) #od 0 do 4095
adc_wybor = machine.ADC(machine.Pin(13)) #do wyboru interwalu
adc_wybor.atten(machine.ADC.ATTN_11DB)
on_limit = False
count_harmonic = 0
count_melodic = 0
pin1 = machine.Pin(5, machine.Pin.IN, machine.Pin.PULL_UP)
def linear_to_inverse_logarithmic(linear_value,f_limit): #powolne zmniejszanie czestotliwosci przy malych wartosciach
# Define your scaling parameters here
min_linear = 0
max_linear = 4095
min_inverse_log = 1
max_inverse_log = f_limit # Adjust this according to your requirements
# Scale the linear reading to an inverse logarithmic value
return min_inverse_log + (max_inverse_log - min_inverse_log) * (math.exp(linear_value / max_linear * 4) - 1) / (math.exp(4) - 1)
while True:
pot_value_wybor = int(adc_wybor.read()/372.27) # odczyt plus przeskalowanie od 0 do 11 (12 liczb)
print("Potentiometer choice:", pot_value_wybor)
ratio_lower = interwaly[pot_value_wybor][1]
ratio_upper = interwaly[pot_value_wybor][0]
f_limit = 700/ratio_upper
while pin1.value()==0: #wcisniety przycisk powoduje brak dzwieku
beeper1 = PWM(Pin(4), freq=ratio_lower*pot_value, duty=0)
beeper2 = PWM(Pin(2), freq = ratio_upper*pot_value , duty = 0)
time.sleep(0.1)
on_limit = False #f_limit
pot_value = int(linear_to_inverse_logarithmic(adc_f.read(),f_limit)) #odczyt z potencjometru
#print("Potentiometer Value freq:", pot_value)
if count_harmonic>2 and count_melodic>2: #reset po odegraniu melodycznie i harmonicznie
count_harmonic = 0
count_melodic = 0
if (pot_value > f_limit-10): #po paru cyklach ma zmienic na tryb melodyczny
on_limit = True
count_harmonic += 1
else:
count_harmonic = 0 #reset wartosci jesli zmieniono czestotliwosc w trakcie naliczania
count_melodic = 0
beeper1 = PWM(Pin(4), freq=ratio_lower*pot_value, duty=512)
if on_limit == True and count_harmonic >3 and count_melodic<3: #odegrano harmonicznie ale nie melodycznie
time.sleep(1)
beeper1.duty(0)
count_melodic += 1
beeper2 = PWM(Pin(2), freq = ratio_upper*pot_value , duty = 512)
time.sleep(1)
beeper1.deinit()
beeper2.deinit()
https://youtu.be/y-jcijBTARc