Skocz do zawartości

Kurs programowania w Processing - #3 - Tekst, pliki, dźwięk, przekształcenia


Pomocna odpowiedź

Kontynuując kurs, dzisiaj poznamy więcej rzeczy związanych z wyświetlaniem obrazu, spróbujemy odczytać i zapisać plik, oraz wytworzyć dźwięk. Postanowiłem specjalnie poświęcić chwilę czasu na przypomnienie tutaj, jak się obchodzić z klasą String. W processingu jest ona bezpośrednio zaimportowana z Javy, więc też można równie dobrze zajrzeć do jej dokumentacji.

Ten artykuł bierze udział w naszym konkursie! 🔥
Na zwycięzców czekają karty podarunkowe Allegro, m.in.: 2000 zł, 1000 zł i 500 zł.

konkurs_forbot_nagrody_1-350x147.png

Potrafisz napisać podobny poradnik? Opublikuj go na forum i zgłoś się do konkursu!
Czekamy na ciekawe teksty związane z elektroniką i programowaniem. Sprawdź szczegóły »

Oznacza to tyle, że wszystkie fajne funkcje z Javy są także w Processingu. Zmienną typu String możemy zatem modyfikować na tysiąc różnych sposobów – zacznijmy zatem od tych najprostszych. We wszystkich przykładach – zmienna robocza będzie nazwana „tekst”.

char c = tekst.charAt(x);

Zwróci znak (zmienną typu char) który jest w zmiennej tekst na pozycji x.

image.thumb.png.5616b135e0a8957154ae0cf1ef220ffa.png

String male = tekst.toLowerCase();
String wielkie = tekst.toUpperCase();

Te dwie funkcje zamienią wszystkie litery na małe lub wielkie.

image.thumb.png.632dc16661afccf0a917a43922f40654.png

Int dlugosc = tekst.length();

Zwróci liczbę znaków zawartą w zmiennej tekst.

image.thumb.png.05bf142a0cde43a9f9dfff1632c5d8d4.png

Jeżeli ktoś nie pracował nigdy ze stringami, to powinien zapamiętać jak się je porównuje:

String a = "korona";
Strinb b = "korona";
if(a.equals(b)) zrobCos(); //jezeli b jest rowne a

W teorii można by użyć „==”, lecz może to wprowadzić trudne potem do wykrycia błędy.

image.thumb.png.e7a98f14b6e0378359812a013e3d4f8a.png

Stringi można także łączyć operatorem „+”:

String hellothere = "hello" + "there";
String wiadomosc = "Liczba zakażonych: " + x + " tysięcy";

image.thumb.png.e80e93e5b528e8f86e7f8cda14ca0027.png

Do tworzenia filtrów może się także przydać replace:

a = a.replace("do zmienienia", "zmiana");

image.thumb.png.cdb22a1874e0b993049c0a682dedca22.png

Aby znaleźć w tekście dany ciąg znaków, posłuży do tego funkcja:

int x = tekst.indexOf("tekst do wyszukania");

image.thumb.png.b4810e78ef85c06cde086d550fe47441.png

Jedną z ciekawszych funkcji jest substring(). Pozwala ona wydzielić ze Stringa kawałek tekstu:

String tekscik = tekst.substring(6, 11);

image.thumb.png.207f216c3b8fd3f94558bf0e72f247ba.png

Aby podzielić tekst, np. długie zdanie na pojedyncze wyrazy (oddzielone spacjami), bardzo przydatna będzie tutaj funkcja split().

String[] wyrazy = split(tekst, " ");

image.thumb.png.2b1ecccc82af270793a575a9814dedb8.png

Przede wszystkim - funkcja ta zwraca tablicę Stringów, a nie jeden String. Do wypisania całej tablicy użyłem funkcji printArray, która wypisuje w konsoli wszystkie elementy tablicy. Do dyspozycji mamy jeszcze funkcję join(), która działa dokładnie na odwrót: skleja nam tablicę stringów w jeden, i oddziela je separatorem.

