background image

Wydanie pierwsze 

ISBN 83-85455-69-8

 

SPIS TRE

Ś

CI 

Spis treści 

SPIS ILUSTRACJI 6 
WSTĘP  7 

1. SOUND BLASTER - PODSTAWY 9 
2. OBSŁUGA PLIKÓW VOC 11 

2. l STRUKTURA PLIKU VOC 11 2.2 STEROWNIK CT-VOICE 16

 

SPOSÓB KORZYSTANIA ZE STEROWNIKA 16 OMÓWIENIE FUNKCJI STEROWNIKA 20 
ZASADY KORZYSTANIA Z FUNKCJI 27 BIBLIOTEKA VOC.TPU 28 PRZYKŁADY 38 2 3 
OBSŁUGA WIĘKSZYCH PLIKÓW 42 

SPIS TREŚCI

 

3. OBSŁUGA PLIKÓW CMF 53 

3.1 STRUKTURA PLIKÓW CMF 53 Blok nagłówka 54 Blok instrumentów 55 Blok 

muzyczny 55 

3.2 FORMATY SBI i IBK 55 
3.3 STEROWNIK SBFM 57 

SPOSÓB KORZYSTANIA ZE STEROWNIKA 58 OPIS FUNKCJI STEROWNIKA 59 
ZASADY KORZYSTANIA Z FUNKCJI 63 BIBLIOTEKA CMF.TPU 64 

3.4 PRZYKŁADY 73

 

4. PROGRAMOWANIE DSP 79 

4.1 ZASADY OBSŁUGI DSP 79 Zerowanie DSP 80 Zapis do DSP 80 Odczyt z 

DSP 81 Obsługa przerwania DSP 81

 

4.2 TRYB BEZPOŚREDNI 82

 

4.3 Tryb DMA 93

 

4.4 OBSŁUGA ZŁĄCZA MIDI 97 Tryb bezpośredni 98 Tryb przerwań 99

 

4.5 KOMENDY DSP 99 Rozkaz 1xh 99 Rozkaz 2xh 101 Rozkaz 3xh 101 Rozkaz 

40h 102 Rozkaz 7xh 102 Rozkaz Dxh 102 Rozkaz E l h 103

 

4.6 BADANIE KONFIGURACJI SB 104

 

5. PROGRAMOWANIE SYNTEZERA FM  109 

5. l FUNKCJONOWANIE SYNTEZERA FM 109

 

SPIS TRE

Ś

CI 

5.2 ZASADY OBSŁUGI SYNTEZERA FM  112 Zapis danej do rejestru 113 Odczyt rejestru 

statusowego 113

 

5.3 PRZYKŁADY 122

 

6. SYGNAŁY l ICH PRZETWARZANIE   131 

6. l Co to są sygnały i jak je dzielimy  131

 

6.2 Przetwarzanie analogowo-cyfrowe  133 Próbkowanie 134 Kwantyzacja 135

 

6.3 Filtracja cyfrowa  136

 

6.4 Analiza widmowa sygnału 139

 

6.5 Rozpoznawanie mowy ludzkiej 140

 

7. FORMAT WAV 147 LITERATURA 151 

6

 

SPIS ILUSTRACJI 

background image

1. 

2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 

Karta Sound Blaster w wersji 2.0 10

 

Struktura pliku VOC z p

ę

tl

ą

 Repeat Loop 15

 

Mechanizm odwoływania si

ę

 do funkcji CT-VOICE 17

 

Obwiednia ADSR (Attack/Decay/Sustain/Relase) 110

 

Synteza operatorowa   112 
Dwa typy obwiedni  116 
Synteza FM i addytywna  120 
Kształt fali generowanej przez oscylator operatora   121 
Widmo prąŜkowe 132 
10. Przetwarzanie analogowo-cyfrowe  134 
11. Efekt niejednoznaczności   134 
12. Aliasing 135 
13. Przykładowa charakterystyka kwantyzatora 136 
14. Charakterystyki filtrów dolno- i środkowoprzepustowego   137 
15. Wpływ dobroci na kształt charakterystyki filtru   137 
16. Ograniczenie zakresu zmian amplitudy   141 
17. Przykładowy wykres widmowy 144 
18. Widmo prąŜkowe 145 
19. Aproksymacja przebiegu wykresu widmowego 145 

WST

Ę

P

 

WSTĘ

Od kilku juŜ lat multimedia to dziedzina zdobywająca coraz większą popularność. Kluczową rolę w technice 
multimedialnej odgrywa dźwięk. Jego źródłem są specjalne karty - np. opisywany w ksiąŜce Sound Blaster. 
Karty takie są teŜ obsługiwane przez programy rozrywkowe. Niestety mało jest publikacji poświęconych 
zasadom ich programowania i omawiających to zagadnienie w sposób wyczerpujący. Mam nadzieję, Ŝe moja 
ksiąŜka wypełni choć w części tę lukę i okaŜe się pomocna dla wszystkich zainteresowanych tworzeniem 
oprogramowania współpracującego z kartami SB. Wszystkie przykłady prezentowane w pracy zostały 
przygotowane przy uŜyciu kompilatora Turbo Pascal w wersji 6.0 firmy Borland Inc. Ich teksty źródłowe oraz 
kompilaty znajdzie Czytelnik na dyskietce dołączonej do ksiąŜki. Zakładam, Ŝe Czytelnik ma umiejętność 
programowania w dowolnym języku oraz elementarną wiedzę na temat funkcjonowania systemu DOS i 
architektury komputerów PC. 

Mimo Ŝe przedstawione przykłady napisane zostały w Pascalu, nic nie stoi na przeszkodzie, aby opisywane w 
pracy algorytmy wykorzystać pisząc programy w innych językach - np. C, Assembler. Ostatni rozdział 
poświęcony jest zagadnieniom związanym z przetwarzaniem dźwięku: filtrom cyfrowym, analizie widmowej, 
rozpo- 

WSTĘP

 

znawaniu mowy. Traktować go naleŜy wyłącznie jako wprowadzenie do opisywanych tematów. 

Na koniec chciałbym podzi

ę

kowa

ć

 Matce oraz Kasi Byczkowskiej bez pomocy której ksi

ąŜ

ka ta by

ć

 mo

Ŝ

e w 

ogóle by nie powstała 

Autor 

9

 

SOUND BLASTER - PODSTAWY 

 

l. SOUND BLASTER - PODSTAWY 

Karta Sound Blaster po raz pierwszy zaprezentowana została w 1989 roku. Kilka miesięcy później była juŜ 
najlepiej sprzedającym się rozszerzeniem muzycznym przeznaczonym dla komputerów PC. Przyczyny 

background image

niewątpliwego sukcesu to z pewnością dość duŜe moŜliwości i niska cena przy zachowaniu zgodności 
programowej z wcześniejszym dominantem - kartą AdLib firmy AdLib Inc. Obecnie, nawet w chwili gdy 
faktycznym standardem są juŜ karty 16-bitowe, poczciwy SB wciąŜ trzyma się dobrze. Dzieje się tak między 
innymi dlatego, Ŝe najprostszą kartę zgodną ze standardem SB 2.0 nabyć juŜ moŜna za cenę niŜszą niŜ 100 
złotych. 
Oto garść podstawowych informacji na temat parametrów karty Sound Blaster: 

•  11-głosowy syntezer FM. MoŜe pracować w dwóch trybach: 

w trybie melodycznym (9 głosów) i w trybie rytmicznym (moŜliwość syntezy sześciu róŜnych brzmień i 
korzystania z pięciu brzmień perkusyjnych: bęben basowy, talerz, werbel, bębenek i high hat). Syntezer 
FM zapewnia zgodność z kartą AdLib -oparty jest na tym samym układzie (FM1312). 

•   MoŜliwość zapisu i odtwarzania próbkowanych dźwięków. Konwersja analogowo-cyfrowa i cyfrowo-

analogowa realizowana jest przez serce karty SB - układ DSP [Digital Sound Processor). 

ROZDZIAŁ 1

 

Próbkowanie i odtwarzanie kolejnych próbek dźwięku moŜe odbywać się z róŜną (w zaleŜności od wersji karty) 
częstotliwością. l tak dla kart w wersjach l.x maksymalna częstotliwość próbkowania wynosi 12 kHz, 
maksymalna częstotliwość odtwarzania - 23 kHz, w wersji 2.0 (wymiana DSP z 1.05 na 2.00) maksymalna 
częstotliwość próbkowania to 15 kHz, a odtwarzania - 44,1 kHz. Zapis dźwięku we wszystkich wersjach karty 
jest dokonywany z 8-bitową rozdzielczością. Układ DSP zapewnia moŜliwość kompresji samplowanego sygnału 
w czasie rzeczywistym według trzech algorytmów (ADPCM 4:1, 3:1, 2:1). Dekompresja moŜe być realizowana 
w czasie rzeczywistym. 

•   MoŜliwość współpracy z urządzeniami MIDI. Urządzeń wyposaŜonych w złącza typu MIDI niestety nie moŜemy 
połączyć bezpośrednio do karty Sound Blaster - konieczne jest uŜycie tzw. Sound Blaster MIDI Kit. Jest tak, 
poniewaŜ na karcie nie znajdują się standardowe gniazda MIDI (DIN). 
•   MoŜliwość współpracy z joystick'iem analogowym. W wersji 1.0 karty znajduje się teŜ moduł CMS upgrade. Był 
on instalowany w celu zapewnienia zgodności z poprzednim wyrobem firmy - kartą Gamę Blaster, zawierającą układ 
12-głosowej syntezy AM. 

Rysunek l przedstawia rozmieszczenie najwaŜniejszych elementów karty Sound Blaster 2.0. 

h UNE-IN LLJP WE MIKROFON

 

|Potefiqomeł fegutwy gtosnosd

 

WYStUCHAWK.

 

Złqcze JOY/MO

 

Rys.! Karta Sound Blaster w wersjl 2.0

 

11 
OBSŁUGA PLIKÓW VOC 

 

2. OBSŁUGA PLIKÓW TOĆ 

Format VOC (Creative Voice File) to przyjęty przez firmę Creative Labs Inc. format zapisu plików 
zawierających dane dźwiękowe. Pliki tego typu obsługują programy dołączane do kart serii Sound Blaster. 
Przykładem mogą być programy VOXK1T i VEDIT. Zaletą jest duŜa funkcjonalność i uniwersalność plików 
VOC. Ich obsługa jest bardzo prosta - informacje w nich zawarte całkowicie opisują sposób odtwarzania (w 
strukturze VOC znalazło się miejsce na dane dotyczące częstotliwości próbkowania dźwięku, a takŜe sposobu 
kompresji danych). Tematem tego rozdziału jest stosowanie sterowników dostarczanych przez Creative Labs 
Inc. przy programowaniu obsługi plików zapisanych w tym formacie. 

2.1 STRUKTURA PLIKU VOC

 

Zasadniczo w strukturze pliku VOC wyróŜnić moŜna dwa bloki: blok nagłówka i blok danych. Blok 
nagłówka lo blok przechowujący identyfikator pliku, numer wersji oraz (bardzo waŜne przy programowaniu) 
adres początku bloku danych. Blok danych to naturalnie część pliku przeznaczona do przechowywania 
danych dźwiękowych. MoŜe być on podzielony na kilka, funkcjonalnie róŜnych, części. 

ROZDZIAŁ 2

 

Blok nagłówka

 

 

PołoŜenie względem początku pliku

 

Opis

 

 
 

0-19 
20-21 
21-23 
24-25 

background image

Opis pliku. W tym miejscu przechowywany Jest napis: 
,.Crealive Voice File" oraz bajt o wartości szesnastkowej 1A. 
Przesunięcie początku bloku danych względem początku pliku. Wartość lego stówa wykorzystujemy programując 
obsługę pliku VOC (długość nagłówka dla róŜnych wersji formatu moŜe być przecieŜ inna). 
Numer wersji formatu pliku. Młodszy bajt przechowuje mniej znaczącą część numeru, starszy - bardziej znaczącą. 
Kod identyfikacyjny pliku VOC ułatwiający rozpoznanie pliku zapisanego w tym formacie. Jest równy sumie słowa 
przechowującego numer wersji formatu i słowa o wartości szesnastkowej 1234. 

 

Blok danych 
Ta część pliku podzielona jest na wiele podbloków spełniających róŜne funkcje. Regułą jest tu, Ŝe pierwszy bajt 
podbloku specyfikuje jego typ. W zasadzie programista nie musi wnikać w strukturę poszczególnych podbloków, 
gdyŜ za odpowiednią interpretację zawartych w nich danych odpowiedzialne są funkcje sterowników CT-VOICE i 
CVDSK, opisywane w dalszej części rozdziału. Znajomość funkcji podbloków jest jednak konieczna do pełnego 
wykorzystania moŜliwości dostarczanych programiście. 
A oto jak przedstawiają się dostępne typy podbloków: 

•  Typ O - Terminator (podblok kończący) 

Pojedynczy bajt o wartości O (BLKTYPE=0). Ten podblok kończy cały blok danych. Procedura odtwarzająca 
dźwięk kończy działanie po napotkaniu tego podbloku. 

•  Typ l - Voice Data (dane dźwiękowe) 

Podblok przechowujący spróbkowany dźwięk wraz z opisem. Jego struktura przedstawia się następująco: 

13

 

OBSŁUGA PLIKÓW VOC 

 

Przesunięcie Opis

 

O        Bajt o wartości l uŜywany przy identyfikacji podbloku (BLKTYPE=1). 
l        Trzy bajty opisujące ilość bajtów zajmowanych przez blok (BLKLEN). Liczba bajtów 

przeznaczonych na próbkę to wartość pola BLKLEN pomniejszona o 2. 

4        Bajt, którego wartość informuje o częstotliwości z jaką dźwięk był spróbkowany (SR). 

Przechowywaną w nim liczbę obliczyć moŜna korzystając ze wzoru: 

SR = 256- 1000000/f gdzie f to częstotliwość wyraŜona w Hz. 

5        Bajt opisujący metodę zastosowanej kompresji danych (PACK). Znaczenie róŜnych wartości: 

O - bez kompresji 

1 - kompresja metodą 4-bit 
2 - kompresjo metodą 2.6-bit 
3 - kompresjo 2-bit 

6         Początek ciągu bajtów próbki. 
Typ 2 - Voice Continuation (kontynuacja) 
Podblok przechowujący dane będące kontynuacją zapisanych w podbloku typu l. Ten typ podbloku 
przydatny jest w sytuacjach, gdy długość zapisywanej próbki jest na tyle duŜa, Ŝe 3 bajty pola BLKLEN 
w podbloku l nie okazują się nie wystarczające. 

Przesuni

ę

cie Opis

 

O        Bajt BLKTYPE o warto

ś

ci 2. 

l         Trzy bajty opisujące długość bloku (BLKLEN). 
4         Początek ciągu bajtów próbki. 
Typ 3 - Silence (cisza) 
Podblok definiujący okres ciszy. UŜycie podbloków tego typu moŜe okazać się przydatne tam, gdzie 
zaleŜy nam na oszczędności pamięci dyskowej (takŜe operacyjnej na czas odtwarzania), a próbka 
dźwiękowa zawiera okresy ciszy (przynajmniej względnej). 

ROZDZIAŁ 2

 

Przesunięcie Opis

 

Bajt BLKTYPE o warto

ś

ci 3.

 

Trzy bajty pola BLKLEN. Warto

ść

 tego pola dla tego typu podbloku wynosi zawsze 3.

 

Dwubajtowe pole PERIOD określające czas trwania ciszy wyraŜony w jednostkach cyklu próbkowania 
(odwrotność częstotliwości próbkowania wyraŜonej wHz). 

Bajt pola SR, którego warto

ść

 wyliczamy według wzoru przedstawionego przy opisie typu Voice 

Data.

 

background image

Typ 4 - Marker 
Funkcja podbloku tego typu jest dość specyficzna. Mianowicie sterownik CT-VOICE, podczas odtwarzania dźwięku, 
modyfikuje słowo statusowe wartością przechowywaną w tym podbloku. Badanie słowa statusowego pozwala więc 
sprawdzić, która część bloku danych pliku VOC jest aktualnie odtwarzana. Ułatwić lo więc moŜe realizację 
prezentacji graficzno-dźwięko-wych, gdzie kluczową rolę spełnia synchronizacja dźwięku z wyświetlanym obrazem. 
Przesunięcie Opis

 

O        Bajt BLKTYPE o warto

ś

ci 4.

 

l         Pole BLKLEN o długo

ś

ci trzech bajtów i stałej warto

ś

ci 2.

 

4        Dwubajtowy marker o wartości zawierającej się w przedziale (l.FFFEh). 
Typ 5 - ASCII text (tekst ASCII) 
W zasadzie funkcja tego podbloku ograniczona jest do przechowywania ciągu znaków ASCII. Zastosowanie tego 
typu jest raczej ograniczone (w zasadzie wyłącznie komentarze dodawane do zdigitalizowanych dźwięków). 
Przesunięcie Opis

 

O        Jednobajtowe pole BLKTYPE przechowujące wartość 5. 

15

 

OBSŁUGA PLIKÓW VOC 

 

l        Pole BLKTYPE o długo

ś

ci 3 bajty i warto

ś

ci równej długo

ś

ci ci

ą

gu znaków ASCII 

powi

ę

kszonej o l.

 

4        Początek ciągu ASCII zakończonego bajtem o wartości równej 0. 

Typ 6 - Repeat Loop (początek pętli repetycji) 

JeŜeli zdarzy się, Ŝe jakiś dźwięk chcemy odtwarzać cyklicznie większą ilość razy, to idealnym 
rozwiązaniem wydaje się być zastosowanie podbloku tego typu. Pozwala on na wielokrotne odtwarzanie 
próbki dźwiękowej umieszczonej w podblokach umieszczonych po nim. Przy załoŜeniu, Ŝe plik zawierać 
ma odgłos (np. strzału - do wykorzystania w grze zręcznościowej) powtarzany cyklicznie n razy i 
zapisany w podbloku typu Voice Data, struktura tego pliku wyglądać moŜe jak na rysunku 2. 

 

 

Nagłówek pliku

 

 

 

BInkAmych

   

 

 

——^ Repeat Loop Vaice Dola

 

 

 

 

——— End Repeat Loop Terminator

   

 

Rys. 2 Struktura pliku VOC z pętlą Repeat Loop Struktura podbloku Repeat Loop: 

Przesunięcie Opis

 

Typ bloku (BLKTYPE=6). Pole BLKLEN o wartości 2. 
Dwa bajty przechowujące licznik repetycji (COUNT). Słowo przechowywane w tym polu 
determinuje liczbę powtórzeń. Po napotkaniu podbloku End Repeat Loop sterownik CT-
YOICE powtórzy odtwarzanie następujących po Repeat Loop COUNT razy. Łączna suma 
odtworzeń jest więc równa COUNT+1. Warto wiedzieć, Ŝe jeśli zadana liczba powtórzeń 
równa będzie FFFFh, pętla realizowana będzie bez końca (tzn. aŜ do momentu uŜycia 
funkcji zakończenia operacji - nr 8). 

POZDZIAŁ 2

 

Typ 7 - End Repeat Loop 
Podblok tego typu nale

Ŝ

y umie

ś

ci

ć

 zaraz po ci

ą

gu podbloków, które chcemy obj

ąć

 działaniem p

ę

tli 

zainicjowanej przez pod-blok typu Repeal Luop. 

Przesunięcie Opis

 

Pole BLKTYPE o warto

ś

ci 7. Pole BLKLEN o warto

ś

ci 0. 

2.2 STEROWNIK CT-VOICE

 

Sterownik CT-VOICE (Crealive memory modę Voice driver) jest dołączany, wraz z resztą oprogramowania, do 
kart dźwiękowych serii Sound Blasier. Jego kod znajduje się w pliku CT-VOICE.DRV. Udostępnia on 
programiście podstawowe funkcje związane z obsługą plików formatu VOC. Jego dostępność jest warunkiem 
koniecznym do uruchomienia części oprogramowania. Przykładem moŜe tu być edytor plików dźwiękowych 
VEDIT korzystający podczas zapisu i odtwarzania dźwięku z jego funkcji. Jego umiejscowienie na dysku moŜemy 
zmienić, naleŜy Jednak odpowiednio zmodyfikować wartość zmiennej środowiskowej SOUND. 

SPOSÓB KORZYSTANIA ZE STEROWNIKA 

background image

W pliku CT-VOICE.DRV znajduje się kod sterownika CT-VOICE. Ogólnie rzecz biorąc, do jego funkcji 
odwołujemy się umieszczająprzekazywane mu parametry w odpowiednich rejestrach i wywołując go 
assemblerową instrukcją CALL (daleką). Wpierw musimy naturalnie załadować go do pamięci. Mechanizm 
odwoływania się do jego funkcji przedstawia schematycznie rysunek 3. 

17

 

OBSŁUGA PLIKÓW VOC 

 

Kod naszego programu —— CALL FAR

 

Kod sterówka CT Voice

 

JMP

 

Ci

ą

g ASCII opisuj

ą

cy' sterownik

 

Re|es'ry na stos <— Wykonanie 'unkc|i t-.piestryze s'osu RETF

 

Rys. 3 Mechanizm odwoływania się do funkcji CT-YOICE Kolejność, w jakiej musimy wykonywać procedury 
przygotowujące sterownik do działania, przedstawić moŜna następująco: 

1. Odszukanie pliku CT-VOICE.DRV. JeŜeli nie ma go w aktualnym dla naszego programu katalogu, naleŜy 

skorzystać ze zmiennej środowiskowej SOUND, w której (pod warunkiem, Ŝe uŜytkownik komputera 
umieścił odpowiednią komendę w AUTOE-XEC.BAT) przechowywana jest ścieŜka do katalogu, w którym 
znajdują się sterowniki do karty Sound Blasier. 

2. Sprawdzenie rozmiaru pliku CT-VOICE.DRV- Testowanie wielkości pliku jest tutaj konieczne, gdyŜ 

rozmiary CT-VOICE.DRV dla róŜnych wersji sterownika mogą się dość znacznie róŜnić. 

3. Rezerwacja odpowiedniego obszaru pamięci operacyjnej. 
4. Wczytanie zawartości pliku CT-VOICE.DRV do zarezerwowanego obszaru. NaleŜy zwrócić uwagę, Ŝe 

przesunięcie początku kodu sterownika względem początku zajmowanego przez niego segmentu musi być 
równe 0. 

5. Sprawdzenie, czy wczytany plik zawiera kod sterownika. Najprościej wykonać to wykorzystując fakt, Ŝe w 

oryginalnym pliku CT-VOICE.DRV od pozycji 3 rozpoczyna się ciąg znaków; „CT-VO-ICE". Czynność 
sprawdzenia poprawności przeprowadzić moŜna oczywiście przed wczytaniem do pamięci całego pliku. 
Przykładowa procedura ładowania sterownika CT-V01CE zaimple-mentowana w języku Turbo Pascal 
wyglądać moŜe następująco: 

Const

 

Sterownik w_pamieci:booiean=false:

 

{ czy juŜ zatad3waliśmy sterownik do pamięci} var

 

ROZDZIAŁ 2

 

sterownik:pointer;

 

{ wskaźnik początku kodu sterownika ustawiany przez } {funkcję Przygot:UJ_sterownik}

 

Function Przygc)tuj_sterowmk:boolean;

 

Var

 

s:flle;

 

specyfikacja:string;

 

rozmianseg s,ofs^s:word;

 

Co_jest_sterownik:boo!ean;

 

Function lstnieje[Plłk:stringi:boolean;

 

Var

 

f:file;

 

Begin

 

assigntf.Plik],

 

{Sl-}

 

reset[fl;

 

closetO;

 

{$!+} tstnieje:=[10result=03 End;

 

Begin

 

if Scerowntk_w_pamieci then exit:;

 

{ gdy wcześniej załadowany} specyfikacja:='CT-VOICE.DRV;

 

if not fscniejeCspecyfikacja) then

 

specyfikacja: =getE^vt

l

SaUNa'3+

l

\DRV\CT-VOICE.DRV';

 

{ gdy nie odnaleziony w bieŜącym katalogu } if not IstniejeCspecyfikacja) then

 

begin

 

Przygotui_sterownik:=fa!se;

 

exit {nie udatosię}

 

end;

 

assignts,specyfikacja);

 

reset[s,1);                  { otwieramy znaleziony plik } rozmiar:=fileSize[s);         {pobieramy rozmiar} getmemCsterownik, rozmiar);  { 

rezerwujemy pamięć } b!ockreadEs,sterownik^,filesize[s]];   {odczyt} cioseEs);                          {zamykamy plik} seg_s:=seg[ste^ow^ik

/\

];     

background image

{segment} ofs_s; ^fstsCerownik^ ],     { przesunięcie } toJest_sterownik:=[MemW[seg_s:ofs_s-3]=$5443);

 

{tutaj sprawdziliśmy, czy wczytany plik zawiera kod }

 

{sterownika CT-YOICE} if not toJest_sterownik then begin

 

19

 

OBSŁUGA PLIKÓW VOC 

 

Przygotu)_scerownik;=false;

 

freemem[sterownik,rozmiar]:

 

exic end:

 

Przygotu]^sterownik:=tnJe { Wszystko jest w porządku }

 

End;

 

Do wczytanego z pomocą tej funkcji sterownika odwołać się moŜna (przy załoŜeniu, Ŝe na jego kod wskazuje 
zmienna Sterownik) np. tak: 

as m

 

Tutaj nadajemy wybranym rejestrom odpowiednie wartości catl sterownik

 

Odczytujemy z rejestrów zwrócone przez funkcję wyniki end;

 

Naturalnie, aby nasz program uczynić bardziej uniwersalnym, moŜemy dołączyć do jego kodu zawartość pliku 
CT-VOICE.DRV na etapie konsolidacji. Wtedy zbędna staje się naturalnie jego obecność na dysku 
uŜytkownika. Programujący w języku Turbo Pascal postąpić moŜe wg następującego schematu: 

1. Przygotować plik CT-VOICE.DRV uŜywając dołączonego do kompilatora programu 

BINOBJ.EXE; 

B1NOBJ CT-VOICE.DRV CT-YOlCE.OBJ Yoice

 

2. Przygotować bibliotekę zawierającą procedurę związaną z kodem sterownika: 

Unit VOCDrv; { nazwa przykładowej biblioteki} Interface

 

procedurę voice;

 

Implementatian

 

{$LcI:-voice.obj}

 

procedurę valce;

 

exCernal End.

 

3. Na początku naszego programu, w linii, w której wyszczególniamy uŜywane biblioteki, po instrukcji 

USES dopisać nazwę VOC-Drv (tak nazwaliśmy stworzoną w punkcie 2 bibliotekę). 

ROZDZIAŁ 2

 

Do włączonego w ten sposób kodu sterownika odwołujemy się korzystając z moŜliwości umieszczania w programie 
wstawek
 assem-blerowych: 

asm

 

Wypełniamy parametran odpowiednie rejestry cali far ptrvoice Odczyt wyników z rejestrów end:

 

Jakkolwiek byśmy kodu sterownika nie umieścili w pamięci operacyjnej, jest jeszcze jedna rzecz, o której pamiętać 
musimy. UŜywanie funkcji CT-VOICE wymaga uprzedniej rezerwacji jednego, szes-nastobitowego słowa w 
pamięci na zmienną uŜywaną przez sterownik. Zmienna ta to Ct-Voice Status. Przechowuje ona wartość dodatnią 
całkowitą (w Turbo Pascalu typ Word). imiennej tej sterownik moŜe więc nadawać wartości z przedziału O - FFFFh. 
Modyfikacji jej wartości dokonuje w następujących przypadkach: 

1. Podczas inicjalizacji. Po wykonaniu funkcji 3 sterownik nadaje zmiennej statusowej wartość 0. 
2. Rozpoczynając odtwarzanie/zapis danych dźwiękowych (wartość FFFFh). 
3. W momencie zakończenia operacji odtwarzania/zapisu danych dźwiękowych (nadawana wartość: 0). 
4. Gdy podczas odtwarzania bloku danych pliku dźwiękowego sterownik natrafi na podblok typu Marker, wpisuje 

do zmiennej statusowej przechowywaną w nim wartość. Jak wspomniałem przy opisie struktury pliku formatu 
VOC, uŜywając podbloków typu Marker, moŜemy podzielić plik dźwiękowy na kilka części i informacje 
odczytywane z Ct-Voice Status podczas odtwarzania wykorzystać do synchronizacji dźwięku z działaniami 
programu. 

OMÓWIENIE FUNKCJI STEROWNIKA 

W tej części ksiąŜki omówię udostępniane przez sterownik CT-VO-ICE funkcje. Generalnie rzecz biorąc, kaŜdą z 
nich wywołuje się jednakowo - przez wywołanie dalekim CALL kodu sterownika. Numer funkcji oraz parametry dla 
niej umieszczamy w rejestrach mikroprocesora (w BX numer, w pozostałych parametry). JeŜeli funkcja ma 

OBSŁUGA PUKOW VOC 

21

 

zwracać jakieś wartości, to na ogół odczytujemy je z rejestru AX (w przypadku danej 4-bajtowej z pary 
DX:AX). Istotny jest fakt, Ŝe wartości pozostałych rejestrów (takŜe flagowego) są zachowywane. 
Funkcja B: Pobierz wersję sterownika

 

Wejście:   BX=0 Wyjście:  AH - główny numer wersji 

background image

AL - mniej znacząca część nuinem wersji 

Opis:      Funkcja zwraca numer wersji sterownika. Sprawdzenie wersji jest wskazane, jeŜeli nasz program 

wczytuje sterownik z dysku uŜytkownika. 

Funkcja 1: Ustawienie adresu bazowego

 

Wejście:  BX=1 
AX- adres bazowy Wyjście:   brak 
Opis:     Ta funkcja pozwala na ustawienie portu we/wy uŜywanego przez sterownik do komunikacji z kartą. 

JeŜeli nasz program korzysta z tej funkcji, to powinien wywołać ją jako pierwszą. Dostępne 
wartości adresu bazowego to: 
210h, 220h, 230h, 240h, 250h i 260h (dla kart Sound Bla-ster 2.0 dopuszczalne wartości to 220h 
i 240h). Warto, by program umoŜliwiał uŜytkownikowi wybór adresu. NaleŜy zwrócić uwagę, Ŝe 
wartością domyślną (ustawioną za pomocą zworek na karcie przez producenta) jest 220h. Taki 
leŜ adres będzie uŜywany przez sterownik w wypadku, jeŜeli nasz program nie odwoła się do lej 
funkcji. 

Funkcja 2: Ustawienie numeru przerwania dla DMĄ

 

Wejście:  BX=2 

AX= numer przerwania 

Wyj

ś

cie:   brak 

Opis:      UŜywając tej funkcji program moŜe ustawić numer iinii IRQ uŜywanej przez kartę Sound Blaster do 

sygnalizacji końca transmisji danych. Funkcja ta powinna być (jeśli wystąpiła potrzeba jej 
uŜycia) wywołana zaraz po funk- 

ROZDZIAŁ 2

 

cji numer l (ustawienie adresu bazowego). Wartości, jakie przekazać moŜemy jako parametr, to 2, 3, 5 i 
7. Domyślny numer przerwania IRQ to 7. 

Funkcja 3: Inicjalizacja sterownika

 

Wejście:  BX=3 
Wyjście:   AX = O, gdy wszystko przebiegło pomyślnie, 

1 - błąd karty Sound Blaster 
2 - błąd operacji zapisu/odczytu (źle ustawiony adres bazowy) 
3 - błąd przerwania 

Opis:      Program powinien wywoływać lę funkcję przed skorzystaniem z pozostałych (oczywiście pomijając funkcje 

zmiany adresu bazowego i numeru przerwania IRQ). Zwrócona w rejestrze AX wartość wskazuje, czy 
procedura inicjalizacji przebiegła bezbłędnie. W przypadku wykrycia błędu działanie naszego 
programu powinno być przerwane. Bardzo istotnym jest fakt, Ŝe po inicjalizacji sterownika układ DAĆ 
zostaje włączony (konwersja danych cyfrowych do postaci analogowej). W zasadzie wyłącznie funkcje 
0-2 mogą być wywoływane przed wykonaniem procedury inicjalizacji. Z funkcji inicjalizacji 
korzystamy jednorazowo. 

Funkcja 4: Włącz/Wytocz DAĆ

 

Wejście:  BX=4 
AL=0, aby wyłączyć AL = l, aby włączyć DAĆ Wyjście:   brak 
Opis:     Korzystając z funkcji moŜna włączać i wyłączać układ DAĆ odpowiedzialny za konwersję danych 

cyfrowych do postaci analogowej. Pozostawienie DAĆ w stanie włączonym na czas zapisu dźwięku do 
pamięci powoduje, Ŝe jednocześnie z zapisem dane kierowane są takŜe na wyjście, co powodować 
moŜe powstawanie dodatkowych szumów podczas samplingu. Dlatego przed rozpo- 

OBSŁUGA PLIKÓW VOC 

23

 

częciem zapisu dźwięku (funkcja 7) naleŜy wyłączyć DAĆ. Piszę o wyłączaniu układu DAĆ, 
mimo Ŝe w rzeczywistości chodzi właściwie o odłączenie wzmacniacza na jego wyjściu. Z 
punktu widzenia programisty nie ma to jednak Ŝadnego znaczenia. 

Funkcja 5: Ustaw adres zmiennej statusowej

 

Wejście:  BX=5 

ES:DI == adres słowa w pamięci operacyjnej przeznaczonego na zmienną Ct-Voice Status 

Wyjście:   brak 

Opis:     Sterownik CT-VOICE modyfikuje podczas działania poszczególnych funkcji szesnasiobitowe słowo, 

którego adres moŜe wskazać korzystający ze sterownika program. Omówienie funkcji zmiennej z 

background image

nim związanej znalazło się w rozdziale „Sposób korzystania ze sterownika". 

Funkcja 6: Rozpocznij odtwarzanie dźwięku

 

Wejście:   BX=6 
ES:DI = adres bufora Wyjście:   brak 

Opis:     Funkcja rozpoczyna odtwarzanie dźwięku z wykorzystaniem układu DMĄ. Zaraz po jej wywołaniu 

