[128] MIDI - język nie tylko instrumentów cz. 4

[128] MIDI - język nie tylko instrumentów cz. 4

W poprzednim artykule napisałem prosty szkic realizujący wysyłanie komunikatów MIDI za pomocą klawiaturki obecnej na płytce edukacyjnej TME. Program jednak nie przewidywał pracy polifonicznej. Dopóki był trzymany któryś z przycisków, całość blokowała się. Spróbujmy to naprawić.


const byte iloscKlawiszy = 5;                                   // Liczba klawiszy.
const byte adresKlawiatury[] = { 5, 4, 8, 7, 6 };               // Adresy klawiszy.
const byte wysokoscNuty[] = { 55, 60, 64, 67, 72 };             // Wysokości nut przyporządkowane klawiszom w powyższej tablicy.
bool stanKomunikatu[] = { false, false, false, false, false };  // Wskaźniki zmieniane po zmianie stanu klawiatury.
byte indeks;                                                    // Zmienna licznika kolejnych pozycji tablic.

void setup() {
  Serial.begin(31250);                                // Inicjuj port z nietypową szybkością 31250 bps
  for (indeks = 0; indeks < iloscKlawiszy; indeks++)  // Dla każdego pstryczka...
    pinMode(adresKlawiatury[indeks], INPUT_PULLUP);   // Deklaruj pin jako wejście podciągnięte wewnętrznie do wysokiego stanu.
}
void loop() {
  for (indeks = 0; indeks < iloscKlawiszy; indeks++) {                                      // Wybieraj kolejne klawisze.
    if (digitalRead(adresKlawiatury[indeks]) == HIGH && stanKomunikatu[indeks] == false) {  // Jeśli kolejny przycisk został wciśnięty i nie był wciśnięty wcześniej...
      stanKomunikatu[indeks] = true;                                                        // Ustaw flagę informacji o wciśnięciu przycisku.
      noteOn();                                                                             // Włącz nutę.
    }
    if (digitalRead(adresKlawiatury[indeks]) == LOW && stanKomunikatu[indeks] == true) {  // Jeśli kolejny przycisk został puszczony i nie był wciśnięty wcześniej...
      stanKomunikatu[indeks] = false;                                                     // Kasuj flagę informacji o wciśnięciu przycisku.
      noteOff();                                                                          // Wyłącz nutę.
    }
  }
}
void noteOn() {                        // Włącz nutę.
  Serial.write(144);                   // Wyślij komunikat "włącz nutę" na kanale pierwszym.
  Serial.write(wysokoscNuty[indeks]);  // Wyślij wysokość nuty.
  Serial.write(64);                    // Wyślij głośność nuty.
}
void noteOff() {  // Wyłącz nutę.
  Serial.write(128);
  Serial.write(wysokoscNuty[indeks]);
  Serial.write(0);
}

Dołożyłem trzecią tablicę stanKomunikatu, w której będą dwustanowe flagi stanu komunikatu: fałsz, jeśli nuta nie gra i prawda, jeśli gra. Wypełniłem ją zmiennymi false dla porządku, w praktyce wpisuje się zera.

W pętli głównej znajdziemy dwa niezależne zdarzenia o bliźniaczym działaniu. Pierwsze sprawdza czy klawisz nie został wciśnięty i jaki jest stan flagi stanKomunikatu. Jeśli został wciśnięty i flaga ma stan niski, włączamy odpowiednią nutę i zmieniamy fladze stan na wysoki. Dzięki temu ponowne wejście w tę pętlę zignoruje konieczność wysyłania komunikatu włączenia nuty, bo flaga na to nie pozwoli.

Bliźniaczy warunek działa na odwrót: jeśli klawisz puszczono i flaga była ustawiona – co zrobił za pierwszym razem warunek poprzedni – wysyłamy komunikat końca grania nuty i zerujemy flagę, dzięki czemu gdy znowu wciśniemy ten klawisz, nuta zagra. Co byłoby, gdyby nie było flag? W przypadku puszczonych klawiszy atak komunikatów MIDI wyłączających nuty – to jeszcze instrument powinien przetrwać, choć retro maszyny mogą trochę wariować. Ale po wciśnięciu któregoś z klawiszy zacznie się bardzo szybka repetycja wciskania i w zależności od instrumentu, dojdzie do katastrofy dźwiękowej albo wręcz zawiesimy maszynę.

Po skompilowaniu kodu możemy już zagrać akordami. Hejnał jednak ma nikłą przydatność, o wiele bardziej praktyczny będzie na przykład sterownik perkusji.

