Przejdziemy teraz do zupełnie innego tematu dotyczącego Javy i zaprezentuję Wam jak utworzyć podstawowy warsztat dla odtworzenia pojedynczej nuty granej za pomocą interfejsu MIDI. Aby w języku Java MIDI dawało oczekiwane rezultaty, wymaga poznania i zrozumienia kilku następnych klas które wspólnie przygotujemy i napiszemy. Zapraszam!

JAVA MIDI. TROCHĘ TEORII

MIDI to akronim od "Musical Instrument Digital Interface", czyli "Cyfrowy interfejs instrumentów muzycznych". Nie wnikam w historię powstania, napiszę jedynie co nam to daje. Umożliwia w sposób komputerowy na dodawanie nut i konfigurowanie instrumentów muzycznych ustalając na przykład głośność w danym momencie lub kiedy dokładnie dana nuta ma być zagrana.

Java też obsługuje MIDI i można bez problemu dostać się do interfejsu i odtwarzać dźwięki. Dla początkujących może sprawiać problem zrozumienie budowy zapisu nutowego, gdyż sposoby widzenia w porównaniu z ludzkim będą się mocno różnić, przynajmniej patrząc przez pryzmat programowania tego w Javie. Dowiedzmy się najpierw z czym musimy mieć do czynienia!

JAVA MIDI. WYJAŚNIENIE KLAS

Interfejs cyfrowy wymaga kilku obiektów o których w ogóle jeszcze nie pisałem na swojej stronie, dotychczas nie było takiej konieczności. Nie będę próbował zastępować literatury, wszystkie opiszę jednym / kilkoma zdaniami:

  • Sequencer
    • obiekt pełniący rolę "odtwarzacza"
    • dodajemy do niego obiekt sekwencji (opisany niżej)
  • Sequence
    • obiekt przechowujący nuty w postaci obiektów "MidiEvent" (opisane niżej)
  • Track
    • ścieżka dla pojedynczego kanału (MIDI obsługuje odtwarzanie na wiele ścieżek jednocześnie dzięki czemu można osobno dodać perkusję, melodię, bass itd.)
  • ShortMessage
    • kontener przechowujący polecenia dla obiektu "MidiEvent"
    • MIDI nie zostało zaprogramowane na automatycznie "uciszanie" danej nuty, zostało to podzielone na włączanie i wyłączanie więc trzeba dodawać osobno zdarzenia dla odtwarzania i zatrzymywania nuty
  • MidiEvent
    • pojedyncze zdarzenie uzyskujące dane pobierane z przekazywanego obiektu "ShortMessage"
    • ustalamy tutaj w którym "miejscu" dane zdarzenie ma nastąpić

Zwróćcie uwagę, że wielokrotnie podałem słowo "zdarzenie". Właśnie tak zachowuje się w języku Java MIDI, które będziemy programować. Zdarzenie może określać włączenie nuty, wyłączenie nuty, ale może też oznaczać zmianę instrumentu na inny. Nie określa się instrumentu na początku jak niektórym może się wydawać, tylko właśnie na podstawie zdarzeń. Niżej przedstawiam kodową demonstrację.

KOD ŹRÓDŁOWY GRAJĄCY NUTĘ

Podane operacje wymagają obsłużenia wyjątków "MidiUnavailableException" oraz "InvalidMIDIDataException"! Najpierw wczytajcie się w kod, skompilujcie go, uruchomcie i wtedy przejdźcie do poniższych wyjaśnień:

  • KLASA "Main"
public class Main
{
	public static void main(String[] args)
	{
		new Launcher();
	}
}
  • KLASA "Launcher"
import javax.sound.midi.*;

public class Launcher
{
	private static final int NOTE_OFF = 128;
	private static final int NOTE_ON = 144;
	private static final int CHANGE_INSTRUMENT = 192;

	public Launcher()
	{
		try
		{
			Sequencer sequencer = MidiSystem.getSequencer();
			Sequence sequence = new Sequence(Sequence.PPQ, 4);
			Track track = sequence.createTrack();

			sequencer.open();
			track.add(newEventToTrack(CHANGE_INSTRUMENT, 1, 6, 0, 1));
			track.add(newEventToTrack(NOTE_ON, 1, 44, 100, 1));
			track.add(newEventToTrack(NOTE_OFF, 1, 44, 100, 16));
			sequencer.setSequence(sequence);
			sequencer.start();
		}
		catch (MidiUnavailableException mue)
		{
			mue.printStackTrace();
		}
		catch (InvalidMidiDataException imde)
		{
			imde.printStackTrace();
		}
	}