sterownik wpisuje do zmiennej statusowej wartość FFFFh. Po rozpoczęciu odtwarzania 
sterownik oddaje sterowanie programowi wywołującemu, zaraz po czym nasz program zająć się 
moŜe realizacją innych zadań (w grach i programach prezentacyjnych np. animacją). Dane 
przeznaczone do odtworzenia z pomocą tej funkcji muszą być zapisane w formacie przyjętym 
przez Creative Labs Inc. (opis w rozdziale „Struktura pliku VOC"). Uwaga: para rejestrów 
ES:DI wskazywać musi nie na początek pliku umieszczonego w pamięci, ale na początek Bloku 
Danych tego pliku. Przypominam tu, Ŝe początek błoku danych znaleźć moŜemy odczytując 
słowo o przesunięciu 20 względem początku nagłówka. Badając wartość 

ROZDZIAŁ 2

 

zmiennej o adresie ustawionym funkcja 5 sprawdzać moŜemy, czy plik dźwiękowy jest odtwarzany, czy 
teŜ procedura odtwarzania została juŜ zakończona (wówczas wartość zmiennej statusowej jest równa 0). 
NaleŜy pamiętać, Ŝe jednocześnie odtwarzać moŜna wyłącznie jeden plik i w momencie, gdy chcemy 
rozpocząć odgrywanie następnego, musimy uŜyć funkcji 8 (zatrzymanie operacji). 

Funkcja 7: Rozpocznij zapis dźwięku

 

Wejście:  BX-7 

AX = częstotliwość próbkowania DX:CX = rozmiar bufora 
ES:Dl = adres bufora przeznaczonego na składowanie odczytanych z przetwornika analogowo-
cyfrowego danych 

Wyjście:   brak 
Opis:     Funkcja pozwala na zapis danych z przetwornika A/C do rozpoczynającego się od komórki wskazywanej 

przez parę ES:Dl bufora o rozmiarze zadanym wartościami rejestrów DX:CX. Sterownik CT-YOICE 
uŜywa układu DMĄ, a co za tym idzie, zapis dźwięku odbywa się (podobnie jak odtwarzanie) „w tle". 
Zmienna statusowa zapisywana jest po rozpoczęciu próbkowania wartością FFFFh i, po jego 
zakończeniu, wartością 0. Jednym z parametrów, jakich oczekuje funkcja, jest częstotliwość 
próbkowania podawana w rejestrze AX. Zakres, w jakim mieścić się ona mieścić, jest ściśle związany z 
typem karty. I tak dla Sound Blaster'a w wersjach l.x maksymalna wartość wynosi 12000, a dla karty 
Sound Blaster w wersji 2.0 największa moŜliwa częstotliwość wynosi 15000. W obu przypadkach 
minimalna wartość to 4000. 

Funkcja 8: Zakończenie operacji We/Wy

 

 

Wejście: 
Wyjście: 

BX=8 brak 

 

25

 

OBSŁUGA PLIKÓW VOC 

 

Opis:      Funkcja przerywa odtwarzanie (zapis) dźwięku i nadaje zmiennej statusowej wartość 0. 

Funkcja 9: Zakończenie pracy ze sterownikiem Wejście:BX==9 Wyjście:   brak

 

Opis:      Funkcja deinicjalizuje kartę dźwiękową i wyłącza układ DAĆ, Program powinien wywoływać ją 

kończąc działanie. 

Funkcja 10: Zawieś odtwarzanie dźwięku

 

Wejście:  BX=10 

Wyjście:   AX = O, gdy operacja przebiegła prawidłowo AX = l, gdy Ŝaden plik nie był odtwarzany 

Opis:     Funkcja pozwala na zawieszenie odtwarzania dźwięku (pauza). Wartość zmiennej statusowej 

zachowuje swoją wartość. Jeśli wywołamy tę funkcję w przypadku, gdy procedura odtwarzania 
nie była aktywna, zwróconą w AX wartością będzie l. 

Funkcja 11: Wznów odtwarzanie dźwięku

 

Wejście:   BX=11 Wyjście:   AX = O, gdy wszystko w porządku 

AX = l, gdy odtwarzanie nie zostało zawieszone 

Opis:      Funkcja słuŜy do wznowienia zawieszonego przy uŜyciu funkcji 10 odtwarzania dźwięku. 

background image

Funkcja 12: Przerwij pętlę

 

Wejście:   BX==12 

AX-= l, aby zakończyć natychmiastowo AX== O, gdy chcemy, aby sterownik odworzył 
powtarzany pętlą blok do końca 

Wyjście:   AX=0, gdy operacja przebiegła pomyślnie AX= l oznacza, Ŝe pętla nie była aktywna 

ROZDZIAŁ 2

 

Opis:      Format VOC pozwala na zdefiniowanie p

ę

tli odtwarzania. Podbloki umieszczone mi

ę

dzy podblokiem typu 6 

a podblokiem typu 7 b

ę

d

ą

 odtwarzane cyklicznie zadan

ą

 liczb

ę

 razy. Je

Ŝ

eli wykonywanie p

ę

tli 

chcieliby

ś

my z jakich

ś

 powodów przerwa

ć

, u

Ŝ

yteczna okazuje si

ę

 by

ć

 wła

ś

nie funkcja 12. Zako

ń

czenie 

p

ę

tli mo

Ŝ

e przebiega

ć

 na dwa sposoby: pierwszy (AX=1) polega na tym, 

Ŝ

e sterownik natychmiast 

„przeskakuje" do podbloku nast

ę

puj

ą

cego po p

ę

tli, drugi (AX=0) polega na tym, 

Ŝ

e sterownik ko

ń

czy 

odtwarzanie podbloków obj

ę

tych działaniem p

ę

tli i (nie zwa

Ŝ

aj

ą

c na warto

ść

 licznika repelycji) 

rozpoczyna odtwarzanie nast

ę

pnych danych. 

Funkcja 13: Ustawienie pułapki uŜytkownika

 

Wejście:  BX=13 

DX:AX== adres procedury uŜytkownika Wyjście:   brak 

Opis:     Sterownik CT-VOICE umoŜliwia wskazanie procedury, która wywoływana będzie kaŜdorazowo, gdy 

rozpoczynane będzie odtwarzanie nowego podbloku. Sterownik przekazuje naszej procedurze adres 
nowego podbloku w parze rejestrów ES:BX. Przy jej tworzeniu zadbać musimy o spełnienie kilku 
warunków: 

• kończyć się powinna instrukcją assemblera RET (daleką); 
• zachowywać wartości wszystkich rejestrów (takŜe rejestru flagowego, ale z pominięciem wskaźnika 

przeniesienia); 

• wskaźnik przeniesienia rejestru flagowego procedura powinna zerować, gdy chcemy, by nowy 

podblok był odtworzony (gdy nie chcemy - powinna go ustawić); 

• powinna zerować wskaźnik przeniesienia, gdy nowy podblok jest podblokiem kończącym 

(Terminator). 

JeŜeli chcemy zabronić wywoływania naszej procedury, wystarczy wywołać funkcję 13 zerując 
uprzednio rejestry AX i DX (wskazać adres 0:0). 

27

 

OBSŁUGA PLIKÓW VOC 

 

ZASADY KORZYSTANIA Z FUNKCJI

 

Zanim zaczniemy wykorzystywa

ć

 podane funkcje sterownika CT-V01CE. musimy pozna

ć

 kilka 

elementarnych zasad, jakich powinni

ś

my si

ę

 trzyma

ć

 przy wykorzystaniu go. Najpro

ś

ciej b

ę

dzie, gdy 

zaprezentuj

ę

 schematy, wg których post

ę

powa

ć

 nale

Ŝ

y chc

ą

c rozpocz

ąć

 lub zako

ń

czy

ć

 prac

ę

 ze 

sterownikiem, odtworzy

ć

 próbk

ę

 d

ź

wi

ę

kow

ą

 lub zapisa

ć

 d

ź

wi

ę

k.

 

Rozpoczynanie pracy:

 

1. Rezerwacja pami

ę

ci i wczytanie do niej sterownika.

 

2. Je

ś

li jest to konieczne, modyfikacja adresu bazowego z wykorzystaniem funkcji l.

 

3. Zmiana numeru przerwania IRQ przy pomocy funkcji 2.

 

4. Inicjalizacja sterownika - wywołanie funkcji 3.

 

5. Ustawienie adresu zmiennej statusowej. Oczywi

ś

cie wykonanie czynno

ś

ci 2 i 3 jest opcjonalne - 

je

Ŝ

eli nie zostan

ą

 wykonane, przyj

ę

te zostan

ą

 domy

ś

lne warto

ś

ci numeru przerwania oraz adresu 

bazowego.

 

Zako

ń

czenie pracy:

 

1. Wywołanie funkcji 9 - deinicjalizacja sterownika.

 

2. Zwolnienie pami

ę

ci operacyjnej przydzielonej sterownikowi.

 

Odtwarzanie d

ź

wi

ę

ku:

 

1. Rezerwacja odpowiedniego obszaru pami

ę

ci operacyjnej i wczytanie do niego zawarto

ś

ci pliku 

d

ź

wi

ę

kowego VOC.

 

2. Odczytanie szesnastobitowego słowa o przesuni

ę

ciu 20 wzgl

ę

dem pocz

ą

tku pliku. Jego warto

ść

 

okre

ś

la długo

ść

 nagłówka.

 

3. Wł

ą

czenie układu DA

Ć

 (funkcja 4). Je

Ŝ

eli odtwarzamy d

ź

wi

ę

k zaraz po inicjalizacji sterownika, 

czynno

ść

 t

ę

 mo

Ŝ

na pomin

ąć

.

 

4. Wywołanie funkcji 6 (odtworzenie d

ź

wi

ę

ku) z podaniem w parze rejestrów ES:DI wyznaczonego 

adresu pocz

ą

tku bloku danych.

 

background image

5. Czeka

ć

 na moment w którym zmiennej statusowej nadana zostanie warto

ść

 O (koniec). Podczas 

oczekiwania na zako

ń

czenie odtwarzania nasz program mo

Ŝ

e wykonywa

ć

 inne czynno

ś

ci. 

U

Ŝ

ywaj

ą

c funkcji 10 i 11 mo

Ŝ

emy zatrzymywa

ć

 i wznawia

ć

 wykonywanie procedury 

odtwarzaj

ą

cej a wywołuj

ą

c funkcj

ę

 8 - zako

ń

czy

ć

 jej działanie. Odczytuj

ą

c warto

ść

 zmiennej 

statuso-

 

ROZDZIAŁ 2 

 

wej mo

Ŝ

emy, pod warunkiem uprzedniego wzbogacenia naszego pliku o podbloki typu Marker, sprawdzi

ć

który fragment próbki d

ź

wi

ę

kowej jest aktualnie odtwarzany. 

Zapis dźwięku: 

2. 
3. 
4. 

Rezerwacja pamięci przeznaczonej na bufor danych. Wyłączenie układu DAĆ (funkcja 4). Wywołanie funkcji zapisu 
danych (nr 7). Oczekiwanie na zakończenie zapisu. Proces moŜemy przerwać z pomocą funkcji 3. Osiągnięcie końca 
bufora lub koniec zapisu spowodowany wykonaniem funkcji 8 sterownik sygnalizuje nadaniem zmiennej statusowej 
wartości 0. Jako ostatnią czynność uwaŜać moŜna zapis spróbkowanego dźwięku do pliku. NaleŜy tu pamiętać, Ŝe 
utworzony przez sterownik blok danych poprzedzić naleŜy spreparowanym odpowiednio nagłówkiem. 

BIBLIOTEKA VOC.TPU 

W rozdziale tym prezentuję wersję źródłową przykładowej biblioteki gotowej do skompilowania przy uŜyciu 
kompilatora Turbo Pascal w wersji 6.0 lub nowszej- Posiadacze starszych wersji mogą w prosty sposób 
zmodyfikować tekst biblioteki (przez zamianę wstawek assemblerowych typu ASM na INLINE). Przy tworzeniu 
procedur główny nacisk postawiłem na czytelność i zrozumiałość. PoniewaŜ pełen tekst biblioteki znajdzie Czytelnik 
na dołączonej do ksiąŜki dyskietce w pliku VOC.PAS, listing zamieszczony w ksiąŜce pozwoliłem sobie przerywać 
komentarzami. unit;\/OC: 

interface

 

typeVRodzajBledu=tVOk,

 

YBrakSterowniks,

 

YZaMaloPamieci,

 

YZłyNaglowekSterownika,

 

YBIadInicjelizacji,

 

YUszkodzonaKarta,

 

YBIadWeWy,

 

VZIyNumepPrzerwaniaD!aDMA,

 

YBIadZwolnienia,

 

YBrakPIiku,

 

VToNieVOC,

 

OBSŁUGA PLIKÓW VOC

 

29

 

Typ VRodzajBledu jest typem wyliczeniowym i okre

ś

la wi

ę

kszo

ść

 bł

ę

dów, jakie mog

ą

 pojawi

ć

 si

ę

 podczas 

realizacji zaimplemetowanych w bibliotece procedur. Poni

Ŝ

ej zadeklarowana została zmienna VOC_Blad typu 

VRodzajBledu, której zadaniem b

ę

dzie przechowanie nadanej w trakcie realizacji procedur (funkcji) warto

ś

ci. 

Naturalnie taki sposób opisania bł

ę

dów (typem wyliczeniowym) mo

Ŝ

e si

ę

 komu

ś

 wydawa

ć

 nienaturalny, ale 

moim zdaniem, przyczyni si

ę

 on znacznie do zwi

ę

kszenia przejrzysto

ś

ci prezentowanego tekstu. 

var

 

VOC_Blad:VRod2ajBledu;

 

\/OCSCatus:word;

 

\/SterownikZainstalowany;Boo[ean;

 

VDIugoscNag!owka:byte;

 

Oprócz zmiennej VOC_BLAD wśród globalnych zmiennych udostępnianych przez bibliotekę znalazły się: 
VOCStatus (zmienna statusowa, której lokalizację w RAM wskaŜemy sterownikowi), VSterownik-
Zainstalowany typu Boolean (informacja o tym, czy CT-VOICE został juŜ wczytany do pamięci operacyjnej) 
oraz VDlugoscNaglowka (jak sama nazwa wskazuje, przechowamy tam wielkość potrzebną przy wyliczaniu 
pozycji bloku danych pliku VOC). 

procedure   VlnicjujSterownik[Port,lrq:word3;

 

function    VWersjaSterownika:word;

 

procedurę YWylaczDAC:

 

procedurę YWIaczDAC;

 

procedurę   VOdczyta|PlikVOC(var bufor:pointer;spec:string);

 

procedurę   VZarezerwujPamiec(var gdzie:painter;ile:longint);

 

procedurę  VZwolnijPamiectgdzie:pomter);

 

function    VOpisBledu:string;

 

background image

procedurę  YDeinstatuJSterownik;

 

procedurę  VOdtworzVOC[buror;pointer);

 

procedurę  VOdtwor'zJeszczeRaz(bufor;poinCer];

 

procedurę  VZakonczOperacjeVOC;

 

procedurę VPauzaVOC;

 

procedurę  VKanCynuuiOdtwarzanieVOC;

 

procedurę  VPrzerwijPet!eVOC[iak:word);

 

procedurę  VZapiszBlokEczesc:word;dlug;word;p'poincer);

 

procedurę  VOdtworzBloktwsk:pointer];

 

ROZDZIAŁ 2

 

W cz

ęś

ci implementacyjnej zadeklarowałem u

Ŝ

ycie dwóch zmiennych globalnych: VSTEROWNIK (przechowa 

wskazanie na obszar zajmowany przez kod CT-VOICE) oraz VDawnaProceduraWyjscia (wykorzystywana do 
przechowania zastanej warto

ś

ci ExitProc). Zmienna VSTEROWNIK jest u

Ŝ

ywana przez wszystkie (za wyj

ą

tkiem 

VOpisBledu) wymienione w cz

ęś

ci interface procedury i funkcje. Nale

Ŝ

y zwróci

ć

 uwag

ę

Ŝ

e przed wykonaniem 

procedury VInicjujSte-rownik ma warto

ść

 nieokre

ś

lon

ą

, a co za tym idzie, niedopuszczalne wtedy jest wykonanie 

jakiejkolwiek innej funkcji (procedury). irnplemenCaton 

uses dos.crt;

 

var

 

Vsterawnik:pointer;

 

VDawnaProceduraWyj'scia:pointer;

 

function lsCnieJe[Plik:string):boolean:

 

var

 

f:file;

 

begin

 

assign[f,P!ik);

 

{$!-} resetCfl;

 

closetf];

 

{$!+} lstnieje:=(!OresulC=03 end;

 

procedurę VZarezerwujPamiec(var gdzie:pointer;i!e:longint);

 

var

 

rregisters:

 

ilasc:word;

 

begrn

 

i

 

ilosc:=[ile+15) shr4; {ile paragrafów} rah:=$48; {numer ustugi DOS-u } rbx;=ilosc;

 

MsDosCr);

 

if Crbx<>ilosc) Chen VOC_blad:=VZaMaloPamieci e!se begin

 

VOC_blad:=VOk;

 

gdzie:=pt;rtr.ax,G) end

 

end:

 

31

 

OBSŁUGA PLIKÓW VOC 

 

Procedura YZarezerwujPamiec wywoływana jest z pozostałych w celu allokacji zadanego obszaru pami

ę

ci 

operacyjnej. Jej parametry to zmienna typu Pointer, pod jak

ą

 postawione zostanie wskazanie 'na 

zarezerwowany fragment RAM, oraz zmienna typu Longint specyti-kuj

ą

ca rozmiar potrzebnego obszaru. 

Procedura allokuje wielokrotno

ść

 16 bajtów. W przypadku wyst

ą

pienia bł

ę

du zmiennej VOC_BLAD nadaje 

warto

ść

 VZaMaloPamieci. Zdefiniowana poni

Ŝ

ej procedura VZwolnij Pami

ęć

 zwalnia wskazywany przez 

parametr obszar. 

prxedureVZwolni)P3miecCgdzie:point,er);

 

var

 

p:registers;

 

besm

 

nah:=$49;

 

nes;=segtgdzie^3:

 

msdostr);

 

if (rax=7)or[nax=93 Chen VOC_blad:=VBIadZwolnienia end;

 

procedur

ę

 VlnicjufSterownik(Port,lrq:word];

 

var

 

s:file;

 

specyfikacja:strlng;

 

seg_SiOfs_s:word, status_seg,status_ofs:word;

 

toJest_sterownik:bDolean;

 

wynik:word;

 

begin

 

if YSterownikZainstalowany then exit;

 

specyfikacja: ='0^0^. DRV

1

;

 

background image

if not IstniejeCspecyfikscja) then

 

specyfikacja: =getEnv('SOUND•)+

l

\DRV\CT-VOICE.DRV':

 

if not IsCnieJsCspecyfikscja] then

 

begin

 

VQC_Blad:=VBrakSterownika:

 

exit

 

end:

 

assignis,specyfikacja), reset(s,1);

 

VZarezer'wuJPamiec[Vsterownik,fileSi2e(sl);

 

ifVOC_blad<>VOkthenexit;

 

blockreadts.Ysterownik^.filesizets]);

 

closets);

 

seg_s:=seg[Vste^

t

ownik

/

');

 

ofs_s:=ofs(Vsterownik^];

 

tOJest;_sCerownik:=[MemW[seg_s:ofs_s+3]=$5443];

 

ROZDZIAŁ 2 

S

 not tOJest_sterown ik Chen begir VCC^b[ad;=VZ!yNaglawekSt;erownika;

 

exit end, if porcoO then asm

 

mov bx,1 mov ax,port callVst;erowmk end;

 

if irq<>0 then asm

 

movbx,2 mov ax,irq całłYstercwnik end, StaCus_seg:=segtVOCstaCus);

 

Stat:us_ofs:=ofs[VOCsC3tus];

 

asm

 

mav bx,3 cali Ysterownik mov wynik,ax mov bx,5

 

mov es,status_seg movdi,staCusJ)fs cali Ysterownik end;

 

case wynik of

 

0:VOC_blad:=VOk;

 

1: VOC_btad:=VL)szkodzonakarta;

 

2: VOC_blad:=VBladWeWy;

 

3: VOC_blad:=VZIyNumerPrzerwaniaDlaDMA

 

end;

 

end;

 

Procedura VInicjujSterownik spełnia kluczową rolę w bibliotece. Jej działania polega na wczytaniu kodu sterownika 
i jego inicjalizacji. Dodatkowo, podając parametry róŜne od O moŜemy spowodować zmianę adresu bazowego i 
numeru uŜywanego podczas transmisji przerwania. Końcowy fragment procedury odpowiedzialny jest za wskazanie 
sterownikowi lokalizacji słowa przeznaczonego na zmienną statusową. Zdefiniowana poniŜej funkcja 
YWersjaSterownika zwraca wartość typu WORD, której bardziej starszy bajt odpowiada bardziej znaczącej części 
numeru wersji, a młodszy - mniej znaczą- 

33

 

OBSŁUGA PLIKÓW VOC 

 

č

ej. Procedury VWyIaczDAC i YWIaczDAC odpowiadaj

ą

 za wł

ą

czanie i wył

ą

czanie układu konwersji DA

Ć

function YWersjaSte równika,vwrd;

 

var

 

begin

 

asm

 

mov bx,0

 

cali Ysterownik

 

niovw,ax

 

end;

 

VWer'SjaSterownjka:=w

 

end;

 

procedur

ę

 YWylaczDAC;

 

assembler;

 

asm

 

mDV bx,4 mov al,0 cal!Vscerownik

 

end;

 

procedurę VWIaczDAC;

 

assembler;

 

asm

 

movbx,4

 

mov al,1

 

całłYsterownik end;

 

Ni

Ŝ

ej znalazła si

ę

 definicja procedury VOdczytajPlikVOC. Oczekuje ona podania zmiennej wska

ź

nikowej, 

której zostanie nadana warto

ść

 odpowiadaj

ą

ca wskazaniu zajmowanego przez plik obszaru oraz podania 

specyfikacji pliku d

ź

wi

ę

kowego, który chcemy wczyta

ć

. Sprawdzenie, czy mamy do czynienia z plikiem 

background image

formatu VOC polega tu na przyrównaniu słowa zło

Ŝ

onego z dwóch pierwszych bajtów pliku do 7243h 

(znaki „C" i „r" z napisu „Creative Voice File"). procedur

ę

 VOdczycajPlikVOCCvarbufarpointer;spec;st ring]; 

var

 

plik_VQC:file;

 

rozmiar_pliku:longlnt;

 

Blokow:wor'd;

 

wynik:word;

 

miejsce.pointer;

 

bogi

ń

 ifnoC IsCmejeEspec] then

 

ROZDZIAŁ 2

 

beoHi VOC_Blad-=VBrakPliku:

 

exit end;

 

assign(plik_VOC.spec]:

 

re5et;(p!ik_VOC,1);

 

rozmiar_pliku:=fileSize(plik_VOC);

 

VZarezerwuiPamiecEbufor,rozmiarJ3liku3;

 

if VOC_blad< >VOk chen exit:

 

Bloków: =0:

 

repeat

 

miejsce: =Pt^[seg(bufo^

A

)+Blokow

ł

4096,ofs(bufo^'

^

]];

 

blockread[plik_VOC,miejsce" $FFFF,wynik);

 

lnc(B!okow3 untilwynik=0;

 

close(plik_VOC]:

 

if MemW[seg(bufo^•

^

]:ofs(bufo^-

^

)]<>$7^43

 

then VOC_blad:=VToNieVOC;

 

yDlugoscNagiawki^MemCsegtbufor^hofstbufor^l+^a]

 

end;

 

No i najwaŜniejsze - odtworzenie wczytanego pliku - procedura VOdtworzVOC. Warto zwrócić uwagę, Ŝe 
przesunięcie w adresie segmentowym przekazywanym sterownikowi powiększane jest o rozmiar nagłówka 
wczytanego pliku. 

procedur

ę

 VOdtwor2VOC(bufor:poini;er);

 

var

 

buf_s,buf_o:word;

 

begin

 

bu^s^segróufor^ł;

 

bufJ^ofsCbufor^+YDIugascNaglowka;

 

YWIaczDAC;

 

as m

 

movbx,6 moves,buf_s movdi,buf_o całłYsterownik

 

end 

end;

 

W bibliotece zdefiniowałem takŜe drugą procedurę odtwarzającą wskazywaną zadaną zmienną próbkę 
(VOdtworzJeszczeRaz). Jedyna róŜnica między nią a procedurą VOdtworzVOC polega na pominięciu w 
VOdtworzJeszczeRaz włączania układu DAĆ. 

procedurę VOdt;worzJeszczeRaz[bufor:pointer);

 

var

 

but s,but o:word;

 

OBSŁUGA PUKÓW VOC 

35

 

buf_s;=seg[bufor^);

 

buf o^ofstbufor^l+YDlugoscNaglowkE aem

 

movbx,6

 

moves,buf_s

 

movdi,buf_o

 

cali Ysterownik end

 

end; 

Procedura YZakonczOperacje moŜe być uŜywana zawsze, gdy chcemy zakończyć odtwarzanie lub zapis 
dźwięku. Procedury VPauza-VOC i VKontynuujOdtwarzanieVOC słuŜą do chwilowego zawieszania i 
wznawiania odtwarzania. 

procedur

ę

 VZakonczOperacjeVOC;

 

assembier;

 

as m

 

mov bx,8 ca!!Vsterownik

 

end;

 

procedur

ę

 VPauzaVOC:

 

background image

var

 

odp;word;

 

begin as m

 

mov bx, 10 cali VsCerownik movodp,ax end;

 

ifodp=1 then voc^b[ad:=VSBNieOdCwarzal end;

 

procedur

ę

 VKontynuujOdtwarzanieVOC;

 

var

 

odp:wor'd:

 

begin as m

 

mov bx, 11 cali Ysterownik movodp,ax end;

 

if odp=1 chen VOC_blad:=VSBNieOdCwarzal end;

 

procedur

ę

 VPrzerwiJPetteVOCCiak:word);

 

ROZDZIAŁ 2

 

 

begin

 

end;

 

odp:word;

 

i [fnot[jakin[0,1I]theniak:=1;

 

asm

 

mav bx,12

 

movax,jak

 

cali Ysterowmk

 

movodp,ax end:

 

ifodp=1 thenVOC_blad:=VNieByloPetli

 

W bibliotece znalazła si

ę

 tak

Ŝ

e procedura zapisu d

ź

wi

ę

ku do pami

ę

ci operacyjnej. Jako parametrów oczekuje ona 

dwóch wielko

ś

ci typu WORD okre

ś

laj

ą

cych cz

ę

stotliwo

ść

 próbkowania d

ź

wi

ę

ku oraz rozmiar bufora oraz wskazania 

na bufor przeznaczony na zapis danych. Nale

Ŝ

y pami

ę

ta

ć

Ŝ

e po dokonaniu zapisu we wskazanym buforze 

znajdowa

ć

 si

ę

 b

ę

dzie wył

ą

cznie blok danych i, przed ewentualnym zapisem do pliku, nale

Ŝ

y poprzedzi

ć

 go 

nagłówkiem. 

procedurę VZapiszBlok[czest:word;dlug:ward;p: pointę?];

 

begin

 

ifczest<400Cthen begin

 

VOC_blad:=VZIaCzestotliwosc;

 

exit end:

 

YWylaczDAC;

 

asm

 

movbx,7 mavax, cześć movdx,0 movcx,dlug les di,p callVsterownik end end;

 

PoniŜsza procedura stanowi pewne uzupełnienie zestawu narzędzi słuŜących odtwarzaniu dźwięku. VOdtworzBlok 
pomija wielkość nagłówka przy wskazywaniu sterownikowi bufora z danymi, a co za tym idzie, doskonale nadaje się 
do odgrywania zapisanego z uŜyciem VZapiszBlok bloku danych. 

procedur

ę

 VOdtworzBIok(wsk:pointer); 

var

 

37

 

OBSŁUGA PLIKÓW VOC 

 

przechowa) :byte:

 

begin

 

przechowaj :=VDIugoscNaglowka;

 

V0ugosc^aglowka:=0;

 

VOdtworzVOC(wsk);

 

VDIugoscNaglowka:= przechowaj

 

end

 

Funkcja VOpisBledu pełni rol

ę

 pomocnicz

ą

. Zwraca ła

ń

cuch ASCII opisuj

ą

cy bł

ą

d zwi

ą

zany z 

aktualn

ą

 warto

ś

ci

ą

 zmiennej VOC_BLAD.

 

function VOpisBledu:sCring;

 

begin

 

case VOCJ)lad of

 

V0k:

 

VapisBledu;='Ok.';

 

YBrakSterownika:

 

VopisBledu:='Nie znaleziono pliku CT-VOICE.ORV';

 

VZaMaloPamieci:

 

VopisBledu:='ZbyC mało pamięci operacyjnej.':

 

VZIyNaglowekSterownika:

 

VapisB!edu:='Zty nagłówek CT-VOICE.DRV';

 

YBIadInicjalizacji:

 

VopisBledu:='Bład podczas inicjalizacji sterownika.';

 

background image

VL)szkodzonaKarta:

 

VopisBledu:='Btędne działanie karty dźwiękowej.';

 

VBladWeWy:

 

VopisBledu:='Btad podczas zapisu/odczytu z portów karty,';

 

YZłyNumerPrzerwaniaDlaDMA:

 

VopisBledu:='Niewłaściwy numer przerwania IRQ.';

 

YBIadZwolnienia:

 

VopisBledu:='Błąd zwolnienia pamięci.';

 

YBrakPtiku:

 

VopisBledu;='BrBk pliku .VOC.';

 

VToNieVOC:

 

VopisBledu:='Błędny nagłówek pliku .VOC,';

 

VSBNieOdtwarzal:

 

VopisBledu:='śaden plik nie byt odtwarzany

1

;

 

VNieByiaPecli:

 

VopisBledu:='Nie było aktywnej pętli.';

 

VZtaCzestotliwosc:

 

VopisBledu:='Zta częstotliwość próbkowania,' end end;

 

Ostatni

ą

 publiczn

ą

 procedur

ą

 biblioteki jest VDeinstalujSterownik. Mo

Ŝ

na j

ą

 wywoła

ć

 w programie, by 

deinicjałizowa

ć

 CT-VOłCE i zwolni

ć

 zajmowan

ą

 przez jego kod pami

ęć

. Jego wywołaniem 

ROZDZIAŁ 2 

w przypadku zako

ń

czenia działania programu zajmie si

ę

 nowa procedura wyj

ś

cia - VOCExit. 

procedur

ę

 YDanstalujScerownik;

 

begm

 

(f YSterownikZainsCalowsny then begin as m

 

mov bx,9 całłYsterownik end;

 

VZwolni|PamiectVsterownik);

 

VSt:erownik2ainsCalowany:=false end end;

 

($F+) procedurę VOCExtt:;

 

begin

 

YOeinscalujScerownik;

 

ExitProc:==VDawnaProceduraWyjscia end;

 

{SF-}

 

begin

 

VDawnaProceduraWyJscia;=ExitProc;

 

ExitProc:=@VOCExit:

 

VSterown ikZainsCalowany: = False;

 

VOCstaCus:=0;

 

VOC 81ad;=VOk

 

end. 

PRZYKŁADY

 

W poprzednim rozdziale zaprezentowałem kompletn

ą

, gotow

ą

 do u

Ŝ

ycia bibliotek

ę

 funkcji i 

procedur u

Ŝ

yteczn

ą

 przy programowaniu obsługi plików VOC. Aby bardziej jeszcze rozja

ś

ni

ć

 

zasady korzystania ze sterownika, przedstawi

ę

 przykład programu wykorzystuj

ą

cego jego usługi. 

Jego zadaniem b

ę

dzie odtwarzanie zawarto

ś

ci zadanego parametrem pliku formatu VOC:

 

program Zagraj;

 

{$M 16000,0,50000} uses crt.YOC:

 

var 

b:pointer;

 

39

 

OBSŁUGA PLIKÓW VOC 

 

procedurę koncZJesli_zle;

 

begin

 

lfVOC_Btad<>VOkthen begin

 

wnteln[VapisBledu);

 

halt end end:

 

begin

 

if paramcount<>1 then begin

 

wricelnC UŜycie: ZAGRAJ plik');

 

wriceinfplik - plik w formacie VOC');

 

halt end;

 

Vlnic}ujSCerownik(0,0);

 

kończ (esli_zle:

 

VOdczytaJPlikVOC[b,paramstrE13);

 

koncz_jesli_zle;

 

background image

VOdCworzVOCEb);

 

wricelnrOdtwarzam. Wciśnij ESC aby przerwać...');

 

repeat untii tkeypressed3orfVOCStatus=0);

 

if keypressed then VZakonczQperacjeVOC

 

end,

 

Wspomniałem, Ŝe parametrami dla VInicjujSterownik mogą być (w przypadku, gdy ustawienia karty nie są 
standardowe) adres bazowy i numer przerwania IRQ. Warto byłoby, aby nasze programy, zanim zainicjują 
działanie sterownika, sprawdziły je. Jednym ze sposobów jest odczytanie wartości zmiennej środowiskowej 
BLASTER. Oczywiście w przypadku, gdyby w pamięci komputera, na jakim uruchomiony został nasz 
program, nie znajdowała się zmienna o tej nazwie, moŜemy np. zwrócić się z zapylaniem do uŜytkownika 
(sposób praktykowany - przekonać się o tym moŜna przyglądając się kilku popularnym grom). Innym 
sposobem jest badanie kaŜdego z portów i przerwań. Ta metoda zostanie omówiona w dalszej części ksiąŜki. 
PoniŜej przedstawiam proste funkcje zwracające interesujące nas wartości po uprzednim odczytaniu ich ze 
zmiennej BLASTER: 

function adres_bazowy:word;

 

var

 

lancuch.string;

 

pozycja:byte;

 

begin

 

ROZDZIAŁ 2

 

ła

ń

cuch: =GeCEnvt'8LAS7OT:

 

if Uancucho"] then pozycjB:=pos['A', ła

ń

cuch)

 

elsepozycja:=0, if pozycJaoOchen begm

 

ad^es_bazowy:=256

fr

[o^d[l6^cuch[pozycja-1]]-4B] +^6

ł

