Strona główna » Kursy » Kurs C++ » Dźwięk z komputera, a C++
 

Dźwięk z komputera, a C++

Dźwięk powiadomienia

Nasz program może wykonywać obliczenia przez dłuższy czas. Użytkownik znudzony długością trwania obliczeń może przejść do innej aplikacji. Komputery osobiste nie mają wibracji jak dzisiejsze urządzenia mobilne, więc warto rozważyć odtworzenie dźwięku. W systemie Windows możemy odtworzyć domyślny dźwięk powiadomienia wypisując na konsole znak specjalny \a.

  1. cout << '\a';

Jest to znak ASCII numer 7, więc możemy też wypisać w drugi sposób:

  1. cout << (char)(7);

Tylko tyle wystarczy, aby zwrócić uwagę użytkownika.

Inne dźwięki

Przypuśćmy jednak, że chcemy być bardziej oryginalni. API Windows'a pozwala nam na odegranie dźwięku dowolnej częstotliwości i dowolnej długości. Posłuży nam do tego funkcja Beep(), którą będziemy mogli użyć po dołączeniu biblioteki windows.h. Innymi słowy w nagłówku projektu dodajemy:

  1. #include <windows.h>

Teraz możemy skorzystać z funkcji Beep(), która przyjmuje dwa argumenty będące liczbami całkowitymi: częstotliwość dźwięku oraz długość dźwięku. Częstotliwość dźwięku podajemy w Hz, a długość dźwięku w milisekundach. Przykładowo możemy wywołać:

  1. Beep(1567, 200);

Dzięki tej prostej funkcji możemy odegrać bardziej skomplikowane utwory.

  1. Beep(1567, 200);
  2. Beep(1567, 200);
  3. Beep(1567, 200);
  4. Beep(1244, 1000);
  5. Beep(1396, 200);
  6. Beep(1396, 200);
  7. Beep(1396, 200);
  8. Beep(1174, 1000);

W celu wydobycia innych dźwięków z komputera warto skorzystać z poniższej tabelki:

NutaHz
C132.7
C#134.6
D136.7
D#138.9
E141.2
F143.7
F#146.2
G149
G#151.9
A155.0
A#158.3
B161.7
NutaHz
C265.4
C#269.3
D273.4
D#277.8
E282.4
F287.3
F#292.5
G298.0
G#2103.8
A2110.0
A#2116.5
B2123.5
NutaHz
C3130.8
C#3138.6
D3146.8
D#3155.6
E3164.8
F3174.6
F#3185.0
G3196.0
G#3207.7
A3220.0
A#3233.1
B3246.9
NutaHz
C4261.6
C#4277.2
D4293.7
D#4311.1
E4329.6
F4349.2
F#4370.0
G4392.0
G#4415.3
A4440.0
A#4466.2
B4493.9
NutaHz
C5523.3
C#5554.4
D5587.3
D#5622.3
E5659.3
F5698.5
F#5740.0
G5784.0
G#5830.6
A5880.0
A#5932.3
B5987.8
NutaHz
C61046.5
C#61108.7
D61174.7
D#61244.5
E61318.5
F61396.9
F#61480.0
G61568.0
G#61661.2
A61760.0
A#61864.7
B61975.5
NutaHz
C72093.0
C#72217.5
D72349.3
D#72489.0
E72637.0
F72793.8
F#72960.0
G73136.0
G#73322.4
A73520.0
A#73729.3
B73951.1

Zadanie

Napisz aplikację, która wczyta n linijek tekstu. Na wejściu będzie podana najpierw liczba całkowita n, a potem n linijek, które będą składały się z opisu nuty, spacji oraz liczby całkowitej która będzie oznaczać długość trwania nuty w milisekundach. Program ma za zadanie odegrać wczytane nuty. Przykładowo

  1. 2
  2. G6 200
  3. G6 200

Rozwiązanie

W celu ułatwienia wybierania częstotliwości danej nuty zadeklarujemy globalną listę częstotliwości - ze względu na długość zamieszczam tylko fragment, całość dostępna jest do skopiowania w kodzie źródłowym:

  1. double nuty[84] = {32.7, ..., 3951.1};

Nuty oktawy zaczynają się od C kończą na B, więc warto dopisać funkcję note_value(), która powie na podstawie zapisu litery i ewentualnie # jaki indeks ma nuta w oktawie:

  1. int note_value(char note, bool up){
  2.   switch(note){
  3.     case 'C': return up ? 1 : 0;
  4.     case 'D': return up ? 3 : 2;
  5.     case 'E': return 4;
  6.     case 'F': return up ? 6 : 5;
  7.     case 'G': return up ? 8 : 7;
  8.     case 'A': return up ? 10 : 9;
  9.     case 'B': return 11;
  10.   }
  11. }

Jako argument przyjmujemy dwa argumenty. Literę nuty oraz czy jest modyfikowana przez #.

Główna funkcja

  1. int main (){
  2.   int n;
  3.   cin >> n;
  4.   cin.clear();
  5.   cin.sync();

(2.) Deklarujemy zmienną n, która określi ile nut zagramy. (3.) Wczytujemy ile nut mamy zagrać. (4. - 5.) Czyścimy strumień, aby mieć pewność, że będziemy wczytywać linijka po linijce to co chcemy.

  1.   char* line = new char[16];
  2.   for(int i = 0; i < n; i++){
  3.     cin.getline(line, 16);
  4.     int note = note_value(line[0], line[1] == '#');
  5.     note += 12 * (line[(line[1] == '#') ? 2 : 1] - '1');

(6.) Deklarujemy zmienną line, która będzie naszym buforem przechowującym wczytane dane. (7.) Rozpoczynamy pętle dla każdej linijki danych. (9.) Wywołujemy note_value(), aby uzyskać indeks noty w oktawie. (10.) Następnie ustalamy indeks nuty na naszej liście - przesuwamy o 12 * cyfra po nucie miejsc w górę. Uwaga, musimy pamiętać, że mamy nuty z # i bez, więc cyfra określająca oktawę może być na 1 lub drugiej pozycji.

  1.     int duration = 0;
  2.     for(int j = (line[1] == '#') ? 4 : 3; line[j]; j++){
  3.       duration *= 10;
  4.       duration += line[j] - '0';
  5.     }
  6.     Beep(nuty[note], duration);
  7.   }

(11.) Deklarujemy zmienną duration do której wczytam ile ma trwać nuta. (12. - 15.) Dopóki nie skończy nam się napis wczytujemy liczbę. (16.) Gramy nutę.

  1.   system("pause");
  2.   return 0;
  3. }

(18.) Czekamy na interakcję użytkownika i (19.) zamykamy program.

Teraz można odegrać muzyczkę uruchamiając aplikację i wpisując:

  1. 8
  2. G6 200
  3. G6 200
  4. G6 200
  5. D#6 1000
  6. F6 200
  7. F6 200
  8. F6 200
  9. D6 1000

Zadania

Zadanie 1

Zmodyfikuj aplikację, aby wczytywała na końcu każdej linijki dodatkowy argument, który określa ile razy nuta ma być zagrana. Uwaga: argument ten nie musi być podany - program powinien to wykryć.

Dzięki tej zmianie wciąż ta sama muzyczka powinna być odegrana zgodnie z tymi danymi wejściowymi:

  1. 4
  2. G6 200 3
  3. D#6 1000
  4. F6 200 3
  5. D6 1000