Do zabaw, jak widać, używam Rolanda JD-Xi, ponieważ Casio z poprzednich artykułów nie ma dostępu do perkusji przez MIDI. Tutaj, jak to zwykle bywa, bębenki dostępne są na kanale dziesiątym – taki jest standard, ale zawsze należy to sprawdzić w dokumentacji instrumentu. Dlatego też podprogramy będą wymagały zmiany kanałów. Zwiększyłem też dynamikę stuków, bo wartość 64 dawała odgłosy niemrawe. Na panelu instrumentu wydrukowano ściągę, więc wybrałem pięć bębnów i wstawiłem ich adresy w tablicy.

const byte iloscKlawiszy = 5;                                   // Liczba klawiszy.
const byte adresKlawiatury[] = { 5, 4, 8, 7, 6 };               // Adresy klawiszy.
const byte wysokoscNuty[] = { 54, 36, 41, 42, 46 };             // Wysokości nut przyporządkowane klawiszom w powyższej tablicy.
bool stanKomunikatu[] = { false, false, false, false, false };  // Wskaźniki zmieniane po zmianie stanu klawiatury.
byte indeks;                                                    // Zmienna licznika kolejnych pozycji tablic.

void setup() {
  Serial.begin(31250);                                // Inicjuj port z nietypową szybkością 31250 bps
  for (indeks = 0; indeks < iloscKlawiszy; indeks++)  // Dla każdego pstryczka...
    pinMode(adresKlawiatury[indeks], INPUT_PULLUP);   // Deklaruj pin jako wejście podciągnięte wewnętrznie do wysokiego stanu.
}
void loop() {
  for (indeks = 0; indeks < iloscKlawiszy; indeks++) {                                      // Wybieraj kolejne klawisze.
    if (digitalRead(adresKlawiatury[indeks]) == HIGH && stanKomunikatu[indeks] == false) {  // Jeśli kolejny przycisk został wciśnięty i nie był wciśnięty wcześniej...
      stanKomunikatu[indeks] = true;                                                        // Ustaw flagę informacji o wciśnięciu przycisku.
      noteOn();                                                                             // Włącz nutę.
    }
    if (digitalRead(adresKlawiatury[indeks]) == LOW && stanKomunikatu[indeks] == true) {  // Jeśli kolejny przycisk został puszczony i nie był wciśnięty wcześniej...
      stanKomunikatu[indeks] = false;                                                     // Kasuj flagę informacji o wciśnięciu przycisku.
      noteOff();                                                                          // Wyłącz nutę.
    }
  }
}
void noteOn() {                        // Włącz nutę.
  Serial.write(153);                   // Wyślij komunikat "włącz nutę" na kanale pierwszym.
  Serial.write(wysokoscNuty[indeks]);  // Wyślij wysokość nuty.
  Serial.write(100);                   // Wyślij głośność nuty.
}
void noteOff() {  // Wyłącz nutę.
  Serial.write(137);
  Serial.write(wysokoscNuty[indeks]);
  Serial.write(0);
}

W tym wydaniu jest to oczywiście sztuka dla sztuki, gdyż instrument ma klawiaturę, do tego dynamiczną i taki mało wygodny dubel nie ma sensu. Ale te styki mogą mieć zupełnie inny wymiar, na przykład elektronicznych bębnów wykonanych z mat przewodzących. Na scenie coś takiego może wyglądać bardzo efektownie, a Arduino – choćby małe Nano – pozamienia zwarcia takich mat na sygnały MIDI i pozwoli wyzwalać już dowolne źródło dźwięku.

Na koniec pragnę jeszcze zwrócić uwagę na polifonię, która przydaje się także w brzmieniach perkusyjnych, choćby po to, by uzyskać jednocześnie brzmienie stopy i talerza. Dobrze byłoby również zadbać o pewność przełączeń, byśmy nie dostali serii wystrzałów. Co prawda samo MIDI blokuje działanie programu na mniej więcej milisekundę dla każdego zdarzenia, ale dla klawiatur to za mało i wypadałoby dołożyć pętle opóźniające. Myślę, że z tym to już każdy sobie poradzi, jak również z rozbudowaniem klawiatury ponad moje pięć przycisków. W kolejnym artykule wyprodukujemy kilka pożytecznych przełączników MIDI.

Powiązane tematy

Płytka edukacyjna TME-EDU-ARD-2Płytka edukacyjna TME-EDU-ARD-2Sprawdź tutaj

Przeczytaj również

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2025 arduino.pl