[o^d[lancuch[pozyc^a+^])-4B] +ord([ancuch[pozycja+3])-

48 end else adres_bazowy:=$220 end;

 

function numer_IRQ:byte' var

 

lancuch:string;

 

pozycja;byte;

 

begin lancuch:=SetEnv['BLĄSTER'];

 

if [lancucho") Chen pozycja:=pas(T.lancuch] else pozycja:=0;

 

ifpozycjaoOthen

 

numer_IRQ:=ord[lancuch[pozycja+1]]-48 else

 

numer_IRQ:=7 end;

 

Bywa, Ŝe chcielibyśmy, aby uŜytkownik programu nie miał dostępu do uŜywanych przez program plików VOC (tzn. 
nie mógł przez np. prostą podmianę zmienić efektów dźwiękowych w naszej grze). Najprostszym sposobem wydaje 
się wtedy zmiana ich nazwy i, częściowo, struktury [np. obcięcie nagłówka i pozostawienie tylko bloku danych) lub 
np. „sklejenie" ich w jeden plik i przechowywanie przez program połoŜenia poszczególnych „składowych". W 
przypadku niewielu plików za sposób moŜna takŜe uznać połączenie ich zawartości z kodem naszego programu. 
PoniŜej prezentuję przykładowy listing. Program odtwarza włączone na etapie konsolidacji dane dźwiękowe zapisane 
w formacie VOC. Korzysta takŜe ze skonsolidowanego ze swoim kodem sterownika. Podobny programik moŜe na 
przykład znaleźć zastosowanie przy tworzeniu plików wsadowych (podczas działania których komunikaty będą np. 
wypowiadane za pośrednictwem SB). 

program p1;

 

usesVOCDrv;

 

{tekst biblioteki zamieszczony przy opisie sposobu

 

obsług/

ą

 PLIKÓW VOC 

korzystane ze sterownika CT-WICE}

 

{$Lexample1,obj} procedurę Dźwięk:

 

external:

 

kod procedury Dźwięk to zawartość pliku example1.obj utworzonego w następujący sposób:

 

BINDBJ example1.voc example1,obj Dźwięk

 

}

 

var

 

Status ;word:

 

segm, przesuń, wynik:ward;

 

procedurę Odtworz_zaw_pliku(si0iword];

 

begin

 

o:=o+$1A:

 

{ długość nagłówka dla tej wersji formatu }

 

asm

 

mov bx,6

 

mov es,s

 

mov di,o

 

cali far ptrvoice end;

 

background image

nepeat unti Status =0 {czekamy na koniec} end;

 

begin

 

Status =0;

 

WriteInCBum bum bum,..'3; {informacja } asm

 

movbx,3

 

cali far ptrvcice

 

nnov wynik,ax end;

 

ifwynikoOthen

 

begin wnteInfBłąd podczas inicjalizacji sterownika.');

 

halt end;

 

segm:=segEStatusl;

 

przesuń; =ofs[Status], asm

 

mov bx,5

 

mov es,segm

 

mov di, przesuń

 

ROZDZIAŁ 2

 

end.

 

OdCworz_zcWJ^Ikutseg[D2wiek),of5EDzwiek]);

 

as m

 

movbx,9

 

cali far ptrvai[:e end end. {iju

Ŝ

}

 

2.3 OBSŁUGA WIĘKSZYCH PLIKÓW

 

Praca z plikami formatu VOC przy uŜyciu standardowego sterownika CT-VOICE jest wygodna, ale nie 
pozbawiona wad. Za podstawową naleŜy uznać fakt, Ŝe niemoŜliwe jest odtworzenie pliku o rozmiarach 
przekraczających wielkość dostępnego do zaallokowa-nia obszaru RAM. Poza tym konieczność ładowania pliku 
do pamięci przed odtworzeniem zmusza nas do walki o niemal kaŜdy bajt. Kłopotliwy Jest teŜ zapis dźwięku o 
nieco większej długości. Okazuje się, Ŝe wśród rozpowszechnianych wraz z kartą plików znajduje się 
CTVDSK.DRV, zawierający kod sterownika (Creative Disk Double-Buffering Voice Driver), przy uŜyciu którego 
moŜemy odgrywać pliki bezpośrednio z dysku i zapisywać prosto do pliku (!). Pomysł jest prosty - sterownik 
wykorzystuje zdefiniowany wcześniej bufor dzieląc go na dwie części, do jednej „doczytując" kolejne partie pliku, 
z drugiej zaś odtwarzając uprzednio „doczytane". Funkcje nowego sterownika niewiele róŜnią się od funkcji 
standardowego CT-VOICE. Oto opis kilku z nich, niezbędnych do zapisu i odtwarzania danych dźwiękowych 
wprost z dysku, a nie udostępnianych (lub wymagających odmiennych parametrów) przez kod zawarty w CT-VOI-
CE.DRV: 

Funkcja 3: Inicjalizacja sterownika

 

Wej

ś

cie:  BX=3

 

AX= rozmiar bufora Wyjście:   AX - kod błędu 
O - wykonanie bezbłędne 

1 - błędne działanie karty Sound Blaster 
2 - zły adres bazowy (błąd odczytu/zapisu) 
3 - zły numer przerwania IRQ 

43

 

OBSŁUGA PLIKÓW VOC 

 

Opis:     Jednym z wymaganych przez funkcję parametrów jest - podawany w rejestrze AX - rozmiar bufora. 

Parametr len rozumiany jest jako ilość bloków wielkości 4 KB, składających się na bufor. Tak 
więc. jeśli na potrzeby bufora allokujemy 32 KB, rejestrowi AX nadajemy wartość 8. Jak juŜ 
wspomniałem, zdefiniowany bufor podzielony zostanie na dwie równe części. MoŜna więc 
powiedzieć, Ŝe w AX podajemy rozmiar kaŜdej z tych dwóch części będący wielokrotnością 2 
KB. 

Funkcja 5: Ustawienie adresu zmiennej statusowej

 

Wejście:  BX=5 

DX = numer segmentu z adresu zmiennej AX == przesunięcie wewnąirzsegmentowe zmiennej 

Wyj

ś

cie:   brak

 

Opis:      Wywołując tę funkcję wskazujemy sterownikowi lokalizację szesnastobitowego stówa 

przeznaczonego na zmienną statusową. Sterownik podczas pracy nadaje lej zmiennej róŜne 
wartości. Badając je moŜemy stwierdzić, na jakim etapie działania znajduje się procedura 
odtwarzania dźwięku. Dokładniejszy opis znajdzie Czytelnik we wcześniejszej części ksiąŜki 
(opis CT-VOICE). 

background image

Funkcja 6: Odtworzenie zawartości pliku

 

Wej

ś

cie:  BX=6

 

AX = uchwyt pliku 

Wyjście:   AX - informacja o tym, czy wystąpił jakiś błąd (O oznacza wykonanie pomyślne) 

Opis:      Działanie funkcji polega na rozpoczęciu odtwarzania pliku z uŜyciem zainicjowanego wcześniej 

bufora. Parametrem funkcji jest uchwyt pliku - wielkość zwracana przez usługi DOS po jego 
otwarciu. Odgrywanie pliku z uŜyciem tej funkcji wiąŜe się z cyklicznymi odczytami z pamięci 
masowej. NaleŜy zwrócić uwagę, Ŝe częstotliwość odwołań do dysku jest odwrotnie 
proporcjonalna do rozmiaru bufora. 

ROZDZIAŁ 2 

Funkcja 7: Zapis dźwięku do pliku

 

Wej

ś

cie:   BX=7 

AX= uchwyt pliku DX= cz

ę

stotliwo

ść

 

Wyjście:   AX - informacja o ewentualnym błędzie (O - wykonanie bezbłędne) 

Opis:      Wywołanie tej funkcji rozpoczyna zapis do pliku danych d

ź

wi

ę

kowych pobieranych z przetwornika 

analogowo-cyfrowego z cz

ę

stotliwo

ś

ci

ą

 zadan

ą

 przez warto

ść

 rejestru DX. 

Funkcja 14: Informacja o błędzie

 

Wej

ś

cie:   BX==14 Wyj

ś

cie:   DX - kod bł

ę

du DOS

 

AX- kod bł

ę

du sterownika

 

Opis:     Funkcję wywołujemy w wypadku, gdy próba wykonania innej funkcji sterownika nie powiodła się. Badając 

zwrócone wartości moŜemy poznać przyczynę powstania błędu. 

Funkcja 15: Inicjalizacja bufora

 

Wejście:   BX=15 

DX = numer segmentu początku bufora 

AX = przesunięcie wewnątrz segmentu początku bufora 

CX = rozmiar bufora w 4 KB blokach Wyj

ś

cie:   brak

 

Opis:      Wykonanie funkcji jest konieczne przed próbą odtworzenia jakiegokolwiek pliku. Wywołując ją 

wskazujemy sterownikowi miejsce w pamięci operacyjnej, gdzie ulokowaliśmy bufor uŜywany przy 
odtwarzaniu. 

Skoro poznaliśmy juŜ nowe funkcje sterownika, czas na zapoznanie się z podstawowymi zasadami korzystania z 
niego: 

45

 

OBSŁUGA PLIKÓW VOC 

 

Rozpocz

ę

cie pracy: 

1. Otwarcie pliku CTVDSK.DRV, sprawdzenie jego rozmiaru i allo-kacja niezb

ę

dnego obszaru pami

ę

ci 

operacyjnej. 

2. Wczytanie kodu sterownika i zamkni

ę

cie pliku. 

3. Inicjalizacja bufora u

Ŝ

ywanego przez sterownik (allokacja pami

ę

ci i u

Ŝ

ycie funkcji 15). Je

Ŝ

eli czynno

ść

 

ta nie poprzedzi inicjali-zacji sterownika, driver sam zarezerwuje bufor w pami

ę

ci operacyjnej. 

4. Modyfikacja adresu bazowego u

Ŝ

ywanego przez sterownik. 

5. Zmiana numeru przerwania IRQ wykorzystywanego podczas transmisji danych. 
6. Inicjalizacja sterownika (funkcja numer 3). 
7. Wskazanie lokalizacji zmiennej statusowej. Przed inicjalizacj

ą

 sterownika nasz program mo

Ŝ

e tak

Ŝ

przej

ąć

 kontrol

ę

 przerwania 24h (obsługa bł

ę

dów krytycznych). Dodatkowo nale

Ŝ

y pami

ę

ta

ć

Ŝ

e w 

przypadku wyst

ą

pienia jakiego

ś

 bł

ę

du u

Ŝ

y

ć

 mo

Ŝ

emy funkcji 14 (informacja o bł

ę

dzie). 

Odtwarzanie: 

1. Otworzenie pliku zawieraj

ą

cego dane d

ź

wi

ę

kowe i zapisanego w formacie VOC. 

2. Wł

ą

czenie układu DA

Ć

 (czynno

ść

 jest zb

ę

dna, gdy odtwarzamy plik zaraz po inicjalizacji sterownika i gdy 

układ DA

Ć

 nie był wył

ą

czany). 

3. Wywołanie funkcji 6 z podaniem w rejestrze AX uchwytu do otwartego pliku. 
4. Oczekiwanie na moment, w którym zmiennej statusowej nadana zostanie warto

ść

 O (koniec). Podczas 

oczekiwania na zako

ń

czenie odtwarzania nasz program mo

Ŝ

e wykonywa

ć

 inne czynno

ś

ci. U

Ŝ

ywaj

ą

funkcji 10 i 11 mo

Ŝ

emy zatrzymywa

ć

 i wznawia

ć

 wykonywanie procedury odtwarzaj

ą

cej, a wywołuj

ą

funkcj

ę

 8 - zako

ń

czy

ć

 jej działanie. Odczytuj

ą

c warto

ść

 zmiennej statusowej mo

Ŝ

emy, pod warunkiem 

uprzedniego wzbogacenia naszego pliku o podbloki typu Marker, sprawdzi

ć

, który fragment próbki 

d

ź

wi

ę

kowej jest aktualnie odtwarzany. Podczas odtwarzania wci

ąŜ

 mo

Ŝ

emy odwoływa

ć

 si

ę

 do nap

ę

dów 

dyskowych z u

Ŝ

yciem usług DOS'u. Istniej

ą

 natomiast pewne ograniczenia dotycz

ą

ce wykorzystania 

przez program przerwa

ń

 8h (Timer), l On (Video), 13h (usługi BlOS-u dotycz

ą

ce operacji dyskowych) 

background image

HOZDZfAŁ 2 

i 28h. Ich obsług

ę

 przejmuje na czas działania sterownik. Mo

Ŝ

na je wykorzystywa

ć

 jedynie:

 

•    przed inicjalizacj

ą

 oraz po deinicjalizacji,

 

po inicjalizacji oraz przed deimcjalizacj

ą

.

 

6. Po zako

ń

czeniu odtwarzania nale

Ŝ

y zamkn

ąć

 plik z danymi d

ź

wi

ę

kowymi

 

Zapis dźwięku: 
1. Otwarcie pliku przeznaczonego do zapisu danych. 
2. Wyłączenie (z uŜyciem odpowiedniej funkcji sterownika) układu DAĆ. 
3. Wywołanie funkcji 7 (zapis) z odpowiednimi parametrami. 
4. Wykonywanie innych czynności. Podczas zapisu dźwięku nasz program moŜe wykonywać inne zadania. Chcąc 
zakończyć zapis dźwięku wystarczy wywołać funkcję 8 sterownika. Informację o tym, czy zapis dźwięku wciąŜ trwa, 
moŜna uzyskać odczytując wartość zmiennej statusowej (O oznacza zakończenie zapisu). 
5. Zamknięcie pliku, do którego zapisywaliśmy dźwięk. NaleŜy zwrócić uwagę, Ŝe dane zapisywane są przez 
sterownik w formacie VOC, nie ma więc juŜ potrzeby (jak przy sterowniku CT-VOICE) zapisu nagłówka przed 
utworzoną przez driver strukturą- 
Zakończenie pracy: 
1. Zakończenie procedury odtwarzania (jeśli jest aktywna) i zamknięcie plików dźwiękowych. 
2. Wywołanie funkcji deinstalacji sterownika. 
3. Zwolnienie pamięci zajmowanej przez kod sterownika i bufor. Jeśli rozpoczynając pracę nasz program przejął 
obsługę przerwania 24h. po deinicjalizacji sterownika powinien przywrócić pierwotną wartość wektora. 
Wykorzystanie CTVDSK.DRV to prosty sposób na wzbogacenie dłuŜszej prezentacji o dźwięk czy teŜ gry o muzykę. 
Moim jednak zdaniem, wykorzystanie plików VOC do przechowywania i odtwarzania „w tle" zdigitalizowanej 
muzyki nie jest pomysłem najlepszym (na jedną sekundę przywoicie słyszalnego dźwięku musimy przecieŜ 
przeznaczyć co najmniej kilka KB) - znacznie wygodniej jest, według mnie, wykorzystać muzykę zapisaną w 
formacie CMF. 

47

 

OBSŁUGA PLIKÓW VOC 

 

Skoro opisałem juŜ metodykę wykorzystania CTVDSK.DRV, czas na przykład; będzie nim tekst źródłowy 
programu odtwarzającego zadany parametrem plik. 

{$M 16000.0,50000}

 

program DskPIay;

 

uses

 

dos,crt;

 

var

 

sCerownik:pointer;     {wskazanie na początek kodu } Dbufferpointer;       { wskazanie na bufor} Error:byte;           { 
numer błędu } YOCStatus.word;        {zmiennastacusowa} P:fite;

 

Uchwyt:word absolute P: { rzut: P na pole 16-bic } ch:char;

 

Zat,rzymany:boolean;      {czy zsCrTymaliśmyodtwarzanie}

 

function lscniejetPEik:string]'boolean;

 

var

 

f:file;

 

begin

 

assigntf.Plik);

 

{$!-}

 

resectf];

 

close(f);

 

{$!+}

 

Istnieje: ^[IDresult^Ol end;

 

function Czy to_plik_VOC[spec:string):boolean, var

 

u:tile;

 

tab:array[Q,.3] of char;

 

begin

 

assign(u,spec3;

 

reset(u,1);

 

iffilesize[u)>31 chen

 

{ nagłówek + początek bloku danych }

 

begin

 

blockreadEu,tab,4);

 

CzyJ:o_plik_VOC

 

^[tab^+tabdl+tab^l+cabtS^Crea');

 

endelse Czy_l;o_plik_VOC:=falsE:

 

background image

ROZDZIAŁ 2

 

cbselu)

 

end;

 

procedur

ę

 2are2erwuj_parniectvargd2)e:poinCer:iie'longinti;

 

var

 

r:regisCers;

 

ilosc:word:

 

begin

 

ilo

ść

: =(ile-15] shr4:

 

nah;=$4S:

 

rbx:=ilosc;

 

MsOosCr-);

 

if [r.bxoilosc] then Error^S e.se begin

 

Error:=0;

 

gdzie:=ptr(r,ax,0) end

 

end;

 

procedur

ę

 Zwolnij^pamiectgdzie.pointer);

 

var

 

p:registers;

 

begin

 

rah:=$49:

 

r.es:=segtgdzie^);

 

msdostr];

 

iftr.ax^7]or[rax=9)then Error:=3 end;

 

procedurę lnicjuj_sterownik[PorC,lrq,Size:word);

 

var

 

sifile;

 

specyfikacja:string;

 

segment, przesuń :word;

 

CoJest_stercwnik:boolean;

 

wynik:word;

 

begin

 

specyfikacja:='CTVDSK,DRV;

 

if not Istniejetspecyfikacjał then

 

specyfikacja: ^getEnyfSaUND^+ADR^CTYDSK.DRY';

 

if not IscriejeCspecyfikacja] then

 

begin

 

Error=1;

 

exit

 

end;

 

S5sign[s,specyfikacjal:

 

resetCs.l);

 

OBSŁUGA PLIKÓW VOC 

Za reasrwJ |_Damec (ste rownik.fileSizetsJ);

 

ifErroroO then exit:

 

blockreadts,sterownik^ ,nlesize[5)], {wczyta) kod } closets], segment, =seg[ste''ownlk

/s

);

 

p^zesun;-=of5[5t.e^ownlk

/\

]; { adres początku kodu } tOJesC^stero^nik^tMemWLsegmenc przesuń+31 =$5443):

 

Co_)est sterownik: ==Co_)est_sterownik

 

and EMemW[segment:przesun+5]=$4456):

 

if noc COJest^sterownik then begin

 

ErrDr':=4;

 

exit

 

end;

 

Za^e^er'vuu^_pam^ec[Dbuffe^.s^^e

ft

4Q95];

 

if ErraroO then exit;

 

segment; =seg[Dbuffe^

/\

];

 

przesuń: ^fstDbuffer-^];

 

asm

 

mov bx,15

 

mov dx,segment

 

mov ax,prze5Lin

 

mov cx,size

 

cali sterownik end:

 

if portoOthen

 

asm

 

mov bx,1 mov ax,port cali sterownik

 

end;

 

background image

ifirqo0then

 

asm

 

mov bx,2 mov3x,irq cali sterownik

 

end;

 

segment: =seg[VOCstatus3;

 

przesuń: =ofs[VOCstatus];

 

asm

 

mov bx,3

 

mov ax,size

 

cali sterownik

 

mov wynik, ax

 

mov bx,5

 

movdx,segment

 

movax,przesuń

 

cali sterownik end:

 

ROZDZIAŁ 2

 

OBSŁUGA PLIKÓW VOC 

 

 

procedurę Kontynuuj;

 

begin ZaCrzymany:=false;

 

asm

 

mov bx,11

 

cali sterownik end end;

 

procedurę Deinstalu^sterownik;

 

begin asm

 

movbx,9 cali sterownik end;

 

Zwolnij_pamlecEsterownik];

 

ZwDlnij_pamiec[Dbuffer3 end;

 

procedurę KonczJesli_zle;

 

begin

 

ifError=Othen exit;

 

case Error of 1: writeInCBrak pliku CTVDSK.DRV3:

 

2: writeInCBład przydziału pamięci']:

 

3: writeln['Btad zwolnienia pamięci');

 

4: writelnC'Zły nagłówek sterownika'];

 

5; writelnC'Błędne dziatanie karty']:

 

B: writeInCBtąd obsługi portów karty');

 

7:

 wntelnC'Btędny numer IRQ');

 

8: wricelnt'Brak wskazanego pliku") end;

 

halt

 

end;

 

begin 
begin 
end; 

if paramcountol then { nie podano parametru }

 

writeInCU

Ŝ

ycie: DiskPtayplik');

 

writefnCplik- plik w formacie VOC');

 

hale

 

if not IstniejeCparamstrII)) then begin

 

wnteln^Brak wskazanego pliku');

 

halt

 

casewynkof

 

0: Error;=0;

 

1: Error:=5;

 

S: Error;=6;

 

3:Error:=7 end

 

end;

 

procedurę DdtworzJ)likl)aki:stnng);

 

var handle:word;

 

begin assIgnLPjaki):

 

{$F-} resetiPI;

 

{$F+} ifioresulcoOthen begin

 

Error:=B;

 

exit end;

 

handle;=Uchwyt:

 

asm

 

movbx,6

 

movax, handle

 

background image

cali sterownik end end;

 

procedurę Zakończ odtwarzanie;

 

hegin

 

ifVOCStatus<>Othen {jeśli jeszcze gra } asm

 

movbx,8 cali sterownik end;

 

{$F-} close(P]:

 

{$F-} end;

 

procedurę Pauza;

 

begin Zatrzymany: =true;

 

asm

 

mov bx,10

 

catl sterownik end end;

 

ROZDZIAŁ 2 

OBSŁUGA PLIKÓW CMF

 

53

 

 

 

ifnotC2y_Co_pllk_VDCtparBmstr(1]} then begin

 

wnteInCPlik nie je

ść

 zapisany w formacie VOC'

 

halt end:

 

Inicjuj sterownik(0,D,103; { bufor 40 KB} koncz_Jesli_zle:

 

2atr'zymany:=false;

 

OdCworz_plik[paramstrt1l];

 

writeInCOdtwarzam...'];

 

writeInfSPACE - pauza, ESC - koniec'3;

 

repeac

 

if keypressedthen ch:=r-eadkeyelse ch:=#0;

 

ifch=^32then

 

case Zatr-zymany of

 

true: KontynJJ);

 

false; Pauza end uncii

 

[VOCStat;us=0)or[ch=^27], Zakoncz^odCwarzanie;

 

Deinst;aluj_sterownik

 

3. OBSŁUGA PLIKÓW CMF 

 

W rozdziale omówię sposób obsługi plików 
zapisanych w formacie CMF (Creative Musie 
File). Struktura ta została stworzona do 
przechowywania danych muzycznych. Przy ich 
odtwarzaniu najłatwiej jest wykorzysta
ć 
sterownik SBFM. Jego funkcje umo
Ŝliwiają 
odtwarzanie muzyki „w tle", a co za tym idzie, 
np. proste wzbogacenie programów 
rozrywkowych o podkład muzyczny.
 

3.1 STRUKTURA PLIKÓW CMF

 

Sposób, w jaki zorganizowane są pliki CMF, 
przedstawia si
ę w następujący sposób: 

1. Blok nagłówka 

Blok zawiera podstawowe informacje o 
zawarto
ści pliku. 

2. Blok instrumentów 

Definicje instrumentów uŜytych przy 
odtwarzaniu muzyki. Warto
ść informująca 
o ilo
ści 16-bajtowych definicji 
przechowywana jest w bloku nagłówka.
 

3. Blok muzyczny 

Muzyka zapisana w zunifikowanej formie. 

ROZDZIAŁ 3

 

Omówi

ę

 teraz ka

Ŝ

dy z 3 bloków pliku CMF. Blok nagłówka

 

 

Przesuni

ę

cie (szesnastkowo)

 

Opis

 

 

background image

 

00-03h    Identyfikator pliku - 4 znaki ASCII „CTMF". 04-05h    Wersja formatu CMF.

 

Starszy bajt słowa przechowuje bardziej znaczącą część numeru wersji, młodsza - mniej znaczącą 
część. 

06-07h    Przesunięcie Bloku instrumentów względem początku pliku CMF. 
08-09h    Przesunięcie Bloku muzycznego względem początku pliku. 

OA-OBh    Ilo

ść

 cykli zegarowych odpowiadaj

ą

cych 

ć

wier

ć

nucie.

 

Dla przykładu: je

Ŝ

eli cz

ę

stotliwo

ść

 zegara wynosi 96 Hz, a warto

ść

 tempa to 120, warto

ść

 ta 

powinna by

ć

 równa 48 (domy

ś

lnie).

 

OC-ODh    Ilość cykli zegarowych w ciągu sekundy. 

Częstotliwość zegara O wyraŜona w Hz (1/sek). Wartością domyślną jest 96. Zalecany przedział 
wartości to (20,160). 

OE-OFh    Przesunięcie tytułu względem początku pliku. 

Warto

ść

 opisuj

ą

ca poło

Ŝ

enie ci

ą

gu znaków ASCII zako

ń

czonych bajtem równym O (ci

ą

ASCIIZ). Brak tytułu utworu sygnalizowany jest zerow

ą

 warto

ś

ci

ą

 przesuni

ę

cia.

 

10-1 Ih    Przesunięcie danych kompozytora względem początku pliku. 

Warto

ść

 opisuje poło

Ŝ

enie danych dotycz

ą

cych kompozytora (np. nazwisko). Dane 

kompozytora musz

ą

 by

ć

 zapisane jako ci

ą

g ASCIIZ. Brak danych sygnalizowany jest zerow

ą

 

warto

ś

ci

ą

 przesuni

ę

cia.

 

12-13h    Poło

Ŝ

enie komentarzy.

 

Szesnastobitowe słowo opisuj

ą

ce poło

Ŝ

enie ci

ą

gu ASCIIZ zawieraj

ą

cego komentarz 

wzgl

ę

dem pocz

ą

tku płiku

 

55

 

OBSŁUGA PLIKÓW CMF 

 

CMF. Długość wskazywanego ciągu nie powinna przekraczać 32 bajtów. 

14-23n    Tabela zaj

ę

cia kanałów.

 

Szesnastobajtowa tablica przechowująca informacje o wykorzystywanych przy odtwarzaniu 
muzyki kanałach. Wartość l bajtu odpowiadającego danemu kanałowi oznacza jego 
wykorzystanie. 

24-25h    Ilo

ść

 u

Ŝ

ywanych instrumentów.

 

Warto

ść

 opisuj

ą

ca ilo

ść

 u

Ŝ

ywanych przy odtwarzaniu muzyki instrumentów.

 

26-27h    Tempo podstawowe. Główne tempo utworu.

 

28h-..     Od tego miejsca rozpoczynają się ciągi znakowe opisujące tytuł, dane kompozytora ulworu i 

komentarze. 

Blok instrumentów

 

Blok zawierający rekordy o długości 16 bajtów, opisujące uŜywane przez utwór instrumenty. Ilość pól opisuje 
słowo nagłówka o przesunięciu 24h względem początku pliku. PołoŜenie bloku instrumentów opisuje słowo o 
przesunięciu 6h. KaŜdy rekord zawiera obraz wartości rejestrów danego kanału FM. Rekord przechowuje 
wartości równe zawartym w obszarze rozpoczynającym się od przesunięcia 24h pliku SBI (SBI - Sound 
Blaster Instrument File to format zapisu opisu instrumentów uŜywany np. przez program IEDIT). 

Blok muzyczny

 

Format, w jakim zapisane s

ą

 dane opisuj

ą

ce muzyk

ę

, jest zbli

Ŝ

ony do formatu SMF (Standard 

MIDI Format). Opisuje on utwory jedno

ś

ci e

Ŝ

k owe, wielokanałowe i polifoniczne. Maksymalna liczba 

kanałów to 16.

 

3.2 FORMATY SBI i IBK

 

Pliki zapisane w formacie SBI (Sound Blaster Instrument) przechowują dane opisujące pojedynczy instrument. 
Większość pól struktury SBI moŜna skojarzyć z odpowiednimi rejestrami układu syntezatora FM i ich 
zawartość moŜe być bezpośrednio do nich wpisana w celu 

ROZDZIAŁ 3

 

zdefiniowania danego brzmienia. Długo

ść

 pliku zapisanego w formacie SBI jest stała i wynosi 51 bajtów. Poni

Ŝ

ej 

prezentuj

ę

, jak przedstawia si

ę

 jego struktura. Dokładniejsze opisy znaczenia poszczególnych pól znajdzie Czytelnik 

w rozdziale po

ś

wi

ę

conym bezpo

ś

redniemu programowaniu syntezatora FM. 

 