	private MidiEvent newEventToTrack(int command, int channel, int data1, int data2, long tick) throws InvalidMidiDataException
	{
		ShortMessage sm = new ShortMessage(command, channel, data1, data2);
		MidiEvent me = new MidiEvent(sm, tick);

		return me;
	}
}

Pakiet "javax.sound.midi" otwiera Wam drogę do interfejsu MIDI. Nowy "Sequencer" pobierany jest ze statycznej metody "getSequencer", nie tworzymy go samodzielnie od zera. Linijkę niżej od razu tworzycie sobie "Sequence" tym razem w sposób tradycyjny. Przyjmuje on dwa parametry:

  1. rodzaj ustalania przedziału czasowego
    1. "pulses per quarter", czyli "PPQ"
    2. "timecode" interfejsu MIDI kryjące się pod skrótem "SMTPE"
  2. "rozdzielczość czasowa"
    • określa ona ile impulsów ma być w jednym takcie (to jest toporne określenie, ale naukowa definicja wymaga wiedzy z teorii muzyki), na przykład wartość 4 określa takt w taki sposób, jakby się mówiło w rytm "raz-dwa-trzy-cztery", a z kolei wartość 3 oznaczałaby mówienie w kółko "raz-dwa-trzy" (możecie znaleźć więcej informacji na ten temat również pod hasłem "time signature")

Trzecim obiektem z rzędu jest "Track". Jego również tworzycie przy użyciu metody zawartej w sekwencji, "createTrack". Nie przyjmuje parametrów. W Java, MIDI wymaga szeregu obiektów, aby móc usłyszeć jedną nutkę. Przed jakimikolwiek wywołaniami metod, koniecznie "otwórzcie" sekwenser za pomocą metody "open". To jest wymaganie i zaniechanie tego skutkować będzie zgłoszeniem wyjątku.

Dopiero od teraz zaczyna się wpływ na melodię. Do ścieżki dodajemy zdarzenia korzystając z metody "add". Zalecam szczególnie napisać sobie osobną metodę dodającą zdarzenie, gdyż każde zdarzenie wymaga dwóch kolejnych obiektów o których już wspomniałem. Są to "ShortMessage" czyli określanie rodzaju zdarzenia na podstawie stałej wbudowanej liczby, jakiego kanału ma to dotyczyć oraz jakie dane w postaci dwóch kolejnych parametrów mają być brane pod uwagę oraz "MidiEvent" pobierający opisany przed chwilą obiekt oraz w którym "impulsie" ma to zostać wywołane.

Warto zapamiętać, że jest to podział określający co ("ShortMessage") i kiedy ("MidiEvent") ma być wykonane. Zapisałem stałe określające typ zdarzenia, abyście nie musieli zgadywać. "NOTE_ON" oznacza "odpalenie" nuty czyli moment, w którym nacisnęlibyście klawisz na pianinie. "NOTE_OFF" to puszczenie klawisza, a "CHANGE_INSTRUMENT" oznacza polecenie zmiany instrumentu na jakiś inny. Zauważcie, że podaje się go PRZED dodaniem zdarzenia "NOTE_ON" oraz w tym samym czasie (ostatni parametr).

Dopuszczalny zakres jest w przedziale <0; 127>, jeśli chodzi o parametry oznaczone jako "data1" oraz "data2". Inaczej będzie zgłoszenie wyjątku. Na samym końcu dorzucacie wywołanie metody "setSequence" i wprowadzacie sekwencję podtrzymującą Waszą zmienioną już ścieżkę. Po wszystkich wyżej wymienionych krokach dopiero teraz wywołujecie metodę "start" i odtwarzacie melodię MIDI w Java.


To wszystko. Jeśli zrozumieliście jak programować ciąg zdarzeń melodii, to bez wahania odpowiecie prawidłowo na pytanie ILE zdarzeń trzeba dodać dla zagrania jednej dodatkowej nuty.

PODOBNE ARTYKUŁY