String zdanie = join(wyrazy, " ");

image.thumb.png.f1b485c5595bea35661acb227a851a42.png

Po przypomnieniu sobie obchodzenia się ze Stringami, dokończmy rozdział o kształtach. W poprzednich częściach, wszystko rysowaliśmy za pomocą najprostszych kształtów. Niedawno nauczyliśmy się rysować złożone krzywe – czas sobie tą wiedzę uporządkować. Także Processing daje nam możliwość tworzenia poligonów, wielokątów. Rysowanie ich jest trochę bardziej złożone:

beginShape();
vertex(x1, y1);
vertex(x2, y2);
//itd.
endShape();

Tak naprawdę wszystkie kształty, i nasze, i te najprostsze, to są obiekty PShape. Dzięki bezpośredniemu dostępowi do PShape, możemy rysować bardziej optymalnie niż kształt po kształcie. Nie przedłużając:

PShape prostokat = createShape(RECT, 100, 100, 100, 50);

No, udało nam się inaczej zrobić prostokąt. Aby teraz go narysować, w funkcji draw() wystarczy wpisać

shape(prostokat);

co da nam poniższy efekt:

image.thumb.png.0e0072c80d49522e85042e3a0499d2aa.png

Jedną z zalet takiego rysowania kształtów jest taka, że możemy tylko do danego kształtu przypisać właściwości rysowania:

image.thumb.png.375dcb3a7144059cfc7e9cdc521435c3.png

Jeżeli chcemy rysować własne kształty, także możemy to zrobić, i to wykorzystując stare funkcje – wystarczy się odwołać do danego PShape.

image.thumb.png.162727a54dd2b2a542d4281f8c860e98.png

Przydatna dla niektórych może być także funkcja wczytująca kształt z pliku .svg:

prostokat = loadShape(„plik.svg”);

Przechodząc teraz do plików: jeżeli chcemy odczytywać dane z różnych plików – czy to danych do wykresu, czy zmiennych do robota – najprościej byłoby je zapisywać w oddzielnym pliku, niż za każdym razem zmieniać program. Zaczniemy od najzwyczajniejszych plików .txt – przykładowy plik:

koronaferie
reaktywacja

marzec 2021

W Processingu plik tekstowy ładujemy podobnie jak obrazy, czyli Processing będzie szukał danego pliku w swoim katalogu szkicu – jeżeli nie wiesz jak się tam dostać, zostało to opisane w poprzedniej części. Plik tekstowy ładujemy do tablicy Stringów – każdy wiersz to jeden String.

String[] dokument = loadStrings("plik.txt");
println("Ten plik ma " + dokument.length + " wierszy.");

image.thumb.png.beedf6d4f13a04dfc73e6f18f1062a98.png

Wczytany tekst możemy dowolnie obrabiać. Możemy także zapisać plik, wystarczy wywołać saveStrings():

saveStrings("dane.txt", tablicaStringow);

image.thumb.png.cca095319a981b6692036db852136c70.png

Teraz na chwilę wrócimy do poprzedniej części, by wykorzystać tą wiedzę w praktyce. Do napisania był program liczący reaktancje w układzie RLC – mój wyglądał tak:

float pojemnosc = 0.00001; //w faradach
float indukcyjnosc = 0.01; //w henrach
float czestotliwosc = 2000; //w hercach
float rezystancja = 100; //w omach

int offset = 100;