Przesunięcie (szesnastkowol

 

Opis

 

 
 

background image

00-03 04-23 
24 
25 
26 
27 
28 
29 
2A 2B 
2C 

Identyfikator pliku. Ci

ą

g znaków „SBI" zako

ń

czony bajtem o warto

ś

ci lAh 

Nazwa instrumentu. Ci

ą

g ASCI1Z (znaki ASCII i bajt o warto

ś

ci 0) zawieraj

ą

cy nazw

ę

 instrumentu Baji opisuj

ą

cy 

charakterystyk

ę

 fali moduluj

ą

cej Bajt opisuj

ą

cy charakterystyk

ę

 fali no

ś

nej 

bit 7 - vibrato wysoko

ś

ci - tremolo (AM) bit 6 - vibrato amplitudy (VIB) bit 5 - d

ź

wi

ę

k podtrzymany (EG-TYP) bit 4 

- skalowanie obwiedni (KSR) bity 3-0 - mno

Ŝ

nik cz

ę

stotliwo

ś

ci (MULTIPLE) Skalowanie/Poziom wyj

ś

ciowy fali 

moduluj

ą

cej Skatowanie/Poziom wyj

ś

ciowy fali no

ś

nej bity 7-6 - skalowanie poziomu (KSL) bity 5-0 - poziom 

wyj

ś

ciowy fali no

ś

nej (TL) 

Bajt opisujący proces narastania i opadania fali modulującej 

Narastanie i opadanie fali no

ś

nej 

bity 7-^ - pr

ę

dko

ść

 narastania (AR) bity 3-0 - pr

ę

dko

ść

 opadania (DR) 

Bajt, którego bity opisuj

ą

 poziom podtrzymania i pr

ę

dko

ść

 wygasania fali moduluj

ą

cej 

Poziom podtrzymania i pr

ę

dko

ść

 wygasania fali no

ś

nej bity 7-^ - poziom podtrzymania (SL) bity 3-0 - pr

ę

dko

ść

 

wygasania fali (RR) 

Wybór fali moduluj

ą

cej 

57

 

OBSŁUGA PUKÓW CMF 

 

2D        Wybór tali no

ś

nej 

bity 7-2 - wy zerowane 

2F 

bity 1-0 -wybór fali (WS) Sprz

ęŜ

enie zwrotne/poł

ą

czenie 

bity 7-4 - wyzerowne 
bity 3-1 - sprz

ęŜ

enie zwrotne układu modulatora 

bit O - poł

ą

czenie 2F-33     Zarezerwowane 

Funkcje i znaczenie poszczególnych bitów odpowiednich pól opisuj

ą

cych fal

ę

 moduluj

ą

c

ą

 i no

ś

n

ą

 s

ą

 identyczne. 

Bajty o przesuni

ę

ciach 24-33h odpowiadaj

ą

 kolejnym polom definicji instrumentu w bloku instrumentów pliku CMF. 

PoniewaŜ uŜywanie plików SBI do przechowywania definicji pojedynczych instrumentów moŜe być, przy większej 
ich liczbie, niewygodne, stworzono format IBK (Sound Blaster Instrument Bank), umoŜliwiający zgrupowanie w 
jednym pliku większej ilości definicji. Maksymalna liczba opisywanych plikiem IBK instrumentów wynosi 128. 
Struktura IBK przedstawia się następująco: 

Przesunięcie  Opis (szesnastkowo)

 

00-03     Identyfikator pliku - ci

ą

g „IBK" zako

ń

czony bajtem O warto

ś

ci lAh. 

04-803    Parametry instrumentów. Obszar o wielkości 2 KB przeznaczony na przechowanie definicji 128 

instrumentów (po 16 bajtów na kaŜdy). 

804-C83   Nazwy instrumentów. Tablica zawierająca nazwy kaŜdego z opisywanych w pliku instrumentów. Nazwę 

pojedynczego instrumentu stanowi ciąg 9 znaków ASCII zakończony bajtem o wartości 0. 

3.3 STEROWNIK SBFM

 

W rozdziale omówię sposób wykorzystania rezydentnego sterownika Sound Blaster FM. UŜywając go moŜna w 
prosty sposób odtwa- 

ROZDZIAŁ 3

 

rzać muzykę zapisaną w formacie CMF, wykorzystując układ syntezatora FM (Frecjuency Modulation) będący 
częścią karty dźwiękowej. 

SPOSÓB KORZYSTANIA ZE STEROWNIKA 
Sterownik SBFM instalujemy w pamięci operacyjnej uruchamiając program SBFMDRV.COM. Program 
przechwytuje obsługę jednego z przerwań o numerze zawierającym się w przedziale 80h..BFh (wybiera pierwsze nie 
zajęte). Przesunięcie wewnątrzsegmentowe początku kodu nowej procedury obsługi przerwania jest równe 0. Do 
funkcji sterownika odwołujemy się wykonując obsługiwane przez niego przerwanie (rozkazem assemblerowym INT). 
Wpierw jednak musimy sprawdzić, obsługę którego przerwania przejął sterownik. Najprościej zrobić to badając dla 
kaŜdego „podejrzanego" (od 80h do BFh) kod procedury obsługi. W kodzie oryginalnego SBFM, od przesunięcia 
103h względem komórki wskazywanej przez wektor przerwania rozpoczynać się powinna 5-bajtowa sygnatura 
„FMDW. 

background image

Wszystkie parametry przekazujemy driverowi z pomocą rejestrów mikroprocesora. Numer funkcji, z której 
chcielibyśmy skorzystać, umieszczamy w BX. a parametry w pozostałych. Po wykonaniu funkcji sterownik 
umieszcza (czasem nie) kod wyniku w rejestrze AX (odczytując jego zawartość dowiedzieć się moŜemy, czy 
operacja przebiegła pomyślnie, czy teŜ nie). 
Wartości wszystkich rejestrów wraz z flagowym (oprócz AX i DX) są przez sterownik zachowywane. 
Podobnie jak sterownik CT-VOICE, SBFM modyfikuje wskazaną przez program zmienną zwaną dalej zmienną 
statusową. Dla drive-ra SBFM zmienna zajmuje jeden bajt. Jej lokalizację wskazujemy korzystając z funkcji l. 
Podobnie jak przy odtwarzaniu zawartości plików VOC, wartość zmiennej statusowej uŜywanej przez sterownik 
informuje nas o działaniu procedur sterownika. Rezydentny driver SBFM modyfikuje ją w przypadku: 
1. Resetu (ustawia na 0) 
2. Rozpoczynając odtwarzanie bloku muzycznego pliku CMF (ustawia na FFh) 
3. Kończąc odtwarzanie bloku muzycznego (ustawia na 0) 
4. Natrafienia na pole Control Event w bloku muzycznym (ustawia wartość zmiennej statusowej zgodnie z 

zawartością pola Control Data) 

59

 

OBSŁUGA PLIKÓW CMF

 

 

Zmienna statusowa nie jest modyfikowana, gdy wywołamy funkcję chwilowego zatrzymania (nr 9) lub 
kontynuacji (nr 10) odtwarzania muzyki. 

OPIS FUNKCJI STEROWNIKA 

Rezydentny sterownik SBFM dostarcza nam następujące funkcje: 

Funkcja 0: Pobranie numeru wersja sterownika                    _____

 

Wejście:  BX=0 
Wyjście:   AX - wersja SBFM 

Opis:     Funkcja zwraca bardziej znaczącą (AH) i mniej znaczącą (AL) część numeru wersji zainstalowanego 

w pamięci sterownika. 

Funkcja 1: Wskazanie bajtu statusowego

 

Wejście:BX=1 
DX:AX== adres bajtu Wyjście:   brak 

Opis:     Wskazanie sterownikowi połoŜenia bajtu przeznaczonego na zmienną statusową. Funkcje tej 

zmiennej omówiłem w części „Sposób korzystania z funkcji". 

Funkcja 2: Wskazanie tabeli instrumentów

 

Wejście:  BX=2 

CX== ilość instrumentów 
DX:AX= adres tabeli Wyjście:   brak 

Opis:     Zanim rozpoczniemy odtwarzanie utworu muzycznego powinniśmy wskazać sterownikowi połoŜenie 

tabeli zawierającej definicje instrumentów (driver zawiera wprawdzie definicję 16 
instrumentów, ale uŜyte w odtwarzanym utworze mogą się od nich róŜnić). Sterownik uŜywa jej 
do programowania układów syntezy FM. NaleŜy pamiętać, Ŝe wielkość praekazywana w 
rejestrze CX nie powinna być większa od 128. 

ROZDZIAŁ 3 

Funkcja 3: Ustawienie częstotliwości zegara systemowego Wejście:BX=3

 

AX= wartość odpowiadająca częstotliwości Wyjście:   brak

 

Opis:      UŜywając tej funkcji informujemy sterownik o częstotliwości, na jaką powinien ustawić Timer O po 

zakończeniu odtwarzania. Welkość przekazywana w rejestrze AX wyliczyć moŜna ze wzoru: 

w = l i 931801 częstotliwość U/Hz l 

Jeśli program nie wywoła tej funkcji lub wywoła z parametrem AX=FFFFh, Timer O pozostanie 
ustawiony na częstotliwość około 18.2 Hz- 

Funkcja 4: Ustawienie częstotliwości zegara SBFM

 

Wej

ś

cie:   BX=4 

AX= warto

ść

 odpowiadaj

ą

ca cz

ę

stotliwo

ś

ci Wyj

ś

cie:   brak 

Opis:      Działanie funkcji polega na ustawieniu częstotliwości, na jaką sterownik powinien przeprogramować Timer 

O na czas odtwarzania dźwięku. Sposób wyliczania wartości przekazywanej w AX jest laki sam jak dla 
funkcji poprzedniej: 

w = H93I80/częstotliivość [1/Hzf 

Częstotliwością domyślną jest 96 Hz. Właściwą dla danego utworu częstotliwość przechowuje 

background image

szesnastobito-we słowo o przesunięciu OCh względem początku pliku. Częstotliwość Timera O 
decyduje o szybkości odtwarzania muzyki. Łatwo więc przez jej zmianę wpływać na tempo gry. 

Funkcja 5: Transpozycja utworu

 

Wej

ś

cie:  BX=5 

AX= parametr transpozycji Wyjście:   brak 

61

 

OBSŁUGA PLIKÓW CMF 

 

Opis:      Działanie funkcji polega na zmianie tonacji utworu. Parametr przekazywany w AX wyraŜony jest w 

półtonach. 

Funkcja 6: Odtworzenie utworu Wej

ś

cie:BX=G 

DX:AX= adres bloku muzycznego 

Wyj

ś

cie:   AX= wynik: 

O - wykonanie bezbłędne l - błąd, inny utwór jest wciąŜ aktywny 

Opis:     Wywołanie funkcji rozpoczyna odtwarzanie utworu muzycznego opisanego w bloku muzycznym. 

Para rejestrów DX:AX określa jego połoŜenie. Wyliczyć j^ moŜemy korzystając z wartości 
przechowywanej w nagłówku pliku CMF (przesunięcie 08h względem początku). W wyniku 
działania funkcji sterownik ustawia wartość zmiennej statusowej na FFh. częstotliwość Timera O 
na 
zdefiniowaną przy uŜyciu funkcji 4, przejmuje obsługę przerwania 8h i rozpoczyna grę. 
Odbywa się ona „w tle". 

Funkcja 7: Zako

ń

czenie odtwarzania muzyki 

Wejście:BX=7 
Wyjście:   AX=0 - bezbłędnie 

AX=1 - Ŝaden utwór nie był odtwarzany 

Opis:     Funkcja kończy odtwarzanie utworu muzycznego, zeruje zmienną statusową i programuje 

częstotliwość zegara Timer O na 18.2 Hz lub na ustawioną z uŜyciem funkcji 3. 

Funkcja 8: Reset sterownika SBFM

 

Wejście:   BX=8 Wyjście:   AX=0 - bezbłędnie 

AX=1 błąd, sterownik odtwarza muzykę 

Opis:     Po wywołaniu tej funkcji sterownik wyłącza układy FM i ustawia domyślną (wewnętrzną) tabelę 

instrumentów. Jeśli sterownik odtwarza utwór muzyczny, naleŜy 

ROZDZIAŁ 3 

 

wpierw wywołać funkcję 7. Wykonanie funkcji reinicjali-zacji sterownika jest konieczne przed 
zakończeniem pracy naszego programu. 

Funkcja 9: Chwilowe zatrzymanie odtwarzania Wejście:BX=9

 

Wyjście:   AX= wynik 

O - przebieg bezbłędny l - błąd, Ŝaden utwór nie był odtwarzany 

Opis:      Zatrzymanie odtwarzania. Funkcja nie modyfikuje wartości zmiennej statusowej. Odtwarzanie muzyki jest 

kontynuowane po wywołaniu funkcji l O i kończone w wyniku działania funkcji 7. 

Funkcja 10: Kontynuacja odtwarzania

 

Wej

ś

cie:

 

Wyjście: 

Opis:

 

BX=10 

AX=0 - bez bł

ę

dów AX=1 - muzyka nie była zatrzymana

 

Funkcja słuŜy do wznowienia odtwarzania utworu zatrzymanego funkcją 9. 

Pułapki uŜytkownika dla Exclusive Commands

 

 

Wej

ś

cie:

 

Wyj

ś

cie:

 

Opis:

 

BX=11 
DX:AX== adres procedury pułapki 
brak 
UŜywając tej funkcji wskazujemy procedurę, którą sterownik wywoła wykorzystując assemblerową komendę CALL 
(międzysegmentową) w chwili, gdy w błoku muzycznym napotka na pole System Exclusive Command. Zdefiniowana 

background image

przez nas procedura musi się kończyć komendą RETF. Musi teŜ zachowywać wartości wszystkich rejestrów. 
Przekazywany do niej przez sterownik parametr to adres następnego po S.E.Command bajtu (w parze rejestrów 
ES:DI). Wyłączenie pułapki uŜytkownika jest konieczne przed zakończeniem działania na- 

63

 

OBSŁUGA PLfKOW CMF 

 

szego programu. Dokonać tego moŜemy zerując przed wywołaniem lej funkcji rejestry AX i DX. 

ZASADY KORZYSTANIA Z FUNKCJI

 

Istnieje kilka zasad, których powinniśmy się trzymać programując z wykorzystaniem rezydentnego sterownika 
SBFM. Najprościej będzie, gdy zaprezentuję uproszczony algorytm, wg którego działać powinien program 
odtwarzający muzykę zapisaną w formacie CMF: 

1. Odszukanie przerwania obsługiwanego przez sterownik. Podczas wykonywania tej procedury okazać się 

moŜe, Ŝe Ŝadne z przerwań nie jest wykorzystane przez SBFM - wtedy nasz program powinien kończyć 
działanie z odpowiednim komunikatem. 

2. Otwarcie pliku do odtworzenia, sprawdzenie jego rozmiaru, al-lokacja niezbędnego obszaru pamięci 

operacyjnej, wczytanie zawartości pliku i jego zamknięcie. Przed wczytaniem warto sprawdzić, czy 
pierwsze 4 bajty pliku układają się w ciąg „CTMF" (gdy nie, plik nie jest zapisany w formacie CMF). 

3. Wywołanie funkcji reinicjalizacji sterownika (nr 8). 
4. Wskazanie połoŜenia zmiennej statusowej (funkcja 1). 
5. Odczytanie Ŝądanej częstotliwości zegara systemowego na czas odtwarzania z odpowiedniego pola 

nagłówka pliku i ustawienie jej z uŜyciem funkcji 4. 

6. Wskazanie sterownikowi połoŜenia tabeli zawierającej definicję instrumentów (jej lokalizację względem 

początku pliku CMF odczytamy z nagłówka). 

7. Obliczenie połoŜenia początku bloku muzycznego (przesunięcie względem początku pliku odczytamy z 

nagłówka). 

8. Wywołanie funkcji odtwarzania muzyki (nr 6). 
9. Oczekiwanie na zakończenie odtwarzania muzyki (sterownik wyzeruje wtedy zmienną statusową). Podczas 

odtwarzania program moŜe wykonywać inne czynności. Odtwarzanie moŜemy zatrzymywać i wznawiać 
przy uŜyciu funkcji 9 i 10 oraz zakończyć uŜywając funkcji nr 7. 

10. Wywołanie funkcji reinicjalizacji sterownika oraz zwolnienie pamięci zajmowanej przez plik CMF. 

ROZDZIAŁ 3 

BIBLIOTEKA CMF.TPL 

W rozdziale tym zaprezentuj

ę

 tekst 

ź

ródłowy gotowej do u

Ŝ

ycia biblioteki CMF. udost

ę

pniaj

ą

cej kilka podstawowych 

procedur obsługi plików zapisanych w formacie Creative Musie File. Teksi biblioteki i kompilat znajduje si

ę

 tak

Ŝ

e na 

doł

ą

czonej do ksi

ąŜ

ki dyskietce. 

Podobnie jak we przedstawionej wcze

ś

niej bibliotece VOC.TPU, zacz

ą

łem od definicji typu wyliczeniowego 

opisuj

ą

cego mo

Ŝ

liwe bł

ę

-dy. 

untCMF;

 

interface

 

type CMoz!iweBledy=[COk,

 

CMaloPamieci,

 

CBIadZwalniama,

 

CNieInstalowany,

 

CBrakPIikuCMF,

 

CZłyNaglowek,

 

CZaDuzoInstr

 

CAkCywnyLJtwor,

 

CNieGral,

 

CNieByloPauzy);

 

Następnie zadeklarowałem kilka zmiennych: CMFSlatus, którą przeznaczyłem na bajt statusowy i którą wskaŜemy 
sterownikowi, CMF_blad typu CMozliweBledy przechowującą wartość odpowiadającą błędowi oraz 
CSBFMZainstalowany, której nadamy wartość True w przypadku, gdy w pamięci operacyjnej znajduje się kod 
sterownika SBFM. 

var

 

CMFStatus:byte;

 

CMF^blad:CMozliweBtedy;

 

CSBFMZainstalowany: boolesn;

 

Dalej następuje lista procedur i funkcji; 

procedurę Cinic|alizu|SBFI\/1:

 

functian CNumerWersJfSBFM'wor'd;

 

background image

procedurę CUstawBajtStatusowySBFM;

 

function CZaladujPlikCMF[spec:string3:pointer;

 

procedurę CLJstawlnst:rumenty[sC3rt:;painCer);

 

procedurę CNastawZegarSBFM[czest;wordJ;

 

procedurę CTranspozycjaUtworu(polt:word);

 

procedurę CZagr'ajCMF[g:pointeri;

 

procedurę CZakonczCMF;

 

65

 

OBSŁUGA PLIKÓW CMF 

 

procedurę CReseOJ)SBFM, procedurę CPauzaCMF;

 

procedurę CWznowCMF;

 

procedurę CZwolnijPamiecCMFtg pointer);

 

function CTytulCMF[g:poinCer):st:nng;

 

funccion CKompozytorCMF[g:poinCert:st;nng:

 

function CKomentarzCMF[g:pointer].string:

 

function COpisBledu:stnng;

 

implementation uses dos;

 

W cz

ęś

ci implementacyjnej biblioteki umie

ś

ciłem definicj

ę

 typu rekordowego składaj

ą

cego si

ę

 z pól 

odpowiadaj

ą

cych kolejnym polom nagłówka pliku zapisanego w formacie CMF. W zmiennej lnt_CMF 

przechowamy numer przerwania, pod które „podczepił" si

ę

 sterownik. 

Cype Naglowek=record

 

ldenCyf]kator:arr8yt0..3] ofchar;

 

Wersja :ward;

 

PolozJnstr :word;

 

PołóŜ Muz    :word;

 

Cwiercnuta :ward;

 

Czestotliwosc.word;

 

PołóŜ Tytułu :word;

 

Poloz_Kompoz :word;

 

Poloz_Koment :word;

 

Tab_kanalow :array[0.,15] of chan;

 

InsCrumentow :word;

 

Podst_Tempa :word;

 

end;

 

lnt_CMF'byte;

 

CStaraProcWyjscia;pointer;

 

funcCiori lstnieje[Plik:string]:boolean;

 

var

 

f:file;

 

begin

 

assign[f,Plik);

 

{$1}

 

reseCCf);

 

closeCf];

 

{$!+}

 

ROZDZIAŁ 3 

end;

 

procedurę Zarezerwuj pamiectvar gdzie:poincer;ile:longint);

 

var

 

r:regisCers:

 

ilosc-word;

 

begin

 

ilosc:=(i)e+15]shr4;

 

rah:=$48:

 

rbx:=ilosc,

 

MsDosEr);

 

if tr,bx<>ilosc) then CMF_blad:=CI\/laloPamiecl

 

else    begin

 

CMF_bl6d:=COk;

 

gdzie:=pt:r[r.ax,G) end end;

 

procedurę Zwolnij_pamiactgdziB:poinCer];

 

var rregisters;

 

begin

 

nah:=a49;

 

nes^segtgdzie^);

 

msdos(r);

 

if (nax=7)oKrax=9) then CMF_b!ad:=CBiadZwalniania end;

 

background image

Procedura CInicjalizujSBFM spełnia kluczową rolę - odnajduje przerwanie, którego obsługę przejął sterownik i 
reinicjalizuje go oraz modyfikuje wartości zmiennych CSBFMZainstalowany oraz CMF_blad. 

procedur

ę

 ClnicjalizujSBFM;

 

function Jest_sygnatura[p:point:er):boolean;

 

typeSign=arrayt0..41 ofchar;

 

const Znak:Sign='FMDRV';

 

begin

 

Jest_sygnatura:=[Sign[p^)=Znald end,

 

var

 

begin 

67

 

OBSŁUGA PLIKÓW CMF 

 

CSBFMZainstalowany: =false;

 

prze rwanie: =$7F;

 

repeat

 

inctprzerwanie);

 

getintyectprze rwanie,wska

ź

niki;

 

wskaznik:==pC^'^segtwskaznil^

/\

),$103);

 

unci!

 

[Jest sygnatura[wskaznik]]or[przBrwanie=$CO),

 

if Jest^sygnaturaCwskaznikJ

 

then

 

l^t_CMF:

:;

=p^zerwanie else

 

CMF^blad:=CNielnstalowany;

 

if przerwanie^ICO then exit;

 

CSBFMZainstalowany: =t:rue;

 

rej.bx:=B;

 

intr[lnt_CMF,rei);

 

If rej. ax<>0 then begin

 

CMF_blad;==CAktywnyUtwor;

 

exit end

 

else CMF blad:=COk

 

end;

 

Dalej następują definicje: funkcji zwracającej daną typu Word opisującą numer wersji sterownika i procedury 
CUstawBajtStatusowySB-FM wskazującej sterownikowi adres zmiennej CMFStatus. 

function CNumerWersJiSBFM:word;

 

vap rej:register's;

 

begin

 

rej.bx:=0,

 

intrCInt^CMP.rej);

 

CNumerWersjiSBFM:=rei.ax end;

 

procedurę CUstawBaJtStatusowySBFM;

 

yar rej:register-s;

 

begin

 

re).bx:=1;

 

rel.dx:=seg[CMFSCatusl;

 

rej.ax:=ofs(CMFScatus):

 

incr[lnt_CMF,re|) end;

 

ROZDZIAŁ 3 

Funkcja ładowania pliku allokuje odpowiedni obszar pami

ę

ci operacyjnej, sprawdza, czy nagłówek pliku 

rozpoczyna si

ę

 od ci

ą

gu „CTMF" oraz ładuje jego zawarto

ść

 do zarezerwowanego obszaru i zwraca wskazanie 

na jego pocz

ą

tek. 

function CZaladuJPIikCMF[spec,str'ing3:poinCer:

 

var

 

f:file, rozmiar^phku, bloków, wynik:word:

 

wsk,mie)sce:point:er, ident:stnng[4]:

 

begin

 

if not(lstme|e(5pec)] then begin

 

CMF_blad;=CBrakP!ikuCMF;

 

exit end;

 

assigntf.spec);

 

resECtf,

1

!];

 

idenria];=^4:

 

blockreadtf,ident[13,4):

 

seektf.O];

 

background image

ifident<>'CTMF' Chen begin

 

closetfl;

 

CMF_Blad:=CZIyNaglowek;

 

exit end:

 

rozmiarJ3liku:=filesize(f);

 

Zarezerwuj_pamiecEwsk,rozmiar pliku);

 

ifCMF_blad<>COkthen begin

 

closetfł:

 

exit end, blokow:=0;

 

repeat miejsce: =Pt^[seg(w5k^)+blokow

<>

4a96,of5[wsk^)];

 

blockread(f,miejsce^,$FFFF,wynik]:

 

lnc(Blokow3 uncii wynik=0:

 

ciosek;

 

CZaladuJPIikCMF:=wsk;

 

CMF_blad:-COk end;

 

Procedura CUstawInstrumenty wymaga jednego parametru - wskazania na początek nagłówka pliku. Jej działanie 
polega na spraw- 

OBSŁUGA PLIKÓW CMF 

69

 

dzeniu, czy ilość instrumentów zadeklarowana w bloku nagłówka pliku nie przekracza 128 i jeśli ten warunek 
zostanie spełniony, wywołaniu funkcji 2 sterownika z odpowiednimi parametrami. 
Z kolei procedura CNastawZegarSBFM słuŜy do określania, jaką częstotliwość zegara Timer O ma ustalić na 
czas odtwarzania muzyki sterownik. Im częstotliwość większa, tym większe tempo odtwarzania. Procedura 
CTranspozycjaUtworu umoŜliwia zmianę tonacji utworu zgodnie z parametrem. 

procedur

ę

 CUstawInsCrumentytsCartipoincer);

 

var

 

rej.registers;

 

begin

 

if Nag!owek[start^).lnst;rumentow>1 28

 

then begin

 

CMF_9lsd:=CZaDuzolnstr;

 

exic end;

 

rej.bx:=2:

 

rej. CK^NaglowekCstart;^), Instrumentów;

 

rej.dK^segCEtart^l;

 

rej. ax:=ofs(start

/\

)+ Naglowektstart '

\

 3, PolozJnstr;

 

intr[lnt_CMF,rej) end;

 

procedurę CNastawZegarSBFM[czest:word);

 

var

 

rej:registers;

 

begin

 

r'ej.bx:=4;

 

rei.ax:=1ia31BQdiv czest;

 

intr[lnt_CMF,rejl end;

 

procedurę CTranspozycjaUtworutpolt;word);

 

var

 

rej':registers;

 

begin

 

ref.bx;=5;

 

re).bx:=polt:;

 

inCrEfnt_CMF,rej) end:

 

Procedura CZagrajCMF rozpoczyna odtwarzanie wskazywanego przez wartość parametru g pliku. Wpierw 
informuje sterownik, na 

ROZDZIAŁ 3

 

jaką częstotliwość ustawić powinien zegar systemowy, potem wskazuje mu tablicę instrumentów, na koniec wywołuje 
funkcję odtwarzania (nr 6). 

procedurę CZagr8|CMFCg:pointer);

 

var

 

rej:registers;

 

begin

 

CNastawZegarSBFM[IMaglowek(g^l. Częstotliwość);

 

CUstawhstrumentytg),

 

ifCMF_blad<>COkthen exit;

 

rej,bx;=6:

 

background image

rej.d^segtg'"

1

);

 

rej. ax: =ofs[g

/1

 ] + Nagtowektg ^). Poloz_muz;

 

intr'tlnt_CMF,rej);

 

if rej,ax<>0 chen CMF_blad:=CAktywnyUtwor end;

 

Zdefiniowane dalej procedury CZakonczCMF, CResetujSBFM, CPau-zaCMF. CWznowCMF, 
CZwolnijPamiecCMF słu

Ŝą

 do sterowania prac

ą

 sterownika oraz do zwalniania pami

ę

ci zajmowanej przez 

plik.

 

procedurę CZakonczCMF:

 

var

 

rej:registers:

 

begin

 

rej.bx:=7;

 

intr(lnt_CMF,rej];

 

if rej.ax<>D Chen CMF_blad:=CNieGral end;

 

procedurę CResetujSBFM;

 

var

 

re|:regiscers;

 

begin 

i re|'.bx:=B;

 

incrElnC_CMF,rej);

 

if re).ax<>0 then CMF_blad:=CAktywnyUtwor elseCMF blad:=COk

 

end: 

procedurę CPauzaCMF;

 

v8r

 

rej:registers;

 

begin

 

reJ^^S;

 

mtr[lnC_CMF,pej);

 

ifrej.ax<>OchenCMF blad:=CNieGral

 

71

 

OBSŁUGA PLIKÓW CMF 

 

elseCMF blad^COk

 

end;

 

procedurę CWznowCMF:

 

var

 

rej^registers;

 

begin

 

rej.bx:=10;

 

intrtlnt_CMF.re)];

 

if rej.ax<>0 then CMF_blad:=CNieGrai

 

efseCMF_blad:=COk end:

 

procedurę CZwolniJPamiecCMFtg:pointer];

 

begin

 

Zwolnij_pamiectg) end;

 

Budowa trzech zdefiniowanych poni

Ŝ

ej funkcji jest podobna - działanie wszystkich polega na 

odnajdowaniu (na podstawie warto

ś

ci przechowywanych w bloku nagłówka pliku) danych 

dotycz

ą

cych tytutu i kompozytora utworu oraz uwag jego dotycz

ą

cych.

 

function CTytulCMF[g'pointer):string;

 

var

 

rob:string;

 

iicz:byte:

 

pol_t_s,pol t a:word;

 

begin rob:=";

 

^fNaglowek[g

/\

).Po!oz_tytulu>0 Chen begin

 

po!_t_s;=segEg^];

 

po!_t_o:=ofstg

/^

);

 

poi t_o:=pol t o+Nagiowektg^l.PolozJiytulu;

 

liczbo;

 

repeat rób: ^rob+chKMemEpol^s:?^^^^-licz]];

 

inctlicz)

 

until

 

chr(Mem[pol_c_s:polJ_o+licz]]=#0 end;

 

CTytu!CMF;=rob

 

end;

 

background image

funccion CKompozytorCMF[g:poincer):stnng;

 

var

 

rob:string;

 

ROZDZIAŁ 3

 

licz:byte;

 

pol_k_s,pol_k_o:word:

 

begin rob:=",

 

if Naglowektg^). Połó

Ŝ

 kompoz>Q Chen begin

 

polJ^s^segCg"];

 

pol_k_o:=ofs[g^);

 

po^kJa^pol^o+Naglowektg^J.Poloz^kompoz;

 

iicz;=a;

 

repeat rób: = rób+chr[Mem[pol^k_s:pol_k_o+licz]);

 

incUicz] until

 

chr[Mem[pol k^s:pol_k_o+licz])=^0 end;

 

CKompazytorCMF:^r'ob end.

 

function CKomentarzCMF[g:pointer):string;

 

var

 

rob:string;

 

licz:byte;

 

pol_k_s,po!_k_o:wor'd;

 

begin rob:=";

 

if Naglowektg ^ l. Po!oz_koment>0 then begin

 

pol_k_s;=seg(g^];

 

pol_k_o:

=

ofs[g

/^

);

 

P^KJ^P

0

^^^^

0

^^^'^

0

!

0

^

0

^

1

^' licz:=0;

 

repeat rób; =rob+chr[Mem[pol_k_s:pol^o^ licz]];

 

incdicz] until

 

chriMemtpol k_s:pol_k_o+licz]]=^0 end;

 

CKomentarzCMF^^rob end;

 

Funkcja COpisBIedu zwraca ciąg znakowy opisujący błąd, jaki wy stąpił. Procedura_wyjscia_CMF to procedura, 
którą „podczepiamy' pod łańcuch procedur wyjścia naszego programu. 

funcCion COpi5BlEdu:string;

 

begin caseCMF_bladof

 

73

 

OBSŁUGA PLIKÓW CMF 

 

COk

 

:CopisBledu:='Ok ;

 

CMaloPamieci

 

•CopisBledu:='Blad allokacji pamięci':

 

CBIadZwalniania

 

:CopisBledu;='Btad zwalniania pamięci', CNieInstalowany

 

.CopisBledu:='Brak sterownika SBFM':

 

CBrakPIikuCMF

 

:CopisBledu:='Brak wskazanego pliku';

 

CZłyNaglowek

 

•CopisBledu:='Zty nagłówek pliku';

 

CZaDuzoInstr

 

:CopisBledu.='Za duŜo Irscrumentów';

 

CAktywnyLJtwor

 

:CopisB!edu:='SBFM odtwarza utwór', CNieGral

 

:CopisBledu:='Ucwór nie jest odtwarzany';

 

CNieByloPauzy

 

•CopisBledu:='Ucwór nie byt zatrzymany' end end,

 

{$F-} procedurę Procedur'a^wyiscia_CMF,

 

begin

 

if CSBFMZainstalowany Chen begin

 

CZakonczCMF;

 

CResetujSBFM end;

 

ExiCProc:=CStar'aProcWy!scia end;

 

{$F-}

 

begin

 

CStaraProcWyjscia:=ExitProc;

 

ExitProc:=@Procedura_wyjscia_CMF;

 

ClnicjalizuiSBFM;

 

CMFStatus:=0;

 

background image

ifCMF_blad=COkthen

 

CUsCawBajtStaCusowySBFM end,

 

3.4 PRZYKŁADY

 

Na koniec chciałbym przedstawić przykładowy program wykorzystujący przedstawioną w poprzednim 
rozdziale bibliotekę. Działanie 

ROZDZIAŁ 3

 

programu polega na odtwarzaniu wskazanego przy wywołaniu pliku CMF. Podczas odtwarzania moŜliwe Jest 
zatrzymanie/wznowienie gry. Program, przed rozpoczęciem odtwarzania, wyświetla na ekranie odczytane z pliku: tytuł utworu, 
nazwisko kompozytora i komentarze. program GrajCMF:

 

{$M 15000,0,50000} uses cmf.crt; 
procedur

ę

 Jesli_b!ad_to_koniec; 

begin 

ifCMFJiladoCOk then begin 

writeCBL

Ą

D: •i;

 

wriceIntCOpisBIedu);

 

halt end end;

 

var Muzyka:pointer;

 

Pauza:boolean;

 

ch:char:

 

begin

 

if paramcount<1 then begin

 

writeInCLJ

Ŝ

ycie: GRAJCMF plik'];

 

writelnC plik-plik w formacie CMF'];

 

hatt

 

end;

 

