Politechnika Warszawska Wydział Elektroniki i Technik Informacyjnych Instytut Informatyki Rok akademicki 2011/2012 Praca dyplomowa inżynierska Bartosz Mateusz Kiełbaszewski Aplikacja ułatwiajaca naukę gry na elektronicznym instrumencie klawiszowym Opiekun pracy: Dr inż. Jacek Raczkowski Ocena...... Podpis Przewodniczacego Komisji Egzaminu Dyplomowego
Specjalność: Informatyka Inżynieria oprogramowania i systemy informacyjne Data urodzenia: 21 wrzesnia 1989 r. Data rozpoczęcia studiów: 1 października 2008 r. Życiorys Urodziłem się 21 września 1989 roku w Łodzi. Swoje dzieciństwo spędziłem w Hajnówce (woj. podlaskie). W 2005 roku rozpoczałem naukę w liceum ogólnokształcacym nr 2 w Hajnówce na profilu matematyczno-fizycznym. Po zdaniu egzaminów maturalnych dostałem się na studia dzienne I stopnia, na wydział Elektroniki i Technik Informacyjnych Politechniki Warszawskiej. Podczas studiów wybrana przeze mnie specjalnościa była Inżynieria oprogramowania i systemy informacyjne.... podpis studenta Egzamin dyplomowy Złożył egzamin dyplomowy w dn.... Z wynikiem... Ogólny wynik studiów... Dodatkowe wnioski i uwagi Komisji......
Streszczenie Praca ta prezentuje proces tworzenia oraz sposób działania aplikacji ułatwiaja- cej naukę gry na elektronicznym instrumencie klawiszowym. Program korzysta ze standardu MIDI w celu uzyskania komunikacji pomiędzy komputerem, a instrumentem muzycznym. Przedstawione zostana wiadomości systemu MIDI oraz ich rola w aplikacji. Omówione będzie wykorzystanie plików MIDI i zostanie wyjaśnione, dlaczego moga one stanowić dobre źródło utworów muzycznych do wykorzystania przez aplikację. Opisane zostana także problemy, jakie zostały napotkane podczas tworzenia programu oraz sposoby na ich rozwiazanie. W pracy znajduja się także informacje na temat javax.sound.midi i kilku wybranych pojęć muzycznych, których znajomość jest niezbędna do zrozumienia działania aplikacji. Omówione zostana także możliwości aplikacji oraz pomysły na jej dalszy rozwój. Słowa kluczowe: MIDI, SMF, pliki MIDI, pianino, muzyka, fortepian, keyboard, instrument klawiszowy, notacja muzyczna, pianino elektroniczne, klawiatura sterujaca, Java Abstract Title: A helper application for learning to play on an electronic keyboard instrument This B.Sc. Thesis comprises the project and implementation of an application which can help in learning to play on a keyboard instrument. The programme uses the MIDI standard to create a connection between the computer and electronic musical instrument. It includes an overview of MIDI messages, Standard MIDI Files and presents their role in the whole system s creation. The thesis also describes the javax.sound.midi package and several basic musical concepts. Moreover, the work was partly dedicated to the demonstrating application features and the ideas for its future development. Key words: MIDI, SMF, Standard Midi Files, music, keyboard, piano, notes, musical notation, Java
Spis treści 1. Wprowadzenie........................................ 1 1.1. Cel pracy........................................ 1 2. Teoria............................................. 2 2.1. Teoria muzyczna.................................... 2 2.1.1. Notacja muzyczna............................... 2 2.1.2. Klawiatura muzyczna i jej dźwięki..................... 5 2.2. Połaczenie instrumentu muzycznego z komputerem............... 5 2.2.1. Rozpoznawanie dźwięków granych przez użytkownika z użyciem mikrofonu................................... 5 2.2.2. Wysyłanie informacji o zagranych przez użytkownika dźwiękach... 6 2.3. MIDI........................................... 6 2.3.1. Wiadomości MIDI............................... 7 2.3.2. Pliki MIDI.................................... 7 2.4. Java........................................... 8 2.4.1. Java Sound API................................ 8 3. Implementacja....................................... 9 3.1. Budowa systemu................................... 9 3.1.1. Łaczenie się z urzadzeniem wejściowym.................. 9 3.1.2. Odbieranie wiadomości MIDI........................ 9 3.1.3. Klasa MidiMessage.............................. 10 3.1.4. Klasa ShortMessage.............................. 10 3.1.5. Wybrane System Common Messages.................... 11 3.1.6. Wybrane System Real-Time Messages (Wiadomości czasu rzeczywistego) 12 3.1.7. Klasa SysexMessage............................. 12 3.1.8. Klasa MetaMessage.............................. 12 3.1.9. Odbieranie wiadomości o wciśniętych klawiszach............ 14 3.1.10.Klasa opisujaca dźwięk............................ 14 3.1.11.Wysyłanie wiadomości MIDI......................... 15 3.1.12.Wczytywanie plików MIDI.......................... 15 3.1.13.Sprawdzanie poprawności gry........................ 16 3.2. Część wizualna.................................... 18 3.2.1. System współrzędnych używanych w programie............. 18 3.2.2. Zmiana wielkości grafik........................... 19 3.2.3. Rysowanie schematu fortepianu na ekranie................ 20 3.2.4. Dwa rodzaje widoków udostępniane przez program........... 22 3.2.5. Główny sposób wyświetlania nut...................... 22 3.3. Inne opcje....................................... 29 3.3.1. Wybór trybu gry................................ 29 3.3.2. Wybór ścieżek MIDI do grania........................ 29 3.3.3. Zmiana szybkości odtwarzania utworu................... 31 3.3.4. Zmiana wielkości pola wyświetlajacego nuty............... 31 3.3.5. Wybór syntezatora, cechy syntezatorów.................. 34 3.3.6. Zmiana wysokości dźwięków w utworze.................. 34 3.3.7. Przewijanie utworu.............................. 36 3.3.8. Wygładzanie krawędzi (antyaliasing).................... 36
Spis treści ii 3.3.9. Pliki konfiguracyjne.............................. 38 3.3.10.Pliki językowe................................. 39 3.3.11.Raportowanie błędów............................. 39 4. Podsumowanie....................................... 41 4.1. Możliwości rozwoju aplikacji............................. 41 4.2. Zakończenie...................................... 42 Bibliografia........................................... 44
1. Wprowadzenie Nauka gry na instrumentach muzycznych wymaga poświęcenia dużej ilości czasu. Wiele osób zaczyna uczyć się grać na instrumencie, ale szybko rezygnuje z tego zajęcia. Moim zdaniem powodem takiej sytuacji może być brak zadowalajacych efektów na wczesnych etapach ćwiczeń. Zanim poczatkuj acy muzyk będzie w stanie zagrać jeden ze swoich ulubionych utworów, musi on pokonać barierę jaka stanowi nauka zapisu nutowego (notacji muzycznej). Nowoczesna notacja muzyczna jest efektem wieloletniej ewolucji zapisu muzyki - zawiera elementy majace uzasadnienie historyczne oraz różnice regionalne. Szybkie czytanie zapisu nutowego może stanowić problem dla poczatkuj acych, znakiem tego moga być próby stworzenia standardów, których czytanie może być szybsze takich jak na przykład Simplified Music Notation 1. Popularyzacja komputerów pozwala nam na stworzenie własnych standardów, które nie maja tak silnych ograniczeń, jak te przeznaczone do zapisania na papierze. Możliwe staje się używanie animacji, dźwięków, a nawet analizowanie gry użytkownika. W swojej pracy przedstawię próby stworzenia aplikacji, której zadaniem będzie uproszczenie nauki gry na elektronicznym instrumencie klawiszowym. Ukazany zostanie przykład zapisu muzycznego, którego zrozumienie nie powinno zajać zbyt wiele czasu. 1.1. Cel pracy Celem pracy jest stworzenie aplikacji, będacej w stanie ułatwić poczatkuj acym muzykom naukę gry utworów muzycznych. Program powinien być w stanie wyświetlać łatwe do zrozumienia instrukcje zawierajace informacje, jaki dźwięk i kiedy powinien zostać zagrany; powinien także sprawdzać poprawność gry. Aplikacja powinna być możliwa do uruchomienia na przeciętnym komputerze z systemem Windows i współpracować z możliwe jak największa gama urzadzeń. Chciałbym, by stworzone narzędzie było dość atrakcyjne wizualnie. 1 http://www.simplifiedmusicnotation.org/
2. Teoria 2.1. Teoria muzyczna 2.1.1. Notacja muzyczna Historia współczesnej notacji muzycznej Najstarsze znalezione sposoby zapisu muzyki zostały stworzone około 2000 r.p.n.e. i przetrwały wyryte na tabliczkach w formie pisma klinowego. Historia współczesnej notacji muzycznej zaczyna się, jednak prawdopodobnie od powstania notacji cheironomicznej używanej do zapisu wczesnego chorału gregoriańskiego. Nie zapisywała ona dokładnej wysokości dźwięku, a jedynie przybliżony przebieg melodii. Nie zawsze była zapisywana także długość poszczególnych dźwięków, poprawny odczyt muzyki był niemożliwy dla osoby nie znajacej oryginalnego utworu. Najstarszy znany zapis pochodzi z Musica disciplina stworzonego przez Aurelianus Reomensis około 850 r.n.e. Około X wieku n.e. zaczęła rozwijać się notacja diastematyczna, w której możliwe było określenie wysokości dźwięku na podstawie wertykalnego ustawienia neum. Format ewoluował, z czasem do zapisu używany zaczał być system czterech równoległych, poziomych linii - rozwiazanie te jest przypisywane Guido z Arezzo, który opisał swoja wizję w traktacie Micrologus. Nowoczesny zapis z użyciem pięciolinii pojawił się po raz pierwszy we Francji i stał się powszechny do końca XVI wieku. Równolegle wraz z rozwojem sposobu zapisania wysokości dźwięku, rozwijał się zapis długości jego trwania. W De Mensurabili Musica pochodzacym z XIII wieku opisano sześć rodzajów rytmu stosowanego w tym czasie. Franco of Cologne opisał w traktacie Ars cantus mensurabilis pomysł określania rytmu z użyciem kształtu nuty. Użycie taktu stało się popularne do końca XII wieku. Jak widać, sposób zapisu muzyki rozwijał się przez wiele lat. Zawiera on cechy, które nabył w czasie swojej ewolucji, które moga stanowić problem dla poczatku- jacych muzyków starajacych się zagrać szybko swój ulubiony utwór. Przedstawię problemy zwiazane z tym układem w dalszej części pracy. Budowa współczesnej notacji muzycznej Współczesna notacja muzyczna (jej przykład widoczny jest na rysunku 2.1) używa pięciolinii do zapisu dźwięku (rys. 2.2). Pięciolinia pozwala na określanie jedynie względnej wysokości nut, by możliwe było wyznaczenie wysokości dźwięku przed pięciolinia ustawiany jest klucz, określajacy położenie na pięciolinii jednego z dźwięków. Na rysunku 2.3 przedstawiony jest przykład położenia popularnych kluczy na pięciolinii - klucza wiolinowego oraz klucza basowego. Klucz wiolinowy jest kluczem G (określa położenie dźwięku G, ukazane na obrazku 2.5), a klucz basowy jest kluczem F (określa położenie dźwięku F, rys. 2.4). Na pięciolinii oraz nad nia i pod nia zapisywane sa inne elementy notacji muzycznej takie, jak nuty, pauzy,
2.1. Teoria muzyczna 3 znaki chromatyczne (zmieniajace tonację utworu, wysokość dźwięków), jednostka metryczna (określajaca długość trwania nut), oznaczenia tempa (element oznaczajacy długość trwania utworu badź liczbę uderzeń metronomu na minutę), oznaczenia dynamiki (określajace siłę dźwięku, stosowane na instrumentach cechujacych się możliwościa wydawania dźwięków o zróżnicowanej głośności) czy oznaczenia artykulacji (określajace sposób wydobywania dźwięku, na przykład legato oznacza sugestię gry dźwięków w płynny sposób - tak by trudno było rozróżnić poszczególne nuty). Rysunek 2.1. Przykład notacji muzycznej (Chopin Preludium A-dur op. 28 nr 7) Rysunek 2.2. Pięciolinia
2.1. Teoria muzyczna 4 Rysunek 2.3. Pięciolinia z widocznym kluczem basowym i wiolinowym. Rysunek 2.4. Klucz basowy, zaznaczony dźwięk F. Rysunek 2.5. Klucz wiolinowy, zaznaczony dźwięk G.
2.2. Połaczenie instrumentu muzycznego z komputerem 5 2.1.2. Klawiatura muzyczna i jej dźwięki Rysunek 2.6. Klawiatura muzyczna (zawierajaca trzy oktawy) Standardowa fortepianowa klawiatura muzyczna zawiera zwykle 12 klawiszy, pozwalajacych na zagranie wszystkich dźwięków ze skali chromatycznej (dwunastostopniowej). Poszczególne stopnie tej skali oddalone sa od siebie o pół tonu. Dłuższe, białe klawisze reprezentuja dźwięki tworzace skalę muzyczna C-dur (C, D, E, F, G, A, B 1 ). Pozostałe dźwięki nie stanowiace części skali sa wydawane przez krótsze, czarne klawisze; nazwy dźwięków tworzone sa od nazwy sasiaduj acych dźwięków skali C-dur oraz znaku krzyżyka badź bemolu (C /D, D /E, F /G, G /A, A /B ). Dźwięk C czytany jest jako cis (dodajemy do nazwy dźwięku przyrostek -is), a dźwięk G jako ges (dodajemy sufiks -es). Fragment klawiatury muzycznej o szerokości trzech oktaw ukazany jest na rysunku 2.6. 2.2. Połaczenie instrumentu muzycznego z komputerem Aplikacja, by mogła spełniać swoja rolę i rozpoznawać nuty grane przez muzyka musi w jakiś sposób komunikować się z komputerem. Wpadłem na dwa sposoby na realizację tego zadania. Pierwszym z nich jest wykorzystanie mikrofonu, nagrywania dźwięków granych przez użytkownika i rozpoznawania ich. Drugim sposobem jest użycie urzadzenia, które poza generowaniem dźwięku byłoby w stanie wysłać nam informacje o zagranych przez użytkownika dźwiękach. Każde z rozwiazań ma swoje wady i zalety. 2.2.1. Rozpoznawanie dźwięków granych przez użytkownika z użyciem mikrofonu Możliwe jest rozpoznawanie wysokości granych dźwięków z użyciem mikrofonu poprzez wykorzystanie algorytmu Szybkiej transformacji Fouriera (FFT). Rozwia- zanie te ma oczywiste zalety - jesteśmy w stanie uniezależnić działanie aplikacji od używanego instrumentu muzycznego oraz nie jesteśmy zmuszeni do używania żadnych standardów przesyłania danych. Możliwość bezprzewodowej pracy z komputerem jest dodatkowa zaleta. Niestety, rozwiazanie te ma także istotne wady, które sprawiły, iż zrezygnowałem z jego wykorzystania w moim programie. Testujac w praktyce działanie programów jako H. 1 Dźwięk B w Niemczech, Europie Wschodniej i Centralnej oraz Skandynawii bywa oznaczany
2.3. MIDI 6 rozpoznajacych dźwięki grane na gitarze elektrycznej 2 odkryłem, że gdy współpracuja one z prostym mikrofonem maja dość spore opóźnienie i sa zbyt niedokładne. Dodatkowa wada może być podatność na zakłócenia otoczenia. Testowałem działanie aplikacji w dość głośnym otoczeniu, co sprawiało dodatkowe problemy. Próbowałem także podłaczać gitarę bezpośrednio do komputera (korzystajac z przejściówki kabla cinch używanego przez gitarę do mini jack), jednak jakość rozpoznawania ciagle nie spełniała moich oczekiwań. 2.2.2. Wysyłanie informacji o zagranych przez użytkownika dźwiękach Kolejnym rozwiazaniem jest skorzystanie ze standardu MIDI do przesyłania informacji o granych nutach. Standard MIDI jest popularnym rozwiazaniem głównie w elektronicznych instrumentach klawiszowych. Z oczywistych powodów nie jesteśmy w stanie użyć tej technologii w przypadku korzystania z instrumentów akustycznych. Istnieja także inne instrumenty z wyjściem MIDI, na przykład gitary elektroniczne produkowane przez firmę Casio (np. model Casio PG-380). Inne zalety zwiazane z użyciem MIDI do przesyłania informacji do komputera to dość niskie opóźnienie (w porównaniu do podejścia zwiazanego z analiza sygnału) oraz właściwie całkowita bezbłędność przesyłanych informacji (w przypadku zbyt małej ilości próbek sygnału konkurencyjny algorytm FFT może błędnie rozpoznawać składowe częstotliwości). Dzięki wysyłaniu informacji o nutach możliwe staje się generowanie dźwięku nie w samym instrumencie, a w komputerze. Jesteśmy w stanie grać z użyciem syntezatora symulujacego taki instrument, w jakim oryginalnie został zagrany utwór. W elektronicznych instrumentach klawiszowych takich, jak keyboard, czy klawiatura sterujaca, istnienie wyjścia MIDI jest bardzo prawdopodobne. Niestety, w komputerach oraz laptopach wejścia midi sa ostatnio dość rzadko spotykanie, przynajmniej w przypadku modeli nie posiadajacych niezintegrowanej karty muzycznej. Na szczęście nie powinno stanowić to poważnego problemu dla użytkowników, nowoczesne keyboardy zamiast wyjścia MIDI posiadaja często wyjście MIDI USB. Jeśli chcemy do pracy z aplikacja używać urzadzenia z wyjściem MIDI możemy zdecydować się na zakup przejściówki MIDI-USB 3. 2.3. MIDI MIDI (skrót od Musical Instrument Digital Interface) jest systemem służacym do przekazywania informacji pomiędzy elektronicznymi instrumentami muzycznymi. Został on stworzony w 1983 roku i pozwolił na ułatwienie komunikacji pomiędzy klawiaturami i syntezatorami różnych firm. Umożliwił on uniknięcie efektu ściany syntezatorów - otaczania się muzyków duża liczba syntezatorów wraz z klawiaturami podczas koncertów. Dzięki popularyzacji standardu możliwe stało się korzystanie z jednej badź kilku klawiatur komunikujacych się z kolekcja syntezatorów z użyciem kabla MIDI. Standard MIDI pozwolił na używanie komputerów do tworzenia muzyki, poczatkowo stosowanych głównie w roli sekwencerów (urza- dzeń sterujacych innym urzadzeniem MIDI, na przykład syntezatorem). Standard 2 http://www.aptuner.com 3 Na przykład konwerter CL036-USB to MIDI
2.3. MIDI 7 MIDI używa wiadomości (ang. messages) do przesyłania informacji. Możliwe jest zapisanie serii wiadomości w formie pliku MIDI. Więcej informacji o standardzie MIDI można znaleźć pod adresem 4. 2.3.1. Wiadomości MIDI MIDI 1.0 używa 16 kanałów do przesyłania wiadomości. Kanały używane sa do odróżnienia od siebie głosów badź instrumentów. Pozwala to na kontrolowanie jednocześnie wielu różnych instrumentów jednocześnie. Do każdego z kanałów możemy przypisać ustawienia takie jak rodzaj instrumentu, czy głośność. MIDI w wersji 1.0 definiuje kilka podstawowych rodzajów wiadomości jakie możemy przekazać na każdy z kanałów: Wiadomość zagrania dźwięku (ang. note on) / wiadomość przerwania gry (ang. note off). Wiadomość ta zawiera informację jaka nuta została zagrana (obsługiwane sa dźwięki o częstotliwości od 8,176 Hz do 12 544 Hz z dokładnościa do półtonu - jesteśmy w stanie przekazać informację o wciśnięciu każdego z 88 klawiszy urzadzenia o układzie klawiatury klasycznego fortepianu). Dodatkowo przesyłania jest informacja o szybkości, z jaka został wciśnięty klawisz (ang. velocity) w zakresie 0-127. Odtwarzanie dźwięku przerywane jest dopiero po otrzymaniu wiadomości przerwania gry. Jest to jedna z najważniejszych wiadomości wykorzystywanych przez program, dzięki niej wiemy, które klawisze wciska użytkownik. Wiadomość zmiany wysokości dźwięku (ang. pitch-bend). Pozwala ona na zmianę wysokości dźwięku w obrębie dwóch półtonów z dokładnościa do 1/8192 półtonu. Ucho ludzkie ma problemy z rozróżnieniem dźwięków różniacych się od siebie o mniej niż 1/20 półtonu. Dzięki tej wiadomości możliwe jest uzyskanie efektu portamento, czyli łagodnego przejścia między jednym, a drugim dźwiękiem. Wiadomość Control Change. Może ona nieść dość różnorodne informacje, na przykład o zmianie barwy dźwięku, panoramy stereo (ang. panning) badź głośności. Może być także używana do kontroli podświetlanych klawiszy (dostępnych w niektórych keyboardach), a nawet zarzadzania działaniem oświetlenia sceny. Wiadomość Program Change. Używana jest do przesyłania rozkazu zmiany programu (na przykład przydatna przy wyborze instrumentu z bazy syntezatora). Wiadomość Aftertouch. Z użyciem tej wiadomości przesyłane sa dane dotyczace zmiany siły nacisku na klawisz instrumentu - funkcja ta jest niedostępna w tańszych instrumentach muzycznych. 2.3.2. Pliki MIDI Seria wiadomości MIDI może zostać zapisana w formie pliku MIDI (nazywanego także SMF - Standard MIDI File). Pliki te używaja zwykle rozszerzenia.mid badź (nieco rzadziej).smf. Z powodu, że zawarta jest w nich instrukcja generacji dźwięku, a nie dość dokładny kształt fali dźwiękowej zajmuja one mniej miejsca od standardów zapisu muzyki takich jak, na przykład mp3, czy wave. Minuta muzyki może zostać zapisana w około 10kB danych (podczas, gdy utwór podobnej 4 http://www.midi.org/
2.4. Java 8 długości zapisany jako plik mp3 potrafi zajać około 1MB danych) - oczywiście sa to dane przykładowe mogace różnić się w zależności od ilości wykorzystanych ścieżek, czy stopnia kompresji pliku mp3. Używane do testów aplikacji pliki MIDI miały wielkość pliku mieszczac a się w zakresie 5-125kB. Mały rozmiar plików MIDI spowodował, iż były one używane jako format zapisu muzyki w czasach, gdy łacza szerokopasmowe nie były jeszcze popularne. Pliki MIDI przeżywały swoja druga młodość na poczatku lat dwutysięcznych, gdy zostały wykorzystane do przechowywania muzyki i dźwięków na telefonach komórkowych. Wykorzystujac wyszukiwarkę internetowa możemy dość łatwo znaleźć wersje popularnych utworów muzycznych zapisanych w tym standardzie - właśnie pliki MIDI sa wykorzystywane przez przedstawiana aplikację jako źródło informacji o nutach zagranych przez użytkownika. 2.4. Java Wybór środowiska oraz języka wpływa mocno na szybkość działania aplikacji, jej przenośność oraz możliwości rozwoju. Założyłem, że program tworzony w ramach tej pracy nie będzie zbyt wymagajacy dla nowoczesnych komputerów - uznałem, że odczyt i analiza niewielkich plików MIDI, komunikacja z elektronicznym instrumentem klawiszowym oraz generowanie prostego dwuwymiarowego obrazu nie powinno stanowić poważnego wyzwania obliczeniowego. Głównym kryterium wyboru stała się przenośność oraz możliwości rozwoju. Nie chciałem być skazany na konieczność kompilowania aplikacji pod różnymi systemami operacyjnymi, zdecydowałem się, więc na wybór jednego z języków kompilowanych do kodu wykonywanego przez maszynę wirtualna (kodu bajtowego). Zastanawiałem się nad wyborem pomiędzy językiem C#, a Java SE. Po dokładniejszym zbadaniu obu opcji odkryłem, że decydujac się na wybór języka programowania Java uzyskam dostęp do paczki javax.sound.midi stanowiacej część Java Sound API bardzo dobrze pasujacej do moich wymagań. Zdecydowałem się na wybór właśnie tej opcji, dodatkowa zaleta jest ewentualna możliwość stworzenia w przyszłości wersji aplikacji na tablety z systemem Android (chociaż w czasie, gdy to piszę brak dobrego wsparcia dla MIDI w tym systemie może stanowić utrudnienie). 2.4.1. Java Sound API Java Sound API jest niskopoziomowym interfejsem ułatwiajacym kontrolowanie urzadzeń dostępnych w systemie odpowiedzialnych za odbieranie i generowanie dźwięku. Pozwala on także na komunikację z urzadzeniami korzystajacymi ze standardu MIDI, co jest istotna cecha wykorzystywana przez moja aplikację. Java Sound API zawiera wsparcie dla dźwięku cyfrowego oraz danych MIDI, jest ono zawarte w dwóch paczkach: javax.sound.sampled - Dostarczajacej interfejsy przydatne przy nagrywaniu, miksowaniu oraz odtwarzaniu cyfrowego dźwięku javax.sound.midi - Udostępniajacej interfejsy przydatne przy syntezie MIDI, sekwencjonowaniu oraz przekazywaniu wiadomości oraz zdarzeń MIDI
3. Implementacja 3.1. Budowa systemu 3.1.1. Łaczenie się z urzadzeniem wejściowym Połaczenie z urzadzeniem wysyłajacym wiadomości MIDI (na przykład elektronicznym keyboardem) uzyskiwane jest z wykorzystaniem paczki javax.sound.midi. Połaczenie jest nawiazywane automatycznie po uruchomieniu programu, bez ingerencji użytkownika. 1 MidiDevice. Info [ ] midideviceinfoarray = MidiSystem. getmidideviceinfo ( ) ; for ( int i = 0; i < midideviceinfoarray. length ; i ++) { try { MidiDevice device = MidiSystem. getmididevice ( midideviceinfoarray [ i ] ) ; 5 Transmitter transmitter = device. gettransmitter ( ) ; transmitters. add ( transmitter ) ; transmitter. setreceiver (new MidiInputReceiver ( ) ) ; 10 device. open ( ) ; (... ) } catch ( MidiUnavailableException e ) { (... ) 15 } } Wydruk 3.1. Łaczenie się z urzadzeniem wejściowym Lista obiektów informacyjnych (MidiDevice.Info) reprezentujacych dostępne urzadzenia w systemie pobierana jest z użyciem funkcji MidiSystem.getMidiDeviceInfo(). Następnie aplikacja próbuje stworzyć Transmitter, poprzez który wiadomości Midi odbierane z urzadzenia wejściowego będa przesyłane dalej, do klasy MidiInputReceiver, która zajmie się ich dokładniejsza analiza. Wszystkie utworzone połaczenia sa zamykane po zakończeniu działania programu. 3.1.2. Odbieranie wiadomości MIDI Za odbieranie wiadomości otrzymywanych od urzadzenia wejściowego MIDI odpowiada klasa dziedziczaca po interfejsie javax.sound.midi.receiver i implementujaca z tego powodu metodę: 1 void send ( MidiMessage message, long timestamp ) Poprzez t a funkcję klasa jest w stanie odebrać wiadomość MIDI (MidiMessage message) wraz z czasem jej wysłania (long timestamp).
3.1. Budowa systemu 10 3.1.3. Klasa MidiMessage Klasa javax.sound.midi.midimessage jest klasa bazowa dla wiadomości MIDI. Korzystajac z niej możemy odebrać treść wiadomości, jej długość oraz status wiadomości określany przez bajt informacji. Dziedzicza po niej klasy ShortMessage, SysexMessage, MetaMessage. 3.1.4. Klasa ShortMessage Klasa javax.sound.midi.shortmessage reprezentuje wiadomości MIDI, które maja najwyżej dwa bajty. Korzystajac z funkcji getcommand() oraz porównujac ja z wartościami odpowiadajacych pól klasy (takich jak, na przykład ShortMessage.NOTE_OFF = 0x80) jesteśmy w stanie rozpoznać typ odebranej informacji. Używajac funkcji getdata1() oraz getdata2() jesteśmy w stanie łatwo pobrać pierwszy i drugi bajt wiadomości, natomiast funkcja getchannel() zwróci nam informację o kanale, którego informacje dotycza. Poniżej przedstawiam listę przykładowych wiadomości wraz z opisami: NOTE ON (0x90) Wiadomość informujaca o rozpoczęciu grania nuty. Wraz z nia przesyłana jest informacja o kanale, którego ona dotyczy (0-15), zagranej nucie (0-127) oraz velocity, sile uderzenia w klawisz instrumentu (0-127). NOTE OFF (0x80) Wiadomość oznaczajaca zaprzestanie grania nuty. Przesyłana jest informacja o użytym kanale (0-15), numerze oznaczajacym zagrana nutę (0-127) oraz velocity (0-127). Wartość velocity w zależności od rodzaju urzadzenia może zawsze przyjmować stała wartość (na przykład zero), ale czasem może też reprezentować prędkość puszczania klawisza instrumentu (może to pozwolić, na przykład na kontrolę czasu zanikania dźwięku). POLY PRESSURE (0xA0) Wiadomość określajaca zmianę siły nacisku na klawisz instrumentu już po zagraniu samego dźwięku, a przed jego zupełnym puszczeniem, określana czasami jako polyphonic aftertouch. Może być wykorzystywana na przykład do uzyskania dodatkowych możliwości ekspresji dźwięku takich jak Vibrato ( drżenie głośności instrumentu). Funkcja ta pojawia się w bardziej profesjonalnych instrumentach muzycznych, przez wiele lat jej wersja polifoniczna (wysyłajaca informacje dotyczace każdego z klawiszy) była trudna do stworzenia z powodu konieczności wysyłania dużych ilości danych. Wraz z wiadomościa przesyłana jest informacja na temat użytego kanału (0-15), zagranej nuty (0-127) oraz siły nacisku na klawisz instrumentu (0-127). CHANNEL PRESSURE (0xD0) Monofoniczna wersja powyższej wiadomości (POLY_PRESSURE). Przez producentów sprzętu muzycznego nazywana jako monophonic aftertouch badź po prostu aftertouch. W wiadomości zawarta jest informacja dotyczaca użytego kanału (0-15), zagranej nuty (0-127) oraz siły nacisku na klawisz instrumentu (0-127). W zależności od implementacji siła nacisku może dotyczyć średniej siły z jaka wciśnięte
3.1. Budowa systemu 11 sa klawisze lub przesyłana jest największa wartość wśród wciśniętych klawiszy instrumentu. CONTROL CHANGE (0xB0) Wiadomość CONTROL_CHANGE pozwala na zmianę ustawień kanału (takich, jak na przykład głośność, czy ustawienia panoramy stereo). Przesyłane sa informacje opisujace, którego kanału ta zmiana dotyczy (0-15), identyfikator ustawienia do zmiany (0-119) oraz ustawiana wartość (0-127). Identyfikatory 120-127 nie moga zostać wykorzystane, gdyż sa zarezerwowane dla Channel Mode Messages (opisane później). PROGRAM CHANGE (0xC0) Wiadomość używana do zmiany programu, określajacego np. rodzaj emulowanego instrumentu. Przesyłana jest informacja o kanale, którego ta zmiana dotyczy (0-15) oraz o numerze programu (0-127). W standardach będacych rozwinięciem standardu MIDI możliwe stało się wybieranie z większej ilości programów (na przykład w General MIDI Level 2 (GM2) możliwe jest wybieranie z 16384 róznych programów, dzięki wysłaniu innej wiadomości przed PROGRAM_CHANGE odpowiadajacej za wybór jednego z 128 banków programów). PITCH BEND (0xE0) Wiadomość pozwalajaca na zmianę wysokości granych tonów, pozwalajaca na dostrojenie instrumentu do własnych oczekiwań. Wraz z nia wysyłana jest informacja o kanale MIDI (0-15) oraz 2 bajty informacji, z których 14 znaczacych bitów określa zmianę tonu. Domyślnym ustawieniem tej wartości jest wartość heksadecymalna 0x2000. Zmiana jest dokonywana w zakresie +/-2 półtonów, a więc możemy zmienić wysokość dźwięku z dokładnościa do około 4096 stopni na jeden półton. Wysokość pomiędzy dwoma sasiednimi stopniami jest raczej nierozróżnialna dla przeciętnego słuchacza. Część syntezatorów wykorzystuje jedynie 7 bitów znacza- cych do określania zmiany wysokości dźwięku, w takim wypadku na jeden półton przypadaja zaledwie 32 zmiany wysokości dźwięku. 3.1.5. Wybrane System Common Messages TIME CODE QUARTER FRAME (0xF1) Wiadomość jest przesyłana podczas aktywnej pracy urzadzenia. Używana jest do synchronizacji czasu poprzez MIDI. 32-bitowy kod zapisujacy aktualny czas (godziny, minuty, sekundy) oraz wysłana ramkę (frame) jest dzielony na osiem 4-bitowych fragmentów, sa one wysyłane cztery razy w ciagu czasu jednej ramki (a więc pełna informacja jest wysyłana co dwie ramki). Wraz z każda wiadomościa przesyłany jest 7-bitowa informacja, 3 bity sa używane do określenia typu informacji (numeru fragmentu), pozostałe 4 bity - do określenia jej wartości. Czas jest kodowany podczas tworzenia fragmentu oznaczanego liczba 0.
3.1. Budowa systemu 12 3.1.6. Wybrane System Real-Time Messages (Wiadomości czasu rzeczywistego) TIMING CLOCK (0xF8) Wiadomość wysyłana w celu zsynchronizowania czasu, przesyła informacje o godzinie, minucie, sekundzie oraz ilości ramek (frame) na sekundę (24, 25, 25.97 badź 30). ACTIVE SENSING (0xFE) Wiadomość przesyłana w celu podtrzymania aktywnego połaczenia. Używanie tej wiadomości jest opcjonalne, jednak jeśli już zostanie użyta oczekiwane jest, że kolejna wiadomość tego typu zostanie przesłana w ciagu 300ms. Jeśli wiadomość taka się nie pojawi, odbiorca może założyć, że połaczenie zostało przerwane. 3.1.7. Klasa SysexMessage Klasa javax.sound.midi.sysexmessage pozwala na odbieranie wiadomości System Exclusive (0xF0) oraz Special System Exclusive (0xF7). Sa to wiadomości tworzone przez producentów sprzętu, moga dzięki temu przesyłać informacje dotyczace specyficznej funkcjonalności wbudowanej w urzadzenia. Koniec wiadomości powinien zostać oznaczony za pomoca End Of Exclusive (0xF7). 3.1.8. Klasa MetaMessage Javax.sound.midi.MetaMessage reprezentuje wiadomość, która nie ma znaczenia dla syntezatorów, a może jednak przechowywać wartościowe informacje dotyczace, na przykład zawartości pliku MIDI. Znajdowana ona zwykle jest w plikach MIDI. Rozpoznawana jest na podstawie wartości pierwszego bajtu równej 0xFF. Kolejny bajt opisuje typ wiadomości, a następne oznaczaja długość wiadomości oraz same przenoszone przez nia dane. Przykładowe typy meta zdarzeń (Meta Event) opisane sa poniżej. W nawiasach podana jest wartość typu zapisana w formie liczby heksadecymalnej. SEQUENCE NUMBER Zdarzenie te definiuje numer wzorca w przypadku pliku MIDI typu 2 badź numer sekwencji, jeśli mamy do czynienia z plikiem MIDI typu 0 lub 1. Jest to zdarzenie występujace opcjonalnie, jeśli go nie ma zakładany jest domyślny sposób numerowania sekwencji. Sequence number, jeśli jednak już wystapi, powinien znajdować się na samym poczatku sekwencji i mieć czas delta równy 0 (sam poczatek odtwarzania). TEXT EVENT Zdarzenie używane do opisywania pliku MIDI. Często jest umieszczany na poczatku pliku, może zawierać informacje na temat nazwy ścieżki dźwiękowej badź autora; bywa używany do wstawienia tekstu utworu muzycznego do pliku MIDI. Typy wiadomości o wartościach od 0x02 do 0x0F sa używane jako oznaczenia bardziej wyspecjalizowanych opisów tekstowych utworu.
3.1. Budowa systemu 13 COPYRIGHT NOTICE Zawiera informację na temat praw autorskich utworu (zalecane jest, by zawierało znak prawa autorskiego c, rok produkcji oraz dane właściciela praw). Zdarzenie powinno się pojawić jako jedno z pierwszych w ścieżce, z czasem delta równym 0. SEQUENCE/TRACK NAME Definiuje nazwę ścieżki/utworu. Zdarzenie to powinno pojawić się jako jedno z pierwszych w ścieżce i mieć czas delta równy 0. INSTRUMENT NAME Określa nazwę instrumentu używanego w danym fragmencie ścieżki. Zdarzenie może zostać połaczone z MIDI Channel Prefix, by nieść także informację, którego kanału dotyczy. LYRIC Pozwala na przekazanie informacji o tekście utworu. Jeśli jako tekst zapisywane sa sylaby, a zdarzenia sa wywoływane w odpowiednim czasie, może to pozwolić na implementację systemu karaoke. MARKER Oznacza ważny punkt utworu, na przykład pierwszy wers zwrotki badź poczatek refrenu. CUE POINT Wiadomość używana do synchronizacji z odtwarzaniem wideo badź akcja na scenie teatralnej, czy uruchomieniem efektów świetlnych sceny. MIDI CHANNEL PREFIX Używane do załaczenia dodatkowej informacji dotyczacej numeru kanału dla meta zdarzeń (Meta Events), które takiej opcji domyślnie nie obsługuja. Działanie jest anulowane przez inne zdarzenie MIDI Channel Prefix badź przez dowolne inne zdarzenie nie należace do grupy Meta Events. END OF TRACK Zdarzenie opcjonalne określajace koniec ścieżki, które może się jednak okazać niezbędne, jeśli będziemy chcieli ja zapętlić badź połaczyć z inna ścieżka. SET TEMPO Zdarzenie te informuje o zmianie tempa, wartość tempa podawana jest w mikrosekundach na ćwierćnutę. Często występuje ono na samym poczatku ścieżki, obok wiadomości Timing Clock, co pozwala na dokładniejsza synchronizację. Jeśli tempo nie jest ustawione, zakładane jest domyślne równoznaczne z 120bpm (beats per minute, uderzeniami na minutę). Aby przeliczać tempo MIDI na ilość uderzeń na minutę i z powrotem możemy skorzystać z wzorów: 1 MikrosekundyNaMinute = 60 000 000 UderzenNaMinute = MikrosekundyNaMinute / MikrosekundyNaCwiercnute
3.1. Budowa systemu 14 MikrosekundyNaCwiercnute = MikrosekundyNaMinute / UderzenNaMinute SMPTE OFFSET Zdarzenie pozwalajace ustawić opóźnienie odtwarzania ścieżki z użyciem formatu SMPTE (opisujacego godzinę, minutę, sekundę oraz ramkę; dodatkowa możliwościa jest ustawienie fragmentu ramki (0-99), którego przesunięcie dotyczy). TIME SIGNATURE Zdarzenie te określa metrum utworu (rysunek 3.20). Zapisywane ono jest z użyciem 4 liczb. Pierwsze dwie liczby oznaczaja odpowiednio liczbę jednostek metrycznych (górna cyfra na obrazku) oraz podstawowa jednostkę metryczna danego utworu (dolna cyfra na obrazku). Podstawowa jednostka metryczna jest wyliczana poprzez potraktowanie drugiej liczby jako ujemnego wykładnika liczby dwa, czyli na przykład wartość 0 odpowiada pełnej nucie, wartość 1 odpowiada półnucie, a wartość 2 będzie oznaczała ćwierćnutę. Trzecia liczba oznacza ilość MIDI clocks (sygnałów zegara MIDI), które powinny upłynać pomiędzy kolejnymi uderzeniami metronomu. Czwarta liczba określa, ile trzydziestodwójek (nut stanowiacych 1/32 pełnej nuty) mieści się w jednej ćwierćnucie MIDI (zostało to wprowadzone, gdyż wartość ćwierćnuty MIDI była interpretowana na różne sposoby, zwykle w tym miejscu będzie się znajdować liczba 8 (tyle trzydziestodwójek mieści się w ćwierćnucie)). KEY SIGNATURE Zdarzenie pozwalajace określić tonację utworu. Zawiera informację o typie skali (durowa (major) badź molowa (minor)) oraz tonacji (jeden z 12 dźwięków skali chromatycznej). SEQUENCER SPECIFIC META EVENT Specyficzne zdarzenie stworzone na potrzeby konkretnego sekwencera sprzętowego badź programu. Pierwszy bajt (badź 3 bajty, jeśli pierwszy bajt jest równy 0) danych powinien zawierać identyfikator producenta. 3.1.9. Odbieranie wiadomości o wciśniętych klawiszach Najważniejsza informacja dla działania programu odbierana przez klasę MidiInputReceiver jest ta dotyczaca wciskanych i puszczanych klawiszy na instrumencie muzycznym (Note On i Note Off). Przekazywana jest ona dalej do innej klasy PressedKeyManager, która zajmuje się przekazywaniem informacji dalej (między innymi do PianoKeyManager, która zajmuje się przechowywaniem stanu klawiszy instrumentu muzycznego). 3.1.10. Klasa opisujaca dźwięk Klasa Note reprezentuje obiekt opisujacy pojedynczy dźwięk, jest wykorzystywana wielokrotnie w programie. Zawiera ona dane takie jak wysokość dźwięku (zapisana jako liczba, zgodna ze standardem MIDI), jej głośność/siłę uderzenia w klawisz instrumentu ( velocity ) czy kanał midi którego dotyczy. Udostępnia także wiele funkcji używanych w innych częściach aplikacji takich jak:
3.1. Budowa systemu 15 3.1.11. Wysyłanie wiadomości MIDI Aby wciskane przez użytkownika klawisze były słyszalne oraz by aplikacja mogła odtwarzać wczytywane pliki MIDI konieczne było stworzenie klasy odpowiedzialnej za generowanie dźwięku. Klasa MidiPlayer korzysta z javax.sound.midi.midisystem. Z jego użyciem stara się znaleźć dostępne w systemie syntezatory oraz nawiazać połaczenie z jednym z nich. MidiPlayer udostępnia reszcie programu użyteczne funkcje: 1 public void playnote ( Note note ) { ShortMessage message = new ShortMessage ( ) ; try { message. setmessage ( ShortMessage.NOTE\_ON, channelnumber, 5 note. getvalue ( ), note. getvelocity ( ) ) ; selecteddevice. getreceiver ( ). send ( message, 0 ) ; } catch ( InvalidMidiDataException e ) { Log. getlogger ( ). warning ( 10 " Invalid Midi Data Exception. Unable to play midi note. " ) ; } catch ( MidiUnavailableException e ) { Log. getlogger ( ). warning ( " Midi Unavailable Exception. Unable to play midi note. " ) ; 15 } } Wydruk 3.2. Funkcja odtwarzajaca dźwięk Funkcja wysyła wiadomość do syntezatora, zlecajac a zagranie dźwięku, którego cechy zdefiniowane sa w zmiennej (Note) note. 1 public void sendchannelmessage ( ChannelMessage message ) { try { selecteddevice. getreceiver ( ). send ( message. getmessage ( ), 0 ) ; } catch ( MidiUnavailableException e ) { 5 Log. getlogger ( ). warning ( " Unable to send midi channel message. " ) ; } } Wydruk 3.3. Funkcja wysyłajaca wiadomość do syntezatora Powyższa funkcja odpowiada za wysłanie dowolnej wiadomości do syntezatora, pozwalajac na uzyskanie takich efektów jak zmiana przypisanych dźwięków do różnych kanałów. 3.1.12. Wczytywanie plików MIDI Za otwieranie plików midi odpowiada klasa MidiFileOpener. Korzysta ona z funkcji MidiSystem.getSequence(InputStream), dzięki której jest w stanie odczytać zdarzenia występujace w pliku MIDI i przekierować je do innej części programu zajmujacej się ich analiza. Zadaniem tym zajmuje się klasa MidiFileReceiver, implementujaca interfejs Receiver (javax.sound.midi.receiver). Odczytuje ona zdarzenia midi z pliku.midi i w
3.1. Budowa systemu 16 zależności od typu odebranych danych przekazuje informacje w inne części programu. Najczęściej występujace i najbardziej istotne dla działania programu sa wiadomości zagrania dźwięku oraz przerwania grania dźwięku, które przekazuja informacje jakie dźwięki powinny zostać zagrane i kiedy. 3.1.13. Sprawdzanie poprawności gry Podczas odtwarzania utworu w trybie manualnym (w którym użytkownik musi zagrać przedstawione na ekranie dźwięki) przesuwajace się nuty zatrzymuja się w oczekiwaniu na wciśnięcie odpowiednich klawiszy. Na obrazkach 3.1, 3.2 widoczny jest utwór przedstawiony na dwóch różnych widokach, w stanie oczekiwania na reakcję użytkownika. Za sprawdzanie czy wciśnięte zostały odpowiednie klawisze odpowiada klasa NotesComparator. Dzieli ona wszystkie klawisze występujace na klawiaturze na trzy kategorie: MUST_BE_PLAYED (musi zostać zagrany) MAY_BE_PLAYED (może zostać zagrany) MUST_NOT_BE_PLAYED (nie może zostać zagrany) Dźwięki, których poczatek brzmienia zaczyna się w danym momencie musza zostać zagrane. Nuty, które już sa grane, ale jeszcze się nie zakończyły (wartość siły uderzenia velocity jest większa od zera) moga, ale nie musza zostać zagrane. Wszystkie pozostałe nuty nie moga być wciśnięte. Gdy te warunki zostana spełnione, utwór odtwarzany jest dalej. Warunki te nie wymuszaja idealnego czasu puszczenia klawisza, który już gramy (uznałem, że idealna synchronizacja czasu puszczenia klawisza nie jest aż tak bardzo istotna, a zwiększała bardzo poziom trudności gry). Dodatkowym ułatwieniem jest pewna niedokładność czasu wciśnięcia klawisza na jaka pozwala aplikacja, akceptuje ona wciśnięcie go odrobinę wcześniej niż jest to wymagane (pozwala to na uniknięcie niepotrzebnych pauz podczas gry). W tym fragmencie aplikacji wykorzystałem HashMultiMap dostarczana przez bibliotekę guava 1. 1 http://code.google.com/p/guava-libraries/
3.1. Budowa systemu 17 Rysunek 3.1. Odtwarzanie utworu jest zatrzymane, oczekiwanie na zagranie odpowiednich dźwięków przez użytkownika (widok główny). Rysunek 3.2. Odtwarzanie utworu jest zatrzymane, oczekiwanie na zagranie odpowiednich dźwięków przez użytkownika (widok przypominajacy notację muzyczna).
3.2. Część wizualna 18 3.2. Część wizualna 3.2.1. System współrzędnych używanych w programie Rysunek 3.3. Układ współrzędnych używany w programie Za cel postawiłem sobie stworzenie programu o możliwie dużej przenośności pomiędzy różnorodnymi systemami. Z powodu wykorzystywania własnoręcznie rysowanych ekranów, spadła na mnie odpowiedzialność za konfigurację ich zachowania w różnorodnych warunkach. Chciałem uniknać tworzenia aplikacji, która z powodu używania stałych wymiarów ekranu działałaby poprawnie jedynie na jednym ustawieniu rozdzielczości monitora, a na innych okienko programu byłoby zbyt małe badź za duże. Postanowiłem używać w aplikacji do określania położenia obiektów liczb zmiennoprzecinkowych. Lewy górny róg ekranu badź panelu jest oznaczany współrzędna ( 0.0, 0.0 ), wartości współrzędnych rosna aż do prawego dolnego rogu ekranu, gdzie przyjmuja maksymalna wielkość ( 1.0, 1.0 ). Sposób zapisu może zostać dokładniej wyjaśniony za pomoca rysunku 3.3. Współrzędne ekranu przed użyciem ich do określenia lokalizacji na ekranie sa konwertowane na odpowiadajace im wartości w pikselach. Używana do tego jest prosta funkcja: 1 long doublecoordinatetopixelcoordinate ( double coordinate, int maxpixels ) { return Math. round ( coordinate maxpixels ) ; } Wydruk 3.4. Funkcja konwertujaca Do funkcji przekazywane sa dwa argumenty, pierwszy typu double reprezentujacy współrzędna typu zmiennoprzecinkowego, drugi określa ilość pikseli w danym
3.2. Część wizualna 19 wymiarze (szerokości badź wysokości). Zwracana jest wartość określajaca współrzędna w pikselach. Aby uniknać niepotrzebnych obliczeń część wartości dotyczacych położenia stałych fragmentów ekranu jest zapisywana, jako już obliczone koordynaty w formie liczb całkowitych. Sa one ponownie przeliczane dopiero w przypadku zmiany wielkości okna aplikacji. 3.2.2. Zmiana wielkości grafik W aplikacji używane jest kilkanaście różnych obrazków, zapisanych głównie w formacie.png. Chciałem zagwarantować wysoka jakość wygladu aplikacji, dlatego użyte przeze mnie grafiki sa bardzo dobrej jakości, wyeksportowane z programów graficznych używanych do edycji grafiki wektorowej. Ich wielkość jest wystarczajaca do wyświetlenia obrazu na ekranach o rozdzielczności Full HD (1920x1080). By móc zapewnić wysoka wydajność aplikacji oraz dobry wyglad na mniejszych ekranach musiałem zadbać o zmianę wielkości obrazków, tak by dostosowywały się do innych wymiarów okna. Gdy zajmowałem się problemem zmiany wielkości obrazków próbowałem skorzystać z przeznaczonych do tego funkcji Java, jednak efekt końcowy był niezadowalajacy (nawet, gdy stosowałem ustawienia RenderingHint, nastawione na uzyskanie jak najlepszej jakości obrazu). Trafiłem na artykuł opisujacy iteracyjny sposób zmiany wielkości obrazów 2, który pozwalał na uzyskanie wysokiej jakości grafik. By nie ódkrywać ponownie koła"i pracować nad problemami, które zostały już rozwiazane zdecydowałem się na wybór biblioteki imgscalr 3 dostępnej na licencji Apache 2 jako narzędzia do zmiany wielkości obrazów. Ostatnim nierozwiazanym problemem pozostał czas, jaki jest potrzebny do zmiany wielkości grafiki. Rozwiazałem ten problem tworzac klasę przechowujac a w pamięci kopię obrazka, już po dokonaniu zmiany jego wielkości, dzięki czemu zmiana wielkości dokonywana jest jedynie po uruchomieniu programu i po zmianie wielkości okna. Wykorzystywanie grafik o zmniejszonej wielkości, dostosowanej do rysowania na ekranie pozwoliło także na przyśpieszenie działania aplikacji - operacje dokonywane sa na obiektach zajmujacych mniej pamięci oraz nie jest konieczna zmiana ich wielkości. 2 http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html 3 http://www.thebuzzmedia.com/software/imgscalr-java-image-scaling-library/
3.2. Część wizualna 20 3.2.3. Rysowanie schematu fortepianu na ekranie Rysunek 3.4. Wyglad rysowanej klawiatury przed dodaniem ozdobników. Za rysowanie klawiatury fortepianu odpowiada klasa PianoPainter. Klasa ta zawiera w sobie zmienne pozwalajace na swobodne konfigurowanie wygladu klawiatury takie jak: ilość klawiszy (domyślnie - 88) klawisz startowy, od którego zaczynamy tworzenie klawiatury (domyślnie klawisz reprezentujacy dźwięk A ) rozkład czarnych klawiszy na klawiaturze proporcje klawiszy Proporcje klawiszy zostały zebrane z keyboardu, którego używałem jako głównego urzadzenia podczas pracy nad aplikacja. Proporcja szerokości wszystkich białych klawiszy do ich wysokości to około 6,80, wysokości czarnego klawisza do jego szerokości to zaokraglaj ac 7,90, a szerokości czarnego klawisza do szerokości białego klawisza to w przybliżeniu 0,545. Jeśli założymy, że czarne klawisze sa równo wyśrodkowane pomiędzy sasiaduj acymi białymi klawiszami, powyższe dane powinny nam wystarczyć do odtworzenia klawiatury na ekranie komputera. Rysowanie keyboardu odbywa się przy użyciu podstawowych funkcji Java Graphics2D. Białe klawisze sa białymi (wypełnionymi) prostokatami, czarne klawisze sa czarnymi (wypełnionymi) prostokatami, krawędzie białych klawiszy sa czarnym prostokatem (bez wypełnienia, rysowane sa same krawędzie), a wciśnięte klawisze sa kolorowane na szaro. Efekt widoczny jest na obrazku 3.4. Niestety tak narysowana klawiatura jest graficznie dość mało atrakcyjna. Postanowiłem poprawić jej wyglad, ciagle korzystajac z tych samych podstawowych funkcji rysujacych Java, pozwalajacych na tworzenie różnokolorowych prostoka- tów. Dolna część białego klawisza ( 1/14 ) została pokolorowana na szaro. W podobny sposób pokolorowałem dolna część czarnego klawisza ( 1/9 ) używajac
3.2. Część wizualna 21 nieco ciemniejszego odcienia szarego koloru. Ustawienia dobrałem eksperymentalnie, starajac się uzyskać efekt najbardziej zadowalajacy artystycznie. Z prawej strony czarnych klawiszy dodałem niewielki, szary prostokat (o szerokości równej 1/5 szerokości czarnego klawisza) symulujacy cień rzucany przez klawisz. Dzięki tym usprawnieniom klawiatura instrumentu wydaje się być wypukła, w przeciwieństwie do płaskiej klawiatury widocznej wcześniej. Rezultat widoczny jest na obrazku 3.5. Rysunek 3.5. Końcowy wyglad rysowanej klawiatury.
3.2. Część wizualna 22 3.2.4. Dwa rodzaje widoków udostępniane przez program Tworzona przeze mnie aplikacja udostępnia dwa główne rodzaje widoków, wyświetlajace nuty, które powinien zagrać użytkownik w odmienny sposób. 3.2.5. Główny sposób wyświetlania nut Rysunek 3.6. Główny widok aplikacji. Na obrazku 3.6 widoczny jest główny sposób wyświetlania nut przez moja aplikację. Nuty (będę używać tej nazwy odwołujac się do graficznej reprezentacji przedstawianej w programie) wyświetlane sa w formie zaokraglonych prostokatów różnej wielkości. Pojawiaja się one nad odpowiadajacymi klawiszami narysowanej w dolnej części ekranu klawiaturze przypominajacej standardowy 88-klawiszowy fortepian. Nuty pojawiajace się nad czarnymi klawiszami klawiatury sa węższe (sa tej samej szerokości co czarny klawisz), co pozwala na ich łatwe odróżnienie. Nuty poruszaja się w kierunku dolnej części ekranu. Gdy docieraja do linii wyznaczanej przez górna krawędź klawiatury zatrzymuja się (w manualnym trybie gry) badź wydawany jest odpowiedni dźwięk (w automatycznym trybie odtwarzania utworu). Zaokraglone prostokaty sa różnokolorowe, w zależności od numeru ścieżki w utworze, który reprezentuja. Ich wysokość jest zależna od różnicy czasu, pomiędzy zdarzeniami Note On (wydania dźwięku) oraz Note Off (zaprzestania wydawania dźwięku), które zostały odczytane z pliku MIDI. Im różnica jest większa, tym większa jest wysokość prostokata. Wysokość stanowi sugestię, co do czasu wciskania klawisza instrumentu podłaczonego do komputera - niskim prostokatom powinny odpowiadać krótkie i szybkie wciśnięcia klawiszy, a wysokim prostokatom dłuższe.
3.2. Część wizualna 23 Wyświetlanie nut na pięciolinii Rysunek 3.7. Alternatywny tryb wyświetlania aplikacji przypominajacy notację muzyczna. Alternatywnym trybem wyświetlania w programie, jest widok przypominajacy notację muzyczna. Przedstawiony jest na obrazku 3.7. Należy jednak zwrócić uwagę, że nie jest to poprawny zapis nutowy, raczej jego uproszczenie. Pliki MIDI nie zawieraja wszystkich potrzebnych informacji do wygenerowania notacji muzycznej. W dodatku uznałem, że niektóre części zapisu, sprawdzajace się na papierze moga nie być tak czytelne w wersji animowanej na monitorze komputerowym. Wyświetlanie wciskanych klawiszy W trybie wyświetlania przypominajacym notację muzyczna użytkownikowi jest dużo trudniej zorientować się, którym klawiszom odpowiadaja jakie nuty na pięciolinii. Z tego powodu dodałem fragment na pięciolinii, mieszczacy się pomiędzy kluczem wiolinowym i basowym, a pionowa linia, przypominajac a nieco kreskę taktowa. Nuty wyświetlane w tym fragmencie nie stanowia fragmentu utworu, reprezentuja natomiast aktualnie wciśnięte klawisze na klawiaturze instrumentu (obrazki 3.8, 3.9, 3.10). Do wyświetlania używane sa całe nuty, jednak ich wartość rytmiczna nie ma w tym przypadku znaczenia, zostały przeze mnie wybrane z powodu ich czytelności oraz braku pałeczki nutowej. Na obrazkach poniżej widoczne sa wciśnięte klawisze na klawiaturze instrumentu oraz ich reprezentacja na pięciolinii. Rodzaje wyświetlanych nut W widoku przypominajacym notację muzyczna wyświetlane jest kilka rodzajów nut - całe nuty (rys. 3.11), półnuty (rys. 3.12, 3.13), ćwierćnuty (rys. 3.14,3.15) i ósemki (rys. 3.16,3.17). Typ nuty w muzyce reprezentuje długość jej trwania. Cała nuta trwa dwa razy dłużej niż półnuta, półnuta trwa dwa razy dłużej niż ćwierćnuta, natomiast ćwierćnuta - dwa razy dłużej niż ósemka. Istnieja także nuty, takie jak szesnastka,
3.2. Część wizualna 24 Rysunek 3.8. Lista aktualnie wciśniętych klawiszy wyświetlana obok kluczy. trzydziestodwójka, sześćdziesięcioczwórka, a nawet stodwudziestoósemka (jednak sa one nieco rzadziej spotykane). Niestety pliki MIDI zwykle nie przechowuja informacji zwiazanych z metrum utworu, a fragmenty utworu nie sa dzielone na takty. Z tego powodu stworzyłem własny system podziału dźwięków pochodzacych z pliku MIDI na odpowiednie nuty. Na poczatku analizowana jest zawartość całego pliku i znajdowana jest długość najdłuższego dźwięku. Następnie nuty sa dopasowywane na podstawie kodu: 1 i f ( czastrwanianajdluzszejnuty / 2 < czastrwaniaanalizowanejnuty ) { //Cala nuta } 5 else i f ( czastrwanianajdluzszejnuty / 4 < czastrwaniaanalizowanejnuty ) { //Polnuta } 10 else i f ( czastrwanianajdluzszejnuty / 8 < czastrwaniaanalizowanejnuty ) { //Cwiercnuta } else 15 { //Osemka } Wydruk 3.5. Funkcja dobierajaca nuty. Oczywiście dopasowanie te jest pewnym rodzajem funkcji heurystycznej i może nie zawsze się sprawdzać, jednak pozwala na wygenerowanie dość dobrze wyglada- jacego rezultatu. Problemy moga się pojawiać z utworami zawierajacymi dźwięki o tej samej długości (wszystkie zostana potraktowane jako cała nuta) badź z utworami w których pojawia się kilka bardzo długich dźwięków (w takim wypadku cała reszta nut zostanie rozpoznana jako ósemki). System ten można jednak poprawić - możliwe, że dobre efekty dałaby próba podziału wszystkich długości dźwięków
3.2. Część wizualna 25 Rysunek 3.9. Wciśnięte białe klawisze klawiatury oraz ich reprezentacja na pięciolinii. na kilka dość równych grup, a nie wyznaczania granic w zależności od wartości najdłuższej, jak jest to teraz robione. W zależności od położenia nuty na pięciolinii rysowana jest jej wersja z pałeczka nutowa skierowana na dół badź do góry. Widoczne jest to na obrazku 3.18, na którym przedstawione sa dwa różne sposoby rysowania półnuty. Różnice pomiędzy widokiem a zapisem nutowym Pomiędzy stworzona przeze mnie notacja, a współczesnym zapisem nutowym istnieje wiele różnic. Wymienię najbardziej istotne: Nie występuje podział na takty. W pliku MIDI zwykle nie jest zapisana wystarczajaca ilość informacji do stworzenia taktów. Działanie znaków chromatycznych przygodnych (na przykład krzyżyka, umieszczanego przed nuta) dotyczy jedynie kolejnej nuty (a powinien dotyczyć wszystkich nut tej samej wysokości, aż do końca taktu). Rozważałem zastosowanie kasownika (widocznego na obrazku 3.19 znaku anulujacego działanie innych znaków chromatycznych) po każdej nucie, jednak uznałem, że zmniejszyłoby to zbyt mocno czytelność zapisu. Przestrzeń obok klucza jest przeznaczona do wyświetlania nut reprezentujacych aktualnie wciśnięte klawisze na klawiaturze. Nie jest w tym miejscu wyświetlane metrum (rysunek 3.20), ani znaki chromatyczne przykluczowe określajace tonację utworu. W zapisie nie sa używane pauzy. Nie występuja oznaczenia tempa, dynamiki, artykulacji. Nie używam frazowania oraz znaków powtórzenia. Jak widać różnice sa dość istotne, sposób wyświetlania można jednak rozwijać dalej w celu coraz większego upodobnienia go do nowoczesnego zapisu nutowego.
3.2. Część wizualna 26 Rysunek 3.10. Wciśnięte czarne klawisze, przy wyświetlanych nutach widoczne sa znaki chromatyczne (krzyżyki). Rysunek 3.11. Cała nuta. Rysunek 3.12. Półnuta. Rysunek 3.13. Półnuta (pałeczka nutowa skierowana w dół). Rysunek 3.14. Ćwierćnuta. Rysunek 3.15. Ćwierćnuta (pałeczka nutowa skierowana w dół).
3.2. Część wizualna 27 Rysunek 3.16. Ósemka. Rysunek 3.17. Ósemka (pałeczka nutowa skierowana w dół). Rysunek 3.18. Różne sposoby rysowania tych samych nut. Rysunek 3.19. Kasownik zapisany przed nuta.
3.2. Część wizualna 28 Rysunek 3.20. Zapis metrum.
3.3. Inne opcje 29 3.3. Inne opcje 3.3.1. Wybór trybu gry Program pozwala na automatyczne odtwarzanie utworu (bez oczekiwania na wciskanie odpowiednich klawiszy przez użytkownika) badź na tryb manualny (w którym zatrzymywanie zatrzymuje się do czasu, aż osoba korzystajaca z programu zagra odpowiednie dźwięki). Wybór trybu gry jest dostępny w głównym menu aplikacji. Tryb automatyczny może okazać się przydatny, jeśli użytkownik chcę usłyszeć, jak brzmi poprawna, idealnie wykonana wersja utworu. 3.3.2. Wybór ścieżek MIDI do grania Jeśli w pliku MIDI zapisane nuty sa podzielone na ścieżki (tracks), aplikacja pozwala na wybranie jednej badź kilku z nich jako wyświetlanych. Efekt działania tej funkcji jest widoczny na obrazkach 3.21, 3.22. W przypadku używania manualnego trybu gry, użytkownik musi zagrać jedynie część nut. Pozwala to na opanowanie gry na przykład najpierw lewa ręka, potem prawa ręka, a dopiero na koniec wyćwiczenia współpracy obu rak. Pozostałe ukryte ścieżki sa odtwarzane równolegle do tych granych przez użytkownika, tworzac podkład muzyczny do jego gry. W plikach midi część zapisanych instrumentów może nie nadawać się do grania z użyciem instrumentu klawiszowego (na przykład jeśli zapisane dźwięki maja symulować na przykład perkusję). W takim wypadku warto wyłaczyć taka ścieżkę podczas ćwiczenia gry. Rysunek 3.21. Odtwarzanie dwóch ścieżek (tracks).
3.3. Inne opcje 30 Rysunek 3.22. Wyglad aplikacji po wyłaczeniu jednej z ścieżek.
3.3. Inne opcje 31 3.3.3. Zmiana szybkości odtwarzania utworu Rysunek 3.23. Menu zmiany szybkości odtwarzania utworu. Aplikacja pozwala na zmianę prędkości odtwarzania utworu (rysunek 3.23). Może to się przydać na przykład do przećwiczenia trudniejszego do zagrania fragmentu utworu - zaczynajac od wolnego tempa możemy je progresywnie zwiększać, aż uda nam się opanować grę z pełna szybkościa. 3.3.4. Zmiana wielkości pola wyświetlajacego nuty Aplikacja posiada opcję zmiany pola widzenia, określajacego zagęszczenie nut na ekranie. W zależności od preferencji użytkownika na ekranie może być widoczne kilka dużych szybko poruszajacych się nut, badź wiele mniejszych i poruszajacych się wolniej. Rezultat różnych ustawień jest widoczny na rysunkach 3.24, 3.25, 3.26, 3.27.
3.3. Inne opcje 32 Rysunek 3.24. Widok domyślny, standardowa wielkość pola widzenia. Rysunek 3.25. Widok domyślny, powiększone pole widzenia.
3.3. Inne opcje 33 Rysunek 3.26. Widok przypominajacy zapis nutowy, standardowe pole widzenia. Rysunek 3.27. Widok przypominajacy zapis nutowy, powiększone pole widzenia.
3.3. Inne opcje 34 3.3.5. Wybór syntezatora, cechy syntezatorów Rysunek 3.28. Przykładowy zestaw wykrytych syntezatorów w systemie Windows 7 z podłaczonym keyboardem. Program pozwala na wybór syntezatora używanego do generowania dźwięków. Syntezatory moga się różnić między soba brzmieniem badź obsługiwanymi wiadomościami MIDI, jednak według mnie bardzo istotna cecha, jest czas pomiędzy przesłaniem wiadomości, a czasem wygenerowania dźwięku (latency). Domyślny syntezator Java - Gervill - posiada maksymalny czas opóźnienia określany na 120ms. Jest to wartość łatwo zauważalna, mogaca być dość denerwujaca dla osoby graja- cej. Jeśli w systemie zostanie wykryty syntezator Microsoft GS Wavetable Synth ustawiany jest on jako preferowalny (przed Gervill), z powodu praktycznego niższego czasu opóźnienia. Lista przykładowych syntezatorów widoczna jest na rysunku 3.28. Jeśli jakość dźwięku wydawanego przez aplikację ciagle stanowi problem, wyjściem może być użycie syntezatora wbudowanego w keyboard, jeśli jest on udostępniany przez urzadzenie. Często syntezatory stanowiace część instrumentów muzycznych moga generować lepszej jakości dźwięk z mniejszym opóźnieniem, niż te emulowane przez system. Przykładowo syntezator dostępny w keyboardzie jest widoczny na obrazku pod nazwa DigitalKBD. 3.3.6. Zmiana wysokości dźwięków w utworze Nie wszystkie instrumenty klawiszowe maja ilość klawiszy identyczna z ta użyta w moim programie (wzorujacym się na układzie 88-klawiszowego fortepianu). Jednym z urzadzeń, na których testowałem działanie aplikacji był keyboard Yamaha PSR-E333, posiadajacy klawiaturę składajac a się z 61 klawiszy. Może to stanowić problem i uniemożliwiać zagranie części utworów. Udało mi się częściowo zaradzić na ten problem dodajac do programu możliwość przesuwania wszystkich dźwięków utworu, tak by mogły się zmieścić w zakresie urzadzenia którego używamy. Do wyboru sa opcje przesunięcia dźwięków o półton, badź o oktawę (dwanaście półtonów). Zmiana wysokości dźwięków możliwa jest w obie strony, a operację możemy wielokrotnie powtarzać. Jeśli przesuniemy dźwięki utworów o ilość półtonów nie będacych wielokrotnościa dwunastu, zmieni się także tonacja utworu. Przykład zmiany wysokości dźwięków utworu przedstawiony jest na rysunkach 3.29 oraz 3.30.
3.3. Inne opcje 35 Rysunek 3.29. Część dźwięków jest niemożliwa do zagrania. Zakres dostępnych klawiszy 61-klawiszowego instrumentu został zaznaczony prostokatem. Rysunek 3.30. Po przesunięciu dźwięków utworu o dwie oktawy staje on się możliwy do zagrania na danym instrumencie muzycznym.
3.3. Inne opcje 36 3.3.7. Przewijanie utworu Rysunek 3.31. Pasek przewijania umieszczony pod wizerunkiem klawiatury. Aplikacja posiada pasek przewijania (widoczny na rysunku 3.31), pozwalajacy na łatwe trafienie do fragmentu utworu, który nas interesuje. Dźwięk wydawany przez syntezator zależy nie tylko od nut (wiadomości Note On, Note Off) ale także od jego aktualnego stanu (czyli od ustawień, jakie zostały przekazane we wcześniejszej części utworu). Z tego powodu po przesunięcie wskaźnika odtwarzania utworu wymaga kilku dodatkowych czynności: zresetowania stanu klawiatury, zatrzymania wszystkich aktualnie wydawanych dźwięków, zmianę tempa odtwarzania utworu na te jakie jest w nowym punkcie oraz przesłania wszystkich istotnych wiadomości MIDI, które miały miejsce przed tym punktem. 3.3.8. Wygładzanie krawędzi (antyaliasing) Aplikacja zawiera opcję, pozwalajac a na wybór sposobu rysowania nut z badź bez wygładzania krawędzi. Właczona opcja może spowodować zwiększenie zużycia procesora, poprawiajac jednak jakość rysowanej grafiki. Różnice przedstawione sa na rysunkach 3.32 i 3.33. Ustawienie te jest zapisywane i wybrana przez użytkownika wartość jest przechowywana pomiędzy kolejnymi uruchomieniami aplikacji. Właściwość ustawiana jest poprzez: 1 setrenderinghint ( RenderingHints. KEY_ANTIALIASING, RenderingHints. VALUE_ANTIALIAS_ON ) ;
3.3. Inne opcje 37 Rysunek 3.32. Wyglad nut z właczonym wygładzaniem krawędzi. Rysunek 3.33. Wyglad nut z wyłaczonym wygładzaniem krawędzi. Widoczny efekt schodkowania obrazu.