void setup()
{
  size(800,400); //inicjalizacja
  background(255);
  stroke(0);
  fill(0);
  textSize(20);
  
  line(0, height/2, width, height/2); //uklad wspolrzednych
  line(offset, 0, offset, height);
  triangle(offset, 0, offset + 2, 15, offset - 2, 15);
  triangle(width, height/2, width - 15, height/2 - 2, width - 15, height/2 + 2);
  text("f [Hz]", width - textWidth("f [Hz]") - 4, height/2 + 25); 
  text("X, R [Ω]", 92 - textWidth("X, R [Ω]"), 25);
  fill(255,0,0);
  textSize(12);
  text("czerwony - reaktancja", width-200, height-100);
  fill(0, 255, 0);
  text("zielony - kapacytancja", width-200, height-80);
  fill(255, 0, 255);
  text("fioletowy - rezystancja", width-200, height-60);
  fill(0, 0, 255);
  text("niebieski - moduł impedancji", width-200, height-40);
  float fr = 1 / (TWO_PI * sqrt(pojemnosc * indukcyjnosc));
  fill(0);
  text("częstotliwość rezonansowa = " + fr + "Hz", width-300, 250);
  
  for(int i = 1; i <= czestotliwosc; i++)
  {
    float Xl = TWO_PI * i * indukcyjnosc; //liczymy
    float Xc = 1 / (TWO_PI * i * pojemnosc);
    float X = Xl - Xc;
    float Z = sqrt(pow(rezystancja, 2) + pow(X, 2));
    
    strokeWeight(2);
    
    stroke(255, 0, 0);
    point(map(i, 0, czestotliwosc, offset, width), map(Xl, height/2, -height/2, 0, height));
    
    stroke(0, 255, 0);
    point(map(i, 0, czestotliwosc, offset, width), map(Xc, height/2, -height/2, 0, height));
    
    stroke(255, 0, 255);
    point(map(i, 0, czestotliwosc, offset, width), map(rezystancja, height/2, -height/2, 0, height));
  
    stroke(0, 0, 255);
    point(map(i, 0, czestotliwosc, offset, width), map(Z, height/2, -height/2, 0, height));
  }
  
}

Na początku definiujemy stałe, dla jakich elementów liczymy wartości. W funkcji setup są standardowe funkcje, jak przygotowanie tła, kreski itd. - następnie jest rysowany układ współrzędnych i niektóre stałe.

Pętla for maluje kropka po kropce wykresy, zgodnie z ich wzorami. Mógłbym oczywiście zrobić tutaj kształt wykorzystując vertexy, lecz myślę że taki program wystarczy na potrzeby przykładu. Jak widać, jest tam dużo funkcji map() wykorzystujących width i height – pomagają one zachować responsywność aplikacji przy innych rozdzielczościach niż domyślna:

image.thumb.png.85f9fee2dde209f9ff5d9100f3155d34.pngimage.thumb.png.064a29485eeab89704b1f0d664e5738b.png

Użyłem jednak kilku stałych, takich jak offset, w teorii powinny być one wyliczane na podstawie szerokości/wysokości 

Teraz, jeżeli chcielibyśmy zmienić elementy na inne, musielibyśmy zmieniać za każdym razem kod programu. Spróbujmy zaimplementować zatem obsługę plików – przede wszystkim zmieniając domyślne wartości na 0

float pojemnosc = 0; //w faradach
float indukcyjnosc = 0; //w henrach
float czestotliwosc = 0; //w hercach
float rezystancja = 0; //w omach

a następnie stworzyć ową tablicę Stringów:

String[] plik = loadStrings("plik.txt");

i przypisać wartości z tablicy:

pojemnosc = parseFloat(plik[0]); //w faradach
indukcyjnosc = parseFloat(plik[1]); //w henrach
czestotliwosc = parseFloat(plik[2]); //w hercach
rezystancja = parseFloat(plik[3]); //w omach

Tak więc nagłówek kodu będzie u mnie wyglądał tak:

float pojemnosc = 0; //w faradach
float indukcyjnosc = 0; //w henrach
float czestotliwosc = 0; //w hercach
float rezystancja = 0; //w omach

int offset = 100;