Jesli_blad_toJ(omec;

 

Muzyka:=C2aladuiPiikCMF(parBmstr(1l];

 

Jesli_blad_CoJ(oniec, Pauza: =false;

 

writeln['Tytuł : ',CTyt:ulCMF[Muzyka]]:

 

wntelnC Kompozytor: '.CKompozytorCMFtMuzyka)):

 

writeInCUwagi : '.CKomenCarzCMFCMuzyka]];

 

writein;

 

writeInfOdtwarzam...'];

 

writeInCSPACJA- Pauza/Kontynuacja ESC - Koniec');

 

white keypressed do ch:=readkey;

 

CZagrajCMF[Muzyka);

 

Jesli_blad_CG_koniec;

 

repeat ch:=#0;

 

75

 

OBSŁUGA PUKÓW CMF 

 

i keypressed then ch: =readkey;

 

ifch=#32then

 

case Pauza of false begin

 

CPauzaCMF;

 

Pauza: =l;rue end;

 

Crue: begin

 

CWznowCMF;

 

i'auza:=false

 

end

 

end;

 

untii (CMFStatus=01ortch=#27);

 

if ch=#27 Chen CZakonczCMF;

 

CZwolnijPamiecCMFtMuzykal

 

end.

 

Drugi przykład to program odtwarzający muzykę z pliku włączonego do jego kodu na etapie konsolidacji. 
Taka metoda „udźwiękowiania" swoich programów jest, jak się wydaje, dosyć wygodna - nie musimy 
troszczyć się o obsługę błędów związanych z odczytem pliku i identyfikacją jego struktury. Ponadto, pliki 
zapisane w formacie CMF mają z reguły niewielką objętość - nie będzie więc zbytnią rozrzutnością (pamięć 
operacyjna O połączenie ich z kodem programu. program Muzyk; 

use5cmf,crt;

 

{SLdoodle.obj} procedurę doodle; excernal;

 

var ch:char;

 

begin 

ifCMF_bladoCOkthen begin

 

background image

wriceln(COpisBledu);

 

halt end;

 

CZagrajCMF[@doodle);

 

repeat

 

untii [CMFStatus=u)or*tkeypressed):

 

if keypressed then ch;=readkey;

 

CZakonczCMF

 

end.

 

ROZDZIAŁ 3

 

Prawdziwe pole do popisu autorom gier i programów rozrywkowych daje jednak dopiero mo

Ŝ

liwo

ść

 jednoczesnego 

odtwarzania muzyki z plików CMF oraz próbek d

ź

wi

ę

kowych zapisanych w formacie VOC. Poni

Ŝ

ej przytaczam 

przykład prostego programu odtwarzaj

ą

cego muzyk

ę

 i, po wykryciu wci

ś

ni

ę

cia klawisza spacji, zawarto

ść

 pliku 

STRZAL.VOC. program Mix; 

{$M 15000.0,50000} uses voc,cmf,crt;

 

var

 

muzyka:poincer, { wskazanie na początek pliku CMF }

 

adglas:pointer: {wskazanie na VOC }

 

ch:char;

 

procedurę CzyBlad;

 

begir

 

ifVOC_blsd<>VClkchen

 

begin

 

wr(Celn[VOpisBledu);

 

halt

 

end;

 

ifCMF_blad<>COkthen

 

begin

 

writeIntCOpisBIedu],

 

halt

 

end end;

 

procedurę Wystrzał;

 

begin

 

VOdtworzVOC(Odgtos3:

 

repeaC untii \/OCStatus=0 end;

 

begin

 

CzyBlad;

 

VinicjujSterowniktO,0]: {wczytanie CT-VOICE } CzyBlad:

 

VOdczytajPlikVOC[Odglos,'STRZALVOC')i CzyBlad;

 

Muzyka:=CZaiadujPlikCMF('BADMAN.CMF'];

 

CzyBtad;

 

wricein;

 

wnteInCSPACJA- Wystrzał, ESC-Koniec');

 

OBSŁUGA PLIKÓW CMF 

 

rozpoczęcie odtwarzania muzyki

 

wnteln.

 

CZagrajCMFtMuzyka): { repeat cn:=#0;

 

if keypressedthen ch:==readkey;

 

ifch=#32then Wystrzał

 

untii [CMFSCatus=0)orCch=#27]

 

if ch=#27 then CZakonczCMF:

 

CResetujSBFM;

 

VZwolnijPamtec[Odglos];

 

CZwolnijPamiecCMFtMuzyka]

 

end.

 

79

 

PROGRAMOWANIE OSP

 

ROZDZIAŁ 3 

 

4. PROGRAMOWANIE DSP 

Rozdział po

ś

wi

ę

cony Jest zagadnieniom zwi

ą

zanym z 

programowaniem układu DSP. Mimo 

Ŝ

e dostarczane z kart

ą

 

sterowniki zawieraj

ą

 procedury komunikacji z kart

ą

, bezpo

ś

rednie 

programowanie układu DSP daje nam znacznie wi

ę

ksze 

mo

Ŝ

liwo

ś

ci. Przede wszystkim jednak, si

ę

gaj

ą

c „w gł

ą

b" karty nie 

jeste

ś

my ju

Ŝ

 zmuszeni do korzystania z formatu VOC przy 

background image

odtwarzaniu plików d

ź

wi

ę

kowych oraz np. mo

Ŝ

emy zaprogramowa

ć

 

własn

ą

 obsług

ę

 zł

ą

cza MIDI. Zreszt

ą

 programowanie DSP nie jest 

wcale trudne - a o funkcjonalno

ś

ci tej metody oprogramowywania 

efektów d

ź

wi

ę

kowych 

ś

wiadczy

ć

 mo

Ŝ

e fakt, 

Ŝ

e relatywnie mało gier 

(czyt. prawie 

Ŝ

adna) korzysta z firmowych sterowników.

 

4.1 ZASADY OBSŁUGI DSP

 

Z układem DSP komunikujemy si

ę

 za pomoc

ą

 czterech portów 

wej

ś

cia/wyj

ś

cia. Adres bazowy (wzgl

ę

dem którego liczymy adresy 

ka

Ŝ

dego z nich) jest ustawiany za pomoc

ą

 zworek na karcie (jako 

220h lub 240h dla karty Sound Blaster 2.0 i jako 210h, 220h, 230h, 
240h. 250h, 260h dla kart w wersji l.x). Domy

ś

lna warto

ść

 tego 

adresu to 220h.

 

ROZDZIAŁ 4

 

Je

Ŝ

eli x oznacza

ć

 b

ę

dzie warto

ść

 wynikaj

ą

c

ą

 z wyboru u

Ŝ

ytkownika (2,4 lub 1,2,3,4,5,6), to adresy 4 portów DSP 

wynosz

ą

•   port zerowania układu (wyjściowy): 2x6h 
•   port odczytu danych (wejściowy): 2xAh 

port zapisu komendy DSP lub danej oraz odczytu statusu wej

ś

ciowego (czy mo

Ŝ

liwy jest zapis) układu: 2xCh 

•   port gotowo

ś

ci danych (wej

ś

ciowy): 2xEh Dodatkowo do pełnej komunikacji z kart

ą

 we wszystkich trybach 

potrzebna jest znajomo

ść

 numeru linii, na której zgłaszany jest koniec transmisji DM

Ą

 oraz wykorzystywany na ni

ą

 

kanał. Numer u

Ŝ

ywanej przez Sound Blaster linii ustawiamy dokonuj

ą

c zwarcia (rozwarcia) odpowiednich zworek 

na karcie. Domy

ś

ln

ą

 warto

ś

ci

ą

 jest 7 (przerwanie IRQ7). Standardowo karla SB wykorzystuje do transmisji kanał l 

DM

Ą

W zasadzie do pracy z kartą potrzebna Jest moŜliwość wykonania trzech podstawowych operacji: zerowania układu, 
zapisu danej (lub rozkazu), odczytu danej. 

Zerowanie DSP 

Zerowanie DSP nale

Ŝ

y przeprowadzi

ć

 jednorazowo przed rozpocz

ę

ciem programowania karty. Proces zerowania 

polega na inicjaliza-cji układu oraz wprowadzeniu go w stan oczekiwania rozkazów. Oto algorytm, według 
jakiego działa

ć

 powinna procedura zeruj

ą

ca DSP: 

1. Zapis bajlu o warto

ś

ci l do portu zerowania (adres 2x6h). 

2. Oczekiwanie przez ok. 3 ms. 
3. Zapis bajlu o warto

ś

ci O do portu zerowania (adres 2x6h). 

4. Powtarzane cyklicznie ok. 100 razy odczyty z portu odczytu danych (2xAh) i oczekiwanie, a

Ŝ

 odebrany bajt b

ę

dzie 

miał warto

ść

 OAAh. 

Je

Ŝ

eli po upływie pewnego czasu (ok. 100 ms) układ DSP nie wy

ś

le w odpowiedzi bajtu o warto

ś

ci OAAh, zerowanie 

uzna

ć

 mo

Ŝ

na za nie udane. 

Zapis do DSP 

Mo

Ŝ

liwo

ść

 wysłania danej lub komendy do układu DSP jest niezb

ę

dna przy jego programowaniu. Do zapisu jest 

u

Ŝ

ywany port 2xCh. Przed prób

ą

 wpisu musimy jednak sprawdzi

ć

, czy DSP jest gotów do odebrania danych - w tym 

celu badamy stan najbardziej znacz

ą

81

 

PROGRAMOWANIE DSP 

 

cego bitu z bajtu odczytanego z tego samego portu. Je

ś

li b

ę

dzie on wyzerowany, mo

Ŝ

emy wysła

ć

 komend

ę

 

(dan

ą

). Procedura wpisu powinna wygl

ą

da

ć

 nast

ę

puj

ą

co: 

l- Odczyt bajtu statusowego z portu 2xCh. 

2. Je

ś

li najbardziej znacz

ą

cy bit odczytanego bajtu jest ustawiony, powrót do poprzedniego punktu. 

3. Zapis danej lub komendy do portu 2xCh. 

Odczyt z DSP 

Odczyt bajtu z DSP realizujemy korzystaj

ą

c z portu 2xAh. Bezpo

ś

rednio przed odczytem nale

Ŝ

y sprawdzi

ć

, czy 

jest ustawiony najstarszy bit bajlu odczytanego z portu gotowo

ś

ci danych (2xEh). Algorytm, wg którego 

powinna działa

ć

 procedura odczytu z DSP, wygl

ą

da nast

ę

puj

ą

co: 

1. Odczyt bajtu z portu gotowo

ś

ci danych (2xEh). 

2. Je

ś

li najstarszy bil odczytanego bajtu jest wyzerowany, powrót do poprzedniego punktu. 

3. Odczyt danej z portu 2xAh. 

Obsługa przerwania DSP 

Układ DSP generuje przerwanie sprzętowe (numer ustawiamy przy instalacji karty, domyślnie IRQ 7) przy 
realizacji: 

•   zapisu dźwięku z przetwornika A/C w trybie DMĄ 

background image

•   odczytu dźwięku z przetwornika C/A w trybie DMĄ 
•   odczytu danych ze złącza MIDI w trybie przerwań Podczas tworzenia procedury obsługi tego przerwania 
naleŜy pamiętać o kilku rzeczach: 
•   Przyjęcie przerwania naleŜy potwierdzić (tzn. poinformować DSP, Ŝe przejęliśmy jego obsługę). 
Realizujemy to odczytując jednorazowo daną jednobajtową z portu gotowości danych (2xEh). Wartość 
odczytanego bajtu nie jest istotna. 
•   Po potwierdzeniu przerwania, w przypadku, kiedy procedura powinna odebrać daną bajtową (odczyt MIDI 
w trybie przerwań), odczytujemy ją z portu 2xAh. 
•   Procedura obsługi przerwania powinna kończyć się wysłaniem sygnału końca (End Of Interrupt) do układu 
kontrolera przerwań 8259. Realizujemy to wysyłając bajt o wartości 20h do portu 20h. 

ROZDZIAŁ 4

 

4.2 TRYB BEZPOŚREDNI

 

Wymiana danych z układem DSP moŜe odbywać się w dwóch trybach: bezpośrednim i DMĄ. Programowanie 
odtwarzania (zapisu) dźwięku w trybie bezpośrednim jest bardzo proste, ale nie pozbawione wad. Na czym 
polega? OtóŜ w trybie bezpośrednim o odbiór i zapis wszystkich danych z i do układu DSP troszczyć się musi nasz 
program. Oznacza to, Ŝe w obu przypadkach jesteśmy zmuszeni do ciągłego, cyklicznie i w równym tempie 
wykonywanego wysyłania lub odbioru danych. Dobrym rozwiązaniem (jeśli chodzi o zachowanie równej na 
róŜnych komputerach prędkości) jest zaprzęgnięcie do pracy obsługi przerwania zegarowego. Jakkolwiek byśmy 
jednak nie postąpili, w trybie bezpośrednim ciągła komunikacja z kartą Sound Blaster oznaczać moŜe 
spowolnienie działania programu. Poza tym w trybie bezpośrednim nie jest moŜliwe odtwarzanie danych 
skompresowanych. 
Do komunikacji z DSP najlepiej przygotować sobie zestaw najbardziej podstawowych procedur. PoniŜej 
przedstawiam tekst biblioteki przygotowanej dla kompilatorów Turbo Pascal. Zawarte w niej funkcje/procedury 
umoŜliwiają zerowanie DSP, wysłanie i odczyt danej z DSP, włączenie i wyłączenie układu DAĆ oraz odczyt i 
zapis bajlu do konwertera DAĆ. Deklarowane w bibliotece zmienne globalne są modyfikowane w części 
wykonawczej modułu. W części wykonawczej Czytelnik moŜe teŜ zauwaŜyć pętlę Repeat-UntiI. Ta część kodu 
biblioteki będzie odpowiedzialna za wykrycie obecności karty dźwiękowej i odpowienią modyfikację wartości 
zmiennej BaseAddr. Wykrycie Sound Blasler'a jest tu realizowane przez powtarzaną, za kaŜdym razem dla innego 
adresu bazowego, próbę zerowania układu DSP. Dla adresu prawidłowego próba powinna się powieść i moŜna 
przyjąć obecność karty. W przypadku, jeśli badany adres przekroczy 260h uznajemy, Ŝe karta nie jest 
zainstalowana w systemie i zmiennej SBInstalled nadajemy wartość False. 

unit DSPDir;

 

{ biblioteka procedur/funkcji bezpośredniego dostępu } { do uktadu DSP kart SB -tryb Direct}

 

interface

 

vsr

 

BaseAddrword; { adres portu bazowego } ResetPort,ReadDataPort,WriteDataPort, 
WriceStatusPorC.DataAYailPorfword;

 

PROGRAMOWANIE DSP 

SBInsCalled:bodean, {czySB ;esc zainstalowany}

 

funccion aSPReset:boolean;

 

funccion DSPRead:byte;

 

procedurę DSPWnte[n:byte);

 

funccion ADCByte:byte;

 

procedurę DACByte(n:byte);

 

procedurę TumDACOn;

 

procedurę TumDACOff;

 

implementation {cz

ęść

 implementacyjna}

 

{resec układu DSP} { odczyt ba|Cu z DSP } {zapis danej/rozkazu} {odczyt bajtu z ADC} { zapis do DAĆ } {włącznie DAĆ} {wyłączenie DAĆ}

 

 

funccion DSPReset:boole3n;

 

van:byte;

 

begin

 

{uwaga ; w procedura nie korzysta ze zmiennych } {opisujących adresy poszczególnych portów DSP,} { poniewaŜ nie mają one jeszcze 
ustalonej wartości} porttBaseAddr+$5l:=1;

 

for i: =3 downco O do; { opóźnienie} porttBaseAddr+$61:=0;

 

repeat incCi] until(port[BaseAddr+$E]and12B = 12B3ar(i"100);

 

ifi=1DOthenDSPReset:=false

 

else DSPReset:=[portEBaseAddr+$A3=$AA) {funkcja zwraca wartość TRUE gdy zerowanie OSP} {przebiegło pomyślnie} end;

 

function DSPRead:byte;

 

begin

 

repeat

 

background image

untii tport[DataAvailPort] and 128 = 128);

 

DSPRead: =port[ReadDataPort] end;

 

procedur

ę

 DSPWritetn;byte];

 

begin

 

repeat

 

untii (popttWriteStatusPort] and 128 = 03;

 

port[WriteDataPort]:=n end;

 

function ADCByte:byte;

 

begin

 

repeat

 

ROZDZIAŁ 4

 

PROGRAMOWANIE DSP 

85

 

 

 

uncil [port[Wrn:eScatusPort] snd 128 = 0);

 

port[WnteDataPort]:=$20:

 

repeac:

 

unt-i! [port[DataAvailPort] and 128 = 128];

 

ADCByte: ^portIReadDataPortl

 

end,

 

procedur

ę

 DACByte[n:byte);

 

begin

 

repeat;

 

uncil tporttWnteStatusPort] and 128 = 0];

 

port[WriteDataPcrt]:=$10,

 

repeat

 

untii [pordWnteStatusPcrt] ard 128 = 0];

 

port[WnteDataPort];=n end;

 

procedur

ę

 TurnDACOn;

 

begin

 

repeat untii [porcEWnteSCatJsPort] and 128 = 03:

 

porctWriteDataPortl: =$01 end;

 

procedur

ę

 TurnDACOff;

 

begin

 

repeat

 

uncil (porttWriteStatusPortl and 126 = O],

 

port[Writ;eDataPortl;=$D3 end,

 

begin

 

{wpierw odszukamy SB próbując zerować DSP przez }

 

{porty 2x0h}

 

BaseAddr:=$20D;

 

repeaC

 

inc(BaseAddr,$103;

 

untii [Ba5eAdd^=$2B01o^EQSPI

:

leset:]:

 

if BaseAddr=$280 then SBInstalled: =false elseSBInstalled:=true;

 

{teraz następuje nadanie odpowiednich wartości}

 

{ kolejnym zmiennym }

 

ResetPort:=BaseAddr+$6;

 

ReadDataPort:=BaseAddr+$A;

 

WriteDataPort;=BaseAddr+$C;

 

WriteStatusPor't:=BaseAddr+$C;

 

DacaAvailPort:=BaseAddr+$E end, {koniec}

 

Wykorzystanie biblioteki jest bardzo proste. PoniŜej przedstawiam przykładowy program pozwalający zorientować 
się w zasadach uŜywania zawartych w niej funkcji. Program jest bardzo prosty, ale ilustruje metodę uzyskiwania 
obrazów fali podawanej na wejście mikrofonowe. Działanie programu jest zbliŜone do działania oscyloskopu - na 
ekranie obserwujemy przebieg fali w pewnym stałym okresie. Rysunek jest cyklicznie uaktualniany z szybkością 
wynikającą z moŜliwości komputera i szybkości układu DSP (w chwili, gdy prędkość wykonywania poszczególnych 
operacji jest odpowiedni wysoka, „wąskim gardłem" staje się tempo odczytu z DSP). Program działa w trybie 
graficznym 13h kart VGA (320x200, bajt na punkt). program Oscyloskop; 

uses DSPDir.crt:

 

const

 

var

 

ScreenBase;word=$AOOO;

 

x:word;

 

ch:char;

 

procedurę Set320x200Mode;

 

background image

{ ustaw tryb ekranowy VGA 13h } assembler;

 

asm

 

end;

 

movax,$0013 int$10

 

procedurę DrawLine[x:word;n:byte);

 

{ narysowanie „słupka" ilustrującego chwilowy poziom } { sygnału podawanego z wejścia mikrofonowego } var y:byte;

 

begin

 

y:=100+t(n-127) div2]; { obliczmy współrzędna }

 

MemtScr-eenBase^D^y+Kl: =10 end,

 

procedurę Clear;

 

{wyczyszczenie roboczej części ekranu}

 

begin

 

FillChartMem[ScreenBase:11200],41600,03 end;

 

 

ROZDZIAŁ 4

 

begn

 

Direct:Video:=false,

 

{ znaki stawiamy korzystając z usfug systemowych }

 

ifnotSBInstałled then

 

begin

 

wnteInCBrak karty Sound Blaster');

 

haft end, TurnDACOff;

 

{wyłączenie DAĆ} Sec320x200Mode;

 

CexCcolorCLight:Red);

 

write['OSCYLOSKOP']:

 

gotoxy(1,24];

 

wriCeCEsc-koniec'];

 

x:-0;

 

repeat ifx<319then incbdelse

 

begin

 

clear:

 

x:=0

 

end;

 

DrawLine[x,ADCByte);

 

untii keypressed;

 

{ powtarzaj, dopóki ktoś nie wciśnie klawisza } if keypressed Chen ch:=readkey;

 

textmode(lsst:mode)

 

end.

 

Wspomniałem, Ŝe w celu zapewnienia równomiernej w czasie prędkości przesyłania danych, wykorzystać moŜemy 
przerwanie zegarowe. OtóŜ w komputerach PC znajduje się układ 8253 lub 8254 o trzech kanałach wyjściowych: 

•   Kanał O - u

Ŝ

ywany do zliczania czasu. Podczas startu komputera procedury zawarte w BIOS 

programuj

ą

 układ zegarowy, tak by wysyłał tym kanałem sygnał 18.2 razy w ci

ą

gu ka

Ŝ

dej sekundy.

 

•  Kanał l - u

Ŝ

ywany przy od

ś

wie

Ŝ

aniu RAM,

 

•   Kanał 2 - u

Ŝ

ywany przy kontroli gło

ś

niczka komputera. Dla nas najistotniejszy jest kanał O, gdy

Ŝ

 jest on 

bezpo

ś

rednio sprz

ęŜ

ony z wej

ś

ciem kontrolera przerwa

ń

, który, ka

Ŝ

dorazowo po pojawieniu si

ę

 sygnału, 

wywołuje przerwanie 8h. Okazuje si

ę

. 

Ŝ

e cz

ę

stotliwo

ść

, z jak

ą

 8253(4) powoduje przerwanie, mo

Ŝ

na 

zmieni

ć

. Wystarczy bowiem zmodyfikowa

ć

 warto

ść

 16-bitowego dzielnika cz

ę

-

 

87

 

PROGRAMOWANfE OSP

 

 

stotliwo

ś

ci układu dla kanału 0. Warto

ść

, któr

ą

 standardowo inicja-lizowany jest dzielnik, wynosi FFFFh. 

Dlatego cz

ę

stotliwo

ść

 wyj

ś

ciowa wynosi pocz

ą

tkowo ok. 18 Hz (cz

ę

st. wej

ś

ciowa równa 1.19318 MHz 

podzielona przez 65535 daje 18.207 Hz). 

Do zaprogramowania generatora związanego z kanałem O potrzebne jest jeszcze kilka informacji. Myślę, Ŝe 
najprościej będzie, gdy przedstawię schemat, wg jakiego powinna działać procedura zmieniająca procedurę 
obsługi przerwania 8h i zmieniająca częstotliwość generatora 0: 

1. Zapamiętujemy wektor przerwania 8h. 
2. Zabraniamy wykonywania przerwania IRQO (ustawiamy bit O w bajcie odczytanym z portu 21h i tak 

zmienioną wartość wysyłamy z powrotem przez port 21h do kontrolera przerwań). 

3. Zmieniamy wektor 8h tak, by wskazywał na naszą procedurę. 
4. Do portu o adresie 43h (rejestr sterujący) wysyłamy rozkaz o kodzie 36h. Dla pełnej jasności podam 

znaczenie poszczególnych bitów rozkazu: 

bity 7-6 - wybór kanału (dla kanału O oba bity wyzerowane) bity 5-4 - rodzaj operacji, jaką ma 

background image

wykonać układ (zapis lub odczyt obydwu bajtów dzielnika - oba bity ustawione) 

bily 3-1 - tryb pracy (tu trzeci: bity Oli) bit O   - sposób odliczania (dla zliczania w kodzie binarnym od 

FFFFh do O bit powinien być wyzerowany). 

5. Do portu 40h wysyłamy kolejno wpierw młodszy, później starszy bajt licznika. Wartość licznika wyliczyć 

moŜemy korzystając z zaleŜności: 

L == 1193180 l częstotliwość 

6. Zezwalamy na wykonywanie przerwania IRQO (zerujemy bit O bajtu odczytanego z portu 21h i tak 

zmienioną wysyłamy z powrotem do kontrolera przerwań). Dla pełnej jasności przytoczę tekst źródłowy 
programu odtwarzającego wskazany parametrem plik dźwiękowy wykorzystując przerwanie 8h. Program jest 
bardzo prosty - nie rozpoznaje formatu odczytywanego pliku, a częstotliwość odtwarzania przyjęta została 
jako 8000 Hz. Rozmiar pliku, którego zawartość ma być odtworzona, ograniczona jest wielkością dostępnej 
pamięci operacyjnej. 

program PlayBIN,

 

{SM 15000.0,50000}

 

uses DSPDindos.crt;

 

var

 

Spec:string, { specyfikacja pliku z danymi} F-file;

 

Segment,SegBuf,afsBuf,Blocks,Res:word:

 

Counc,Len:longint;

 

{licznik i zmienna przechowująca długość pliku } Old6h:painter;

 

{ wskazanie na dawna procedurę obsługi przerwania } ch:char;

 

{$F+}

 

procedurę SendDneByte; {wysyła bajt do DAĆ }

 

interrupt;

 

begin

 

ifCounC<=Lenthen

 

{jeśli jeszcze nie cały plik } begin ifOfsBuf=$10then begin IncCSegBuf);

 

OfsBuf:=0 end:

 

DACByte(MemESegBuf:OfsBuf]);

 

incECount);

 

incEOfsBuf) end;

 

port[$20]:=$20{EO!} end;

 

{$F-}

 

procedur

ę

 AllacateMem[parag:word);

 

{aibkacja zadanej parametrem liczby paragrafów}

 

assembler,

 

as m

 

movbx,parag

 

movah,$48

 

int$21

 

cmp bx,parag

 

je @koniec

 

xorax,ax @koniec: movSegBuf,ax

 

movOfsBuf,Q

 

ROZDZIAŁ 4 

PROGRAMOWANIE OSP 

end:

 

procedurę FreeBuf;

 

{ zwolnienie pamięci przydzielonej plikowi} assembler:

 

as m

 

movah,$49

 

mov es,Segment:

 

int21h end;

 

procedurę SecIntrRoutme:

 

{ zaprogramowanie Timer'a i ustawienie wektora 8h } begin

 

getinCvec[$a,01d6h3;

 

porHSSIh-port^l ] or $01;

 

setintvect$8,@SendOneByte),

 

port[$431i=$36;

 

porc[$40];=lot1193180divBOOO);

 

porc[$4ai:-hi[11931BOdivB0003;

 

port;l$21]:=port;[$21]and$FE end;

 

procedurę ScopPlaying;

 

{ przeprogramowanie Timer'a i przywrócenie } { pierwotnej wartości wektorowi 
przerwania } begin

 

port[$21]:=port[$21] or $01;

 

portt$43]:=$36;

 

background image

port^O^O;

 

port[$40];=0;

 

setintvect$6,QldBh);

 

por'c[$21l:=portt$21] and $FE end;

 

begin

 

ifnotSBInstaIlsdthen

 

begin

 

writeln['Sound Blaster nie zainstalowany!'];

 

halt end;

 

if paramcount^D then begin

 

writefPlik, który mann odtworzy

ć

 : '];

 

readIntSpec];

 

ifSpec="then halt end slse 
Spec:=paramstr[1];

 

ROZDZIAŁ 4

 

355 ignIF; Sp

ę

d;

 

{$!-}

 

resettF),

 

{$!+}

 

iflOResultodthen

 

begin

 

writeirCBrak wskazanego pliku !'];

 

hale

 

end;

 

reset[F,1];

 

Len:=filesize[R;

 

AtlocateMemCtLen+15] shr4);

 

ifSegBuf=Ochen

 

begin

 

wriCeInCBtad allokacji pami

ę

ci!');

 

halt

 

end;

 

Segment::=SegBuf;

 

Blocks:=0;

 

repeat eiockReadEF,Mem[SegBuf+BlockEMD96;OfsBuf],$FFFF.Res];

 

inc(B!ocks3 untii Fles=0;

 

close(F);

 

TurnDacOn;

 

SeCinCrRoutine;

 

Count:=0;

 

writeInCOdtwarzam (cz

ę

stotliwo

ść

 8 kHz)...');

 

repeat

 

untii [Count=Len]or(keypressed);

 

ifkeypressedthen ch:=readkey:

 

StopPlaying;

 

FreeBuf

 

end,

 

Na koniec przedstawię jeszcze jeden program. Jego zadanie to odtwarzanie zapisanego wcześniej dźwięku w 
ustalanym przez uŜytkownika tempie. Program jest bardzo prosty - szybkość jego działania zaleŜy od prędkości 
komputera, na jakim go uruchomimy. Przy szybkiej maszynie moŜe się więc okazać, Ŝe zakres zmian opóźnienia przy 
odtwarzaniu jest zbyt mały. Komunikacja z kartą odbywa się w trybie bezpośrednim (w programie korzystam z 
przedstawionej wcześniej biblioteki). Zasady zabawy są następujące: klawisze kursora - zmiana prędkości 
odtwarzania dźwięku, klawisz ESC -zakończenie pracy. Zapisu dźwięku dokonujemy przy wciśniętym klawiszu Shifl 
(w momencie zwolnienia klawisza program rozpocz- 

91

 

PROGRAMOWANIE DSP 

 

nie wysyłanie kolejnych bajtów próbki do przetwornika cyfrowo-analogowego). Program nie jest 
bardzo u

Ŝ

yteczny, ale jego samodzielna analiza z pewno

ś

ci

ą

 pomo

Ŝ

e w zrozumieniu zasad 

programowania operacji zapisu (odtwarzania) d

ź

wi

ę

ku w trybie bezpo

ś

rednim DSP. 

{$tvn5oaG,o,5aoou}

 

program Zabawa;

 

uses 

var

 

Councer,SegBuf,OfsBuf:Word;

 

i,Mernory:Longint;

 

background image

j,Loop:Byte;

 

ch:char;

 

procedurę Allocat;eMem[parag:ward);

 

{ allokacja zadanej parmetrem liczby paragrafów } assembler;

 

as m

 

mov bx,parag

 

movah,$46

 

int$21

 

cmp bx,panag

 

je (©koniec

 

xor ax,ax <§>koniec: mov SegButax

 

mov OfsBuf.O end;

 

procedurę FreeBuf;

 

{ zwolnienie przydzielonej pamięci} assembler:

 

as m

 

movah,$49

 

moves,SegBuf

 

int21h

 

end;

 

funcCion ShifcPressed:Boolean;

 

{ wciśnięty klawisz Shift} begin

 

ShiftPressed:=[Mem[0;$417] and 3] o O end;

 

ROZDZIAŁ 4

 

begin

 

{ reset DSP - przy okazji sprawdzimy obecno

ść

 SB } ifnotdspreset then

 

begin

 

wncetnCSB nie zainstalowany');

 

hale {kamee gdy bł

ą

d resetu }

 

end:

 

Memory:=600DG; { wielko

ść

 bufora na zapis } AllocateMemtMemory shr 4 + 1); {rezerwujemy pami

ęć

 } ifSegBuf=Ot;hen

 

begin

 

wntelnIZbyt mało pami

ę

ci operacyjnej'];

 

hale { bł

ą

d allokacji} end;

 

DirectVideo:=True;

 

cirscr:

 

Loop;=125; {zawo

Ŝ

ona warto

ść

 opó

ź

nienia} gDtoxyt40,3);

 

write^27,^26,' - Szybko

ść

 odtwarzania ; '];

 

gotoxy[5,33;

 

write[");

 

gotaxyt6-[Loopdiv10),3];

 

writeE#2193;

 

gotaxy[70,3];

 

wntetLoop];

 

gotoxy[5,5];

 

writeCShift - zapis d

ź

wi

ę

ku ');

 

gotoxy(5,S];

 

writeCEsc -koniec zabawy');

 

repeac ifShiftPressedchen

 

begin

 

TurnDacOff; {wył

ą

czenie DA

Ć

}

 

Counter:=0; {wska

ź

nik = O }

 

got;oxy[1,253;

 

TextAt;cr:=112;

 

wriCefZAPIS']:

 

repeac

 

Mem[SegBuf:Ofs3uf+CounCer]:=ADCByte;

 

{ pobranie bajtu z przetwornika } inctCounter] {inkrementacja wska

ź

nika }

 

untii ECounter'=Memory]or[not[ShiftPressed]);

 

gotoxy[1,253;

 

TextAttr:=7;

 

write['ODTWARZAM'];

 

i:=0.

 

TumDACOn;

 

repeat

 

93

 

PROGRAMOWANIE DSP

 

 

forj:=0 to Loopdo, { opó

ź

niamy)

 

DACByte[Mem[SegBuf:OfsBuf+i]]; { wysłanie do DA

Ć

 incU)

 

background image

until l>Counter,

 

got;oxy[1,25];

 

wntet'        ')

 

end:

 

ch:=#255;

 

if keypressed Chen ch' =readkey;

 

ifch=#0then ch:=readkey;

 

if [ch= #75)and(Loop>5] Chen dec[Loop,5];

 

if[ch=#77)and[Loop<245)chen inc[Loap,5):

 

if(cho#255]then

 

begin

 

gotoxy[5,33;

 

writet");

 

gotoxy[6+[Loopdiv 10),3);

 

write(#219];

 

gocoxy[70,3);

 

write(Loop:3]

 

end;

 

unt:ilch=#27;

 

TurnDacOff; { wył

ą

cz DA

Ć

 } FreeBuf {zwolnij pami

ęć

 przydzielona na bufor) end.

 

4.3 Tryb DMĄ

 

Odtwarzanie i zapis próbek dźwiękowych w trybie bezpośredniej komunikacji z układem DSP z wielu 
względów jest niepraktyczne. Po pierwsze, transmisja danych w trybie bezpośrednim w znacznym stopniu 
absorbuje procesor. Po drugie, bardzo trudno jest zapewnić równomierną prędkość przesyłania danych. 
Dlatego najlepszą drogą jest korzystanie z moŜliwości transmisji w trybie DMĄ - nie zabiera ona czasu 
procesorowi i nie ma problemów z kontrolą jej prędkości (zajmuje się tym układ DSP). Poza tym, w trybie 
DMĄ moŜliwy jest zapis i odtwarzanie skompresowanych przez DSP według trzech dostępnych schematów: 4-
bitowego (kompresja 2:1), 2.6-bitowego (kompresja 3:1) i 2-bitowego (4:1). W trybie bezpośrednim jest to 
niemoŜliwe. 
Ŝądanej szybkości transmisji informujemy układ DSP, ustawiając odpowiednią wartość 
jednobajtowego parametru TIME_CONSTANT
 

ROZDZIAŁ 4 

za pomocą rozkazu 40h (patrz opis komend DSP). Reguła, według której ustalamy wartość zmiennej, jest 
następująca: 

TIME_CO^'STAN7'=256-1'OOOOOO/'częstotliwość próbkowania 

Drugim parametrem jest długość przesyłanego bloku danych, opisywana przez wartość dwubajtowego parametru 
DATA_LENGTH. Jego wartość ustawiamy uŜywając komendy ł4h. NaleŜy pamiętać, Ŝe wartość DATA_LENGTH 
powinna być mniejsza od ilości bajtów przeznaczonych do przesłania o l. Oznacza to więc, Ŝe dla przesyłu l bajta 
wartość parametru powinna być równa 0. Ponadto istotny jest fakt, Ŝe jako pierwszy, po kodzie rozkazu ł4h, 
powinien być wysłany do DSP młodszy bajt danej. Maksymalna długość bloku przeznaczonego do przesłania wynosi 
64 KB (dla DATA_LENGTH = FFFFh). Wynika to z charakterystyki kontrolera DMĄ, dla którego ograniczeniem 
jest rozmiar fizycznej strony pamięci (64 KB). Nie oznacza to bynajmniej braku moŜliwości transmisji większej 
ilości danych - blok bajtów składających się na próbkę dźwiękową naleŜy podzielić na kilka mniejszych, l tak próbkę 
rozpoczynającą się od adresu 7FOO-.0000 i kończoną bajtem znajdującym się pod adresem 7FOO:2FFF podzielić 
musimy na dwa bloki: pierwszy - rozpoczynający się od adresu 7000:FOOO. drugi - od adresu 8000:0000. Najlepiej 
będzie, jeŜeli „stronicowanie" pamięci przedstawię w formie tabelki: 

Strona Segment:0ffset 
O       0000:0000-0000:FFFF 

1       1000:0000-1000:FFFF 
2       2000:0000-2000:FFFF 
3       3000:0000-3000:FFFF 

F       FOOO:0000-FOOO:FFFF 

Podczas programowania transmisji w trybie DMĄ nie wystarczy samo programowanie karty SB - na rozpoczęcie 
transmisji naleŜy teŜ przygotować układ DMĄC. Co waŜniejsze, układ DMĄC musi być zaprogramowany, jeszcze 
zanim przystąpimy do programowania DSP karty Sound Blaster. Parametry, jakich wymaga DMĄC, są następujące: 

l. Numer strony - numer fizycznej strony pamięci, w której znajdują się dane do przesłania. 

PHOGRAMOWANfE DSP

 

95

 

2. Adres bazowy - 2-bajtowa wartość określająca przesunięcie (offset) wewnątrzstronicowe początku danych 

background image

przeznaczonych do transferu. Numer strony i adres bazowy w sposób jednoznaczny opisują połoŜenie komórki 
pamięci w obszarze l MB. Składają się na 20-bitowy adres, którego wartość wyliczyć moŜemy następująco: 

ADR20 = I6*SEGMENT + OFFSET 

Numer strony to wielkość 4-bitowa stanowiona przez najstarsze 4 bity 20-bilowego adresu, a adres 
bazowy to młodsze 16 bitów adresu 20-bitowego. 

3. Licznik - 2-bajtowa wielkość odpowiadająca parametrowi DA-TA_LENGTH (długość bloku danych w 
bajtach pomniejszona o l). 
4. Tryb transmisji - bajt określający kierunek przepływu danych (zapis danych do pamięci bądź ich pobranie). 
I tak wartość 45H odpowiada zapisowi dźwięku (zapis do pamięci), a wartość 49H odtworzeniu (odczyt z 
pamięci). 

Samo programowanie układu DMĄC nie jest bardzo skomplikowane, ale na potrzeby tej ksiąŜki ograniczę się 
do opisu algorytmu. który moŜemy wykorzystać przy ustawianiu transmisji. Zakładam, Ŝe karta SB jest tak 
skonfigurowana, Ŝe wykorzystuje pierwszy kanał DMĄ. 

1. Zapis bajtu o wartości 5 do portu OAH. Informujemy w ten sposób sterownik DMĄC, Ŝe będziemy 
programować kanał l. 
2. Zapis bajtu o wartości O do portu OCH. 
3. Zapis bajtu o wartości odpowiedniej dla trybu transmisji do portu OBH. Zapis wartości 49H spowoduje 
więc ustawienie trybu odczytu z pamięci (odtworzenie dźwięku). 
4. Zapis młodszego bajtu adresu bazowego (patrz wyŜej) do portu 02H. 
5. Zapis starszego bajtu adresu bazowego do portu 02H. 
6. Zapis numeru strony do portu 83H. 
7. Zapis do portu 03H młodszego bajtu licznika. 
8.  Zapis do portu 03H starszego bajtu licznika. 
9. Zapis do portu OAH bajtu o wartości l (odblokowanie kanału l). PoniŜej przedstawiam tekst źródłowy 
przykładowego programu, w którym wykorzystana jest transmisja w trybie DMĄ. Program odtwarza próbkę 
dźwiękową skonsolidowaną z kodem programu. 

ROZDZIAŁ 4

 

ppogramDMABeep;

 

uses dos,dspdir,crt:

 

consc Czestotitwosc:word=130GD;

 

Dlugosc-word=4372;

 

var

 

Adres;loriginC; {tu przechowamy adres 20-bitowy} Przesuniecie:word; { przesunięcie wewnatrzstronicowe } Strona:byte;   { numer scrony} 
Old!RQHandler':pointer; { stary wektor przerwania 1RQ7 } Zakonczone:boo!ean;   { czy transmisja zakończana }

 

{$!_ beepl.obj}             { plik z danymi dźwiękowymi} procedurę Dbeep; external,

 

procedurę ProcIRO; interrupt:{ nasza procedura obsługi} begin                     { przerwania zgłaszanego na IRQ7 } Zakonczone:=true;

 

port[$20]:=$20   {EDI} end;

 

begin

 

if not SBInstalledthen { czy wykryto kart

ę

 } begin writeInCBrak karty Sound Blaster'!

1

];

 

haft end;

 

getintvec[15,OldlRQHandler): { zapamiętujemy stary wektor} setintvec[15,@ProclRQl; { „naginamy"}

 

Zakonczone:=false;

 

TurnDACOn; {włączenie głośnika }

 

port[S213:=port[$21]andnott1 shl7):

 

{ odblokowanie linii IR07 }

 

port[$OA]:=5; { ustawienie maski dla kanału DMĄ 1 } port[$OC]:=0;

 

port[$OB]:=$49; {transmisja 2 pamięci do karty}

 

Adres^lS^longint^egtDbeep^+ofstDbeep);

 

Strona: =byteCAdres div 65535);

 

97

 

PROGRAMOWANIE OSP 

 

Przesuniecie.=word[Adnesand$FFFFl:

 

port[$02] =Przesuniecie and $FF: { LSB przesunięcia } po"t[$02]=Pr2esuniecieshr8,   { MSB pr-zesunięcia }

 

pQ'-t[$B3]:=3trana:

 

portt$D31:= Długość and $FF; { młodszy bajt długości} port[$03]:=DlLjgosc shr6. { starszy bajt długości} port[$OA]: = 1;    
{ aktywacja kanału DMĄ 1 }

 

{teraz podamy stała odpow- częstotliwości odtwarzania 

DSPWnte[$40);

 

DSPWrite[256-1000000 div Częstotliwość],

 

{ poinformujemy o długości próbki i jazda i} DSPWntet$483:

 

DSPWriteLDIugosc and $FF];

 

background image

DSPWrite[Dlugoscshr8);

 

OSPWrite[$14);

 

DSPWrite[Dlugosc and $FF3;

 

DSPWritetDlugoscshrB);

 

wntein;

 

whte['B');

 

repeat

 

write['e'];

 

delay[4Q]

 

unti) Zako

ń

czone; { oczekuj na koniec } wnteln['p!');

 

TurnDacOff; { wyłączenie DAĆ } SetlntVec[15,01dlRQHandler]; {stary wektor} port[$21]-port[$21]or(1 shl7) 
end.

 

4.4 OBSŁUGA ZŁĄCZA MIDI

 

Karty serii Sound Blaster mają moŜliwość współpracy z instrumentami wyposaŜonymi w złącze typu MIDI 
(Musical Instrument Digital Interface). Niestety - nie bezpośrednio - do karty dokupić naleŜy tzw. SB MIDI 
KIT (podłączamy go do złącza joystick'a). Standard MIDI określa sposób transmisji: szeregowo, 
asynchronicznie, z prędkością 31250 bitów na sekundę przy ośmiobitowym słowie i bez kontroli parzystości. 

ROZDZIAŁ 4 

Komunikację ze złączem M1DI nadzoruje układ DSP. Realizować ją moŜemy korzystając z rozkazów 3xh. 
Zasadniczo istnieją dwa tryby komunikacji z M1DI: tryb bezpośredni i tryb przerwań. Operacji wysiania danej przez 
złącze M1DI dokonać moŜna tylko w trybie bezpośrednim. 

Tryb bezpośredni 

W trybie tym komunikujemy się ze złączem obustronnie wykorzystując rozkazy układu DSP. PoniŜej przedstawiam 
tekst procedury MłDIWrite(what:byte), realizującej wysłanie zadanej parametrem jed-nobajtowej danej złączem 
MłDI oraz funkcji MłDłRead:byte realizującej odczyt w trybie bezpośrednim danej jednobajtowej ze złącza M1DI. 
Globalna zmienna MIDIReadErr typu Boołean przyjmuje po wywołaniu funkcji MlDIRead wartość False w 
przypadku, gdy karta przyjęła daną oraz True w przypadku wystąpienia błędu (zbyt długi czas oczekiwania na 
nadejście danej). JeŜeli chcemy włączyć którąś z procedur do programu, zadeklarować w nim musimy uŜycie 
biblioteki DSPDir. 

var 

MIDIReadErr: Boołean:

 

procedur

ę

 MIDIWriteCwhafbyte];

 

begin

 

DSPWrite($38); { kod rozkazu zapisu do M1DI}

 

DSPWriteCwhat) { wystanie bajtu }

 

end;

 

funccion M!DIResd:byte;

 

var

 

Tinie:byte; {licznik pętli} begin

 

Time:=0;

 

DSPWrite[$30); { odczyt z MIDt w trybie Direct} repeat

 

incCTime) {zwiększ licznik} untii

 

tport[DataAvailPort] and 128 = 12B]ar(Time=100);

 

ifTime=100then

 

MIDIReadErr^True { upłynął czas na odpowiedź } else

 

begin

 

M!DIRead:=port[ReadDataPortl; { odczyt} MIOlReadErr=False {wszystko Ok} end end,

 

99

 

PROGRAMOWANIE DSP 

 

Tryb przerwa

ń

 

W trybie przerwań moŜliwy jest tylko odczyt nadchodzących na złącze MłDI sygnałów. W trybie tym nie 
musimy cyklicznie „odpytywać" portu M1DI - układ DSP sam „zawiadomi nas" o nadejściu danych. Schemat 
postępowania jest tutaj następujący: 

1. „Nagiąć" wektor przerwania obsługującego transmisję w trybie DMĄ z kartą tak. by wskazywał naszą 

procedurę. 

2. Odblokować odpowiednią linię 1RQ zerując właściwy bit rejestru maskującego pisząc do portu 21 h. 
3. Wysłać kod rozkazu 31 h do układu DSP, aby zainicjować odczyt ze złącza M1DI w trybie przerwań. 
4. Realizować inne zadania. W razie pojawienia się danej na złączu MIDł na odpowiedniej linii IRQ pojawi 

się sygnał i wywołana zostanie zainstalowana przez nas procedura. Jej działanie ograniczać się powinno 
do potwierdzenia przyjęcia przerwania oraz odczytu danej z portu odczytu danych układu DSP. 

background image

5. Zatrzymać operację odczytu z MłDI moŜemy wysyłając ponownie do układu DSP kod rozkazu 31 h. 
6. Zablokować odpowiednią linię IRQ (z kontrolerem przerwań 8259 komunikujemy się tu przez port 21 h). 
7. Odtworzyć oryginalną (pierwotną) wartość wektora odpowiedniego przerwania. 

4.5 KOMENDY DSP

 

W rozdziale tym przedstawię zestawienie komend DSP. KaŜda z nich jest związana z jednobajtowym kodem, 
który musimy wysłać (patrz procedura zapisu) do układu DSP. 

Rozkaz lxh 

Komendy lxh dotyczą 8-bitowej konwersji cyfrowo-analogowej w trybach bezpośrednim i DMĄ oraz 
konwersji 2-bitowej ADPCM w trybie DMĄ. Uwagę naleŜy zwrócić na fakt, Ŝe podczas zainicjowanej 
rozkazami lxh transmisji DMĄ moŜemy wysyłać dane do portu MłDI OUT. 
lOh - Wysłanie tego rozkazu do DSP umoŜliwia bezpośrednie wysłanie do przetwornika cyfrowo-
analogowego pojedynczego bajtu. 

ROZDZIAŁ 4

 

14h - Inicjacja transmisji danych 8-bitowych do przetwornika cyfro-wo-analogowego w trybie DMĄ. JeŜeli chcemy 
wykorzystać len nasz rozkaz, fragment naszego programu odpowiedzialny za odtwarzanie dźwięku działać powinien 
według poniŜszego schematu: 

•   umieścić dane przeznaczone do transmisji w pamięci operacyjnej, 

zaprogramować układ 8237 (przygotować kontroler DMĄ do transmisji), 

•   ustalić stałą czasową TIME_CONSTANT dla transmisji z uŜyciem rozkazu 40h (odpowiednio dla częstotliwości, 
z Jaką odtwarzany sygnał był próbkowany), 
•   wysłać do układu DSP rozkaz 14h, 

wysłać do układu DSP wielkość szesnastobitową (DATA-JLENGTH) opisującą długość przeznaczonego do 
transmisji bloku danych pomniejszoną o jeden. NaleŜy zwrócić uwagę, aby Jako pierwszy wysłany został mniej 
znaczący (młodszy) bajt wartości. 

Uwaga: Transmisja zostanie rozpoczęta bezpośrednio po ostatniej z wymienionych czynności. NaleŜy pamiętać o 
podzieleniu bloków danych leŜących na granicy 64 KB stron pamięci na mniejsze. Wysłanie kaŜdego następnego z 
nich inicjować moŜe procedura obsługi przerwania końca transmisji DMĄ. 
16h/17h - Inicjacja transmisji w trybie DMĄ danych i ich konwersji w trybie 2-bitowym ADPCM. Schemat działania 
procedury wykorzystującej ten rozkaz jest następujący: 

•   umieścić dane przeznaczone do transmisji w pamięci operacyjnej, 
•  odpowiednio zaprogramować układ kontrolera DMĄ, 
•   ustalić stałą czasową (TIME_CONSTANT) odpowiednio do częstotliwości, z jaką sygnał był próbkowany, wystać 
do układu DSP rozkaz 16h lub 17h (17h dotyczy transmisji danych do DAĆ z tzw. bajtem odniesienia), 
•   wysłać do DSP 2 bajty opisujące długość bloku przeznaczonego do transmisji (jako pierwszy wysyłamy młodszy 
bajt). Jak widać, algorytm działania jest tu taki sam jak przy rozkazie 14h. Sposób, w jaki przygotować musimy dane 
do transmisji, jest obłoŜony ograniczeniami wynikającymi z samej metody. 

101

 

PROGRAMOWANIE DSP

 

 

Rozkaz 2xh 

Komendy 2xh dotyczą transmisji z przetwornika analogowo-cyfro-wego danych 8-bitowych w trybach DMĄ i 
bezpośrednim. 
20h - Bezpośredni odczyt jednego bajtu z przetwornika. Po wysłaniu wartości 20h do układu DSP moŜemy 
odczytać z niego pojedynczy bajt odpowiadający chwilowej wartości podanego na wejście mikrofonowe 
sygnału. Schemat działania procedury odczytu ciągu bajtów z przetwornika wygląda następująco: 

•  wysłanie komendy 20h do układu DSP 
•   odczyt pojedynczego bajtu z DSP 
•   oczekiwanie (wtedy, gdy chcemy np. zapisywać próbki co określony czas) i powtórzenie pierwszych dwóch 
operacji lub zakończenie odczytu danych. 

24h - Inicjacja transmisji z przetwornika ADC w trybie DMĄ. Algorytm: 

•  wysłać kod komendy(24h) do układu DSP 
•   wysłać 2 bajty (pierwszy wysyłamy młodszy bajt !) opisujące 

długość bloku danych, jaki chcemy przyjąć. Zasady, jakich powinniśmy przestrzegać przygotowując samą 
transmisję wynikają z reguł programowania i działania DMĄC. 

Rozkaz 3xh 

background image

Komendy umoŜliwiają komunikację z portami MIDI. 
30h - odczyt z portu MIDI (tryb odpytywania). Po wysłaniu kodu tej komendy do układu DSP moŜemy 
odczytać daną podaną na wejście MIDI z portu odczytu danych DSP (2xAh). Oczywiście pamiętać naleŜy o 
uprzednim testowaniu stanu bitu 7 (czy jest ustawiony na l) portu statusowego (2xEh). 31 h - odczyt z portu 
MIDI w trybie przerwań. Odczyt danych z portu MIDI z wykorzystaniem przerwań jest bardziej wskazany. 
Zasada jest tutaj prosta: układ DSP generuje sygnał przerwania na linii IRQ. Procedura obsługi przerwania, 
instalowana przez nasz program, odczytywać powinna daną z portu MIDI. Odczyt wartości z portu 
statusowego (2xEh) regeneruje sterownik przerwań. Powtórne wysłanie rozkazu 31h zatrzymuje cały proces. 
38h - Zapis do portu MIDI. Jako pierwszy wysyłamy do układu DSP kod rozkazu 38h, a zaraz potem bajt, 
który chcemy wysłać przez port MIDI. 

ROZDZIAŁ 4

 

Rozkaz 40h 

Rozkaz 40h słu

Ŝ

y do ustawiania stałej czasowej (TIME^CONSTANT) determinuj

ą

cej cz

ę

stotliwo

ść

 próbkowania przy 

transmisji DM

Ą

. Jed-nobajtow

ą

 warto

ść

 stałej czasowej wysyłamy do układu DSP zaraz po przestaniu kodu rozkazu. 

Poni

Ŝ

ej przedstawiam zale

Ŝ

no

ść

, z której korzystamy przy obliczaniu wymaganej dla danej cz

ę

stotliwo

ś

ci wielko

ś

ci 

TIME_CONSTANT; 

TIME_CONSTANT=256-H)00000/częstotliu}ość 

Dla przykładu dla cz

ę

stotliwo

ś

ci 8 kHz warto

ść

 stałej czasowej powinna wynosi

ć

 131 (256-1000000/8000). Z 

przedstawionej zale

Ŝ

no

ś

ci wynika jednoznacznie minimalna cz

ę

stotliwo

ść

 próbkowania mo

Ŝ

liwa przy 

transmisji DM

Ą

, równa około 3.9 kHz (gdy TiME_CON-STANT=0). 

Rozkaz 7xh 

Komendy 7xh umoŜliwiają ustawianie trybów pracy układu DSP. 

74h - ustawienie trybu 4-bitowej konwersji cyfmwo-analogowej AD-PCM (transmisja DMĄ). 

75h - ustawienie DSP w tryb 4-bitowej konwersji C/A ADPCM z bajtem odniesienia (DMĄ). 

76h - ustawienie DSP w tryb 2.6-bilowej konwersji C/A ADPCM (DM

Ą

). 

77h - ustawienie DSP w tryb 2.6-bitowej konwersji C/A ADPCM z bajtem odniesienia (DMĄ). 
W zasadzie rozkazy 7xh przypominają (choć naturalnie dotyczą innych modów pracy) rozkazy lxh. Zasady 
korzystania z nich są więc takie same. Nie naleŜy jednak zapominać, Ŝe wszystkie rozkazy 7xh dotyczą transmisji w 
trybie DMĄ. 

Rozkaz Dxh 

Komendy Dxh umoŜliwiają kontrolowanie transmisji DMĄ oraz włączanie i wyłączanie DAĆ. 
Dlh - włączenie wzmocnienia sygnałów z układu DAC- 

D3h - wyłączenie wzmocnienia sygnałów z układu DAĆ. UŜycie lego rozkazu spowoduje, Ŝe sygnały pojawiające się 
na wyjściu DAĆ nie będą przesyłane do wzmacniacza (nie będą wzmacniane i przesyłane na odpowiednie gniazdo 
karty). UŜycie rozkazu nie zakłóci 

103

 

PROGRAMOWANIE DSP 

 

odtwarzania syntezowanego przez układ FM dźwięku. NaleŜy zwrócić uwagę, Ŝe uŜycie rozkazu D3h jest 
wymagane przed rozpoczęciem konwersji analogowo-cyfrowej. Powody są tu jasne - próbkowany dźwięk jest 
jednocześnie kierowany na wyjście układu DAĆ i bardzo łatwo o powstawianie sporych zakłóceń podczas 
samplin-gu. Kierowany na wyjście DAĆ sygnał jest przy tym mocno zniekształcony - winy naleŜy upatrywać w 
prędkości działania samej karty. Dowodem na to moŜe być fakt, Ŝe przetwarzaniu analogowo-cyfrowemu przy 
włączonym wzmacnianiu DAĆ w trybie emulacji Sound Blaste^a na kartach Gravis Ultra Sound towarzyszy 
dźwięk bardzo dobrej jakości. Rozkazu D3h moŜemy uŜywać do chwilowego wyciszania odtwarzanej próbki 
dźwiękowej. 
D8h - testowanie włączenia układu wzmacniania sygnałów z DAĆ. JeŜeli do układu DSP wyślemy kod 
rozkazu D8h, odczytana zaraz potem z DSP wartość będzie równa FFh, gdy wyjście układu DAĆ jest 
połączone ze wzmacniaczem lub OOh, gdy tak nie jest. W praktyce rozkaz rzadko uŜywany (powtórne 
wysłanie do DSP komendy Dlh lub D3h nie powoduje przecieŜ Ŝadnego błędu). 

DOh - zatrzymanie transmisji DM

Ą

D4h - kontynuacja wstrzymanej rozkazem DOh transmisji DM

Ą

Rozkaz E l h 

Rozkaz El h nie został opisany w dokumentacji firmowej, mimo 

Ŝ

e jest przyjmowany nawet przez starsze 

układy. Wysyłaj

ą

c go do DSP sprawdzi

ć

 mo

Ŝ

emy wersj

ę

 karty. Pierwszy odebrany zaraz po wysłaniu kodu 

komendy bajt to bardziej znacz

ą

cy numer wersji karty, drugi bajt - mniej znacz

ą

cy. Oto tekst 

ź

ródłowy 

programu wy

ś

wietlaj

ą

cego numer wersji zainstalowanej karty: 

Program Wersja;

 

background image

Uses

 

DSPDir;

 

begin ifnotDSPResetthen

 

begin

 

wnteInCBrak karty Sound Blaster');

 

halt

 

end;

 

DSPWrite[$E1], {wystanie kodu rozkazu } writefSound Blaster');

 

writeln(DSPRead,'.',OSPRead) end.

 

ROZDZIAŁ 4 

4.6 BADANIE KONFIGURACJI SB

 

Elegancko napisany program powinien zwalniać uŜytkownika z obowiązku samodzielnego ustalania parametrów 
pracy. W szczególności dotyczy to programów rozrywkowych. Programy obsługujące karty dźwiękowe powinny 
więc automatycznie rozpoznawać ich konfigurację. W odniesieniu do kart serii Sound Blaster oznacza to 
sprawdzenie numeru linii 1RQ, adresu bazowego i numeru kanału DMĄ uŜywanych przez kartę. Sprawa badania 
adresu bazowego jest dosyć prosta - wystarczy podejmować próby resetowania układu DSP, za kaŜdym razem 
zakładając inną wartość adresu. Poprawna (i odebrana w określonym czasie) odpowiedź układu oznaczać będzie, 
Ŝ

e przyjęta wartość jest prawidłowa. Algorytm ten został zaim-plementowany w przedstawionej wcześniej 

bibliotece DSPDir. Więcej problemów nastręcza jednak sprawdzanie numerów kanału DMĄ i linii IRQ- O ile 
załoŜymy, Ŝe komputer komunikuje się z kartą uŜywając pierwszego kanału procedura odnajdywania numeru 
przerwania IRQ przypisanego karcie działać powinna według schematu: 

•   Zainstalowanie własnych procedur obsługi przerwań IRQ 2, 3, 5, 7. KaŜda z procedur powinna modyfikować 
wartość zadeklarowanej wcześniej zmiennej globalnej przypisując jej swój numer. 
•   Zainicjowanie transmisji przyjętym kanałem DMĄ. Zakończenie transmisji spowoduje wywołanie ustawionego 
zworka-mi przerwania. Procedura jego obsługi nada odpowiednią wartość zmiennej, z której następnie będziemy 
mogli odczytać numer linii IRQ, na jakiej pojawił się sygnał. 

W przypadku, gdy nie znamy numeru kanału DMĄ uŜywanego podczas transmisji, algorytm powtarzamy dla 
kolejnych, prawdopodobnych kanałów. PoniŜej prezentuję tekst źródłowy programu wyświetlającego informacje o 
konfiguracji karty Sound Blaster (przy załoŜeniu transmisji kanałem DMĄ l): 

program SBIRGInfo;

 

uses dos.dspdir;

 

var

 

01dlRQHand2,01dlRQHand3,01dlRQHand5;pointer;

 

01dlRQHand7;pointer;

 

{stare wektory przerwań IRQ}

 

PROGRAMOWANIE OSP

 

Numberbyte;

 

OLD21 byte:

 

procedur

ę

 ProclRQ2; interrupt, { dla IRQ2 } begin

 

Number:=2;

 

portE$20]:=$2C   { EOI} end;

 

procedurę ProclRQ3; inCerrupt; { dla IRQ3 } begin Number:=3;

 

port[$20]:=$20   {EO!} end:

 

procedurę PraclRQ5; mterrupt: { dla IRQ5 } begin

 

Number:=5;

 

port[$20]:=$20   {EOI} end;

 

procedurę Pr'oclRQ7; inCerrupt; { dla IRQ7 } begin

 

Number:=7;

 

port[$203:=$2Q   {EOI} end;

 

begin

 

if noc SBInstalled Chen { czy wykryto kartę } begin

 

wntelnC'Brak karty Sound Blaster''];

 

halt end;

 

getintvec(8-2,aidlRQHand2); { zapamiętujemy stary wektor} setintvec(8+2,@ProclRQ2]; {„naginamy"}

 

getintvecES*3,OldlRQHand3); { zapamiętujemy stary wektor} 5etintvec(8+3,@ProclRQ3]; {„naginamy"}

 

getintvec[8-5,OldlRQHand51; { zapamiętujemy stary wektor} setintvec(S+5,@ProclRQ5]: {„naginamy"}

 

getintvec[8-7,OldlRQHand7); { zapamiętujemy stary wektor} setintvec(8+7,(o)ProclRQ7); {„naginamy"}

 

OLD21 :=port[$21];

 

p0(t[$21]:=porc[$213and89:

 

ROZDZIAŁ 4

 

{odblokowane l;nnlRa2,3.5,7}

 

port[$OA]'=5: { ustawienie maski dla kanafu OMA 1 } port[$OC]:=0;

 

portISOB): =$49; {transmisja z pamięci do karty} porC[$02]:=0;

 

port[$02]:=0;

 

background image

{młodszy bajt długości} { starszy bajt długości}

 

{ aktywacja kanału DMĄ 1 }

 

DSPWritet$4D];

 

DSPWritet'131]; { wartość bez znaczenia: tu dla 8000 Hz }

 

DSPWrite($4B], DSPWhtem;

 

DSPWriteEO];

 

DSPWrite[$14];

 

DSPWrite[1);

 

DSPWriteCO)'

 

{scary wektor IRQ2} {scary wektor IRQ3} {stary wektor IRQ5} {starywektorlRQ7}

 

wriceln['Karcie przypisano linię IRQ',number] end,

 

PoniewaŜ maksymalna częstotliwość próbkowania udostępniana przez kartę jest determinowana wersją układu DSP, 
przydatna jest moŜliwość sprawdzenia numeru wersji karty. PosłuŜyć się tu moŜna rozkazem El h, opisywanym przy 
okazji omawiania komend DSP. Podany tam leŜ został tekst przykładowego programu. 
Warto zauwaŜyć, Ŝe pomimo iŜ testowanie konfiguracji karty jest dość proste, wiele z programów zwraca się z 
pytaniem o ustawienia do uŜytkownika (a przynajmniej jest moŜliwość wymuszenia przyjmowanych przez program 
parametrów). JeŜeli juŜ zdecydujemy się na takie podejście do problemu, przed zadaniem pytania o kanał DMĄ, 
numer linii IRQ czy adres portu bazowego warto sprawdzić wartość zmiennej systemowej BLASTER. Przykład 
proce- 

107

 

PROGRAMOWANIE DSP 

 

dur testuj

ą

cych podstawowe ustawienia karty przedstawiony został w rozdziale po

ś

wi

ę

conym obsłudze plików 

zapisanych w formacie 
VOC. 

109

 

ROZDZIAŁ 4

 

PROGRAMOWANIE SYNTEZERA FM

 

 

5. PROGRAMOWANIE SYNTEZERA FM 

Programowanie syntezera FM jest najbardziej naturalnym sposobem 
zmuszenia karty Sound Blaster do zagrania choćby najprostszej melodii. 
Opisywany w jednym z poprzednich rozdziałów sterownik SBFM, 
korzystając z układu FM, udostępnia nam prosty sposób udźwiękowienia 
swoich programów. Warto zdawać sobie sprawę, Ŝe do wszystkich 
moŜliwości karty daje dostęp dopiero znajomość zasad jej 
bezpośredniego programowania. W niniejszym rozdziale pragnę 
przekazać podstawowe informacje na temat funkcjonowania układu 
syntezy FM i zasad tworzenia wykorzystujących go programów. 

5.1 FUNKCJONOWANIE SYNTEZERA FM

 

Syntezer FM, który znajdujemy na kartach Sound Blaster, oparty jest o 
układ Yamaha oznaczany przez FM1312. MoŜe pracować w dwóch 
trybach: melodycznym (moŜliwość kształtowania brzmienia 9 
instrumentów) oraz rytmicznym (definiujemy brzmienie 6 instrumentów 
i moŜemy korzystać ze zdefiniowanych 5 instrumentów perkusyjnych: 
bębna basowego, bębenka, werbla, talerza i ni hat). 

Zanim przystąpimy do programowania układu syntezy FM, warto 

przypomnieć sobie kilka podstawowych informacji na temat dźwię- 

ROZDZIAŁ 5

 

ku w ogóle, a dźwięków generowanych przez naszego Sound Bla-stera w szczególności. 
Najprostszym dźwiękiem jest ton, czyli drgania akustyczne o przebiegu sinusoidalnym (mówimy tak, poniewaŜ 
wykres zmian natęŜenia dźwięku w funkcji czasu ma postać sinusoidy). Na przykład z dźwięku wydawanego przez 
instrumenty strunowe wyizolować moŜna sygnał o częstotliwości podstawowej oraz szereg sygnałów o 
częstotliwościach wyŜszych niŜ podstawowa (tzw. harmonicznych). O barwie dźwięku decyduje tu liczba i amplituda 
kolejnych składowych. Ciąg zmian amplitudy całkowitego sygnału akustycznego w funkcji czasu to obwiednia 
dźwięku. Obwiednia charakteryzuje takie podstawowe parametry czasowe dźwięku, jak: 

•   Czas narastania, czyli czas, w jakim amplituda sygnału osiągnie wartość maksymalną. 
•   Czas opadania, czyli czas, w jakim amplituda sygnału osiąga (zmniejszając się) wartość związaną z fazą ustaloną. 

background image

•   Czas ustalania, czyli czas, w jakim amplituda osiągnie poziom, na jakim pozostanie do końca fazy ustalonej. 
•  Czas zanikania (wybrzmiewania) - czas, w jakim wartość względna amplitudy sygnału spadnie do poziomu 

zerowego. 

OdB

 

Poziom podtrzymania

 

96 dB

 

Narastanie Opadanie PodtaTymanie W/brzmiewenie (ARack)   (Decay)    (Sustsin)      (Retase)

 

Rys.4 Obwiednia ADSR (AttackfDecay/Sustain/Relase) 
Układ syntezera FM zawiera 18 operatorów. KaŜdy z nich składa się z oscylatora, generatora obwiedni i sterownika 
głośności. Oscylator odpowiedzialny jest za generowanie fali o przebiegu opartym na sinusoidzie. Generator 
obwiedni na podstawie ustalonych parametrów moduluje w czasie amplitudę sygnału wyjściowego. Parametry pracy 
kaŜdej z części operatora: 
Oscylator: 
•   kształt bazowej fali (do wyboru jedna z czterech opcji) 

111 

PROGRAMOWANIE SYNTEZERA FM

 

 

•   mnoŜnik częstotliwości (współczynnik, przez który mnoŜona jest częstotliwość generowanego sygnału) 
•   vibrato (ustawienie tego efektu powoduje niewielkie wahania częstotliwości dźwięku w funkcji czasu) 
•   intensywność sprzęŜenia zwrotnego FB (dla syntezy FM) 

Generator obwiedni: 

czas narastania (czas, po jakim amplituda sygnału osiągnie wartość maksymalną) 

•   czas opadania (czas, w jakim amplituda sygnału spadnie z poziomu maksymalnego do ustalonego) flaga 
fazy ustalonej EG-TYP (flaga ustawiona oznacza występowanie w obwiedni fazy ustalonej) 
•   poziom amplitudy w fazie ustalonej 
•   czas zanikania (czas, po jakim amplituda sygnału spadnie z poziomu ustalonego do 0) 

skala długości KSR (decyduje o tym, czy długość dźwięku ma być częściowo zaleŜna od jego wysokości) 

Sterownik głośności: 

•   głośność ostateczna (na wyjściu z operatora) 
•  wibracje amplitudy (decyduje o tym, czy amplituda sygnału ma ulegać niewielkim wahaniom w czasie) 
•   skala intensywności KSL (ustawienie powoduje uzaleŜnienie głośności od wysokości dźwięku) 

KaŜdy z 18 operatorów bierze udział w syntezie dźwięku. W trybie melodycznym dwa operatory przypadają 
na kaŜdy z dziewięciu instrumentów, a w trybie rytmicznym operatory 1-12 uŜywane są przy syntezie brzmień 
6 instrumentów, a operatory 13-18 uŜyte są do syntezy brzmienia 5 instrumentów perkusyjnych. Dla trybu 
rytmicznego pracy syntezera przyporządkowanie jest następujące: 

•  bęben basowy (bass drum) - operatory 13 i 16 
•   hi hat - operator 14 

•  bębenek (tom tom) - operator 15 

werbel (snare drum) - operator 17 
talerz (top cymbał) - operator 18 Podczas syntezy FM brzmienia jednego instrumentu pracują dwa 
operatory. Jeden w funkcji modułu modulatora, drugi w funkcji 

ROZDZIAŁ 5 

modułu nośnika. Ideę dwu operatorowej syntezy FM przedstawia rysunek 5. 

p

ę

<o sprz

ęŜ

enia zwiokiego

 

 

 

•i

 

 

 

 

 

 

 

 

 

 

Operator 1

   

 

 

smusotd 1

   

 

 

 

 

 

 

 

 

 

 

 

 

Foła sinusoid. 2

 

Rys.5 Synteza divuopera torowa FM

 

ZaleŜność między częstotliwością generowanego w ten sposób sy-nału a parametrami syntezy przedstawia się 

background image

następująco: 

F(t) •== A sin((Ot-\-I sinuJt) gdzie: 
A    ~ amplituda wyj

ś

ciowa /    - mno

Ŝ

nik cz

ę

stotliwo

ś

ci 

ą

   - cz

ę

stotliwo

ść

 

no

ś

nika 

ą

^   - cz

ę

stotliwo

ść

 modulatora 

PoniewaŜ podczas syntezy FM mamy do dyspozycji 9 kanałów instrumentalnych, kaŜdemu kanałowi przypisane 
są 2 operatory (modulator i nośnik). Ich przyporządkowanie przedstawia się następująco: 
Kanał l: operatory l i 4 (o adresach OOh i 03h) Kanał 2: operatory 2 i 5 (o adresach O l h i 
04h) Kanał 3: operatory 3 i 6 (o adresach 02h i 05h) Kanał 4: operatory 7 i 10 (adresy 08h i 
OBh) Kanał 5: operatory 8 i 11 (adresy OOh i OCh) Kanał 6: operatory 9 i 12 (adresy OAh i 
ODh) Kanał 7: operatory 13 i 16 (adresy l On i 13h) Kanał 8: operatory 14 i 17 (adresy llh i 
14h) Kanał 9: operatory 15 i 18 (adresy 12h i 15h) 

5.2 ZASADY OBSŁUGI SYNTEZERA FM

 

Zasady obsługi syntezera FM są proste. Układowi syntezy przypisane są dwa porty. Na oryginalnej karcie AdLib 
mają one adresy 388h i 389h. Na kartach Sound BIaster komunikacja z układem jest moŜ- 

113

 

PHOGRAMOWANIE SYNTEZERA FM

 

 

liwa takŜe przez porty 2x8h i 2x9h (x zastępujemy naturalnie wartością wynikającą z przyjętego adresu 
bazowego). PoniewaŜ pisząprogramy komunikujące się z kartą za pośrednictwem portów 388h i 389h 
mamy pewność, Ŝe będą leŜ współpracowały z kartami SB i AdLib, we wszystkich podawanych niŜej 
przykładach będę uŜywał wyłącznie tych adresów. 

Oto algorytmy, według których działać powinny procedury komunikacji z układem syntezy: 

Zapis danej do rejestru:

 

1. Zapis do portu indeksowego 388h numeru rejestru 
2. Oczekiwanie ok. 3 mikrosekund 
3. Odczyt wartości jednobajtowej z portu danych 389h 

Uwaga: Przed ponownym zapisem do dowolnego rejestru naleŜy 

odczekać około 23,3 mikrosekundy. 

Odczyt rejestru statusowego 

Odczyt rejestru stanu realizujemy odczytując jeden bajt z portu indeksowego (388h). Rejestr statusowy nie ma 
swojego adresu i nie moŜna do niego zapisać Ŝadnej wartości. 
Pewną trudność wydaje się nastręczać konieczność stosowania czasowych zwłok. Wydaje się, Ŝe najprościej 
będzie skorzystać z jednej z usług BIOS w wersji AT (usługa 86h przerwania 15h). JeŜeli chcemy, aby nasz 
program poprawnie działał takŜe na komputerach klasy XT, musimy postarać się skonstruować pętlę czasową 
o ilości repetycji związanej z prędkością komputera. 

PoniŜej przedstawiam zestawienie rejestrów układu FM: 
Rejestr statusowy 
Rejestr jest 8-bitowy, ale znaczenie mają jedynie najstarsze 3 bity. Bit 7 ustawiany jest w sytuacji, w której w 
którymś z rejestrów liczników (lub w obu naraz) wystąpi przepełnienie, a bity 6 i 5 sygnalizują wystąpienie 
przepełnienia liczników odpowiednio l i 2. 

Blh-Testowy 

Przed rozpoczęciem programowania układu naleŜy wpisać do niego bajt o wartości 0. 

ROZDZIAŁ 5

 

B2h - Liczniki

 

Liczniki jest 8-bitowym licznikiem zliczającym od O do 255 okresy o długości 80 mikrosekund. Zasada korzystania z 
niego jest prosta. Wprowadzamy do niego jakąś wartość początkową N. Od tej pory jest ona (co 80 mikrosekund) 
zwiększana o l. W momencie, gdy zapisana w rejestrze wartość „przeskoczy" z 255 do O (nastąpi przepełnienie), 
ustawiany jest bit 6 w rejestrze statusowym. Tak więc czas T, jaki odmierzy licznik, zanim nastąpi przepełnienie, 
wyliczyć moŜna z zaleŜności: 

r = ( 256- N ) * 0.08 fmsl 

03h - Licznik2

 

Licznik2 pełni identyczną funkcję jak Liczniki, z tą róŜnicą, Ŝe zlicza okresy o długości 320 mikrosekund. 
Przepełnienie sygnalizowane jest przez ustawienie bitu 5 rejestru statusowego. ZaleŜność między wartością startową 
N a czasem przepełnienia T jest więc w jego przypadku następująca: 

r== (256 -N ) * 0.32 fmsl 

background image

04h - Rejestr kontroli liczników

 

Rejestr uŜywany jest do uruchamiania i zatrzymywania liczników l i 2. Znaczenie poszczególnych bitów jest 
następujące: 
Bit O - ustawienie powoduje rozpoczęcie odliczania przez Liczniki, wartość O oznacza zatrzymanie pracy licznika, 
Bit l - jak bit O, ale w odniesieniu do Licznika2. 
Bit 5 - ustawienie tego bitu powoduje zerowanie bitu 5 rejestru statusowego (przepełnienie Licznika 2 nie jest 
sygnalizowane). Bit 6 - analogicznie jak bit 5, ale w odniesieniu do bitu 6 rejestru statusowego (brak sygnalizacji 
przepełnienia licznika l). Bit 7 - ustawiony powoduje zerowanie bitów 5,6,7 rejestru statusowego. 
Dalej przedstawiam pięć grup rejestrów powiązanych z poszczególnymi operatorami. Adres rejestru w grupie 
związanego z danym operatorem wyznaczyć moŜna dodając do adresu pierwszego rejestru w grupie wartość 
odpowiedniego przesunięcia: 

115

 

PROGRAMOWANIE SYNTEZERA FM 

 
 

 

Operator

 

Przesuni

ę

cie

 

 

l

 

OOh

 

 

2

 

Olh

 

 

3

 

02h

 

 

4

 

03h

 

 

5

 

04h

 

 

6

 

05h

 

 

7

 

08h

 

 

8

 

OOh

 

 

9

 

OAh

 

 

10

 

OBh

 

 

11

 

OCh

 

 

12

 

ODh

 

 

13

 

lOh

 

 

14

 

llh

 

 

15

 

12h

 

 

16

 

13h

 

 

17

 

14h

 

 

18

 

15h

 

 

 
 

 

20h-35h

 

AM/Y1B/EG/KSR/MULTIPLE dla operatorów 1..18

 

 

 

Grupa 8-bitowych rejestrów. Znaczenie poszczególnych bitów kaŜdego z nich: 
Bit 7 - AM - ustawienie wahań natęŜenia dźwięku (tremolo). Dla wartości O bitu wahań nie ma, dla wartości l 
wahania będą zachodzić z częstotliwością 3.7 Hz. Maksymalną „głębokość" zmian określa wartość bitu 7 
rejestru BDh (l lub 4.8 dB). 
Bit 6 - VIB - ustawienie efektu vibrato (wahania częstotliwości dźwięku). Częstotliwość wahań jest równa 6.4 
Hz. Stopień zmian częstotliwości w czasie określamy ustawiając bit G rejestru BDh (7% lub 14%). 

Bit 5 - EG-TYP - typ obwiedni dźwięku. Wyzerowanie bitu oznacza wybranie typu l obwiedni, ustawienie - 
typu 2. 

ROZDZIAŁ 5

 

 

typ l

 

Fazo nofasionio

 

Fara opadon.a

 

TrP 2

 

Faza norastoniB

 

Feza obadania

 

_Paziom podtrzymani b

 

Wyb r? miewani y

 

 

Klawisz wci

ś

ni

ę

ty

 

/?ys.6' Dwa typy obimedni 

Bit 4 - KSR - włączenie następuje, gdy bit ustawimy, wyłączenie, gdy wyzerujemy. Włączenie KSR umoŜliwia 
podkreślenie efektu znanego z instrumentów strunowych - uzaleŜnienia czasu trwania dźwięku od jego wysokości. 
Bity 3..0 - MULTIPLE - mnoŜnik. Równanie syntezy FM uwzględniające wartość mnoŜnika wygląda następująco: 

background image

F(t)= A stn( a^-ą/ + f-5in(M^co^) ) gdzie: 

A - amplituda wyjściowa, 
l - amplituda modulatora, 
ą

- częstotliwość nośnej, 

co^- częstotliwość modulatora, 
M- mnoŜnik dla operatora nośnej, 
M^- mnoŜnik dla operatora modulatora. 
Wielkość MULTIPLE moŜe przybierać wartości od O do 15 (4 bity). Dla wartości O mnoŜnik przyjmuje wartość 0,5. 
Dla wartości z przedziału Ih-Fh odpowiednio 1 do 15. 

40h-55h KSL/Total Level

 

Zestaw rejestrów słuŜących do kontroli poziomu sygnału operatora i definiowaniu stopnia spadku głośności dla 
wyŜszych częstotliwości. Znaczenie bitów rejestrów: 
Bity 0-5 - TOTAL LEVEL - 6 bitów opisujących poziom wyjściowy sygnału operatora związanego z rejestrem. 
Zakres wartości: od O to 63. Znając wartość przechowywaną przez te 6 bitów, łatwo wyznaczymy poziom sygnału 
wyraŜony w dB: WARTOŚĆ*0,75 fdB). 
Bity 6-7 - KSL - ustawiając bity 6 i 7 moŜemy regulować spadek natęŜenia dźwięku wraz ze wzrostem 
częstotliwości. I tak, spadek ten w zaleŜności od wartości przyjmowanych przez dwubitowe słowo (młodszy bit to 
naturalnie bit 6) przedstawia się następująco: 

117

 

PROGRAMOWANIE SYNTEZERA FM

 

 

KSL

 

Spadek [dB/oktawę]

   

0 l 2 3

 

0 3 1,5 6

   

 
 

 

 

60h-75h - Attack/DecayRatc

 

U

Ŝ

ywaj

ą

c tej grupa rejestrów mo

Ŝ

na kształtowa

ć

 faz

ę

 narastania i opadania d

ź

wi

ę

ku. Warto

ść

 8-

bajtowego rejestru nale

Ŝ

y podzieli

ć

 na 2 4-bitowe słowa. Młodsze (bity 0-3 rejestru) z nich opisywa

ć

 

b

ę

dzie czas trwania fazy opadania, starsze (bity 4-7) - narastJ^a. Najogólniej bior

ą

c, im wielko

ść

 

opisuj

ą

ca czas jest mniejsza, tym czas jest dłu

Ŝ

szy. Dokładnie ilustruje to poni

Ŝ

sza tabela (czasy 

podane s

ą

 w ms):

 

Warto

ść

 

Czas narastania (10% do 90%)

 

Czas narastania (-96dBdo0dB)

 

Czas opadania (90% do 10%)

 

Czas opadania (0dB do -96dB»

 

 

63

 

0.00

 

0.00

 

0.51

 

2.40

 

 

62

 

0.00

 

0.00

 

0.51

 

2.40

 

 

61

 

0.00

 

0.00

 

0.51

 

2.40

 

 

60

 

0.00

 

0.00

 

0.51

 

2.40

 

 

59

 

0.11

 

0.20

 

0.58

 

2.74

 

 

58

 

0.11

 

0.24

 

0.63

 

3.20

 

 

57

 

0.14

 

0.30

 

0.81

 

3.84

 

 

56

 

0.19

 

0.38

 

1.01

 

4.80

 

 

55

 

0.22

 

0.42

 

1.15

 

5.48

 

 

54

 

0.26

 

0.46

 

1.35

 

6.40

 

 

53

 

0.31

 

0.56

 

1.62

 

7.68

 

 

52

 

0.37

 

0.70

 

2.02

 

9.60

 

 

51

 

0.43

 

0.80

 

2.32

 

10.96

 

 

50

 

0.49

 

0.92

 

2.68

 

12.80

 

 

49

 

0.61

 

1.12

 

3.22

 

15.36

 

 

48

 

0.73

 

1.40

 

4.02

 

19.20

 

 

47

 

0.85

 

1.56

 

4.62

 

21,92

 

 

46

 

0.97

 

1.84

 

5.38

 

25.56

 

 

45

 

1.13

 

2.20

 

6.42

 

30.68

 

 

44

 

1.45

 

2.76

 

8.02

 

38.36

 

 

43

 

1.70

 

3.12

 

9.24

 

43.84

 

 

 

ROZDZIAŁ 5 

42

 

1.94

 

3.68

 

10.76

 

51,12

 

 

41

 

2.26

 

4.40

 

12.84

 

61.36

 

 

background image

40

 

2.90

 

5.52

 

16.04

 

76.72

 

 

39

 

3.39

 

6.24

 

18.48

 

87.68

 

 

38

 

3.87

 

7.36

 

21.52

 

102.24

   

37

 

4.51

 

8.80

 

25.68

 

122.72

   

36

 

5.79

 

11.04

 

32.08

 

153.44

   

35

 

6.78

 

12.48

 

36.96

 

175.36

   

34

 

7.74

 

14.72

 

43.04

 

204.48

   

33

 

9.02

 

17.60

 

51.36

 

245.44

   

32

 

11.58

 

22.08

 

64.16

 

306.88

   

31

 

13.57

 

24.96

 

73.92

 

350.72

   

30

 

15.49

 

29.44

 

86.08

 

408.96

   

29

 

18.05

 

35.20

 

102.72

 

490.88

   

28

 

23.17

 

44.16

 

128.32

 

613.76

   

27

 

27.14

 

49.92

 

147.84

 

701.44

   

26

 

30.98

 

58.88

 

172.16

 

817.92

   

25

 

36.10

 

70.40

 

205.44

 

981.76

   

24

 

46.34

 

88.32

 

256.64

 

1227,52

   

23

 

54.27

 

99.84

 

295.68

 

1402.88

   

22

 

61.95

 

117.76

 

344.32

 

1635.84

   

21

 

72.19

 

140.80

 

410.88

 

1963.52

   

20

 

92.67

 

176.84

 

513.28

 

2455.04

   

19

 

108.54

 

199.68

 

591.36

 

2805.76

   

18

 

123.90

 

235.52

 

688.64

 

3271,68

   

17

 

144.38

 

281.60

 

821.76

 

3927.04

   

16

 

185.34

 

353.28

 

1026.56

 

4910.08

   

15

 

217.09

 

399.36

 

1182.72

 

5611.52

   

14

 

247.81

 

471.04

 

1377.28

 

6543.36

   

13

 

288.77

 

563.20

 

1643.52

 

7854.08

   

12

 

370.69

 

706.56

 

2053.12

 

9820.16

   

11

 

434.18

 

798.72

 

2365,44

 

11233.04

  

10

 

495.62

 

942.08

 

2754.56

 

13086.72

  

9

 

577.54

 

1126.40

 

3287.04

 

15708.16

  

8

 

741.38

 

1413.12

 

4106.24

 

19640.32

  

7

 

868.35

 

1597.44

 

4730.88

 

22446.08

  

6

 

991.23

 

1884.16

 

5509.12

 

26173.44

  

5

 

1155.07

 

2252.80

 

6574.08

 

31416.32

  

4

 

1482.75

 

2826.24

 

8212.48

 

39280.64

  

 

PROGRAMOWANIE SYNTEZERA FM 

119

 

68h-95h -Sustain Level'Relase Ratę

 

I znów 8-bitowy rejestr podzielony na 2 4-bitowe słowa. Młodsze z nich (bity 0-3) opisuje czas wybrzmiewania 
d

ź

wi

ę

ku. Starsze (bity 4-7) - odnosz

ą

 si

ę

 do poziomu podtrzymania obwiedni d

ź

wi

ę

ku. Ka

Ŝ

dy bit 4-bitowego 

słowa wyznaczaj

ą

cego poziom podtrzymania ma swoj

ą

 wag

ę

. Bit najmłodszy - 3 [dB], bit nast

ę

pny - 6 |dB|, 

nast

ę

pny - 12 [dB], a najstarszy - 24 |dB]. Najwy

Ŝ

szy poziom stanu podtrzymania wymusimy ustawiaj

ą

wszystkie bity na „l" (wtedy poziom = 93 d B). 

Rejestry tej grupy słuŜą do ustawiania częstotliwości dźwięku dla 9 głosów. W trybie melodycznym kaŜdy 
rejestr jest związany z jednym głosem. W trybie rytmicznym pierwszych sześć par rejestrów związanych jest z 
6 głosami, a dalej przyporządkowanie jest następujące: 

•   bębenek - rejestry A8h i B8h, 
•  bęben basowy - A6h i B6h. 

Dla reszty instrumentów perkusyjnych zmiana częstotliwości dźwięku jest niemoŜliwa. 

Rejestry AOh-A8h odpowiadaj

ą

 młodszej cz

ęś

ci 10-bitowej wielko

ś

ci F-NUMBER opisuj

ą

cej 

cz

ę

stotliwo

ść

 generowanego d

ź

wi

ę

ku.

 

Znaczenie bitów rejestrów BOh-B8h jest zró

Ŝ

nicowane:

 

Bity 0-1 - przechowuj

ą

 najstarsze 2 bity F-NUMBER. Bity 2-4 - opisuj

ą

 oktaw

ę

, w której b

ę

dzie 

grany d

ź

wi

ę

k.

 

Bit 5 - KEY - bit ma kluczowe znaczenie. Jego ustawienie powoduje rozpoczęcie generowania dźwięku (jak 
przy wciśnięciu klawisza), jego wyzerowanie spowoduje wyłączenie głosu (jak przy zwolnieniu klawisza 

background image

klawiatury muzycznej). 
Uwaga - częstotliwość generowanego dźwięku moŜna dość prosto wyznaczyć korzystając z zaleŜności: 

F = 50000 * F-NUMBER » 2 ^(OKTAWA - 20) 

Dla oktawy 4 wartości F-NUMBER odpowiadające kolejnym dźwiękom: 

ROZDZIAŁ 5

 

 
 

 

Dźwięk

 

Częstotliwość

 

/-NUMBER

 

 

C^

 

277,2

 

363

 

 

D

 

293,7

 

385

 

 

D#

 

311,1

 

408

 

 

E

 

329,6

 

432

 

 

F

 

349,2

 

458

 

 

F#

 

370

 

485

 

 

G

 

392

 

514

 

 

G#

 

415,3

 

544

 

 

A

 

440,0

 

577

 

 

A#

 

466,2

 

611

 

 

C

 

523,3

 

686

 

 

 
 

 

CBh-CBh -

 

• Feedback/Connection

 

 

Rejestry

 

pozwalają ustalić współczynnik intensywności sprzęŜenia

   

zwrotnego oraz sposób torów.

 

połączenia dwóch współpracujących opera-

 

 

Bity 1-3

 

tworzą 3 bitowe słowo. W zaleŜności od jego wartości

 

 

współczynnik jest równy:

 

 

FB

 

Współczynnik

 

 

 

 

0

 

0

 

 

 

 

l

 

p/16

 

 

 

 

2

 

P/8

 

 

 

 

3

 

P/4

 

 

 

 

4

 

p/2

 

 

 

 

5

 

P

 

 

 

 

6

 

2p

 

 

 

 

7

 

4p

 

 

 

 

 

Bit O określa, jak połączone są operatory. Z trybem syntezy FM mamy do czynienia, gdy bit jest wyzerowany, 
z syntezą addytywną - gdy bit został ustawiony. 

PROGRAMOWANIE SYNTEZERA FM

 

121

 

B<0(CON)

 

Schemat syntezy

   

0

 

1

 

^ ——

 

P1 ——

 

——.(.) ———

 

T

 

Op«tt*if 1

  

 

T

 

l P2

 

Operalor Z

 

W^

ś

de

 

 

 

 

 

 

 

 

 

 

 

 

 

 

background image

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Operator l

  

 

 

 

 

 

P1 ——

 

—1»)—)

 

T

 

 

 

 

(

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ł

)——————————————————————————7

 

Wi

ś

cie

 

 

 

 

 

Operator 2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Rys. 7 Synteza FM i syntezo addytywną

 

Pojedynczy rejestr, pozwalający nam kontrolować stopień wahań natęŜenia dźwięku dla efektu vibrato, stopień 
zmian częstotliwości dźwięku dla efektu tremolo. Poza tym uŜywając rejestru przełączamy układ w tryb 
melodyczny lub rytmiczny. 
Bity O - 4 - mają znaczenie w trybie rytmicznym. Pełnią rolę identyczną Jak bit KEY rejestrów BOh-B8h 
(ustawienie rozpoczyna generowanie dźwięku, wyzerowanie - kończy). Bit 4 jest związany z bębnem basowym, 
bit 3 - z werblem, bit 2 włącza/wyłącza bębenek, l - talerz, O - high hat. NaleŜy pamiętać, Ŝe jeŜeli operujemy 
instrumentami perkusyjnymi z wykorzystaniem tego rejestru, bity KEY rejestrów B6h, B7h, B8h muszą być 
wyzerowane- W trybie rytmicznym przyporządkowanie operatorów poszczególnym instrumentom wygląda 
następująco: 

Instrument

 

Operator(y)

 

 

bęben basowy

 

13,16

 

 

hi hat

 

14

 

 

bębenek

 

15

 

 

werbel

 

17

 

 

talerz

 

18

 

 

 

Bit 6 rejestru pozwala na ustalenie intensywności efektu vibrato, Ustawiony oznacza 14% wahania 
częstotliwości, wyzerowany - 7%, 

ROZDZIAŁ 5 

Bit 7 - ustawianie maksymalnej zmiany natęŜenia dźwięku przy efekcie tremolo. Bit wyzerowany - l dB, 
ustawiony - 4,8 dB. 
E0h-F5h-WaveSelect

 

Rejestry umoŜliwiają ustalanie kształtu fali generowanej przez oscy-latory poszczególnych operatorów. Dwa 
najmłodsze bity kaŜdego rejestru kodują kształt jak na rysunku 8. 

brt 1 

bit O 

Kształt generowanej fali

 

Rys.8 Kształt fali generowanej przez oscylator generatora 

5.3 PRZYKŁADY

 

Po takiej dawce informacji na temat funkcjonowania układu FM czas na jaki

ś

 przykład. Pierwszym z nich 

jest tekst 

ź

ródłowy biblioteki implementuj

ą

cej dwie najprostsze procedury:

 

procedurę FMWriteCreg,value) - Zapis do rejestru wyspecyfikowanego parametrem reg wartości value. Procedura 