void setup()
{
	size(800,400); //inicjalizacja
	background(255);
	stroke(0);
	fill(0);
	textSize(20);
  
	String[] plik = loadStrings("plik.txt");
	pojemnosc = parseFloat(plik[0]); //w faradach
	indukcyjnosc = parseFloat(plik[1]); //w henrach
	czestotliwosc = parseFloat(plik[2]); //w hercach
	rezystancja = parseFloat(plik[3]); //w omach
  
	// reszta kodu

taki kod wyeksportowałem (Plik > Wyeksportuj aplikację), po zmienieniu wartości w pliku – pozostaje cieszyć się efektem. Widać też że jednak powinno się rysować wykresy funkcji korzystając z kształtów, nie kropek...

image.thumb.png.7f705e8051efd9c1e9dd561bb6824264.png

Po udanej obsłudze plików tekstowych, przenieśmy się spowrotem do czcionek. Processing obsługuje czcionki wbudowane (domyślną), systemowe (zainstalowane na komputerze), oraz wczytane. Listę czcionek systemowych możemy elegancko odczytać tym kodem:

String[] fontList = PFont.list();
printArray(fontList);

Dość podobnie jak odczytując z pliku, każda linijka to każda nazwa czcionki. Odwołujemy się tutaj do PFont, obiektu czcionki. Znowu korzystamy z printArray aby wszystko wypisać

image.thumb.png.132ea6604608ada94940805c8160085d.png

Aby użyć czcionki systemowej, musimy ją najpierw „przekonwertować” w format zrozumiały dla processingu. Tworzymy zatem nową zmienną:

Pfont czcionka;

Następnie przypisujemy do niej wybraną czcionkę.

czcionka = createFont(„Txt”, 32);

Funkcja createFont może przyjąć trzy wartości: źródło czcionki, jej wielkość, i antyaliasing (true albo false). Po zapisaniu tej funkcji zostało teraz pisać tą czcionką:

textFont(czcionka);
text(„koronaferie: reaktywacja”, 0, 100);

Całość powinna wyglądać tak:

image.thumb.png.f5d38b53a9de8eccbd5f50df8c1611f4.png

Wczytywanie czcionki z pliku jest równie proste. Do folderu ze szkicem dołączamy czcionkę, i zamiast samej nazwy w createFont, dodajemy format.

image.thumb.png.9c31babbf591554b6109177db0b02e86.png

Idąc dalej, bardzo ciekawą funkcją processingu są przekształcenia samej siatki współrzędnych. Daje to ogrom możliwości, a przede wszystkim może ułatwić rysowanie wykresów, bo środek płótna możemy ustawić na środek okienka. Przede wszystkim trzeba zapamiętać, że każdy narysowany element po przekształceniu nie zmieni swojego położenia; tylko siatka współrzędnych się przestawi. Mogą się pojawić pytania „po co przestawiać siatkę, skoro możemy narysować element w innym miejscu?” - spróbuję pokazać, że przestawianie siatki może okazać się bardziej optymalne. Zacznijmy zatem od narysowania dwóch punktów, jeden o współrzędnych (10,10) a drugi na (30,30).

image.thumb.png.02a7a977d6dc64b8bf0cc6331b509901.png

Spróbujmy teraz narysować ten sam punkt, ale uzyskać przy okazji ten sam efekt – czyli przesunąć go o wektor. Punkt czerwony został przesunięty o wektor [20,20], więc znalazł się na koordynatach (30,30). Spróbujmy zatem nie „przesuwać” punktu, a całą siatkę.

image.thumb.png.f151eec9e96a4e51c2f0d5ca4e1b24a4.png

Jak widać, dzięki translate() udało nam się przesunąć całą siatkę o wektor [20,20]. Dzięki temu punkt (10,10) narysowaliśmy dwa razy, a jednak w różnych miejscach. Spróbujmy to samo, ale z drugim przykładem: krzywą Beziera.

image.thumb.png.79fbffce4536281375a9ec9b87581c60.png

Jak widać, każdy punkt tutaj musimy ręcznie „przesunąć”. Przy bardziej złożonych kształtach, po prostu bardzo się napracujemy. O wiele prościej można przesunąć siatkę:

image.thumb.png.f6352b47c88ee9c8b5151feae2002359.png

To teraz sprawdźmy co się stanie, jak przesuniemy siatkę trzeci raz.

image.thumb.png.174ee528f10b67b204f86cc71175f1d1.png

Jak widać, siatkę przesuwamy relatywnie, czyli dodajemy i odejmujemy od aktualnej wartości położenia siatki. Po wielu przesunięciach, możemy nawet nie wiedzieć jak wrócić do lewego górnego rogu! Dlatego bardzo często stosuje się komendy push i popMatrix.

image.thumb.png.b6bd4b3c8df071c203a1869ba6ed3fd8.png

Jak widać, ostatnia niebieska krzywa nałożyła nam się na pierwszą, czerwoną. PushMatrix zapisuje obecną pozycję siatki „do schowka” - u nas przed jakimkolwiek przekształceniem – a po przekształceniach wracamy do lewego górnego rogu, przez popMatrix. Oczywiście, moglibyśmy siatkę przesunąć o wektor [-40, -40], lecz w bardziej zaawansowanych programach, prawdopodobnie nie będziemy wiedzieli o jaki wektor przesunąć siatkę, by wrócić dokładnie w lewy górny róg.

Co więcej, siatkę poza przesuwaniem, możemy obracać! Jednak przypomnę jeszcze raz, że w Processingu kąty podajemy w radianach. Na szczęście Processing ma wbudowaną funkcję radians(x), która zwróci wartość radianów z podanego kąta w stopniach. 0 radianów (0 stopni) znajduje się maksymalnie po prawej stronie, czyli na wschód. Południe to będzie pi/2, zachód pi, a północ, czyli 270 stopni, to 3pi/2 radianów. Funkcją odpowiedzialną za obrót jest rotate() - spróbujmy krzywą obrócić o 45 stopni:

image.thumb.png.0e9951b40b92dcdf94bbae8694fc23eb.png

Aby efekt był bardziej widoczny, środek całości dałem na środek okienka. Czerwona krzywa jest zatem „oryginalną”, a zielona jest obrócona o 45 stopni. Jeszcze raz, popMatrix przywróci wszystko do lewego górnego rogu. Żeby pokazać dokładnie jak działa obrót, spróbujmy taki przykład:

image.thumb.png.3f3c53f7aaf699f87ce644130d3b27c7.png

Myślę, że teraz kwestia obrotu stała się klarowna. Pamiętaj, że jeżeli obrócimy siatkę zbyt bardzo, obraz może być poza obszarem okienka i się nie wyświetlić! Przede wszystkim – siatka obraca się względem swojego punktu (0, 0). Dlatego ustawiłem go na środek okienka, żeby zrobić takie ładne „koło”.

Ostatnią funkcją operacji na siatce jest scale(). Pozwala przeskalować obraz względem punktu (0, 0):

image.thumb.png.dfd7b675dbb18199cdbba3128d01e973.png

Wyraźnie widać, że praktycznie wszystkie wartości są narysowane tutaj dwa razy więcej. I kreska jest 2x grubsza, kwadrat dwukrotnie większy, i dwa razy dalej jest oddalony od lewego górnego rogu. A jednak jest to ten sam kwadrat, tylko powiększony dwa razy. Scale() przyjmuje liczbę typu float, więc możemy siatkę zmieniać o ułamki. Spróbuj samemu!

Warto jeszcze zaznaczyć, że bardziej uważni czytelnicy mogą natrafić na taki problem: Jeżeli funkcja draw() wykonuje się 60 razy na sekundę, i tyle samo razy przekształcam siatkę, to po chwili powinna być ona zupełnie inna, tutaj: kwadrat powinien się stawać coraz większy (siatkę powiększam dwukrotnie, potem znowu, i znowu). Na szczęście siatka samoczynnie wraca do lewego górnego rogu za każdym razem, kiedy funkcja draw() jest wywoływana – więc nie ma się czym martwić. 

Jak do tej pory, Processing umożliwiał nam zabawę tylko obrazem. Do Processingu, zarówno jak i do Arduino, istnieje mnóstwo przeróżnych bibliotek – dzięki czemu Processing może połączyć się z dowolnym API, odczytać temperaturę procesora komputera itd. Dzisiaj wgramy pierwszą bibliotekę, umożliwiającą nam pracę z dźwiękiem. Musimy zacząć od wgrania biblioteki – na szczęście Processing ma wbudowany menedżer bibliotek. Wchodzimy w Szkic > Importuj bibliotekę > Dodaj bibliotekę. Pokazuje nam się nowe okienko:

image.thumb.png.56855c13af1f57849fc65305d1d0c756.png

Jak widać, naprawdę jest wiele bibliotek do zainstalowania. I to tylko domyślnych! Wyszukujemy bibliotekę „sound”, konkretnie tą od Processing Foundation:

image.thumb.png.efb87512c3480071889d1b22983531b0.png

Instalujemy, i jak dostaniemy taką informację:

image.png.f8d36ac622376478f6f6e5ba51a60298.png

Możemy zamknąć okienko menedżera. Następnie importujemy bibliotekę:

image.thumb.png.a0599beeb51e7c0f9ee52810524e25a0.png

Jak widać, wpisała nam się następująca linijka:

import processing.sound.*;

zamiast #include, mamy import*, zapożyczone z javy: dołączamy do programu wszystko zawarte w paczce processing.sound. Spróbujemy teraz wygenerować dźwięk – proces ten jest zwany syntezą; pierwsze elektroniczne syntezatory powstały w latach 60. ubiegłego wieku, umożliwiając rozkwit całkowicie nowej muzyki – zaczynając od prawdopodobnie nieznanych już „popcorn”, czy „I feel love”.

Wszystkie dźwięki w tych utworach są wygenerowane przez elektronikę. Oczywiście, dźwięk w tych utworach to coś więcej niż zwykła sinusoida, czy kwadrat; można powiedzieć, że to różne dźwięki się nakładają. Spróbujmy zatem stworzyć syntezator składający się z 4 podstawowych dźwięków, a będą to sinusoidy o różnych częstotliwościach. Dlatego stwórzmy tablicę sinusów, ich liczbę, oraz tablicę ich częstotliwości:

SinOsc[] sinusy;
float[] czestotliwosc = {200, 440, 500, 1000};
int liczbaGeneratorow = 4;

Następnie, podobnie jak każdy inny element, definiujemy nasze tablice.

Sinusy = new SinOsc[liczbaGeneratorow];

Pojawiło się słówko kluczowe new; oznacza ono, że do danej zmiennej przypisujemy już dane miejsce w pamięci. W przeciwnym wypadku, może nas dopaść java:

image.thumb.png.087617d673ce987c6deb54617527f2c2.png

Tak więc stworzyliśmy generatory – teraz musimy je skonfigurować, najlepiej będzie to zrobić w pętli for():

for(int i = 0; i < liczbaGeneratorow; i++)
{

}

I teraz musimy przemyśleć, jak chcemy skonfigurować nasze oscylatory. Przede wszystkim każdy z nich musi mieć inną głośność (amplitudę), i inną częstotliwość. Zacznijmy od obliczenia amplitudy dla każdego z nich:

float amp = (1.0 / liczbaGeneratorow) / (i+1);

To jest przykładowy sposób, możemy także sztywno ustawić, że pierwszy ma mieć głośność 0.1, drugi 0.35, itd. - wartości powinny być od 0 do 1. Częstotliwości na razie zostawię z góry ustalone. Kontynuując, musimy już konkretnie stworzyć nasze oscylatory.

sinusy[i] = new SinOsc(this);

Tym razem jest słowo „this”. Wskazuje ono na obiekt, dla którego został on wywołany. Można powiedzieć, że po prostu tworzymy już fizycznie nasze syntezatory. Jak widać java nie jest łatwa. Bez zrobienia tego kroku dostaniemy 𝕟𝕦𝕝𝕝 𝕡𝕠𝕚𝕟𝕥𝕖𝕣 𝕖𝕩𝕔𝕖𝕡𝕥𝕚𝕠𝕟.

Cytat

image.png.4031266398c9bc54a6bd526e7c82e561.png

Teraz zostało już ustawić częstotliwość generatorów:

sinusy[i].freq(czestotliwosc[i]);

oraz głośność:

sinusy[i].amp(amp);

Na końcu zostało generatory włączyć.

sinusy[i].play();

Proponuję tylko przed uruchomieniem programu wyciszyć system, aby przez przypadek nie być niemile uderzonym falą czystej sinusoidy. Całość powinna wyglądać tak:

import processing.sound.*;

SinOsc[] sinusy;
float[] czestotliwosc = {200, 440, 500, 1000};
int liczbaGeneratorow = 4;

void setup()
{
	size(200, 200);
	background(255);

	sinusy = new SinOsc[liczbaGeneratorow];
	for (int i = 0; i < liczbaGeneratorow; i++)
	{
		float amp = (1.0 / liczbaGeneratorow) / (i+1);
		sinusy[i] = new SinOsc(this);
		sinusy[i].freq(czestotliwosc[i]);
		sinusy[i].amp(amp);
		sinusy[i].play();
	}
}

void draw()
{

}

Po odtworzeniu programu, powinniśmy zostać przywitani dźwiękiem. Jest on połączeniem wszystkich 4 oscylatorów. Aby zrobić program ciekawszym, możemy zmodyfikować program aby zmieniał częstotliwość względem położenia myszki:

void draw()
{
	float f = map(mouseY, 0, height, 20, 10000);
	for(int i = 0; i < liczbaGeneratorow; i++)
		sinusy[i].freq(f);
}

Po odtworzeniu programu, możemy teraz sterować częstotliwością. Problem w tym, że każdy oscylator teraz gra tak samo. Spróbujmy zatem wprowadzić jakąś "niedoskonałość":

void draw()
{
	float f = map(mouseY, 0, height, 20, 10000);
	float d = map(mouseX, 0, width, -1, 1);
	for(int i = 0; i < liczbaGeneratorow; i++)
		sinusy[i].freq(f * (i + 1 * d));
}

Teraz pozycja kursora w osi X spowoduje zmianę częstotliwości każdego oscylatora. Poza oscylatorem sinusoidy, mamy do wyboru

WhiteNoise(); //szumy, pewnie chodzi o barwę dźwięku
PinkNoise();
BrownNoise();

SinOsc(); //sinusoida
SawOsc(); //piła
SqrOsc(); //kwadrat
TriOsc(); //trójkąt
Pulse(); //pwm

Jeżeli jednak zależy nam nie na syntezie dźwięku, a na jego odtwarzaniu – wystarczy zamiast oscylatora zdefiniować plik dźwiękowy:

SoundFile[] plik;

Wczytać plik:

plik = new SoundFile(this, sciezkadopliku.mp3”);

I go włączyć.

plik.play();

Więc całość może wyglądać tak:

image.thumb.png.19b28c0c6cfec92b39808f9c15028e1a.png

Problem może być tylko taki, że dźwięk zostanie odtworzony dopiero po załadowaniu w całości. I jeżeli chodzi o dźwięk, to biblioteka ta daje o wiele więcej możliwości, więc jeżeli ktoś chce je zgłębić – zapraszam do dokumentacji

W następnej części w końcu połączymy Processing z Arduino, spróbujemy zrozumieć wyświetlanie przy użyciu OpenGL, i omówimy niektóre, przykładowe programy oraz algorytmy. Proponuję już nie wykonać żadnego programu specjalnie, a po prostu pobawić się możliwościami, przede wszystkim zrozumieć, jak to wszystko działa.

 

  • Lubię! 1
Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.