background image

uwzględnia zwłoki czasowe konieczne przy programowaniu układu (realizuje je korzystając z usług BIOS-a w 
wersji AT). 
function FMStatus:byte - Zwraca wartość rejestru statusowego układu. 
Oprócz tego w części inicjacyjnej biblioteka testuje obecność układu FM karty Sound Blaster (AdLib) i nadaje 
odpowiednią wartość zmiennej globalnej FMInstalIed. Wartość True tej zmiennej oznacza 

123

 

PROGRAMOWANIE SYNTEZERA FM 

 

jego obecność. Procedura FMCheck testująca kartę wykorzystuje w tym celu licznik pierwszy (80 
mikrosekund). 

unitFMDir;

 

interface var FMInstalted:boolean;

 

procedurę FMWriCe(reg,value'byte); { zapis do rejestru } function FMStatus:byte; { odczyt rejestru statusowego }

 

implementation uses dos:

 

{doahnumerfunkcji} {starsze stówo =0} {młodsze stówo} {wykonać!}

 

procedurę Waittmicros:word);

 

var

 

reg:registers;

 

begin

 

reg.ah:=$86;

 

reg.cx:=D;

 

reg.dx:=micros;

 

intr[$15,regl end,

 

{oczekiwanie 3.3us} port[$3891:==value:   { zapis wartości} Wait(24]             { oczekiwanie 24 us }

 

procedurę FMWrite[reg,value:byte);

 

begin

 

port[$388]:=reg;     { numer rejestru } WaitC3);

 

end; 

{odczyt statusu}

 

function FMStacus:byte:

 

begin

 

FMScatus:=port[$38B] end;

 

procedurę FMCheck;

 

var

 

A,B,i:byte;

 

begin

 

FMWriteCI .0); { zerujemy rejestr testowy}

 

FMWrit:e[4.$60);

 

FMWhce[4,$803;     {rejestr kontrolny}

 

A-=FMStatus;

 

{ zapamiętanie stanu rejestru statusowego }

 

FMWrite[2,$FE);      { odlicz BO mikrosekund }

 

ROZDZIAŁ 5 

 

{wystartowanie licznika 1 { pętla opóźniająca }

 

FMWnte(4,$11),

 

for i=1 to 255 do,

 

B:=FMStatus,

 

{Jeśli byt ustawiany bit przepełnienia licznika 1 }

 

FMWrite[4,$603,

 

FMWnce[4.S80);

 

FMfnsta!led:=[[BxorA)=192J

 

FMCheck

 

end:

 

begin

 

f end,

 

Napiszmy teraz najprostszy program wykorzystujący naszą kartę dźwiękową do syntezy FM. Zaczynamy naturalnie 
tak:
 

Program Bdziang:

 

uses

 

crt.FMDir:

 

begin

 

if noc FMInstalled then halt;

 

Teraz, po kolei, wypełniamy kolejne rejestry układu odpowiednimi wartościami. Najpierw wpisujemy O do rejestru 
testowego: 

FMWriCe($01,0);

 

background image

Teraz decydujemy, 

Ŝ

e nasz program b

ę

dzie przeł

ą

czał układ FM w tryb melodyczny. Ustawiamy te

Ŝ

 du

Ŝą

 gł

ę

boko

ść

 

efektu vibrato: 

FMWrite($BD,128];

 

Nast

ę

pnie dobieramy mno

Ŝ

niki dla operatorów l i 2 i wybieramy typ obwiedni. Ustawimy te

Ŝ

 efekt vibrato. 

Pami

ę

tamy, 

Ŝ

e dla głosu l (wykorzysta go nasz programik) adresy wzgl

ę

dne dwóch operatorów wynosz

ą

 

odpowiednio O i 3.

 

FMWrite[$20,64+32+103;

 

FMWnte[$23,64+32+1);

 

Teraz określamy poziom wyjściowy sygnału na kaŜdym z operatorów: 

FMWrite($40,1);

 

FMWrit;e($43,1);

 

125

 

PROGRAMOWANIE SYNTEZERA FM

 

 

Pozostaje jeszcze dobra

ć

 czasy narastania, opadania i wybrzmiewa-nia d

ź

wi

ę

ku oraz poziom podtrzymania 

oraz ustali

ć

 kształt generowanej fali, ustawi

ć

 tryb syntezy FM; 

FMWnt:e[$60,128-1), FMWnce[$63,128+1):

 

FMWnte($80,128T31:

 

FMWntet$83,128+3), FMWnCet$EQ,a);

 

FMWntet$E3,0);

 

FMWrite($CO,0);

 

No i na koniec wprowadzi

ć

 do pary AO i BO kod odpowiadaj

ą

cy cz

ę

stotliwo

ś

ci d

ź

wi

ę

ku, jaki wyda

ć

 ma nasz 

program, ustali

ć

 oktaw

ę

 i ustawi

ć

 bit KEY: 

FMWrite[$AO,255):

 

FMWrite[$BO,2+16+32];

 

Dalej nast

ę

puje krótka zwłoka czasowa, zerujemy bit KEY odpowiedniego rejestru i ko

ń

czymy 

działanie:

 

delay(500);

 

FMWnte[$BO,1+16) end.

 

Przyszedł czas na nieco bardziej skomplikowany program. Jego zadaniem będzie umoŜliwienie nam gry na 
wielogłosowych „cymbałkach". 

program Cymbałki;

 

uses

 

dos.FMDir;

 

ConsC

 

Var

 

convert:array[0.,71 of byte

 

=[$10,$n,$12,$13,$14,$15,$16,$01);

 

{tu przechowamy scan-code'y kolejnych klawiszy}

 

{naszej klawiatury}

 

{ Dalej: parametry obwiedni dźwięku naszego }

 

{instrumentu. KaŜdy moŜe przybrać wartości}

 

{ od O do 63 }

 

Narastanie ^yte^;

 

Opadanie     ^yte^B;

 

Poziom :byte=2;

 

Wybrzmewarie:byCe=4;

 

8iaS_handler:pointer; { stary wektor przerwania 9h } ScanCode,Contrcl,counter:byte; { zmienne robocze }

 

ROZDZIAŁ 5

 

keys;arrayt0..6] of byte;

 

{ tutaj będziemy trzymali wartości odpowiadające } { wyłączonym głosom 0,1.2,3,4,5,6 } keyboard:arrayt0..7] of boolean;

 

{klawisze 0..6-nasza „muzyczna" klawiatura, } { klawisz? -kod Esc } pressed:array[O..B] of boolean;

 

{ czy kanał 0..6 )est włączony}

 

{$F+} procedurę MapKeyHandier; incerrupt:

 

{ procedurę, którą zastąpimy oryginalną procedurę } { obsługi 9h i która będzie wypełniać odpowiednimi} {wartościami tablicę keyboard!]} var

 

n:byte;

 

begin

 

ScanCode:=por't[$60); { pobranie kodu znaku }

 

CQntrol:=portt$61];

 

port[$61]:=Control or $90:

 

port[3i61]:=Control; { regeneracja sterownika }

 

for n: =0 Co 7 do Begin

 

if ScanCode=convert[n] then keyboardCnL^true;

 

if ScanCode'=converl:En] or $90 then keyboard[n];=fa)se end;

 

port[$20J:=$20{EOI} end;

 

{$F-}

 

background image

procedurę tnstallMapProc:

 

var

 

i: byte;

 

begin

 

set!ntvecC$9,©MapKeyHandier]; { nastaw wektor}

 

for i: =0 to 7 do keyboard[i3:=false

 

{ na początku wszystkie klawisze są zwolnione } end;

 

procedurę UninstaUMapProc;

 

begin

 

seCintvec[$9,aiOS_handler)

 

{ przywróć oryginalny wektor} end;

 

procedurę Prepareinstruments:

 

{ przygotowanie brzmienia kaŜdego głosu instrumentu } const

 

PROGRAMOWANIE SYNTEZERA FM 

\^J 

fnumb:arrayt0 .6] of word=t385,432,458.514,577,647,686),

 

{ stałe F-NUMBER dla kolejnych głosów } var

 

ofs.byte, { zmienna pomocnicza } begin

 

FMWrite[$BD,128);

 

{tryb metodyczny i duŜa głębokość vibrato } forofs:=$00to$05do

 

begin

 

{ ustawienie parametrów dla głosów 0-3 )

 

ifofs>2 then FMWrite($20+ofs,64+32+2)

 

else FMWrite[$20+ofs,64+32+1).

 

{vibrato, drugi typ obwiedni i mnoŜnik=2 }

 

FMWrite[$40+ofs,1);

 

{TotalLevel= 1 }

 

FMWrite[$60+ofs. Narastanie shl4 + Opadanie);

 

{czas trwania fazy narastania dźwięku}

 

{i fazy opadania}

 

FMWriteCSBO+ofs,Paziom shl 4 + Wybrzmiewanie)

 

{ poziom stanu ustalonego i czas wybrzmiewania }

 

end;

 

forofs:=$OBto$OOdo

 

begin

 

{ ustawienie parametrów dla głosów 4-B} ifofs>$ODthenFMWrite($20+ofs,64+32+2) else 
FMWriceC$20+ofs,64+32+15:

 

FMWriteE$40+ofs,1];

 

FMWriteESBO+ofs,Narastanie shl 4 + Opadanie];

 

FMWriteE$80+ofs.Poziom shl 4 + Wybrzmiewanie)

 

end;

 

{ ustawienie parametrów dla głosu 6}

 

FMWrite($20+$10,64+32+1);

 

FMWrice($20+$13,64+32+2);

 

FMWrite($40+$10,1);

 

FMWrite($40+$13,1);

 

FMWrite($60+$10, Narastanie shl 4 + Opadanie);

 

FMWrite($60+$13, Narastanie shl 4 + Opadanie);

 

FMWrice($80+$10.Poziom shl 4 + Wybrzmiewanie);

 

FMWrite($80+$13,Poziom sh! 4 + Wybrzmiewanie];

 

forofs;=0to6do

 

begin

 

{wyznaczenie młodszej części F-NUMBER } ( dla kaŜdego głosu } FMWnte[$AO+ofs,byte(fnumbEofs] 
and $FF]];

 

keys[ofs]:=byte([fnumb[ofs]and$3FF]shr8)+1S;

 

{ najstarsze 2 bity F-NUMBER do $BD i 4 oktawa } FMWrice[$90+ofs,keys[ofs])

 

end

 

ROZDZIAŁ 5

 

end:

 

ifnotFMinstalled then begio

 

{jeŜeli karcą nie jest zainstalowana } writeInCBrak karty SB lub AdLib,'), hale end;

 

geCincvec[$9,BIOS^handler]; { zapamiętanie wektora } FMWrtteE$01,0): { wyzerowanie rejestru testowego } Prepareinstruments; { 
przygotowanie brzmień } forcounter:=0to G do

 

pressed[counter]:=false;

 

{ wszystkie głosy wyłączone } wnteCTerazgraj uŜywając klawiszy Q,W,E,R,T,Y,U.'];

 

wnteln[' Koniec- Esc.'J;

 

InstallMapProc; { przejęcie obsługi przerwania 9h } repeat

 

for counter:=0 to 6 do { dla kaŜdego głosu } begin

 

if Ckeyboar'd[counter]]and[nat pressedEcounter]] Chen begin { właśnie został wciśnięty}

 

FMWrite[$BD+countenkeys[counter] or 32);

 

background image

pressed[counter]:=true;

 

end;

 

if [tnot keybaard[cQunter]]and[pressed[counter']]) then begin { właśnie został zwolniony}

 

FMWrite[$BO+counter,keys[counter]);

 

pressed[councer];=false;

 

end:

 

end,

 

unti) keyboard^]; { aŜ nie zastanie wciśnięty Esc } UninstallMapProc;

 

{ oddanie obsługi orygianiej procedurze } forcounter:=0to6 do FMWr-ite[$BO+counter,keys[counter]) 
{wyłączenie głosów}

 

end.

 

Korzystając z programu moŜna, uŜywając klawiszy Q, W, E, R, T, Y i U zagrać prostą melodię najprostszym 
brzmieniem. Program nie jest bardzo skomplikowany, ale osoby chcące zrozumieć zasady programowania układu 
syntezy FM powinny skupić się szczególnie na analizie procedury Prepareinstruments. 

PROGRAMOWANIE SYNTEZERA FM 

129

 

Tym, którzy nie bardzo rozumieją sposób, w jaki zapewniłem moŜliwość odczytu stanu jednocześnie 
wciśniętych klawiszy (gra wielogłosowa), naleŜy się słowo wyjaśnienia. W chwili, gdy wciskamy lub 
zwalniamy któryś z klawiszy, klawiatura PC wysyła do komputera ciąg składający się z jednego iub kilku 
bajtów. Ciąg ten, jednoznacznie określający pojedynczy klawisz, to tzw. scan code. Rotę odgrywa tu fakt, Ŝe 
scan code wysyłany przy wciskaniu klawisza (nazywany dalej kodem wciśnięcia) jest róŜny od ciągu 
wysyłanego przy zwalnianiu (kod zwolnienia) tego samego przycisku. W przypadku ciągu jednobajtowego 
róŜnica polega na tym, Ŝe bit 7 kodu zwolnienia jest zawsze ustawiony. W chwili wciśnięcia/zwolnienia 
jakiegokolwiek klawisza na linii IRQ1 pojawia się sygnał i wywołane zostaje przerwanie 9h. To właśnie 
procedura jego obsługi jest odpowiedzialna za odbiór ciągów kodujących zmiany stanu klawiszy i 
podejmowanie związanych z tym akcji (wypełnianie bufora klawiatury, modyfikacja bajtów statusowych w 
obszarze zmiennych BiOS-u itd). Pomysł rozwiązania problemu odczytu stanu wciśniętych jednocześnie 
klawiszy naszej „muzycznej klawiatur-ki" zasadza się właśnie na przejęciu obsługi tego przerwania i analizie 
nadchodzących z klawiatury sygnałów. 

131

 

ROZDZIAŁ 5                SYGNAŁY l ICH PRZETWARZANIE 

 

6. SYGNAŁY I ICH PRZETWARZANIE 

W rozdziale tym chciałbym wprowadzi

ć

 Czytelnika w dziedzin

ę

 

przetwarzania sygnałów cyfrowych. Poniewa

Ŝ

 omówienie 

ka

Ŝ

dego z problemów to wła

ś

ciwie temat na oddzieln

ą

 prac

ę

, nie 

chciałbym, aby ktokolwiek potraktował poni

Ŝ

szy tekst inaczej, jak 

tylko wprowadzenie w tematyk

ę

 omawianych zagadnie

ń

. Mam 

nadziej

ę

Ŝ

e lektura lego rozdziału skłoni niektórych do 

samodzielnych eksperymentów i do pogł

ę

bienia wiedzy na 

wybrane tematy.

 

6.1 Co to są sygnały i jak je dzielimy

 

Je

Ŝ

eli zało

Ŝ

ymy, 

Ŝ

e z pewnego 

ź

ródła docieraj

ą

 do nas 

informacje o zmianie jaki

ś

 wielko

ś

ci Fizycznych (np. ci

ś

nienia, 

temperatury), to wielko

ść

 elektryczn

ą

 bezpo

ś

rednio zwi

ą

zan

ą

 z 

pierwotn

ą

 postaci

ą

 tej informacji nazywamy sygnałem. Ka

Ŝ

dy 

sygnał mo

Ŝ

e by

ć

 funkcj

ą

 wielu zmiennych niezale

Ŝ

nych. 

Najcz

ęś

ciej tak

ą

 zmienn

ą

 jest czas (t) lub cz

ę

stotliwo

ść

 (0.

 

Zasadniczo sygnały podzieli

ć

 mo

Ŝ

na na deterministyczne (je

Ŝ

eli 

mierzon

ą

 wielko

ść

 fizyczn

ą

 mo

Ŝ

na opisa

ć

 zale

Ŝ

no

ś

ciami 

matematycznymi) oraz losowe (gdy nie jest mo

Ŝ

liwe 

przewidzenie warto

ś

ci sygnału). W przypadku sygnału 

deterministycznego dwa zbiory danych opisuj

ą

ce sygnał 

uzyskane w tych samych warunkach powin-

 

ROZDZIAŁ 6 

ny być identyczne. Często zdarza się, Ŝe obok interesującego nas sygnału pojawił się szum, a sam sygnał uległ 
zniekształceniom. Wyłowienie go stać się wtedy moŜe nie lada problemem (wszelkie szumy nakładające się na sygnał 

background image

mogą mieć zarówno deterministyczny, jak i losowy charakter). 
Zmiany sygnału deterministycznego opisać moŜemy zaleŜnościami matematycznymi. Na sygnał taki składa się 
powtarzający się przebieg (okresowy) lub przebieg zanikający po upływie czasu (przejściowy). 
MoŜna załoŜyć, Ŝe sygnały okresowe składają się z jednego lub wielu przebiegów sinusoidalnych uzaleŜnionych od 
okresu powtarzania (przedziału czasu, po jakim sygnał powtórzy się). Najmniej skomplikowany jest sygnał 
okresowy opisywany funkcją: 

x(t)= A*sin(2nft+(pj 

gdzie A to stała określająca amplitudę, f to częstotliwość w Hz, a <p to początkowy kąt fazowy. Sygnał taki powtarza 
się co okres T równy l/f. 
Uogólniając, sygnał okresowy moŜna przedstawić za pomocą szeregu Fouriera (zakładamy, Ŝe sygnał okresowy 
składa si
ę z sinusoid harmonicznych): 

x(t) ^A,sin(2nft-^-(p^ +A^in(2*2nft+^... ^-A^inCn *2nft-^^) 

Występowanie w sygnale składowych o róŜnych częstotliwościach przedstawić moŜna za pomocą wykresu 
widmowego (jak na rysunku 9). 

A a 

Cz

ę

stotliwo

ść

 Rys.9 Widmo pr

ąŜ

kowe 

Sygnały przejściowe są sygnałami zanikającymi do zera w określonym (skończonym) czasie. Widmo takich sygnałów 
ma charakter 

133

 

SYGNAŁY l ICH PRZETWARZANIE 

 

ci

ą

gły, poniewa

Ŝ

 (teoretycznie) sygnał taki zawiera niesko

ń

czon

ą

 liczb

ę

 cz

ę

stotliwo

ś

ci składowych. 

Sygnały losowe analizujemy posługując się opisami propabilistycz-nymi i statystycznymi. Sygnały losowe 
podzielić moŜna na stacjonarne i niestacjonarne, O tym. czy sygnał uznać moŜna za stacjonarny, decyduje to, 
czy wyznaczone wg poniŜszego opisu xśr(t) i R(T) są stałe dla wszystkich wartości t, i T. 
ZałóŜmy, Ŝe uzyskaliśmy N zapisów sygnału o lej samej długości. x^ będzie wtedy wartością średnią sygnału w 
chwili t, po ilości zbiorów danych (np. dla 3 zbiorów opisujących sygnał mamy: 

xJt,) - (xft^ 4-x/^ +x//^/^. 

Funkcję autokorelacji R(T) wyznaczymy obliczając średnią ilo"7-. -nów dwóch próbek wziętych w dwóch 
oddzielnych chwilach ^ i ^ \: KaŜdym zbiorze (z zachowaniem odległości T=r^r,), Sygnał niestacjonarny to 
taki, dla którego wielkości x^ i R(T) nie zmieniają się z czasem. 

Istotną sprawą przy obróbce i analizie sygnałów jest ich prezentacja. 2 uwagi na to, Ŝe mają one na ogół 
charakter ciągły wśród form ich przedstawiania dominuje graficzna. W celu ułatwienia interpretacji sygnałów 
stosujemy często Filtrację i wygładzanie. Wygładzanie przeprowadzić moŜna stosując np. algorytm Hanna: 

y,= 0,25y^l + 0,5y, + 0,25y,+/ 

Naturalnie wygładzając w taki sposób ciąg danych eliminujemy z niego składowe o wyŜszych 
częstotliwościach - jest to toŜsame z filtracją dolnoprzepustową. JeŜeli Filtracja zachodzi w dziedzinie czasu, 
mówimy o splocie. Okno splotu to określenie, którego uŜywamy, gdy mówimy o oknie czasowym, spoza 
którego sygnały zostaną wytłumione. 

6.2 Przetwarzanie analogowo-cyfrowe

 

Przetworzenie sygnału ciągłego do postaci zdyskretyzowanej nosi nazwę przetwarzania analogowo-
cyfrowego. Sam proces podzielić moŜna na dwa etapy - próbkowanie i kwantyzację. KaŜdy z nich spełnia 
inną rolę i na kaŜdym z nich pojawiają się błędy innej natury. Zakładając dwuetapowość procesu, sam 
schemat przetwarzania przedstawić moŜna jak na rysunku 10. 

ROZDZIAŁ 6 

 

Sygnał we^

ś

oowy

 

Próbkowanie

 

Kwantyzacja

 

 

10.15.20.30.25.

 

Rys. 10 Przetwarzanie analogowo-cyfroioe

 

Jak widać, wynikiem przetworzenia sygnału ciągłego jest ciąg liczb opisujących wartości kolejnych próbek. Oba 
elapy procesu zostały omówione poniŜej. 

Próbkowanie

 

Próbkowanie polega na pobieraniu próbek wejściowego sygnału w pewnych odstępach czasu (dla stałych odstępów 
mówimy o próbkowaniu ze stalą częstotliwością). Wynikiem próbkowania jest szereg próbek, czyli wartości 
odpowiadających uśrednionej w krótkim czasie amplitudzie- Okres, w którym sygnał wejściowy jest uśredniany, to 

background image

apertura. Dla jakości próbkowania istotne jest, aby czas aper-tury był mały w stosunku do okresu próbkowania 
(czasu upływającego między pobraniem kolejnych próbek). 
Jak wspomniałem, waŜnym parametrem procesu jest częstotliwość, z Jaką pobierane są próbki. Zjawiskiem mającym 
bardzo niekorzystny wpływ na jakość są wahania tej częstotliwości (jej chwilowe zmiany). Nieokreśloność okresu 
próbkowania nosi nazwę fluktuacji impulsów próbkujących. 
Największą trudnością tego etapu przetwarzania analogowo-cyfro-wego jest niejednoznaczność. Chodzi tutaj o to, Ŝe 
ten sam zbiór próbek moŜe opisywać sygnały o bardzo róŜnych przebiegach. Ilustruje to rysunek 11. 

Rys.11 Efekt niejednoznaczno

ś

ci 

W praktyce moŜe więc dojść do tego, Ŝe gdy np. spróbkujemy z odpowiednio niską częstotliwością sygnał o 
przebiegu sinusoidalnym i częstotliwości f, to otrzymane próbki opisywać mogą teŜ sygnały sinusoidalne o 
częstotliwościach niŜszych. Zjawisko to nosi nazwę aliasingu. Ilustruje je rysunek 12. 

SYGNAŁY f fCH PRZETWARZANIE 

135

 

Warto

ść

 próbek

 

Rys. 12 Aliasing

 

Jest ono bardzo niekorzystne. Zapobiega się mu stosując tzw. filtry antyaliasingowe (lub mówiąc inaczej, 
odfiltrowując składowe o zbyt wysokich częstotliwościach). 
Dosyć łatwo wykazać, Ŝe częstotliwość próbkowania powinna być co najmniej dwukrotnie większa od 
częstotliwości sygnału, którego przebieg chcemy opisać próbkami. Tak więc dla częstotliwości próbkowania 
2f maksymalna częstotliwość przetwarzanego sygnału wynosić powinna f. W odniesieniu do dziedziny czasu 
twierdzenie przybiera postać kryterium Rayleigha: 

r > o.5*f

 

gdzie T to okres próbkowania wyraŜony w sekundach, a f to górna częstotliwość pasma, w jakim mieści się 
sygnał wyraŜona w Hz. 
Naturalnie nie wolno zakładać, Ŝe na sygnał nie składają się fale o wyŜszych niŜ dopuszczalne 
częstotliwościach. Aby zredukować efekty niejednoznaczności, sygnał przed próbkowaniem poddaje się 
filtracji dolnoprzepustowej. Oczywiście charakterystyka zastosowanego filtra nigdy nie będzie prostokątna - 
składowe o częstotliwościach tylko nieznacznie wyŜszych od załoŜonej f będą tłumione słabo. Stąd wynika 
konieczność próbkowania z wyŜszą niŜ 2f częstotliwością (w praktyce 4-60. 

Kwantyzacja

 

Kwantyzacja jest procesem, który polega na przedstawieniu w postaci szeregu liczb dyskretnych ciągu 
dyskretnych wartości próbek. PoniewaŜ sygnał analogowy moŜe przyjąć praktycznie nieskończoną 

ROZDZIAŁ 6

 

liczb

ę

 stanów, kwantyzacj

ę

 potraktowa

ć

 nale

Ŝ

y jako przybli

Ŝ

enie. Podziałem sygnału wej

ś

ciowego na ograniczon

ą

 

liczb

ę

 poziomów zajmuje si

ę

 kwantyzator. 

' * Wy]

ś

ae

 

Rys.13 Przykładowa charakterystyka kiDontyzatora

 

Proces kwantyzacji wprowadza pewien błąd związany ze skokiem kwantyzacji q. Błąd ten powoduje powstanie 
szumu zwanego szumem kwantyzacji. W mierze logarytmicznej odstęp szumów kwantyzacji od sygnału 
fonicznego przy kwantyzacji n-bitowej moŜna oszacować jako: 

L = 6*n + 1,8 IdBj 

Łatwo więc wyliczyć, Ŝe odstęp szumu kwantyzacji dla słowa 8-bi-towego (jak w SB) wynosić będzie 49,8 dB. Dla 
słowa dwukrotnie dłuŜszego (16 bitów) odstęp wyniesie juŜ 97,9 dB. 

6.3 Filtracja cyfrowa

 

Bardzo istotnym problemem z dziedziny cyfrowego przetwarzania sygnałów jest ich filtracja w dziedzinie 
częstotliwości. 
ZałóŜmy, Ŝe mamy sygnał, na który składa się wiele składowych o róŜnych częstotliwościach. ZałóŜmy teraz, Ŝe 
zaleŜy nam na wytłumieniu składowych o danej częstotliwości. UŜyjemy wówczas nitru zaporowego. W 
przypadku, gdy z sygnału chcemy „wyłowić" częstotliwości leŜące w pewnym zakresie, uŜyć musimy filtru 
przepustowego. 

137

 

SYGNAŁY / ICH PRZETWARZANIE

 

 

f                    i

 

Rys. 14 Charakterystyki filtrów dolna- i 

ś

rodkowoprzepustowego 

background image

Rysunek 14 przedstawia przykładowe charakterystyki często spotykanych typów filtrów. Kształt kaŜdej 
charakterystyki zaleŜy od parametrów filtru: jego dobroci Q, częstotliwości granicznych i wzmocnienia. 
Dobroć Q określa „stromość" charakterystyki. 

Rys.15 Wpływ dobroci no kształt charakterystyki filtru

 

Większa dobroć oznacza, Ŝe wykres charakterystyki jest bardziej stromy - wzrost wzmocnienia przy 
przekraczaniu częstotliwości granicznej jest szybszy. 
Wzmocnienie określa stosunek amplitudy sygnału wejściowego do amplitudy sygnału wyjściowego w zakresie 
częstotliwości objętych działaniem filtru: 

k = AJA^. 

lub w skali logarytmicznej 

k = 20log(AJAJ IdBj 

Najogólniejsza posiać równania opisującego działanie najprostszego filtru cyfrowego to równanie, w którym 
po lewej stronie stoi y, (wartość i-tej próbki sygnału wyjściowego), a po prawej suma wymna-Ŝanych przez 
stałe wartości poprzednich próbek sygnału wyjściowego oraz sygnału wejściowego x,. Wartość stałych 
wyznaczyć moŜna stosując odpowiednie algorytmy. 

ROZDZIAŁ 6 

Du

Ŝ

ym problemem filtracji cyfrowej jest czasochłonno

ść

 oblicze

ń

. Filracja realizowana w czasie 

rzeczywistym wymaga bardzo du

Ŝ

ych mocy obliczeniowych.

 

A oto tekst procedury filtrującej środkowoprzepustowo wskazany obszar pamięci. Procedura wymaga następujących 
parametrów: 

Fsamp:word    cz

ę

stotliwo

ść

 próbkowania wyra

Ŝ

ona w Hz,

 

Fsr:word        cz

ę

stotliwo

ść

 

ś

rodkowa filtru,

 

Wzmocn:real    wzmocnienie (musi by

ć

 wi

ę

ksze od jedno

ś

ci),

 

Dobrocrreal     dobro

ć

 filtru (wi

ę

ksza od 0.707).

 

Bl:potnter      wska

ź

nik na bufor z danymi do przefilirowania,

 

B2:potnler      wska

ź

nik na bufor, gdzie procedura ma umie

ś

ci

ć

 przefiltrowane dane,

 

Liword          ilo

ść

 bajtów ci

ą

gu przeznaczonego do filtracji.

 

procedurę Filtruj [Fsamp,Fsr:word:Wzmocn,Dobroc:real;B1,B2:pointer:L:word);

 

type

 

T8b=arrayEO..$FFFE] of byte;

 

var

 

X,Y,Z,A,B,C,D,E:rea!:

 

Licz:word;

 

3in

 

ifW2mocn<1 then exit; {to nie filtr zaporowy} ifDobroc<0.707then exit;

 

{wpierw obliczamy stałe filtracji A,B,C,D,E } X:=1/[sin[Pl

ł

Fs^/FsampVcos[PI

<

Fsp/Fsamp));

 

Y:=Wzmocn/Dobroc;

 

Z:=1/Dobroc;

 

A^DW+Y^+D/OW+Z^+D;

 

B^+S^WO^+Z^+I);

 

C^DW-Y^+W^+Z^+I];

 

D:=B;

 

E: ^^-Z^1 l/K^łZ^1 ];

 

{teraz mo

Ŝ

emy przysc

ą

pi

ć

 do filtrowania } for licz: =2 to L do

 

TBb(B2^Klicz]:=trunct

 

CTabtBmiicz-S]

 

+B

ł

Tab[B1

;^

)[^^cz-1]

 

+A

ł

Tab(B1"3[licz]

 

-ETabtBS^iicz-S]

 

-D^TabtBS^nicz-l]);

 

end;

 

139

 

SYGNAŁY f ICH PRZETWARZANIE 

 

6*4 Analiza widmowa sygnału

 

Analiza widmowa to poj

ę

cie odnosz

ą

ce si

ę

 do analizy amplitudy chwilowej lub mocy chwilowej 

sygnału w dziedzinie cz

ę

stotliwo

ś

ci. Podstawowym jej zagadnieniem jest wyznaczenie funkcji 

g

ę

sto

ś

ci widmowej mocy okre

ś

laj

ą

cej, jak

ą

 energi

ę

 nios

ą

 składowe sygnału o ró

Ŝ

nych 

cz

ę

stotliwo

ś

ciach. Metodami pozwalaj

ą

cymi na okre

ś

lenie rozkładu mocy sygnału w funkcji 

background image

cz

ę

stotliwo

ś

ci jego składowych s

ą

 mi

ę

dzy innymi bezpo

ś

rednia metoda szybkiego przekształcenia 

Fouriera i filtracja cyfrowa.

 

Pierwsza metoda, w najogólniejszym zarysie, polega na zastosowaniu algorytmu FFT (szybkiej transformaty 
Fouriera) w celu wyznaczenia kolejnych współczynników szeregu Fouriera określających udział kolejnych 
harmonicznych w sygnale. 
Metoda druga polega na poddaniu sygnału wejściowego filtracji z uŜyciem filtrów przepustowych o 
sąsiadujących charakterystykach, a następnie podniesieniu do kwadratu i uśrednieniu pojawiających się na ich 
wyjściach wartości w celu wyznaczenia mocy „przepuszczanej" przez kaŜdy z nich składowej. 
Jak wspomniałem, najczęściej interesuje nas moc poszczególnych składowych sygnału. Co oznaczać jednak 
moŜe moc w odniesieniu do ciągu dyskretnych próbek? OtóŜ przez pojęcie mocy sygnału rozumieć będziemy 
wartość średnią kwadratu amplitudy. Dla 4 próbek x(l), x(2), x(3) i x(4) moc wyrazimy więc następująco: 

P= (x(l) *x(I)+x(2) *x(2) +x(3) ^x(3) +x(4) *x(4))/4 

Pojęcie estymaty odnosi się do gęstości mocy, to znaczy określa, jaka moc przenoszona jest przez sygnały o 
częstotliwościach zawierających się w przedziale l Hz (dlatego wyraŜamy ją np. w W/Hz - wat na hertz). JeŜeli 
więc teraz wyobrazimy sobie np. filtr pasmowy o częstotliwości środkowej f, o szerokości pasma B, z którego 
otrzymaliśmy n próbek, to estymatę wyznaczyć moŜemy ze wzoru: 

G(f)=P(n)/B 

Obliczanie estymat dla częstotliwości „wyławianych" przez kolejne filtry o róŜnych częstotliwościach 
ś

rodkowych pozwala na wyznaczenie widma prąŜkowego mocy. W powyŜszym wzorze znak równości 

naleŜałoby w zasadzie zastąpić znakiem przybliŜenia, a to z uwagi na to. Ŝe w praktyce nie zrealizujemy filtru 
o nieskończenie wąskim paśmie, a ilość uśrednianych wartości jest ograniczona. W większości zastosowań nie 
ma to jednak większego znaczenia. 

ROZDZIAŁ 6 

6.5 Rozpoznawanie mowy ludzkiej

 

Rozpoznawanie mowy to dziedzina, w jakiej bez cyfrowego przetwarzania sygnałów trudno byłoby mówić o 
jakichkolwiek wymiernych osiągnięciach. Tymczasem wydaje się, Ŝe znalezienie prostych schematów 
rozpoznawania dźwięków mowy ludzkiej przenieść nas moŜe w zupełnie nową erę komunikacji między 
człowiekiem a maszyną. O ile dźwięk jako medium jest juŜ wykorzystywany przy przekazywaniu informacji przez 
maszynę człowiekowi („mówiące" zegarki, całe mnóstwo udźwiękowionych programów), o tyle ciągle Jeszcze 
trudno jest mówić o dwukierunkowej komunikacji. O tym, Ŝe zbliŜamy się jednak do chwili, w której nowoczesne 
programy obsługiwać będziemy wypowiadając polecenia do mikrofonu, świadczyć moŜe pojawienie się np. 
specjalnych aplikacji dla systemu Windows, które moŜemy „nauczyć" brzmienia prostych rozkazów wydawanych 
naszym głosem. W tej części rozdziału postaram się przekazać w zarysie podstawowe informacje związane z 
zagadnieniem rozpoznawania mowy. 
ZłoŜoność dźwięków mowy jest pochodną skomplikowania procesu jego artykulacji. Jego brzmienie zaleŜy od 
bardzo wielu czynników; własności osobniczych, intonacji, akcentu. Sygnał mowy niesie olbrzymią ilość 
informacji - mózg ludzki potrzebuje zaledwie małej części, aby dokonać prawidłowego rozpoznania. 
Zasadniczo biorąc najprostsza jest analiza sygnału w dziedzinie czasu - badamy wtedy jego amplitudę i szybkość 
jej zmian. W odniesieniu do amplitudy stosujemy miarę logarytmiczną. Jest ona bardziej naturalna, poniewaŜ dla 
dźwięków o małym natęŜeniu odczuwamy minimalną ich zmianę, a wraŜliwość naszego narządu słuchu na 
zmienność sygnału dźwiękowego spada wraz ze wzrostem jego natęŜenia. 
JeŜeli w sygnale zawiera się wiele częstotliwości składowych (a tak jest w przypadku sygnałów mowy), warto 
skupić się nad zmianami widma sygnału w funkcji czasu. ZałóŜmy więc, Ŝe mamy szereg wykresów widmowych 
obrazujący zmiany widma w dziedzinie czasu. Naturalnie porównywanie „na oko" wykresów widma nie ma 
większego sensu. Dlatego naleŜy wyróŜnić kilka jego podstawowych parametrów. 

Pierwszym jest średnia waŜona amplitudy składowych w widmie. Wielkość tę rozumieć moŜna jako środek 
cięŜkości wykresu widmowego. W amatorskich zastosowaniach aproksymuje się go uśre- 

141

 

SYGNAŁY / ICH PHZETWARZANfE 

 

dnioną częstotliwością sygnału w danym przedziale czasowym. Jej oszacowanie Jest bardzo proste - 
wystarczy zliczyć ilość przejść przez zero (PPZ) sygnału. I tak - załóŜmy, Ŝe mamy zapis dźwięku o czasie 
trwania
 10 ms (0,01 sekundy). Dźwięk próbkowaliśmy z częstotliwością 10 kHz. Mamy więc 100 próbek 
(10000*0,01=100). JeŜeli teraz wystąpienie przejścia przez zero notować będziemy wtedy, gdy z dwóch 
„sąsiadujących" próbek jedna będzie miała wartość poniŜej pewnej pewnej ustalonej wartości, druga - 

background image

powyŜej (będą miały przeciwne znaki), przy wystąpieniu n przejść przez zero powiemy, Ŝe uśredniona 
częstotliwość sygnału równa jest około n/ (2*0,01) [l/s=Hz], W praktyce moŜliwe jest przekonanie sięŜ
parametr liczby przej
ść przez zero w zupełności wystarcza Judzkiemu mózgowi do rozpoznania dźwięku. 
Łatwo przeprowadzić odpowiednie doświadczenie - wystarc/y graniczyć zakres zmian amplitudy do dwóch 
wartości - jedynym zachowanym parametrem tak „okaleczonego" sygnału będzie właśnie liczba PPZ. Rysunek 
16 przedstawia dwa sygnały - w pierwszym zmiany amplitudy są ciągłe, w drugim nie - jej wartości zostały 
ograniczone do dwóch poziomów. 

Sygna-f- wej

ś

ciowy

 

Sygnał po ograniczeniu zakresu zmian amplitudy

 

RysJ6 Ograniczenie zakresu zmian amplitudy

 

Dalej przedstawiam teksty źródłowe dwóch programów. Pierwszy z nich (RAW2PPZ) na podstawie 
zawartości wskazanego parame- 

]42 

ROZDZIAŁ 6 

trem pliku tworzy drugi, o

ś

miokrotnie krótszy. Ka

Ŝ

dy bit kolejnego bajtu nowego pliku koduje nam informacj

ę

czy warto

ść

 kolejnej próbki pobrana z pliku wej

ś

ciowego przekracza pewn

ą

 warto

ść

, czy te

Ŝ

 nie. Program 

drugi, na podstawie pliku „wyprodukowanego" przez RAW2PPZ tworzy plik opisuj

ą

cy kolejne próbki, z tym 

jednak, 

Ŝ

e amplituda (przez utrat

ę

 informacji na poprzednim etapie) mo

Ŝ

e przyj

ąć

 ju

Ŝ

 tylko dwa stany. Oba 

programy traktowa

ć

 mo

Ŝ

na jako kompresor i dekompresor danych d

ź

wi

ę

kowych. Mimo 

Ŝ

e jedyn

ą

 informacj

ą

 

zachowywan

ą

 po takiej „kompresji" jest liczba PPZ, „ro

Ŝ

kom presowany" d

ź

wi

ę

k nadal rozpoznajemy! program 

RAW2PPZ; 

uses dos; 

var

 

F:file;

 

Ce):flleofbyte;

 

D:dirstr;

 

N:namestr;

 

E;extst;r;

 

!le_8:lonsinC;

 

Buf:array[Q..71 ofbyte;

 

q,Bajt:byte;

 

begin

 

ifparamcounc=0t:hen

 

begin {gdy brak parametrów }

 

wriceInCU

Ŝ

ycie; RAW2PPZ pliki [plik2n;

 

halt end;

 

fsplii;[paramstr[1],D,N,E);

 

assignCF.paramscriI));

 

{$'-} reset[R1];

 

{$i+} ifioresuitoOthen

 

begin

 

writeInfBł

ą

d otwarcia pliku!'];

 

hale

 

end;

 

HeJ:=filesize(F)div8;

 

If paramcount=1 then

 

assign[Cel,n+'.PPZ') { rozszerzenie ,PPZ } else

 

ass!gn[Cel,paramstr(2]];

 

rewritetCeR;

 

SYGNAŁY / ICH PRZETWARZANIE 

repeaC

 

blockread[FBuf[0].83;

 

Ba|t:=0;

 

forq:=7downto O do

 

if BufE7-q]> 127 then Bajt: =Ba)t or t1 shl q];

 

wriceCCel.Belt],

 

dect!le_B] uncii ile_8=0;

 

BajC:=0;

 

fillchBrt6uf[Q),8,Q); {wyzerowanie bufora} blockread[F,Buf,filesize(F] mód 8); {reszta } 
forq:=7downto0do

 

if Buft7-q]> 127 then Bajt: =Bajt or [1 shl q);

 

write(Cel,Baft);

 

close(F);

 

closetCel] { zamykamy i po wszystkim} end.

 

tekst drugiego programu:

 

program PPZ2RAW;

 

background image

uses dos;

 

vsr

 

F:fileofbyte;

 

CBhftle;

 

D:dirstr;

 

N:namestr;

 

E:extstr;

 

Rozmiar: longint:

 

Buf:arr8y[0..7]ofbyte:

 

q.Bajt:byte;

 

begin

 

if paramcount:=0 then

 

begin {brak parametrów}

 

wnteInfU

Ŝ

ycie: PPZ2RAW pliki [plik21');

 

halt end;

 

fsplittparamstr(1),a,N,E);

 

assigntF.paramstKD);

 

{Si-} resettF]; { próba otwarcia pliku }

 

{$i+} ifiaresultoOthen begin

 

wrIteInCBl

ą

d otwarcia pliku !'3;

 

halt

 

ROZDZIAŁ 6 

end, rozmiar. =fi!esizs[R, if paramcount=1 chen

 

assign(Ce! N-'.RAW') { do pliku z rozszerzeniem RAW } else

 

assigntCel,parametry]);

 

re^nte[Cel,1]; { utworzenie }

 

repeat

 

read[F,BajC); { odczyt pojedynczego bajCu }

 

fillchar[Buf[Q],Q,0]; {wyzerowanie bufora }

 

forq:=7downto O do

 

ifBajtandCl shiq) oOthen Buf[7-q]:=12B;

 

blockwnte[Cel,Buf[0],8); { zapis 8-miu bajtów }

 

dectr-ozmiari untii Rozmiar=G;

 

close[F);

 

closeCCeD { zamykamy oba pliki i juŜ }

 

end. 

Za drugi wa

Ŝ

ny parametr uzna

ć

 mo

Ŝ

na szeroko

ść

 widma, której zmiany w czasie tak

Ŝ

e nios

ą

 informacje o przebiegu 

procesu artykulacji. 

Oprócz tego przy analizowaniu dźwięków mowy zwraca się uwagę na tzw. formanty. Co to? ZałóŜmy, Ŝe mamy 
dany wykres widmowy jak na rysunku 17. 

fl      f2        f3           f

 

Rys J 7 Przykładowy wykres widmowy

 

Widzimy na nim trzy maksima. Częstotliwości, dla których je obserwujemy (fl,f2,f3), to właśnie formanty. Ich 
połoŜenie i wielkość ulegają zmianie w czasie i wiele mówią o procesie emisji dźwięków. W mowie wyróŜnić moŜna 
kilka formantów. KaŜdego z nich poszukujemy w innym przedziale częstotliwości. Analiza formantów jest dość 
wygodna, bo zmieniają one swoje połoŜenie i wielkość dosyć wolno. Pewnych trudności nastręcza głównie ich 
odnalezienie. Wyobraźmy sobie, Ŝe dysponujemy widmem prąŜkowym (otrzymanym np. w wyniku zastosowania 
szybkiej transformaty Fouriera) jak na rysunku 18. 

SYGNAŁY f ICH PRZETWARZANIE

 

145

 

RysJ 8 Widmo pr

ąŜ

kowe 

Mimo Ŝe na pierwszy rzut oka trudno jest odnaleźć częstotliwość, której udział w badanym sygnale jest 
największy, moŜna postarać się aproksymować przebieg krzywej widmowej i załoŜyć np. Ŝe częstotliwość 
formantu równa jest 0,5(fl+f2). 

prawdopodobne poło

Ŝ

enie maksimum

 

Rys. 19 Aproksymacja przebiegu wykresu widmowego W procesie wykrywania formantów często stosuje się 
sieci neuronowe. 
Wydzielenie parametrów ściśle powiązanych z procesem artykulacji jest kluczowym problemem w dziedzinie 
rozpoznawania mowy. Gdy je wyznaczymy, pozostaje nam przyrównywanie otrzymanych wielkości do tych, 
jakie wyznaczyliśmy w procesie uczenia. Naturalnie nie jest to proste. JeŜeli jednak zbiór parametrów 
opisujących dokonane nagranie potraktujemy jako pewien obiekt, to odnalezienie klasy, do której on naleŜy, 
jest JuŜ problemem innej natury. Najogólniej rzecz biorąc, identyfikacji dokonuje się najczęściej przyjmując 
istnienie pewnej „przestrzeni cech", w której kaŜdy wymiar opisuje inny parametr. KaŜdej klasie obiektów, 

background image

które mają podlegać identyfikacji, przypisujemy pewną przestrzeń, której granice wyznaczane są przez 
wartości kaŜdej ze współrzędnych-parametrów. Obiekt naleŜy do klasy, jeśli punkt w przestrzeni cech 
wyznaczany przez jego parametry mieści się w „objętości" tej klasy. JeŜeli parametry ulegają zmianom w 
dziedzinie czasu, to naturalnie dokładamy jeszcze współrzędną czasu i zamiast punktu otrzymujemy krzywą. 

I tak na przykład, gdy dźwięk rozpoznajemy tylko na podstawie zmienności liczby przejść przez zero, to mamy 
do czynienia z prze- 

ROZDZIAŁ 6 

strzeni

ą

 dwuwymiarow

ą

 (płaszczyzn

ą

). Ka

Ŝ

dy punkt na tej płaszczy

ź

nie wyznaczaj

ą

 współrz

ę

dna czasu i 

liczby przej

ść

 przez zero. Je

Ŝ

eli rozpoznaniu ma np. podlega

ć

 cały wyraz, to ka

Ŝ

de kolejne nagranie 

pozostawi na tej płaszczy

ź

nie 

ś

lad w postaci krzywej nios

ą

cej informacje o zachowaniu liczby PPZ w 

funkcji czasu. „

Ś

lady" wielu nagra

ń

 dokonywanych w procesie uczenia wyznacz

ą

 na naszej płaszczy

ź

nie 

cech obszar. Je

Ŝ

eli teraz dokonujemy nowego nagrania, to „nakładamy" otrzyman

ą

 krzyw

ą

 na taki 

„wzorzec" i podejmujemy decyzj

ę

 - albo wypowiedziane słowo zaliczamy do klasy (krzywa zmie

ś

ciła si

ę

 w 

obszarze), albo nie. Naturalnie takie podej

ś

cie do problemu ma pewne wady. Mo

Ŝ

e okaza

ć

 si

ę

Ŝ

e obszar 

jest tak „pojemny" (podczas uczenia brało udział wiele osób wypowiadaj

ą

cych słowo z ró

Ŝ

nym akcentem i 

intonacj

ą

), 

Ŝ

e krzywa zmie

ś

ci si

ę

 we wzorcu, mimo 

Ŝ

e opisuje zupełnie inny obiekt. Có

Ŝ

. mo

Ŝ

na wi

ę

c na 

przykład bada

ć

 tendencje krzywej (do wzrostu, do spadku) - konieczne jest wówczas ró

Ŝ

niczkowanie 

przebiegu PPZ(t) w pewnych przedziałach czasu.

 

Istotnym problemem, o jakim nie wolno nam zapominać, jest takŜe normalizacja dokonywanych nagrań. Mówiąc 
prosto, chodzi o „wyrównywanie" nagrań przed ich analizą. KaŜdy z ludzi wypowiada to samo słowo inaczej. Dla 
przykładu; imię „Kasia" moŜe być przez jedną osobę wypowiedziane jako „Kaasia", przez drugą: „Kassiia". l nie 
wystarczy tutaj zwykłe przeskalowanie w dziedzinie czasu -róŜne są bowiem takŜe proporcje między czasem 
wypowiadania kolejnych fonemów (elementarna część dźwięku mowy - jak litera w alfabecie). 

FORMAT WAV 

147

 

 
 

7, FORMAT WAV 

Format WAV (Microsoft Waveform Audio File) to format zapisu danych d

ź

wi

ę

kowych u

Ŝ

ywany przez 

programy pracuj

ą

ce pod kontrol

ą

 systemu Microsoft Windows. My

ś

l

ę

Ŝ

e jego znajomo

ść

 mo

Ŝ

e si

ę

 

przyda

ć

 np. przy tworzeniu oprogramowania d

ź

wi

ę

kowego mog

ą

cego współpracowa

ć

 z aplikacjami 

Windows.

 

Pliki zapisywane w formacie WAV spełniają załoŜenia struktury RIFF 

(Resource Interchange File Format). 

Podstawowym elementem pliku jest pakiet o następującej budowie: 

•   Identyfikator pakietu - czleroznakowy ciąg „RIFF" 

•   Ilość danych w pakiecie (dana 4-bajtowa) 

Pakiet danych o długości specyfikowanej w poprzedzającej go 

części 

Pakiet RIFF składać się moŜe z podstruktur róŜnych formatów i przechowujących róŜne typy danych. Nas 
interesują tylko dane dźwiękowe. I lak, „podpakiet" formatu WAVE ma następującą strukturę: 

•   Identyfikator (4-znakowy ciąg „WAYE") 

Pakiet formatu (przechowuje parametry dotyczące danych) 

•   Pakiet danych 

HOZDZIAŁ 7

 

Na pakiet formatu składaj

ą

 si

ę

 kolejno:

 

1. Identyfikator pakietu formatu (4-znakowy ciąg „fmt ") 
2. 4-bajlowe słowo przechowujące długość dalszej części pakietu 
3. 2 bajty przechowujące wartość odpowiadającą rodzajowi formatu 
4. 16-bitowe słowo przechowujące ilość kanałów (dla dźwięku mono ma wartość l, dla stereo - 2) 
5. Czterobajtowe słowo określające częstotliwość próbkowania w Hz uŜyta przy zapisie danych 
6. Dana czterobajtowa charakteryzująca prędkość przepływu danych (wyraŜoną w bajtach na sekundę). Wielkość 
uwzględnia liczbę kanałów. Na przykład dla monofonicznego dźwięku sprób-kowanego 8-bitowo z częstotliwością 
10 kHz wartość słowa równa jest 10000 [bajtów/sęk] 
7. Wielkość (2-bajtowa) określająca wyrównanie danych w pakiecie danych (w bajtach). Dla próbkowania 8-
bitowego równa jest ilości kanałów 

background image

8. Dwa bajty przeznaczone na inne parametry Pakiet danych formatu WAVE ma postać: 
1. Identyfikator (ciąg „data" 4-znakowy) 
2. 4-bajtowe słowo określające długość pakietu danych (bez uwzględnienia pierwszych 8 bajtów - na identyfikator i 
długość) 
3. Wartości kolejnych próbek dźwiękowych PoniŜej przedstawiam krótki programik, którego działanie polega na 
wyświetlaniu parametrów wskazanego przy wywołaniu pliku zapisanego w formacie WAV: 

program ShowWAY;

 

type

 

Header=record

 

RIFFId:array[1..4]ofchar;

 

CLen:longint;

 

WAVEId:arTay[1.,4]ofcnar;

 

FMTId:arr'8yt1..4] ofchar:

 

FMTLen:longint;

 

FMTTag:word;

 

Channels:word;

 

Fpeq;longint:;

 

Transmis;bngirt;

 

B/ces:vwrd;

 

FORMAT WAV 

Bts.word:

 

DATAId,array[1,,4] of char, DATALen;longint

 

end:

 

var

 

f:file:

 

Bu^painter;

 

begm

 

if paramcountol then begm

 

wnteInfUzycie, ShowWAV plik'),

 

halt end, assign(f,paramstr[1)]:

 

1$!-}

 

reset(f,1); { próba otwarcia pliku } {$!+} ifioresultoOthen begin

 

wnteInCBtad otwarcia pliku !');

 

halt { nie udało si

ę

 } end;

 

getmem[Buf,sizeof[Header)]; { pami

ęć

} blockreadtf.Buf^sizeoftHeaderIJ; { odczyt} ctosetfl; { zamykamy plik} with 

HeadertBuf) do begin

 

wnteh:

 

whCefPlik    : ');

 

writeln[paramstr[1)3:

 

writeETyp     ; '3;

 

ifChanne!s=1 then writelnt'MONO'1 elsewnteln['STEREO'l;

 

writeInfCz

ę

stotiiwo

ść

: ',Freql;

 

writeInCHo

ść

 bajtów : 'iDATALen);

 

writeln('-> Czas Es]: ',DATALen/frcq:5:2) end

 

end.

 

ROZDZIAŁ? 

LITERATURA 

]5] 

LITERATURA 

[l ] K.G. Beauchamp „Przetwarzanie sygnałów metodami analogowymi i 

cyfrowymi", WNT, 1978 

|2| N. Kilen „Z Turbo Pascalem w gł

ą

b systemu", LYNX-SFT, 1994 [3) 

P. Norton „The New Peter Norton Programmer^s guide to the IBM 

PC & PS/2", Microsoft Press, 1988 [4] A. Stolz „Le grand Iwre de 
la Sound Blaster",
 Micro Application. 

1992 

[5] R. Tadeusiewicz „Sygnał mowy", WKŁ, 1988 [6] A. Wojtkiewicz 
„Elementy syntezy Filtrów cyfrowych". WNT, 1984 [7] „Sound Blaster 
Deueloper Kit",
 Creative Labs Inc.. 1990