Hangkártyák programozása retro 23.

9. Mesterséges hangzás kialakítása a gyakorlatban

  1. A kialakítandó hangzásnak megfelelő terem, illetve hangtér kiválasztása. Ilyenkor ügyelni kell a hangforrás (egy vagy több hangszóró) és a mikrofon, illetve mikrofonok, vagy műfej elhelyezésére. Ugyanis a kialakítandó hangzás attól is függ nagymértékben, hogy hova képzeljük a hallgató személyt, illetve pl. a zenekart.
  2. A programok és hangminták telepítése a lemezmellékletről történik, alapesetben a Program Files\Spiller\Diplomamunka könyvtárba. Ezen belül az ASF könyvtárban található maga a mesterséges hangzást kialakító program, melynek az Artificial Sound Field (Mesterséges hangtér) fantázianevet adtam. A Program Files\Spiller\Diplomamunka\CppDLL alkönyvtárban az ASF program által használt Fourier transzformációs és konvolúciós függvényeket tartalmazó DLL C++ kódja található. A Program Files\Spiller\Diplomamunka\Test könyvtár tartalmazza mindazon programokat, amelyekkel a hangkártya programozása során foglalkoztunk, illetve itt található a legfontosabb program: Rec.exe, amely az impulzusválasz tesztet végrehajtja. A Program Files\Spiller\Diplomamunka\Samples könyvtárba történik a hangminták telepítése. Ezen belül négy alkönyvtár található: Others: egyéb vizsgálódásra alkalmas hangminták, Responses: különböző hangterek impulzusválaszai, Studio: stúdió minőségű, vagy rendkívül kicsi „teremhangot” tartalmazó felvételek, Results: a stúdiófelvételek és az impulzusválaszok konvolúciójaként létrehozott, az adott hangtérnek megfelelő hangzással rendelkező hangminta eredmények.
  3. Ha mindent megfelelően elrendeztünk, jöhet a teszt. Miután megtettük a programbeli szükséges beállításokat is, a dolgunk csupán annyi, hogy a DOS parancssorába begépeljük a felvevő program nevét és paraméterét, amely nem lesz más, mint a felvétel eredményének mentési útvonala. Például Rec C:\dokumnetumok\test.sam.
  4. Ellenőrzés képen vissza is játszhatjuk a felvétel eredményét, hasonló módon, mint ahogy a felvétel történt: Play C:\dokumnetumok\test.sam. A Scroll.pas nevű Pascal programmal meg is tekinthetjük a felvétel időfüggvényét. Ez azonban nem paraméteresen működik, így futtatásához szükségünk van valamilyen keretrendszerre.
  5. Ha minden rendben, akkor elindíthatjuk az ASF programot, s nekiláthatunk függvényeink elemzésének, illetve a mesterséges hangzás létrehozásának.
  6. Miután elmentettük a konvolúció eredményét, azt visszahallgathatjuk a 3. pontban bemutatott módon.

Az ASF program használatának ismertetése:

  1. A File menüpont Open Impulse Response parancsával nyithatjuk meg az impulzusválasz teszt eredmény-függvényét. A megjelenő párbeszédablakból kiválaszthatjuk a keresett hangmintát. Természetesen akármilyen hangmintát kiválaszthatunk vizsgálat céljából, de ha értelmes eredményt kívánunk elérni a konvolúcióval, célszerű itt az impulzusválaszt megnyitni. Megnyitást követően a függvény grafikusan is megjelenik az ablakban, amelynek időfüggvénye a gördítősáv segítségével vizsgálható.
  2. A függvényen önmagában is végezhetünk transzformációkat, de ügyeljünk arra, hogy a konvolúció előtt mindig eredeti állapotba állítsuk vissza. A Tools menüpont Fast Fourier Transform almenüjéből két pont választható: Normal és Inverse. Közvetlenül a hangminta megnyitását követően az Inverse lesz kipipálva, ami azt jelzi, nincs transzformált állapotban a függvényünk. Ha rákattintunk a Normal-ra, megkapjuk a függvény gyors Fourier transzformáltját, amely grafikusan is látható lesz, s amely az időfüggvényhez hasonlatosan görgetéssel vizsgálható. Természetesen kattintáskor a Normal kipipált állapotba kerül, jelezve, hogy a függvényünk transzformált állapotban van. Ha az Inverse pontra kattintunk, megtörténik a visszatranszformálás.
  3. A második függvényünk esetében is ugyanúgy kell eljárni, mint az első esetében, csupán annyi a különbség, hogy azt a File Open Sample menüpontjával nyithatjuk meg. A két beolvasott függvény, illetve a majdani eredmény között az ablak alsó részén elhelyezkedő gombok segítségével lehet váltani.
  4. Az Options Log of NFFT paranccsal adhatjuk meg, hogy hány alappontra kívánjuk elvégezni a transzformációt. Ha például 10-et választunk, akkor az alappontok száma: 1024, mivel a listából az alpontok számának kettesalapú logaritmusát kell kiválasztani.
  5. A legfontosabb parancs a Tools menü Convolution with the help of FFT pontja, amely magát a konvolúciót végzi. Elvégzése után három függvényt vizsgálhatunk. Természetesen az eredmény spektrumát is megnézhetjük, majd az eredeti állapotába visszaállítva elmenthetjük a File\Save Result As menüponttal.

Érdemes néhány vizsgálatot is elvégezni a telepített hangminták segítségével. A telepítést követően a Program Files\Spiller\Diplomamunka\Samples\Studio könyvtár hangmintái között található egy stúdió felvétel 2cd.sam néven, amely egy részlet a The Music of Ireland című CD válogatásról. Ezt a hangmintát fogjuk szoftveresen más és más hangzással ellátni.

Első lépésben nyissuk meg az impulzusválaszok közül a dist68.sam nevű hangmintát. Ez egy viszonylag kicsi, 2,2m x 3m x 2,2m-es, néhány bútordarabbal berendezett szobában készült. A hangforrás és a mikrofon egymással szemben helyezkedett el, 68 cm távolságra. Ha ezt konvolúciós viszonyba hozzuk a stúdió felvételünkkel és elmentjük annak eredményét, akkor hangmintát kapunk, amely úgy szól, mintha az eredeti felvételt a szobában a mikrofontól 68 cm távolságban vettük volna fel. Az eredmény függvény egyébként megtalálható a Program Files\Spiller\Diplomamunka\Samples\Results könyvtárban 2cd68.sam néven, amelyet összehasonlítva az eredetivel lényeges különbséget fogunk tapasztalni. További vizsgálatokat érdemes elvégezni a dist102.sam, dist136.sam és a dist200.sam impulzusválaszokkal, amelyek az említett szobában készültek különböző távolságokban.

Eddig csak olyan eseteket vizsgáltunk, amikor az első hanghullámok közvetlenül érték a mikrofont. Most hasonlítsuk össze a 2cd200.sam és a 2cd220.sam hangmintákat. Ugyanabban a szobában történt a felvétel különböző távolságban, azonban a második esetben a hangforrást és a mikrofont egymásnak ellenkező irányba fordítottuk, úgy hogy az utóbbi egy sarokban helyezkedett el. A két hangmintát meghallgatva meglepődötten tapasztalhatjuk, hogy a nagyobb távolságban végrehajtott teszt „erősebb” hangzást produkál. Erre a hangforrás és a mikrofon elhelyezése ad magyarázatot: a szoba sarka felerősítette a nem közvetlenül érkező hanghullámokat.

Miután megvizsgáltuk, hogy a távolság illetve a hangforrás és a mikrofon helyiségen belüli pozíciója miként befolyásolja a hangzást, nézzük meg, hogy különböző akusztikai terek milyen különbségeket okoznak összességében. Az eredmény függvényeink között található 2cdb2.sam, 2cdr2.sam és 2cdg.sam, amelyek közül az első egy fürdőszoba, a második egy nagyobb szoba és a harmadik egy garázs hangzását adja vissza A legmarkánsabb az összes közül talán a fürdőszoba, amely igen nagy utózengési idővel rendelkezik a rendkívül nagy csempe és tükörfelületnek köszönhetően.

Végül nézzük meg a mesterséges visszhang kialakításának lehetőségeit. Itt ahelyett, hogy egy valódi visszhangot produkáló hangteret kerestem volna, csaláshoz folyamodtam. Ugyanúgy jártam el az impulzusválasz tesztnél, mint bármilyen más terem esetében, csupán annyi volt a különbség, hogy több impulzust generáltam egymás után, attól függően, hogy milyen időkéséssel akartam létrehozni a visszhangot, illetve egyszeres, vagy többszörös visszhangot akartam-e. Ha például a tihanyi visszhangot szeretném modellezni, akkor egymás után két impulzust kell generálni, mivel egyszeres visszhangról van szó. Természetesen az első nem lehet olyan „magas”, mint a másik, mivel számolnunk kell bizonyos energiaveszteséggel. A hangnak kb. 1000 m-t kell megtennie, ami 340 m/s-os sebesség mellet 2,94 s-ot jelent, esetünkben hangmintára átszámítva: 129654. Azonban a rendszer is rendelkezik bizonyos időkéséssel, így 129654-nél kisseb kell, hogy legyen a két impulzus távolsága. Az eredményt visszahallgatva egyértelmű, hogy az nem tökéletes, mert rendkívüli módon zeng. Ennek az oka az, hogy nem szabadtérben hajtottuk végre az impulzusválasz tesztet, hanem egy teremben, így annak az utózengése megmaradt a hangmintán. Ez azonban jó példa lehet arra, hogy miként lehet különböző termek hangzásait „összekeverni”. A gyakorlatban is sokszor találkozunk olyan esettel, amikor van egy visszhangosított termünk, amelynek nem megfelelő az utózengése, ugyanakkor szeretnénk a termenek a visszhang tulajdonságát megőrizni. Ilyenkor nincs mit tennünk, mint a megfelelő utózengésű teremben mesterségesen létrehozni a visszhangot és az így keletkező válaszfüggvénnyel konvolúciós szűrést végezni a stúdió felvételen. A 3.4. pontban említett, nem kívánatos teremvisszhangok kiküszöbölése is lehetséges hasonló módszerrel.

Egyszeres visszhanggal rendelkező termet modellez 2cdecho2.sam. Szintén egyszeres visszhangra mutat példát vecho3.sam, a különbség annyi, hogy itt egy rövid mondat hangzik el. A vfecho.sam  a csörgővisszhangot igyekszik bemutatni, amely a többszörös visszhang egy speciális fajtája. Végül pedig a tihanyi visszhang időkésését utánzó hangállományt érdemes megvizsgálni: vtihany.sam.

Látható tehát, hogy ahelyett, hogy az adott akusztikai térben vettük volna fel a teljes hanganyagot elég volt csupán az impulzusválasz tesztet elvégezni, majd annak eredményét tárolni, ezáltal időt spórolva és lehetővé téve az újboli felhasználáhatóságot. Magyarán nem kell mindig az adott hangversenytermbe, vagy templomba rohanni, hogy a teljes zeneművet ott vegyük fel az adott hangzás elérése érdekében, ha ezt megtehetjük otthon, vagy a stúdióban egy számítógép segítségével.

Végül eljutottunk odáig, hogy magát a tesztet sem a valóságos helyen hajtottuk végre, hanem modelleztük annak valamilyen tulajdonságát.

Irodalomjegyzék

[1]  Tarnóczy Tamás: Hangnyomás, hangosság, zajosság, Akadémia Kiadó, Budapest, 1984

[2]  Tarnóczy Tamás: Teremakusztika I-II. kötet, Akadémia Kiadó, Budapest, 1986

[3]  Peter M. Ridge – David M. Golden – Ivan Luk – Scott E. Sindorf: Sound Blaster: The Official Book, Panem – McGraw-Hill, Berkeley, California, 1994

[4]  László József: Hangkártya programozása Pascal és Assembly nyelven, Computer Books, Budapest, 1999

[5]  Bajusz – Bors – Csibra – Horváth: A PC-k hangja, COM-WARE, Budapest, 1995

[6]  Székely Vladimír: Képkorrekció, hanganalízis, térszámítás PC-n (Gyors Fourier transzformációs módszerek), Computer Books, Budapest, 1994

[7]  Gyimesi László: Digitális jelfeldolgozás, SZIF-UNIVERSITAS Kft., 1999

[8]  Giczy Gergő: Hanghatár, CHIP magazin, XII. évf., 1. szám, 92-96. oldal

[9]  Köhler Zsolt: Hangoskodás, Computer Panoráma, XI. évf., 6. szám, 40-47. oldal

[10]        Szittya Ottó: Digitális és analóg technika, LSI Oktató Központ, Budapest 1999

[11]        Markó Imre: PC-k konfigurálása és installálása (A hardver), LSI Oktató Központ, Budapest 1999

Hangkártyák programozása retro 22.

8.3. A Fourier térben végezhető konvolúció C függvénye

A következő struktúra definiálása szükséges:

typedef struct { int n;         // A függvény elemek száma

int n0;        // Az első elem valódi indexe

double *fv;    // Mutató a függvény elemek tömbjére

int ndft;      // A dft alappontok száma, vagy 0

complex *dft;  // Mutató a dft értékekre, vagy NULL

} fvtype;

A függvény az fvtype struktúrákban a Fourier transzformáltat is tárolja, azt csak akkor számolja újból, ha szükség van rá.

extern “C” __declspec(dllexport)

int WINAPI convol_by_fft(fvtype *fvta,fvtype *fvtb,fvtype *fvtres)

// A bemenő függvényeket és az eredmény függvényt kapja paraméternek

{ long int i,na,nb,nres,ndft,logndft;

double s,*dbuff,*dp1,*dp2;

complex *cp1,*cp2,*cp3;

//Előkészítések

na=fvta->n;

nb=fvtb->n;

nres=na+nb-1; //Az eredmény vektor hossza

ndft=1; //A DFT hossza

logndft=0;

while(ndft<nres) { ndft<<=1; logndft+=1; }

refft_prepare(logndft); //Az FFT előkészítése

//Operandusok transzformálása

if(dft_fvtype(fvta,ndft)) goto alloc_err;

if(dft_fvtype(fvtb,ndft)) goto alloc_err;

//Konvolúció

free_fvtype(fvtres);

fvtres->dft=(complex*)farcalloc(ndft,sizeof(double));

if(fvtres->dft==NULL) goto alloc_err;

cp1=fvta->dft;

cp2=fvtb->dft;

cp3=fvtres->dft;

cp3->re=cp1->re*cp2->re;

cp3->im=cp1->im*cp2->im;

cp1++; cp2++; cp3++;

for(i=1;i<ndft/2;i++) cx_mul(cp1++,cp2++,cp3++);

fvtres->ndft=ndft;

//Visszatranszformálás

dbuff=(double*)farcalloc(ndft,sizeof(double));

if(dbuff==NULL) goto alloc_err;

dp1=(double *)fvtres->dft;

dp2=dbuff;

s=(double)ndft;

for(i=0;i<ndft;i++) *(dp2++)=*(dp1++)*s;

reifft(dbuff);

//Az eredmény elhelyezése az eredményvektorban

fvtres->fv=(double*)farcalloc(nres,sizeof(double));

if(fvtres->fv==NULL) goto alloc_err2;

dp1=dbuff;

dp2=fvtres->fv;

for(i=0;i<nres;i++) *(dp2++)=*(dp1++);

fvtres->n=nres;

fvtres->n0=fvta->n0+fvtb->n0; //Kezdőpont offset

//Kilépések, hibajelzések

farfree(dbuff);

refft_close();

return(0);

alloc_err2:

farfree(dbuff);

alloc_err:

refft_close();

return(1);

}

A fenti függvény szintén megtalálható a F.27-ben.

A Delphi-ben közvetlenül alkalmazott C függvények deklarálása, illetve rövid leírása:

function ReFftPrepare(log_n:longint):integer;

stdcall; external ‘prjconvoldll.dll’ name ‘refft_prepare’;

A fenti függvény feladata, hogy előkészítse a bemenő valós vektor transzformációjához szükséges munkaterületeket és az exponenciális függvény táblázatos kezelését. A transzformáció elvégzése előtt mindig ezt a függvényt kell meghívni, s itt kell megadni, hogy hány alappontú transzformációt szeretnénk végezni.

procedure ReFftClose;

stdcall; external ‘prjconvoldll.dll’ name ‘refft_close’;

A ReFfftClose függvény, illetve eljárás a ReFftPrepare által lefoglalt munkaterületek felszabadítását végzi, ha már nem kívánunk további transzformációkat végezni.

procedure ReFft(poi:pdouble);

stdcall; external ‘prjconvoldll.dll’ name ‘refft’;

Ez az eljárás végzi a valós bemenő vektor gyors Fourier transzformációt, miután megtörtént a munkaterületek előkészítése és az exponenciális függvény táblázatos megadása. Algoritmusát és az azt megvalósító program kódját részletesen tárgyaltuk az előző fejezetekben.

procedure ReiFft(poi:pdouble);

stdcall; external ‘prjconvoldll.dll’ name ‘reifft’;

A ReiFft eljárás végzi az inverz gyors Fourier transzformációt, amely szintén a szükség előkészítések után végezhető el eredményesen. Paraméterként a transzformálandó vektor kezdetére mutató pointert vár.

procedure InitFvtype(fvt:pfvtype);

stdcall; external ‘prjconvoldll.dll’ name ‘init_fvtype’;

A fenti eljárás a kezdeti, üres állapotnak megfelelően kitölti az fvt struktúrát, illetve rekordot. Deklarációt követően használata kötelező, de a tapasztalat azt mutatja, hogy a Delphi eleve az üres állapotnak megfelelően tölti ki a struktúrát, vagyis elvégzi helyettünk az inicializálást. Delphi-ben használata inkább ajánlatos, mint kötelező érvényű.

function FillFvtype(n:longint;n0:longint;data:pdouble;fvt:pfvtype):integer;stdcal;external ‘prjconvoldll.dll’ name ‘fill_fvtype’;

A megfelelő adatsor alapján a FillFvtype függvény kitölti az adott struktúrát, illetve rekordot.

function Convolution(fvta:pfvtype;fvtb:pfvtype;fvtres:pfvtype):integer;

stdcall; external ‘prjconvoldll.dll’ name ‘convol_by_fft’;

A tulajdonképpeni konvolúciót végző függvény, amelynek mindhárom paramétere egy fvtype típusú rekord. A rutin meghatározza a konvolúció eredményének hosszát és az FFT alappontjainak minimálisan szükséges hosszúságát, majd előállítja a két operandus transzformáltját. Kiszámítja az operandusok DFT-inek szorzatát, és ezt az eredmény DFT mezőjére helyezi. Végül elvégzi az eredmény visszatranszformálását.

A következő metódus magának, a konvolúciónak a teljes menetét mutatja be:

procedure TForm1.Convolution1Click(Sender: TObject);

var i:longint;

begin

//A kurzorunk jelzi, hogy dolgozunk

screen.cursor:=crHourglass;

try

//Függvények inicializálása

InitFvtype(@fv1);

InitFvtype(@fv2);

InitFvtype(@fv3);

//Az operandus függvények előkészítése, hiba esetén jelzés, majd kilépés

if(FillFvtype(high(p1^)+1,0,@p1^,@fv1))<>0 then

begin

ShowMessage(‘Allocation error while FillFvtype!’);

exit;

end;

if (FillFvtype(high(p2^)+1,0,@p2^,@fv2))<>0 then

begin

ShowMessage(‘Allocation error while FillFvtype!’);

exit;

end;

//Konvolúció elvégzése, hiba esetén jelzés, majd kilépés

if (Convolution(@fv1,@fv2,@fv3))<>0 then

begin

ShowMessage(‘Allocation error while Convolution!’);

exit;

end;

//A konvolúció eredményének kiolvasása

for i:=low(pres^) to high(pres^)-1 do

begin

pres^[i]:=fv3.fv^;

inc(fv3.fv);

end;

//Ha végeztünk üzenet, illetve a kurzor visszaállítása

ShowMessage(‘Convolution ready!’);

finally

screen.cursor:=crDefault;

end;

button:=3;

//Az eredmény függvény grafikus megjelenítése

DrawWaveRes(pres^);

end;

Egy, az FFT elvégzésére alkalmas metódust is bemutatok:

procedure TForm1.normal1Click(Sender: TObject);

begin

//Jelölések tájékoztatásul: most normal FFT-t végzünk

normal1.checked:=true;

inverse1.checked:=false;

screen.cursor:=crHourGlass;

try

//A transzformálandó függvény kiválasztása

case button of

1:

begin

// Munkaterület, exp.tábla előkészítése,

// transzformáció alappontjainak száma

if ReFftPrepare(lognfft)<> 0 then

begin

ShowMessage(‘Allocation error while FFT!’);

exit;

end;//if

//Gyors Fourier transzformáció elvégzése

Refft(@p1^);

//Lefoglalt munkaterületek felszabadítása

ReFftClose;

//A transzformált grafikus megjelenítése

DrawWaveSam(p1^);

end;//1

2:

begin

if ReFftPrepare(lognfft)<> 0 then

begin

ShowMessage(‘Allocation error while FFT!’);

exit;

end;//if

Refft(@p2^);

ReFftClose;

DrawWaveSam(p2^);

end;//2

3:

begin

if ReFftPrepare(lognfft+1)<> 0 then

begin

ShowMessage(‘Allocation error while FFT!’);

exit;

end;//if

Refft(@pres^);

ReFftClose;

DrawWaveRes(pres^);

end;//3

end;//case

finally

screen.cursor:=crDefault;

end;

end;

Lejjebb, F.24-ben példát láthatunk a konvolúció eredményére.

A konvolúciót, illetve az FFT-t végző teljes Delphi forrás pedig a F.28-ban tanulmányozható.

F.24.a

Impulzusválasz függvénye

F.24.b

Tetszőleges gerjesztés függvénye

F.24.c

Konvolúció eredménye

F.24. Mesterséges hangzás megvalósítása konvolúcióval

Hangkártyák programozása retro 20.

8. Konvolúció a Fourier térben

Miután megismerkedtünk a diszkrét Fourier transzformációval és az azt megvalósító FFT-vel, a mesterséges hangzás létrehozásának elvét tárgyaljuk. Ahogy előzőleg említettem, erre a konvolúciót fogjuk használni. Nézzük meg először az elvét, illetve algoritmusát alapján és a megvalósítást végző program kódját [6] alapján.

8.1. Folytonos függvények konvolúciója

A konvolúció fogalmát egy egyszerű elektromos hálózat példáján keresztül fogjuk levezetni. Legyen adott egy áramkör, amely ellenállásokból és kondenzátorokból áll. A kondenzátorok miatt a hálózat bementére adott jel késéssel és némileg torzult formában jut el a kimenetre. Vezessünk be egy egyezményes mérőjelet, amelyet a hálózat bementére juttatunk és mérjük kimenetét, vagyis a bemenetre adott válaszát. E mérőjel legyen egy elég keskeny Dt szélességű feszültség impulzus. Az impulzus integrálja legyen egységnyi. Következésképp 1/Dt magas lesz. A Dt szélességű mérőimpulzusra adott rDt(t) válasz független Dt tényleges értékétől. Ez annál inkább tapasztalható, minél kisebb a Dt idő a válasz-impulzus lefutásának teljes tartamához képest. Ebből következik, hogy a Dt időt a hálózat tulajdonságait figyelembe véve kell megválasztani, mindig úgy, hogy Dt igen kicsi legyen a hálózat tranziens időihez képest. (Lásd F.21.)

F.21. Elektromos hálózat impulzusválasza

Természetesen a mérőjelet függetlenné tehetjük az adott hálózattól, mégpedig a Dirac-d gerjesztés bevezetésével. Ez esetben Dt kicsi bármely hálózat tranziens időihez képest, tehát Dt->0, vagyis mérőjelünk végtelenül keskeny, de ugyanakkor egységnyi területű impulzus, tehát végtelen magas is egyben. A hálózatnak a Dirac-d bemenetre adott válasz-függvénye a hálózat súlyfüggvénye. Ha tehát Dt tart zérushoz, a Dt szélességű impulzusra adott válasz tart a súlyfüggvényhez:

(8.1)

Dirac-d tulajdonságai a követezőek:

  • Értéke minden t értéknél zérus, kivéve a t=0 pillanatot, amikor végtelen,
  • Integrálja egységnyi.

Látható, hogy válasz csak a gerjesztés követően jön létre, vagyis míg a bemeneti Dirac-d a t=0 pillanatban jelentkezik, addig a súlyfüggvény csak a t>=0 pillanatokban lehet zérustól eltérő.

Ahhoz, hogy megértsük a hálózat tetszőleges gerjesztésére adott válasz számolását, tekintsük a következő példát. A bemenő függvényünket bontsuk fel Dt szélességű szakaszokra és közelítsük a függvényt a szakasz elején felvett értékével. Eredményként a gerjesztő függvényt, mint impulzus sorozatot kapjuk meg. Egyetlen Dt szélességű impulzusra ismerjük a hálózat válaszát: rDt(t). Jelen esetben pedig több, különböző magasságú, különböző időpontban jelentkező bemenő impulzusunk van. (Lásd F.22.)

F.22. Tetszőleges gerjesztő függvény felbontása Dt szélességű impulzusokra

Az i-edik impulzus a t=0 időponthoz képest iDt-vel később jelentkezik és magassága nem 1/Dt, hanem g(iDt). Tehát a válasz több rDt(t) formájú függvény összege lesz, amelyek a gerjesztő impulzus időbeni eltolásának és változó magasságának megfelelően eltolva és konstanssal szorozva jelentkeznek:

(8.2)

Ne felejtsük el, hogy lineáris hálózatról van szó, így több gerjesztés együttes hatása egyenlő  az egyenkénti gerjesztések hatásának összegével.

Finomítsuk most végtelenül a gerjesztő függvény impulzusokra bontását, azaz Dt tartson zérushoz. Ekkor (8.2) summája alatt az rDt(t) válaszfüggvény az s(t) súlyfüggvényhez tart. Maga a summa egy integrál közelítő összege, ami Dt->0 határátmenettel a t=iDt szerinti integrálba megy át a következő módon:

(8.3)

A fenti képlet a hálózatelméletből ismeretes, lineáris rendszerekre vonatkozó Duhamel tétel alapján vezethető le. A képlet maga pedig az úgynevezett konvolúciós integrál.

Az integrálási határok abból adódnak, hogy az integrálásnak csak azon t tartományra kell kiterjednie, amelyen az integrandus nullától eltérő lehet. Mivel feltételezzük, hogy

s(t)=0 ha t<0,

g(t)=0 ha t<0                            (8.4)

a t<0 esetben g(t) biztosan zérus, a t>t esetben s(t-t) biztosan zérus.

A v(t) válaszfüggvény tehát a g(t) gerjesztési- és az s(t) súlyfüggvény konvolúciója szerint számolható:

v(t)=g(t) X s(t)                            (8.5)

A gyakorlatban a konvolúciós integrál értelmezése igen érdekes: a g(t) függvényre rárajzoljuk az s(t)-t, a negatív t értékek felé áttükrözve és az origóját t pontba eltolva. Majd a közös szakaszon szorzatukat integráljuk, vagyis nem teszünk mást, mint vesszük g(t) függvénynek az s(t) súlyfüggvénnyel súlyozott integrálját. A v(t) függvény t paraméterét változtatva az s(t) függvény kezdőpontja eltolódik, s az s(t) szinte végigcsúszik a g(t)-n, miközben mindig más szakaszon képződik a súlyozott átlag.

Meg kell említeni, hogy a konvolúciós szűrés átalában információveszteséggel jár. Ennek vizsgálatához ismét egy egyszerű elektromos hálózatot választunk. (Lásd F.23.)

F.23. Integráló tag és súlyfüggvénye

Az integráló tag súlyfüggvénye a következőképpen alakul:

s(t)=(1/t)e-t/t  ha t<=0

s(t)=0 ha t<0                            (8.6)

Az ezzel való konvolúció durván t nagyságú időtartományra való átlagolásnak felel meg. Ha t igen nagy a vizsgált jel tartamához képest, akkor ez a konvolúció közelítőleg a jel integrálját adja. Az átlagolás miatt a magasabb frekvencia összetevők egyre kisebb amplitúdóval jutnak át a rendszeren. Ha a szűrés hatását semlegesíteni szeretnénk, megpróbálhatjuk alkalmazni a konvolúció inverz műveletét, a dekonvolúciót, azonban számolnunk kell azzal, hogy a kimeneten véletlenszerű feszültségingadozás, vagyis zaj jelenik meg. Ha a jel magas frekvenciájú összetevői annyira legyengülnek, hogy a zajszint alá esnek, akkor a kiegyenlítő visszaerősítés már nem az eredeti komponenseket, hanem hozza vissza, hanem a zajt.

Profi UI-fejlesztő kerestetik!

A user interface-hez kötődő szakmák ma nagyon keresettek a nemzetközi porondon. De mi a helyzet Magyarországon? Az Adecco vezető toborzási és kiválasztási tanácsadója, Spiller László írásából kiderül, hogy hazánkban ezek közül szinte csak fejlesztőket keresnek, akik gyakran a tervezési munkát is maguk végzik. Milyen képességekre és tudásra van szükség az elhelyezkedéshez?

Sokak szerint felhasználói felületet (user interface – UI) tervezni nem is annyira szoftverfejlesztői feladat, hiszen mi sem egyszerűbb annál, minthogy az ember papíron rajzol néhány színes ablakot gombokkal, legördülő menükkel, a többit majd megoldják a programozók. Első ránézésre lehet, hogy ilyen egyszerűnek tűnik a dolog és inkább egy absztrakt festőművészre illik a fenti leírás, azonban a valóság másképp fest. Már most az elején szeretném leszögezni: manapság nagyon nehéz a munkaerőpiacon profi user interface-fejlesztőt találni, legalábbis az évek során ezzel szembesültem mint IT-fejvadász.

Folytatás: Computerworld 

Hangkártyák programozása retro 19.

7.1.8. FFT valós vektor esetén

A (3.34) szerinti általános rész-transzformáltra áll, hogy

(7.40)


Ebből az fi valós esetben egyenesen következik, hogy

(7.41)


Ahol * a konjugált képzést jelenti. Más szóval:

(7.42)

számsorozat redundáns, hiszen az r=N/2p fölötti rész konjugálással képezhető a kisebb indexűekből.


Nézzük, mennyi a transzformált nem redundáns része. Ez a következőkből áll:

valós érték (7.34) szerint egyértelmű,

valós érték,

komplex értékek.

Ha összeszámláljuk, kiderül, hogy ez éppen N darab adatot jelent (a komplex számot itt két adatnak vettük). Ez megfelel a várakozásnak, hiszen a transzformáció N darab adatból indul.

A valós bemenő vektorra egyszerűsített algoritmusnál tehát nem tároljuk a teljes rész-transzformáltat, csak a nem redundáns részt. Ennek megfelelően át kell rendeznünk a (7.39) szerinti rekurziós formulát. Ez a rendezés az alábbi eredményt adja, amelyet nem részletezek:

                                        (7.43)

 

(7.44)

 

(7.45)

Egy N/2 elemű komplex vektorban a 0 indexű elem nem tényleges komplex szám, hanem a valós illetőleg a képzetes rész helyén a transzformált két tisztán valós elemét, D0 és DN/2 –t tároljuk. A transzformáció eredményét ezen rend szerint kapjuk. Ez a tárolási séma a rész-transzformáltakra is vonatkozik.

A transzformációt végző C függvény [6] alapján a következő:

//A megfelelő paraméterátadási konvenció beállítása: WINAPI,

//és a függvény láthatóvá tétele a külvilág számára: __declspec(dllexport)

extern “C” __declspec(dllexport)

void WINAPI refft(double *poi) //transzformálandó vektor, mint paraméter

{ unsigned long int p,p2,q,r,i,nper2p,nper4p;

double s,*poi1,*poi2;

complex *p_exp;

complex *p_from,*p_to,*p1_from,*p1_to,*p2_from,*p2_to;

complex cx,cxexp;

//Első rekurziós lépés megvalósítása

p=n/2;

poi1=poi; //Munkaterületek kezdőcímeinek kijelölése

poi2=poi+p;

p1_to=fftbuff;

for(q=0;q<p;q++)

{ p1_to->re=*poi1+(*poi2);

p1_to->im=*poi1-(*poi2);

poi1++;

poi2++;

p1_to++;

}

//A további rekurziós lépések ciklusa

p_from=fftbuff;

p_to=(complex *)poi;

for(i=1;i<logn;i++)

{ p2=p;

p>>=1;

nper2p=n/p2;

//Az r=0 és r=n/2p eset

p2_from=p+(p1_from=p_from);

p1_to=p_to;

for(q=0;q<p;q++) { p1_to->re=p1_from->re+p2_from->re;

p1_to->im=p1_from->re-p2_from->re;

p1_from++;

p2_from++;

p1_to++;

}

p1_from+=p;

p2_from+=p;

p2_to=p_to+n/2-p;

//Általános eset

if(n>=4*p)

{ p_exp=exp_tabl+p-1;

nper4p=nper2p/2;

for(r=1;r<nper4p;r++)

{ cx_copy(p_exp,&cxexp);

for(q=0;q<p;q++)

{ cx_mul(p2_from,&cxexp,&cx);

cx_add(p1_from,&cx,p1_to);

cx_sub(p1_from,&cx,p2_to);

p2_to->im=-p2_to->im;

p1_from++;

p2_from++;

p1_to++;

p2_to++;

}

p_exp+=p;

p1_from+=p;

p2_from+=p;

p2_to-=p2;

}

}

//Az r=n/4p eset

p2_from=p+(p1_from=p_from);

for(q=0;q<p;q++)

{ p1_to->re=p1_from->im;

p1_to->im=-p2_from->im;

p1_from++;

p2_from++;

p1_to++;

}

p1_from=p_from;

p_from=p_to;

p_to=p1_from;

}

if(logn%2) //Visszamásolás a poi által mutatott adaterületre

{ poi1=(double *)fftbuff;

poi2=poi;

for(i=0;i<n;i++)

*(poi2++)=*(poi1++);

}

s=1.0/(double)n; //Az eredmény végigosztása n-nel

for(i=0;i<n;i++) *(poi++)*=s;

return;

}

Az inverz transzformáció hasonló meggondolások szerint kódolható, listája megtalálható a [6] alapján készült F.27-ben.

A fenti függvényt Delphi-ben a következőképpen kell deklarálni:

procedure ReFft(poi:pdouble); //pdouble=^double

stdcall; external ‘prjconvoldll.dll’ name ‘refft’;

ahol prjconvoldll.dll az állomány, illetve a dinamikusan szerkesztett könyvtár, ahonnét a kérdéses függvényt behívjuk, refft pedig a C függvény eredeti neve.

 

[6]  Székely Vladimír: Képkorrekció, hanganalízis, térszámítás PC-n (Gyors Fourier transzformációs módszerek), Computer Books, Budapest, 1994


Hangkártyák programozása retro 18.

7.1.6. A gyors Fourier transzformáció (FFT)

Az FFT eljárás algoritmusát Cooley és Tukey dolgozták ki, majd 1965-ben publikálták. Ez az eljárás mondhatni forradalmasította a digitális jelfeldolgozást, mivel az addigi módszer rendkívül számításigényes volt, s így természetesen sok időt is vett igénybe.

Egy nyolc alappontú transzformáció lépései a következők:

  1. nyolc alappont,
  2. ezekből négy darab két alappontos transzformált előállítása,
  3. ezekből két darab négy alappontos transzformált előállítása,
  4. ezekből a nyolc alappontos transzformált, vagyis a végeredmény előállítása.
A 0-dik alappont a transzformációban részt vevő két alappont összege, míg az N/2-dik az előbbi két alappont különbsége, és így tovább. (Lásd F.19.)

F.19.

7.1.7. Az FFT algoritmus általános megfogalmazása és megvalósítása

Az algoritmust N alappontra adjuk meg, amely kettő hatványa. A transzformálandó függvény fi elemei komplex értékűek lehetnek. Az FFT algoritmus alapgondolata, hogy az N alappontú transzformáltat N/2 alappontú résztranszformáltból kell előállítani, utóbbiakat N/4 alappontra támaszkodó résztranszformáltakból, stb. Fogadjuk el a résztranszformált definíciójának a következőt:

(7.34)

Ahol r a résztranszformált elemeinek futó indexe, a felülre írt indexek pedig:

  • q: a rész-transzformált alappont sorozata az fq alapponton kezdődik,
  • p: a rész-transzformált alappont sorozata fi egymástól p-re lévő elemeiből áll (p mindig kettő hatványa).

F.20. alapján:

(7.35)

Mivel fi-nek N eleme van, a rész-transzformált N/p alappontra támaszkodik, ezért a transzformált ugyanennyi elemből áll:

(7.36)

Állítások:

Ha p=N, N darab rész-transzformáltunk van, amelyek egyenként megegyeznek az fi transzformálandó minta-sorozat egymás utáni elemeivel. Igazolás a (7.36) alapján Dr futó indexe csak r=0 lehet.  (7.34) kifejezésben ezért az exponenciális tényező értéke 1. Maga a szumma az egyetlen k=0 értékre terjed ki. Ezzel:

(7.37)

Ha p=1, egy darab transzformáltunk van, ami azonos a kiszámítandó diszkrét Fourier transzformálttal. Igazolás: ha p=1, (7.35) szerint q csak 0 értékű lehet. Ezt felhasználva a (7.34) képletben, az alábbi egyenletre jutunk:

(7.38)

Ami megegyezik a diszkrét Fourier transzformált definíciójával.

A p=2P eset rész-transzformáltjainak birtokában a p=P eset összes rész-transzformáltja kiszámítható az alábbi rekurzív formula segítségével:

(7.39)

Igazolását nem vezetjük le.

A fenti három állítás alapján megadható az FFT algoritmus:

  1. Az első állítás alapján a kiinduló fi adatsor egyben a p=N eset rész-transzformáltjainak sora.
  2. A harmadik állítás log2N-szeri egymást követő ismétlésével megkapjuk a p=N/2, p=N/4, stb., végül a p=1-hez tartozó rész-transzformáltakat.
  3. A második állítás szerint utóbbi azonos a keresett transzformálttal.

Hangkártyák programozása retro 16.

7.1.2. A Fourier sor exponenciális alakja

Ha felhasználjuk, hogy a trigonometrikus függvények a komplex exponenciális függvénnyel kifejezhetők, a következőkhöz jutunk:

Sin(x)=(1/2j)(ejx-e-jx)

(7.5)

Cos(x)=(1/2)(ejx+e-jx)

(7.6)

Ezek után az (7.1) egyenlet a következő alakban is felírható:

(7.7)

Ha átcsoportosítjuk a szumma alatti tagokat, akkor pedig így:

(7.8)

Ha bevezetjük a harmonikus összetevőkre a

Cn=(an-jbn)/2

(7.9)

komplex amplitúdót, a szumma alatti rész egyszerűbb alakját kapjuk:

(7.10)

ahol a

C-n=Cn* és C0=c0

(7.11)

jelöléseket is használtuk. (* a komplex konjugáltképzés jele).

Ha (7.9)-be behelyettesítjük a (7.3)-(7.4) integrálokat, közvetlenül kiszámolhatjuk a bevezetett komplex amplitúdót:

(7.12)

A fenti egyenlet valós f(t) függvény esetében eleget tesz a Cn=C-n* feltételnek, sőt az n=0 esetben (7.2)-t is kiadja.

Az L hosszúságú periodikus, valós f(x) függvényre a Fourier sor együtthatói:

(7.13)

Az együtthatókból a függvényt így állítjuk vissza:

(7.14)

Hangkártyák programozása retro 15.

7. Mesterséges hangzás megvalósítása

Elérkeztünk e munka legérdekesebb, ám legnehezebb részéhez. Azt akarjuk, hogy a stúdióban, „steril” körülmények között rögzített hanganyag úgy szóljon, mintha az a kívánt teremben történne. De vajon mit kell mindehhez tennünk?

Először is, ismerjük a termünknek az egyetlen impulzusra adott válaszát, ez lesz a súlyfüggvényünk. Másodszor, ismerjük a stúdióban felvett hanganyagot, ez lesz a gerjesztési függvényünk. A kívánt eredmény elérése érdekében elő kell állítanunk mindkét függvény Fourier transzformáltját, majd az eredményül kapott függvényeket elemenként össze kell szorozni, s ha ez megtörtént, a szorzás eredményének inverz Fourier transzformálásával meg is kapjuk a kívánt eredményt. A fenti folyamat nem más, mint két függvény konvolúciója.

Így igen egyszerűnek tűnik a dolog, de tisztában kell lennünk azzal, hogy a megvalósításhoz komoly matematikai ismeretek szükségesek, amelyeket a következő fejezetekben ismertetetünk [6], illetve [7] alapján.

7.1.1. A Fourier sor

Egy f(t) periodikus időfüggvény felbontható egy konstans tag, valamint végtelen sok szinuszos, koszinuszos időfüggésű összetevő összegére. A periodikus jel T periódusidejének reciproka adja az alapharmonikus frekvenciáját, a további összetevők frekvenciája ezen alapfrekvencia egész többszöröse. Matematikai formában ez a következőképpen írható fel:

(7.1)

Ahol c0 stacionárius egyenáramú összetevőt így kapjuk:

(7.2)

A koszinuszos, illetve szinuszos összetevők amplitúdóját pedig így számolhatjuk:

(7.3)

(7.4)

Egy függvény akkor fejthető Fourier sorba, ha a következő feltételeket teljesíti:

  • f(t) függvény legyen T tartományon belül korlátos,
  • legyen integrálható,
  • legyen legalább szakaszonként differenciálható.

Ha a fenti feltételek teljesülnek, a (7.1) sor az f(t) függvény folytonos pontjain magához a függvényhez, szakadási helyein a bal- és a jobboldali határértékek számtani közepéhez konvergál. A hangfeldolgozásban előforduló függvények mindig megfelelnek az említett feltételeknek. (7.3 ) és (7.4) tanulmányozásával megállapíthatjuk, hogy páratlan f(t) függvény esetén minden an együttható, páros függvényesetén minden bn együttható zérus.

[6]   Székely Vladimír: Képkorrekció, hanganalízis, térszámítás PC-n (Gyors Fourier transzformációs módszerek), Computer Books, Budapest, 1994

[7]   Gyimesi László: Digitális jelfeldolgozás, SZIF-UNIVERSITAS Kft., 1999

Hangkártyák programozása retro 14.

6.3. Az impulzusválasz teszt kódja

A rendszer felépítésének ismertetése már megtörtént, s láthattuk, hogy milyen programozási eszközök vannak a kezünkben ahhoz, hogy megvalósítsuk az általunk kitűzött célokat. Tudunk hangfelvételt készíteni a Sound Blaster segítségével és képesek vagyunk hangot megszólaltatni a Gravis Ultrasound ACE hangkártyával. Ezek után tekintsük át az impulzusválasz tesztet megvalósító program kódját.

uses crt, sb16mix, sb_aidma, gus;{ A szükséges unitok }

var     filehandle: file;

filename: string;

rec,w,r,rs:word;

Procedure SetSBMixer; { A Sound Blaster mixerének beállítása }

begin

SetMasterVolume($ff,$ff);   { Bemeneti eszközök hangereje maximumon}

SetDSPVolume($ff,$ff);      { DSP hangereje maximumon }

SetMicVolume($ff);          { Mikrofon hangereje maximumon }

SetLineVolume($ff,$ff);     { Vonali bemenet hangereje maximumon }

OutputSelect($00);          { Nincs eszköz a kimeneten }

InputSelect($01,$01);       { Csak a mikrofonról van bemenet: $01}

MicAGC(off);                { AGC lekapcsolva }

SetRecordSampleRate(44100); { Beállítjuk a mintavételi frekvenciát }

end;

Procedure GravisSign; { Hangminta generálása Gravis Ultrasound-dal }

var i,y:longint;

begin

GravisReset; { A hangkártya alapállapotba hozása }

{ Egyetlen hangminta széles impulzus betöltése a GUS DRAM-ba }

for i:=0 to 0 do

PutByte(i,127);

for i:=1 to 65535 do

PutByte(i,0);

Volume($00,64); { Hangerő közvetlen beállítása}

Note($00,36);   { A csatornához tartozó frekvencia beállítása }

GravisBalance($00,$08); { Egy csatorna balanszértékének beállítása }

SetGF1Byte($00,$4c,$03); { Inicializálás }

PlayVoice($00,$08,0,0,524288); { A hangminta lejátszása }

end;

BEGIN

clrscr;

if AllocDMAbuffer(16384)<0 then{ Helyfoglalás a DMA buffer részére}

begin

writeln(‘Failed to allocate DMA buffer!’);

halt;

end;

SetSBMixer;

InitInterrupt(nil);  { Beállítjuk a megszakításvezérlőt  }

SetDMAblockSize(8192);  { Beállítjuk a DSP blokkméretét  }

InitDMAcontroller(RecordMode);{ Inicializáljuk a DMA vezérlőt (REC)}

{ A felvétel mentési útvonalát várja paraméterként a parancssorba }

Writeln(‘Recording, press any key to stop…’);

if paramcount=0 then

begin

writeln;

writeln(‘Usage: REC1 <path>’);

delay(5000);

exit;

end;

filename:=paramstr(1);

assign(filehandle,filename);

rewrite(filehandle,1);    { Megnyitjuk a hangmitna fájlt írásra }

rs:=Halfsize;

GravisSign;              { Impulzus generálásának elindítása }

StartAutoInitDMArecord;   { Elindítjuk a felvételt }

rec:=0;

repeat

if RfshRequest then { Ha a fél buffert feltöltötte… }

begin

inc(rec);

RfshRequest:=false;  { RfshRequest= FALSE ! }

{ Kiírjuk a blokkot a HDD-re   }

blockwrite(filehandle,DMAptr^,HalfSize,rs);

write(‘*’);

NextBuffer;     { A következő félbufferre váltunk }

end;

until rec=8;  { Kilépünk, 65536 minta felvétele után }

close(filehandle); { Lezárjuk a fájlt }

writeln;

StopAutoInitDMA;   { Leállítjuk a DMA átvitelt }

DoneDMAcontroller; { Tiltjuk a DMA csatornát }

DoneInterrupt;     { Visszaállítjuk a megszakítást }

FreeDMAbuffer;     { Felszabadítjuk a DMA buffert }

StopVoice($00);    { Gravis elhallgat }

readln;

END.

A teszt eredménye egy hangállomány, amely visszajátszható a mellékelt Play program segítségével, és hullámformája megtekinthető a Scroll nevű programmal. (A Play program listája a F.29-ben tanulmányozható, a Scroll pedig a F.30-ban.) Az időfüggvény sok mindent elárul az adott terem hangzásáról. Példaként már említettük, hogy segítségével igen pontosan meg lehet határozni a visszhangok helyét, illetve el lehet választani a közvetlenül a hangforrásból származó hangokat és az egyéb visszaverődéseket.  Illusztrációként nézzük meg egy ilyen impulzusválasz időfüggvényének részletét. (Lásd F.17.)

F.17. Az impulzusválasz időfüggvénye. Az ábra alapján kiszámolható, hogy a mikrofon a hangforrástól 136 cm-re volt. 0,5 ms-ra volt szüksége az impulzusnak ahhoz, hogy a hangszórón megjelenjen, és 4 ms telt el, míg az első hanghullámok eljutottak a hangforrástól a mikrofonig.

Hangkártyák programozása retro 13.

6.2.2. Gravis Ultrasound lehetőségei és programozása

A Gravis hangkártyák általános felépítése helyett nézzük meg, milyen lehetőségekkel bír az általam használt GUS ACE:

  • Erősített vonali sztereó kimenet (Line Out),
  • Erősített vonali sztereó bemenet más audio eszközöktől (Line In),
  • 512 K, illetve 1 MB saját memória hullámformák tárolására (GUS DRAM),
  • 32 digitális csatorna,
  • GF1 hangprocesszor (Voice Sound Synthetizer),
  • hullámforma táblákkal megvalósított FM hangkeltés,
  • minden csatornán különálló hangerő és balansz,
  • állítható mintavételi frekvencia 44,1 kHz-ig,
  • 8- vagy 16 bites sztereó, illetve mono lejátszás,
  • szoftverből állítható IRQ vonal és DMA csatorna,
  • jumperrel állítható I/O báziscím,
  • a hangkártya minden egysége elérhető processzorral, ugyanakkor képes függetlenül dolgozni,
  • több pontos burkológörbe előállítása hardveres úton.

Itt most nem térünk ki részletesen a regiszterek ismertetésére, mivel elég sok van belőlük és véleményem szerint azok felépítése sokkal bonyolultabb a Sound Blaster esetén tapasztaltnál. Akit részletesebben érdekel a téma, bőven talál leírásokat az ezzel foglalkozó szakirodalomban. Inkább [4] alapján a konkrét programozást bemutató, a feladat szempontjából fontosabb Pascal unitot közlök:

{$G+}

unit gus;

interface uses DOS,CRT;

type

memptr = ^memory;

memory = array[0..65534] of byte;

const

Hdg : array[0..15] of char=’0123456789ABCDEF’;

{ Hangkártya hardverbeállítások }

Base    : word = $250; { I/O báziscím }

GF1irq  : byte = 11;   { Megszakítási vonal }

MIDIirq : byte =  7;   { MIDI megszakítási vonal }

DMA_1   : byte =  3;   { GUS DRAM DMA csatorna }

DMA_2   : byte =  3;   { Felvétel DMA csatorna }

{ Frekvenciatáblázat 5 oktáv számára }

FreqTAB : Array[0..59] of word=(

$0038,$003B,$003E,$0042,$0046,$004A,$004F,$0053,$0058,$005E,$0063,$0069,

$0070,$0076,$007D,$0085,$008D,$0095,$009E,$00A7,$00B1,$00BC,$00C7,$00D3,

$00E0,$00ED,$00FB,$010A,$011A,$012B,$013D,$014F,$0163,$0179,$018F,$01A7,

$01C0,$01DB,$01F7,$0214,$0234,$0256,$027A,$029F,$02C7,$02F2,$031E,$034E,

$0380,$03B6,$03EE,$0429,$0469,$04AD,$04F4,$053F,$058F,$05E4,$063D,$069C);

{ Hangerőtáblázat a 128 lépéses lineáris szabályozáshoz }

VolTAB: Array[0..127] of word=(

$0000,$8ff0,$9ff0,$a800,$aff0,$b400,$b800,$bc00,

$bff0,$c200,$c400,$c600,$c800,$ca00,$cc00,$ce00,

$cff0,$d100,$d200,$d300,$d400,$d500,$d600,$d700,

$d800,$d900,$da00,$db00,$dc00,$dd00,$de00,$df00,

$dff0,$e080,$e100,$e170,$e200,$e280,$e300,$e370,

$e400,$e480,$e500,$e580,$e600,$e680,$e700,$e780,

$e800,$e880,$e900,$e990,$ea00,$ea80,$eb00,$eb80,

$ec00,$ec80,$ed00,$ed90,$ee00,$ee80,$ef00,$ef90,

$eff0,$f030,$f070,$f0c0,$f100,$f140,$f170,$f1c0,

$f200,$f240,$f280,$f2b0,$f300,$f340,$f380,$f3b0,

$f400,$f440,$f470,$f4c0,$f500,$f540,$f590,$f5c0,

$f600,$f650,$f690,$f6c0,$f700,$f740,$f790,$f7c0,

$f800,$f840,$f880,$f8b0,$f900,$f940,$f980,$f9b0,

$fa00,$fa50,$fa80,$fac0,$fb00,$fb40,$fb80,$fbc0,

$fc00,$fc40,$fc80,$fcd0,$fd00,$fd40,$fd80,$fdb0,

$fe00,$fe40,$fe80,$fec0,$ff00,$ff40,$ff80,$ffd0);

function  GetByte(Address:longint):byte;

procedure PutByte(Address:longint; value:byte);

procedure MoveSample(Adr:longint; var Src; count:word);

procedure GravisWait;

procedure GravisRESET;

function  GravisTEST:boolean;

procedure SetGF1Byte(Ch,Reg,Value:byte);

procedure SetGF1Word(Ch,Reg:byte; Value:word);

function  GetGF1Byte(Ch,Reg:byte):byte;

function  GetGF1Word(Ch,Reg:byte):word;

function  GravisFindBase:word;

function  GravisMemory:word;

procedure GravisVolume(ch:byte; volume:word);

procedure GravisBalance(ch:byte; balance:byte);

procedure GravisFreq(ch:byte; freq:word);

procedure PlayVoice(ch,mode:byte; b,s,e:longint);

procedure StopVoice(ch:byte);

function  VoicePos(ch:byte):longint;

function  Hex(b:byte):string;

function  HexW(w:word):string;

procedure Note(ch,n:byte);

procedure Volume(ch,n:byte);

procedure SetIRQservice(Irqr:pointer);

procedure ResetIRQservice;

implementation

function  GetByte(Address:longint):byte; assembler;

asm

mov   dx,Base

add   dx,$103 { Globálisregiszter-kiválasztás }

mov   al,$43

out   dx,al   { A DRAM alsó szó regiszter }

inc   dx

mov   ax,Address.word

out   dx,ax   { Kiírjuk a cím alsó 16 bitjét }

dec   dx

mov   al,$44  { A DRAM felső bájt regiszter }

out   dx,al

add   dx,$0002

mov   al,[Address+2].byte

and   al,00001111b

out   dx,al       { Kiírjuk a cím felső 4 bitjét }

add   dx,$0002    { DX: $3×7, DRAM read/write}

in    al,dx       { Beolvassuk az adatot az AL-be }

end;

GetByte: a GUS DRAM tetszőleges bájtjának lekérdezése.

procedure PutByte(Address:longint; value:byte); assembler;

asm

mov   dx,Base

add   dx,$103    { Globálisregiszter-kiválasztás }

mov   al,$43

out   dx,al       { A DRAM alsó szó regiszter }

inc   dx

mov   ax,Address.word

out   dx,ax       { Kiírjuk a cím alsó 16 bitjét }

dec   dx

mov   al,$44       { A DRAM felső bájt regiszter }

out   dx,al

add   dx,$0002

mov   al,[Address+2].byte

and   al,00001111b

out   dx,al        { Kiírjuk a cím felső 4 bitjét }

add   dx,$0002     { DX: $3×7, DRAM read/write}

mov   al,value     { AL: a bájt új értéke }

out   dx,al        { Beírjuk az értéket a DRAM-ba }

end;

PutByte: egy GUS DRAM bájt beírása.

procedure MoveSample(Adr:longint; var Src; count:word); assembler;

asm

mov   di,Adr.word;

mov   bl,[Adr+2].byte

and   bl,00001111b       { BL:DI=DRAM cím (első bájt)}

cld

les   si,Src

mov   ax,ES:[si]

mov   ES,ES:[si+2]

mov   si,ax              { ES:SI a forrásadatra mutat }

mov   cx,count           { CX a bájtok számát tartalmazza }

@putbytes:                 { Kiírás: PutByte-hoz hasonlóan }

mov   dx,Base

add   dx,$103

mov   al,$43

out   dx,al

inc   dx

mov   ax,di

out   dx,ax

dec   dx

mov   al,$44

out   dx,al

add   dx,$0002

mov   al,bl

out   dx,al

add   dx,$0002

segES lodsb           { Beolvasás a forrásadatokból }

out   dx,al           { Kiírás a célhelyre (GUS DRAM)}

add   di,$01

adc   bl,$00          { Növeljük a DRAM címet (BL:DI)}

loop  @putbytes       { CX darab bájtot írunk ki }

end;

MoveSample: egy legfeljebb 64K hosszú memóriaterület betöltése a GUS DRAM-ba.

procedure GravisWait;           assembler;

asm

push  dx       { Tároljuk a DX, AX regisztereket }

push  ax

mov   dx,$0000  { Egy semleges I/O báziscím }

in    al,dx

in    al,dx

in    al,dx

in    al,dx

in    al,dx

in    al,dx     { Várakozási ciklusok }

pop   ax        { AX, DX visszahozása }

pop   dx

end;

GravisWait: a Gravis csővezetékes rendszere miatt néhol várakozásra van szükség.

procedure GravisRESET;      assembler;

asm

mov   dx,Base

add   dx,$103      { Globálisregiszter-kiválasztás }

mov   al,$4C

out   dx,al        { $4C: Reset regiszter}

add   dx,$0002

mov   al,$00       { A GF1 inicializálása }

out   dx,al

call  GravisWait   { Várakozás }

sub   dx,$0002

mov   al,$4C

out   dx,al        { Ismét Reset regiszter}

add   dx,$0002

mov   al,$07       { Az eredeti normális működés visszaállítása}

out   dx,al

sub   dx,$0002

mov   al,$0E

out   dx,al        { 14 aktív csatorna }

mov   al,$CE

add   dx,$0002

out   dx,al

mov   dx,Base

mov   al,$0C

out   dx,al        { Bemenetek engedélyezése }

end;

GravisReset: a hangkártya alapállapotba állítása.

function  GravisTEST:boolean;

begin

asm

mov   dx,Base

add   dx,$103

mov   al,$4C       { A hangkártya inicializálásának megkísérlése }

out   dx,al

add   dx,$0002

xor   al,al

out   dx,al

call  GravisWait

call  GravisWait

sub   dx,$0002

mov   al,$4C

out   dx,al

add   dx,$0002

mov   al,$01

out   dx,al

end;

PutByte(256,$55);       { Néhány bájt kiírása a memóriába }

PutByte(512,$aa);

PutByte(000,$0f);

{ Ha ugyanazt olvassuk vissza amit kiírtunk, akkor van Gravis }

GravisTest:=(GetByte(256)=$55)and(GetByte(512)=$aa);

end;

GravisTest: a függvény igaz értékkel tér vissza, ha az adott báziscímen található Gravis hangkártya.

procedure SetGF1Byte(Ch,Reg,Value:byte);                assembler;

asm

mov   dx,Base

add   dx,$102           { Csatorna lapregiszter ($3×2)}

mov   al,&Ch            { Kiírjuk a csatornaszámot többszőr}

out   dx,al

out   dx,al

out   dx,al

inc   dx                { Regiszter-kiválasztás ($3×3)}

mov   al,Reg

out   dx,al             { Kiírjuk a regiszter számát }

add   dx,$0002          { Regiszter adatai I/O cím ($3×5)}

mov   al,Value

out   dx,al             { Kiírjuk a regiszter új értékét }

end;

procedure SetGF1Word(Ch,Reg:byte; Value:word);  assembler;

asm

mov   dx,Base

add   dx,$102       { Csatorna lapregiszter ($3×2)}

mov   al,&Ch        { Kiírjuk a csatornaszámot többszőr}

out   dx,al

out   dx,al

out   dx,al

inc   dx            { Regiszter-kiválasztás ($3×3)}

mov   al,Reg

out   dx,al         { Kiírjuk a regiszter számát }

inc   dx            { Regiszter word adat cím ($3×4)}

mov   ax,Value

out   dx,ax         { Kiírjuk a word adatot }

end;

SetGF1Byte és SetGF1Word: a GF1 processzor globális és csatorna specifikus regisztereinek közvetlen módosítása.

function  GetGF1Byte(Ch,Reg:byte):byte;                 assembler;

asm

mov   dx,Base

add   dx,$102      { Csatorna lapregiszter ($3×2)}

mov   al,&Ch       { Kiírjuk a csatornaszámot többszőr}

out   dx,al

out   dx,al

out   dx,al

inc   dx           { Regiszter-kiválasztás ($3×3)}

mov   al,Reg

out   dx,al        { Kiírjuk a regiszter számát }

add   dx,$0002     { Regiszter adata I/O cím ($3×5)}

in    al,dx        { Beolvassuk a regiszter értékeket }

end;

function  GetGF1Word(Ch,Reg:byte):word;                 assembler;

asm

mov   dx,Base

add   dx,$102

mov   al,&Ch

out   dx,al

out   dx,al

out   dx,al

inc   dx

mov   al,Reg

out   dx,al

inc   dx

in    ax,dx

end;

GetGF1byte és GetGF1word: a GF1 regisztereinek kiolvasása.

function  GravisFindBase:word;

var i:byte;

w:word;

begin

w:=0;

for i:=1 to 6 do

begin

Base:=$200 or i*16;

if GravisTEST then w:=Base;

end;

GravisFindBase:=w;

Base:=w;

end;

GravisFindBase: az I/O báziscím meghatározása.

function  GravisMemory:word;

var b:word;

begin

PutByte($3ffff,$01); {  256*1024-1 }

if GetByte($3ffff)=$01 then b:=256;

PutByte($7ffff,$02); {  512*1024-1 }

if GetByte($7ffff)=$02 then b:=512;

PutByte($bffff,$04); {  768*1024-1 }

if GetByte($bffff)=$04 then b:=768;

PutByte($fffff,$08); { 1024*1024-1 }

if GetByte($fffff)=$08 then b:=1024;

GravisMemory:=b;

end;

GravisMemory: a memória meghatározása.

procedure GravisVolume(ch:byte; volume:word);

begin

SetGF1Word(ch,$09,volume);

end;

GravisVolume: direkt hangerő szabályozás.

procedure GravisBalance(ch:byte; balance:byte);

begin

SetGF1Byte(ch,$0C,balance);

end;

GravisBalance: egy csatorna balanszértékének beállítása.

procedure GravisFreq(ch:byte; freq:word);

begin

SetGF1Word(ch,$01,freq);

end;

GravisFreq: a lejátszási frekvencia beállítása.

procedure PlayVoice(ch,mode:byte; b,s,e:longint);

begin

SetGF1Word(Ch,$03,(s and $7f) shl 9);

SetGF1Word(Ch,$02,(s shr 7));

SetGF1Word(Ch,$05,(e and $7f) shl 9);

SetGF1Word(Ch,$04,(e shr 7));

SetGF1Word(Ch,$0B,(b and $7f) shl 9);

SetGF1Word(Ch,$0A,(b shr 7));

SetGF1Byte(Ch,$00,mode);

{SetGF1Byte(Ch,$4C,$03);}

end;

PlayVoice: egy GUS DRAM-ba betöltött hangminta lejátszása, ch: a lejátszáshoz használt csatorna száma, mode: a lejátszás módja, b: a hangminta aktuális címe, s: a hangminta kezdőcíme, e: a hangminta végcíme.

procedure StopVoice(ch:byte);                   assembler;

asm

push  &ch.word

push  $0080        { Lekérdezzük a voice control regiszter értékét}

call  GetGF1Byte

and   al,$DF

or    al,$03       { Voice Stop bit=1 }

xor   ah,ah

push  ax

push  &ch.word

push  $0000

push  ax

call  SetGF1Byte     { Beállítjuk a Voice controlt }

call  GravisWait

pop   ax

push  &ch.word

push  $0000

push  ax

call  SetGF1Byte

end;

StopVoice: a hangminta lejátszásának leállítása.

function  Hex(b:byte):string;

begin

Hex:=Hdg[b shr 4]+Hdg[b and 15];

end;

function  HexW(w:word):string;

begin

HexW:=Hdg[w shr 12]+Hdg[(w shr 8  ) and 15]+

Hdg[(w shr 4) and 15]+Hdg[w and 15];

end;

Hex és Hexw: bájt és word hexadecimális érétkének meghatározása.

procedure Note(ch,n:byte);

begin

GravisFreq(ch,FreqTAB[n]);

end;

Note: egy adott hangjegyhez tartozó frekvencia beállítása.

procedure Volume(ch,n:byte);

begin

GravisVolume(ch,VolTAB[n and 127]);

end;

Volume: egy csatorna hangerejének lineáris beállítása.

procedure FirstIRQ; interrupt;

var a:byte;

begin

a:=port[Base+$0006];

a:=$00;

while a and $c0<>$c0 do

a:=getgf1byte($00,$8f);

port[$20]:=$20;

port[$a0]:=$20;

end;

FirstIRQ: a unit belső megszakítási rutinja, amely kívülről nem érhető el.

procedure SetIRQservice(Irqr:pointer);

var i:byte;

begin

SetGF1Byte($00,$4C,$03);      { Minden GF1 IRQ-t tiltunk       }

SetIntVec($73,@FirstIRQ);     { Beállítjuk az init IRQ-t       }

port[Base]:=$4c;              { Kiválasztjuk az IRQ regisztert }

port[Base+$000b]:=$05;        { IRQ11 , int 73h  00001000      }

port[$a1]:=port[$a1] and $F7; { Engedélyezzük a vonalat        }

for i:=0 to 31 do

begin                        { Minden csatorna letiltva       }

setgf1byte(i,$0d,$02);

setgf1byte(i,$00,$02);

end;

SetGF1byte($00,$41,$00);      { DRAM DMA IRQ tiltva      }

SetGF1byte($00,$49,$00);      { Felvétel IRQ tiltva      }

SetGF1Byte($00,$4C,$07);      { GF1 IRQ engedélyezve     }

delay(400);                   { várakozás                }

SetIntVec($73,IRQR);          { Az új megszakítási rutin }

end;

SetIRQsercvice: a megszakítás inicializálása és a megszakítási rutin címének beállítása.

procedure ResetIRQservice;

begin

port[$a1]:=port[$a1] or $08;  { Tiltjuk a vonalat }

SetGF1Byte($00,$4C,$03);      { GF1 IRQ letiltva  }

end;

ResetIRQservice: a GF1 megszakítás tiltása.

[4] László József: Hangkártya programozása Pascal és Assembly nyelven, Computer Books, Budapest, 1999

Hangkártyák programozása retro 12.

6.2.1.4. Hangfelvétel és lejátszás 8 bites auto-init DMA segítségével

Ebben a fejezetben nem térek ki külön a megszakítás vezérlő és a DMA vezérlő ismertetésére, illetve programozására, mivel arról egy külön könyvet lehetne írni. Azonban a jobb megérthetőség érdekében a [4] alapján készült Pascal unitot teljes terjedelmében közlöm:

unit sb_aiDMA;

{$g+}

interface uses dos;

type

mymem  = array[0..65534] of byte;

memptr = ^mymem;

const

SBbase    : word = $220;{ Báziscím }

SB_IRQ    : byte = $05; { Megszakítási vonal száma }

SB_DMA    : byte = $01; { DMA csatorna száma }

SB_TYPE   : byte = $00; { Hangkártya típus }

{ A DMA controller chip I/O címei }

DMA_cmd  = $08; { 8 bites DMA parancsregiszter}

DMA_mask = $0a; { 8 bites DMA maszkregiszter }

DMA_bpt  = $0c; { 8 bites DMA bájtmutató }

DMA_mode = $0b; { 8 bites DMA üzemmód regiszter }

DMA_stat = $08; { 8 bites DMA állapotregiszter }

DMA_req  = $09; { 8 bites DMA szoftveres kérés regiszter }

DMA_init = $0d; { 8 bites DMA inicializáló regiszter }

{ A buffer feldolgozás jelzése a hívó programnak }

RfshRequest : boolean = false;

PlayMode   = $58; { MEM->I/O auto-init mód (lejátszás)}

RecordMode = $54; { I/O->MEM auto-init mód (felvétel)}

var

DMAbuffer   : memptr; { A teljes DMA buffer mutatója }

DMAptr      : memptr; { A feldolgozandó bufferrész mutatója}

BufferSize  : word;   { A teljes DMA buffer mérete bájtban }

HalfSize    : word;   { A DMA buffer méretének fele }

{Sound Blaster DSP specifikus eljárások és függvények}

function  GetBlasterENV(var Base:word; var IRQ,DMA,Typ:byte):boolean;

function  DSPreset:boolean;

procedure DSPwrite(DSPdata:byte);

function  DSPread:byte;

procedure SetSampleRate(SR:word);

procedure SetDMAblockSize(size:word);

{ Megszakítás vezérlő és DMA vezérlő specifikus eljárások }

procedure EnableIRQline(Line:byte);

procedure DisableIRQline(Line:byte);

procedure DMA_addr(a:pointer; var p,s,l:word);

procedure InitDMAcontroller(Transfermode:byte);

procedure DoneDMAcontroller;

procedure InitInterrupt(UserDefinedIRQ:pointer);

procedure DoneInterrupt;

{ DMA bufferkezelő eljárások és függvények }

function  AllocDMAbuffer(size:word):integer;

procedure FreeDMAbuffer;

procedure ClearDMAbuffer;

procedure NextBuffer;

{ DMA átvitel vezérlő eljárások }

procedure StartAutoInitDMAplay;

procedure StartAutoInitDMArecord;

procedure StopAutoInitDMA;

implementation

const

{ A 8 bites DMA vezérlő regisztereinek I/O címei }

DMApages   : array[0..3] of byte=($87,$83,$81,$82);

DMAoffsets : array[0..3] of byte=($00,$02,$04,$06);

DMAsizes   : array[0..3] of byte=($01,$03,$05,$07);

{ Az aktuális DMA csatorna regisztereinek címei }

DMApage   : byte = $83;

DMAoffset : word = $0002;

DMAsize   : word = $0003;

var

{ Az eredeti megszakításvektort tároló változó }

OldSBorgIRQ : procedure;

{ A DMA buffer lefoglalásához szükséges változók }

DMAp1,DMAp2 : pointer;

DMAs1,DMAs2 : word;

function GetBlasterENV(var Base:word; var IRQ,DMA,Typ:byte):boolean;

var

s : string[128];

i : integer;

begin

GetBlasterENV:=false;

s:=getenv(‘BLASTER’); { Lekérdezzük a BLASTER környezeti változót }

if s[0]=#0 then exit; { Kilépünk, ha nincs ilyen változó }

{ Megállapítjuk az I/O báziscímet }

i:=pos(‘A2’,s);

if i>0 then val(‘$’+copy(s,i+1,3),Base,i);

if i<>0 then exit;

{ Megállapítjuk a megszakítási vonal számát }

i:=pos(‘ I’,s);

if i>0 then val(copy(s,i+2,1),IRQ,i);

if i<>0 then exit;

{ Megállapítjuk a DMA csatorna számát }

i:=pos(‘ D’,s);

if i>0 then val(copy(s,i+2,1),DMA,i);

if i<>0 then exit;

{ Megállapítjuk a kártya típusszámát }

i:=pos(‘ T’,s);

if i>0 then val(copy(s,i+2,1),Typ,i);

if i<>0 then exit;

GetBlasterENV:=true;

end;

A GetBlasterEnv függvénysegítségével kérdezzük le a BLASTER környezeti változót, amelyből megtudhatjuk a kártya báziscímét, az általa használt megszakítási vonal sorszámát és DMA csatorna számát.

function DSPreset:boolean; assembler;

asm

…{ Ezt a rutint már ismertettem a DSP programozása kapcsán}

end;

procedure DSPwrite(DSPdata:byte); assembler;

asm

… { Ezt a rutint már ismertettem a DSP programozása kapcsán}

end;

function DSPread:byte; assembler;

asm

… { Ezt a rutint már ismertettem a DSP programozása kapcsán}

end;

A fenti három szubrutinnal már találkozhattunk a DSP programozása kapcsán.

procedure SetSampleRate(SR:word);

var

TC : byte;

begin

TC:=256-round(1000000/SR);     { Kiszámítjuk az időállandót, }

DSPwrite($40);                 { majd kiírjuk a DSP-re }

DSPwrite(TC);

end;

A SetSampleRate eljárás a mintavételezési frekvencia beállítására szolgál.

procedure SetDMAblocksize(size:word);

begin

DSPwrite($48);                 { Set DMA blocksize parancs }

dec(size);                     { A méretet csökkenteni kell!}

DSPwrite(LO(size));            { Kiírjuk az alsó bájtot }

DSPwrite(HI(size));            { Kiírjuk a felső bájtot }

end;

A SetDMABlocksize arra szolgál, hogy általa beállítsuk a DMA blokk méretét a DSP számára. Auto-init DMA használatakor a Set DMA blocksize DSP paranccsal kell megadni a DSP számára az átvitt bájtok számát.

procedure SB_AutoInitDMA_IRQ; assembler;

asm

push  DS                 { Tároljuk a használt regisztereket }

push  ax

push  dx

mov   ax,Seg @DATA       { A DS szegmensregisztert az        }

mov   DS,ax              { eredeti adatszegmensre állítjuk   }

mov   dx,SBbase

add   dx,$0e             { Olvassuk DSP data avail portot,   }

in    al,dx              { ez jelzi a hangkártyának, hogy a  }

{ megszakítást fogadtuk              }

mov   RfshRequest,TRUE   { A buffer frissítése szükséges jel }

mov   al,020h            { EOI jelet küldünk a               }

out   020h,al            { megszakítás vezérlőnek            }

pop   dx

pop   ax

pop   DS                 { Visszaolvassuk a regiszterek értékét  }

iret                     { Visszatérünk a megszakításból         }

end;

Az SB_AutoInitDMA_IRQ egy megszakítási rutin. Az auto-init DMA adatátvitel folyamán a hangkártya minden blokk lejátszásakor, illetve felvételekor megszakítást generál. A megszakítási rutin nyugtázza a megszakításkérést a hangkártya felé, és igaz értékűre állítja RfshRequest változót, majd nyugtázza a megszakítást a megszakításvezérlőnek is. A DMA átvitel ez alatt is zavartalanul folyik tovább.

procedure NextBuffer; assembler;

asm

mov   ax,DMAbuffer.word  { Az aktuális mutató a buffer elejére }

cmp   ax,DMAptr.word     { mutat? }

jnz   @1sthalf           { Ugrás tovább, ha nem }

@2ndfalf:

add   ax,HalfSize        { Ha igen, a második fele következik }

@1sthalf:

mov   DMAptr.word,ax     { Egyébként az első }

end;

A buffer felének feldolgozásakor a NextBuffer eljárás segítségével válthatunk aktív buffert.

procedure EnableIRQline(Line:byte); assembler;

asm

mov  cl,Line       { A vonal száma a CL regiszterbe kerül  }

and  cl,00000111b  { Csak az alsó három bitet hagyjuk meg  }

mov  ah,11111110b  { Maszk: ez a bit nulla lesz            }

rol  ah,cl         { A 0 bitet a megfelelő helyre toljuk   }

in   al,021h

and  al,ah         { Engedélyezzük az adott vonalat        }

out  021h,al

end;

procedure DisableIRQline(Line:byte); assembler;

asm

mov  cl,Line       { A vonal száma a CL regiszterbe kerül  }

and  cl,00000111b  { Csak az alsó három bitet hagyjuk meg  }

mov  ah,00000001b  { Maszk: ez a bit egy lesz              }

rol  ah,cl         { Az 1 bitet a megfelelő helyre toljuk  }

in   al,021h

or   al,ah         { Tiltjuk az adott vonalat  }

out  021h,al

end;

Az EnableIRQline, illetve a DisableIRQline engedélyezi, illetve tiltja az adott megszakítási vonalat.

procedure DMA_addr(a:pointer; var p,s,l:word);

var

la : longint;

begin

{ la=20 bites fizikai cím, azaz 16*szegmens+offszet }

la:=(longint(seg(a^)) shl 4)+ofs(a^);

p:=la shr 16;            { Lapszám: 0..15 közé fog esni }

s:=la and 65535;         { Start Address a page-en belül }

l:=65536-s;              { A page-ből még ennyi bájt van hátra }

end;

A DMA_addr segítségével az a pointer által mutatott címből előállítja a hozzá tartozó 64K-s DMA lapszámot és a lapon belüli eltolást. A lapszám: p, az eltolás: s, a lapon rendelkezésre álló bájtok száma pedig: l.

procedure InitDMAcontroller(Transfermode:byte);

var

p,s,l : word;

begin

{A táblázatokból kiolvassuk az adott csatornához tartozó I/O címeket}

DMApage   := DMApages[SB_DMA];       { Lapregiszter }

DMAoffset := DMAoffsets[SB_DMA];     { DMA címregiszter }

DMAsize   := DMAsizes[SB_DMA];       { Számláló regiszter }

port[DMA_mask]:=SB_DMA or 4;         { Tiltjuk a DMA-t a csatornán  }

port[DMA_bpt]:=$00;                  { Töröljük a bájtmutatót       }

port[DMA_mode]:=Transfermode or SB_DMA;   { auto-init mód a csatornára }

DMA_addr(DMAbuffer,p,s,l);           { Kiszámítjuk a cím komponenseit  }

port[DMApage]:=p;                    { Beállítjuk a lapregiszter,      }

port[DMAoffset]:=LO(s);              { majd a csatorna címregiszterét  }

port[DMAoffset]:=HI(s);

port[DMAsize]:=LO(BufferSize-1);     { Beállítjuk a számláló regisztert}

port[DMAsize]:=HI(BufferSize-1);     { blokk mérete -1 !               }

port[DMA_mask]:=SB_DMA and 3;        { Engedélyezzük a DMA-t           }

end;

InitDMAcontroller: a 8 bites DMA vezérlő beállítása az auto-init DMA módhoz. Az eljárás beállítja a DMA vezérlő üzemmódját, a lapregiszter, a csatorna cím- és számlálóregiszterét.

procedure DoneDMAcontroller;

begin

port[DMA_mask]:=SB_DMA or 4;         { Tiltjuk a DMA-t a csatornán }

end;

procedure InitInterrupt(UserDefinedIRQ:pointer);

begin

GetIntVec($08+SB_IRQ,@OldSBorgIRQ);  { Tároljuk az eredeti vektort }

if UserDefinedIRQ=nil then

SetIntVec($08+SB_IRQ,@SB_AutoInitDMA_IRQ) { Beállítjuk az újat    }

else

SetIntVec($08+SB_IRQ,UserDefinedIRQ);

EnableIRQline(SB_IRQ);               { Engedélyezzük az adott vonalat}

end;

Az InitInterrupt eljárással állítjuk be a megszakítási vektort. Az eredeti megszakítási vektort elmenti, majd beállítja az általunk definiáltat.

procedure DoneInterrupt;

begin

DisableIRQline(SB_IRQ);            { Tiltjuk az adott vonalat }

SetIntVec($08+SB_IRQ,@OldSBorgIRQ);{ Visszaállítjuk az eredeti vektort }

end;

procedure StartAutoInitDMAplay;

begin

DSPwrite($d1);               { Parancs: DSP speaker on       }

DSPwrite($1c);               { Parancs: DSP auto-init DMA DAC}

end;

StartAutoInitDMAplay: az auto-init DMA lejátszás megkezdése.

procedure StartAutoInitDMArecord;

begin

DSPwrite($d3);                    { Parancs: DSP speaker off      }

DSPwrite($2c);                    { Parancs: DSP auto-init DMA ADC}

end;

StartAutoinitDMArecord: az auto-init DMA felvétel elindítása.

procedure StopAutoInitDMA;

begin

DSPwrite($d3);                     { Parancs: DSP speaker off      }

DSPwrite($d0);                     { Parancs: Stop DMA transfer    }

DSPwrite($da);                     { Parancs: Stop auto-init DMA   }

DSPwrite($d0);                     { Parancs: Stop DMA transfer    }

end;

StopAutoInitDMA: az auto-init DMA átvitel leállítása.

procedure ClearDMAbuffer;

begin

fillchar(DMAbuffer^,BufferSize,$80);{ Minden minta nulla jelszintű  }

end;

ClearDMAbuffer: a DMA buffer törlése.

function AllocDMAbuffer(size:word):integer;

var

p,s,l,i : word;

begin

DMAs1:=size;                { Első menet }

DMAs2:=0;

AllocDMAbuffer:=-1;

if MaxAvail<size then exit;{ Azonnal kilépünk, ha nincs elég hely} getmem(DMAp1,DMAs1);        { Lefoglaljuk a megfelelő blokkot }

DMA_addr(DMAp1,p,s,l);     { Meghatározzuk a cím komponenseit }

if l>=size then            { Van elég hely a lapon? }

begin                     { Ha igen…             }

DMAbuffer:=DMAp1;        { Ez lesz a DMAbuffer    }

DMAptr:=DMAp1;           { A DMAptr is ide mutat  }

BufferSize:=size;        { Beállítjuk a buffer méretét }

HalfSize:=BufferSize shr 1;{ Ennek fele a HalfSize     }

AllocDMAbuffer:=1;         { A lefoglal s sikeres volt (1. menet)}

ClearDMAbuffer;            { Töröljük a buffert (csend)  }

exit;                      { Visszatérünk a függvényből  }

end;

inc(p);                      { A következő lap lesz a jó }

DMAs2:=l;                    { Itt még ennyi hely kell   }

getmem(DMAp2,DMAs2);         { Lefoglaljuk               }

DMAbuffer:=ptr(p shl 12,$0000);{ Előállítjuk a buffer mutatóját }

DMAptr:=DMAbuffer;           { A DMAptr is ide mutat            }

BufferSize:=size;            { Beállítjuk a buffer méretét      }

HalfSize:=BufferSize shr 1;  { Ennek fele a HalfSize            }

AllocDMAbuffer:=2;           { A lefoglal s sikeres volt (2. menet)}

ClearDMAbuffer;              { Töröljük a buffert (csend)          }

end;

AllocDMAbuffer: helyfoglalás a DMA buffer számára.

procedure FreeDMAbuffer;

begin

if DMAs2<>0 then freemem(DMAp2,DMAs2); { A terület felszabadítása }

if DMAs1<>0 then freemem(DMAp1,DMAs1); { A terület felszabadítása }

DMAs2:=0;

DMAs1:=0;

end;

FreeDMAbuffer: az AllocDMAbuffer eljárás által lefoglalt területek felszabadítása.

END.

A felvételre természetesen használhattunk volna 16 bites auto-init DMA átviteli módot is, de a tapasztalat az, hogy a Windows nem igazán örül neki, ha a 16 bites DMA csatornát szeretnénk használni, ugyanis azt előszeretettel lefoglalja saját erőforrásai számára. Emiatt már a kártya telepítésekor érhetnek minket meglepetések, hiszen csakis a 8 bites DMA csatornákat ajánlja fel az említett operációs rendszer a hangkártya számára. Erről egyébként több hardveres kézikönyv is említést tesz és azzal vígasztal minket, hogy ilyen esetben a 16 bites DMA átvitel is a 8 bites DMA csatornán valósul meg.

Esetünkben nem követelmény, hogy az általunk rögzítendő hanganyag CD, esetleg HI-FI minőségű legyen, de 44,1 kHz-es mintavételi frekvencia és 8 bites kvantálási hossz mellett is megfelelő minőséget lehet elérni, még akkor is, ha mono felvételt készítünk. Így nyugodtak lehetünk, hogy nem lesz túl nagy az információveszteség, legalább is nem akkora, hogy érdemes lenne a hangállomány méretét többszörösére növelni, nem mintha olyan óriási méretekről lenne szó.

[4] László József: Hangkártya programozása Pascal és Assembly nyelven, Computer Books, Budapest, 1999

Hangkártyák programozása retro 11.

6.2.1. Sound Blaster 16 felépítése és programozása

6.2.1.1. Lehetőségek

1)      FM chip:

  • Bővített OPL–3 FM chip, amely négyoperátoros hangkeltésre is alkalmas,
  • Teljes kompatibilitás az előző ADLIB és SB (kivéve SB Pro) változatokkal,
  • 18 kétoperátoros, vagy 6 négyoperátoros hangcsatorna,

2)      Továbbfejlesztett DSP egység:

  • 8- és 16 bites felvétel és lejátszás,
  • Sztereó és mono felvétel és 8- és 16 bites módban,
  • Dinamikus szűrők a felvételhez és a visszajátszáshoz,
  • Két DMA csatorna a DMA vezérelt felvételhez és lejátszáshoz,
  • Kibővített DSP parancskészlet,
  • Lineáris lépésekben programozható mintavételi frekvencia 5-től 48 kHz-ig,

3)      Bővített keverő (mixer):

  • A bemeneti eszközök lehetséges keverése,
  • Magas és mély állítási lehetőségek,

4)      Szoftverből állítható hangerő minden kimenethez

5)      Beépített sztereó erősítő:

  • 4 W/csatorna, 4 ohmos hangszórókhoz,
  • Automatikus, vagy fix erősítés mikrofonhoz,
  • Külső, vagy belső erősítő a kimenetekhez

6)      MIDI csatlakozó:

  • SB, vagy MPU401–UART kompatibilis mód,

7)      CD-ROM csatlakozó

8)      Hardverbeállítások szoftverből:

  • Szoftverből állítható megszakítási szint,
  • Szoftverből állítható 8- és 16 bites DMA csatorna szám.

6.2.1.2. A DSP programozása

Az SB16 DSP-je a következő I/O címeken keresztül érhető el:

I/O relatív cím I/O alapbeállítású cím DSP funkció
Base+$06 $226 (write only) A DSP alapállapotba hozása (DSP Reset)
Base+$0A $22A (read only) Adat olvasása az ADC-ről (Read Data)
Base+$0C $22C (read/write) Parancs/adat írása a DAC-ra (Write command/data)Bufferállapot olvasása (Read Buffer Status)
Base+$0E $22E (read only) Adatérvényesség olvasása, a 8 bites megszakítás nyugtázása (Data Available, 8 bit IRQ Acknowledge)
Base+$0F $22F (read only) A 16 bites megszakítás nyugtázása (16 bit IRQ Acknowledge)

A fontosabb SB16 DSP parancsokat a T.2. foglalja össze.

T.2. Fontosabb SB16 DSP parancsok

Kód Parancs Parancsfunkció
$1C 8 bit auto-init DMA play 8-bites auto-init DMA lejátszás
$D0 Pause 8 bit DMA 8-bites DMA átvitel felfüggesztése
$D1 Speaker On A DSP kimenetének bekapcsolása
$D3 Speaker Off A DSP kimenetének kikapcsolása
$D4 Continue 8 bit DMA 8-bites DMA átvitel folytatása
$D5 Pause 16 bit DMA 16-bites DMA átvitel felfüggesztése
$D6 Continue 16 bit DMA 16-bites DMA átvitel folytatása
$D9 Exit 16 bit auto-init DMA 16 bites auto-init DMA leállítása
$DA Exit 8 bit auto-init DMA 8 bites auto-init DMA leállítása
$E1 Get DSP version DSP verziószámának lekérdezése
$2C 8 bit auto-init DMA record 8bites auto-init DMA felvétel
$40 Set Time Constant A mintavételezési időállandó beállítása
$41 Set play sample rate Mintavételi frekvencia beállítása lejátszáshoz
$42 Set record sample rate Mintavételi frekvencia beállítása felvételhez
$48 Set DMA block size DMA blokkméret beállítása
$B6 16 bit auto-init DMA play 16 bites auto-init DMA lejátszás
$BE 16 bit auto-init DMA record 16 bites auto-init DMA felvétel
$B0 16 bit normal DMA play 16 bites normál DMA lejátszás
$B8 16 bit normal DMA record 16 bites normál DMA felvétel
$C6 8 bit auto-init DMA play 8 bites auto-init DMA lejátszás
$CE 8 bit auto-init DMA record 8 bites auto-init DMA felvétel
$C0 8 bit normal DMA play 8 bites normál DMA lejátszás
$C8 8 bit normal DMA record 8 bites normál DMA felvétel
$F2 IRQ request (8 bit) DSP szoftveres megszakításkérés 8 bites átvitelhez
$F3 IRQ request (16 bit) DSP szoftveres megszakításkérés 16 bites átvitelhez
$FB Read DSP status DSP állapotának lekérdezése
$FC Get DSP information További információ a DSP állapotáról
$FD Get last DSP command Az utolsó DSP parancs lekérdezése

A következő Pascal-ban használt Assembly betéteknek a DSP programozása során vehetjük hasznát. A DSP programozására készített teljes unit forrását F.25-ben találhatjuk.

function DSPreset:boolean; assembler;

asm

mov   dx,BaseAddr { A báziscím:$220 a DX regiszterbe kerül. }

add   dl,$06      { DX=$226, DSP Reset. }

mov   al,$01

out   dx,al       { Egy $01 érték kiküldése. }

mov   cx,100

rep lodsb         { Várakozás. }

xor   al,al

out   dx,al       { Egy $00 érték kiküldése. }

add   dl,$08      { DX=$22E, Data Available. }

mov   cx,128

@wait1:             { Várakozás, amíg az adat érvényes nem lesz. }

in    al,dx

and   al,$80

jnz   @test2      { Kiugrás, ha érvényes. }

loop  @wait1

jmp   @bad        { Hiba, ha lejárt a ciklus. }

@test2:

mov   cx,10240

sub   dl,$04      { DX=$22A, DSP Read Data. }

@wait2:

in    al,dx

cmp   al,$0aa     { A $AA értéket kell adnia. }

jz    @good       { Ugrás, ha a Reset sikeres. }

loop  @wait2

@bad:

xor   al,al       { Hiba, ha Al=false. }

jmp   @exit

@good:

mov   al,$01      { Nincs hiba, ha AL=true. }

@exit:

end;

DSPreset: a Sound Blaster DSP egységének alapállapotba hozása. A függvény megpróbálja inicializálni a DSP egységet. Ha ez sikerül, vagyis a beállított időintervallumon belül a megfelelő értéket észleli, akkor igaz értékkel tér vissza. Ha az időzítési ciklus lejártával sem érzékeli a megfelelő eredményeket, akkor a DSP nem működik helyesen és hamis értékkel tér vissza.

procedure DSPwrite(DSPdata:byte);  assembler;

asm

mov   dx,BaseAddr { Báziscím:$220 a DX regiszterbe. }

add   dl,0ch      { DX=$22C, Read Buffer Status. }

@c1:

in    al,dx

and   al,$80      { Szabad már a regiszter? }

jnz   @c1         { Várakozás, ha még nem. }

mov   al,DSPdata  { AL=adat/parancs. }

out   dx,al       { Write Command/Data. }

end;

DSPwrite: egy adat, illetve parancs kiküldése a DSP-nek.

function DSPread:byte;  assembler;

asm

mov   dx,BaseAddr { Báziscím:$220 a DX regiszterbe. }

add   dl,$0e      { DX=$22E, Data Available. }

@wait:

in    al,dx       { Várakozás, amíg az adat érvényes nem lesz. }

and   al,$80

jz    @wait

sub   dl,$04      { DX=$22A, DSP Read Data. }

in    al,dx       { Az adat beolvasása AL-be. }

end;

DSPread: adat beolvasása a DSP-ről.

F.31-ben további példákat láthatunk a DSP programozására.

6.2.1.3. A mixer programozása

Az SB16 mixer a következő I/O címeken keresztül érhető el:

Regiszter I/O cím Alapbeállítású I/O cím Funkció
Base+$04 $224 (w) Mixer címregiszter (address)
Base+$05 $225 (r/w) Mixer adatregiszter (data)

Az SB16 mixerének regisztereit a T.3. ismerteti részletesebben.

T.3. Az SB mixerének fontosabb regiszterei

Regiszter Funkció
$30 Master Volume Left Kimeneti hangerő, bal oldal
$31 Master Volume Right Kimeneti hangerő, jobb oldal
$32 DSP Voice Volume Left DSP hangerő, bal oldal
$33 DSP Voice Volume Right DSP hangerő, jobb oldal
$34 MIDI/FM Volume Left MIDI/FM egység hangerő, bal oldal
$35 MIDI/FM Volume Right MIDI/FM egység hangerő, jobb oldal
$36 CD Volume Left Audio CD hangerő, bal oldal
$37 CD Volume Right Audio CD hangerő, jobb oldal
$38 Line In Volume Left Vonali bemeneti hangerő, bal oldal
$39 Line In Volume Right Vonali bemeneti hangerő, jobb oldal
$3A Microphone Volume Mikrofon hangerő
$3B PC-Speaker Volume PC-Speaker hangerő
$3C Output Select Kimeneti eszközök kiválasztása
$3D Left Input Device Select Bal oldali bemeneti eszközök kiválasztása
$3E Right Input Device Select Jobb oldali bemeneti eszközök kiválasztása
$3F Left Input Gain Level Bal oldali bemeneti erősítési szint
$40 Right Input Gain Level Jobb oldali bemeneti erősítési szint
$41 Left Output Gain Level Bal oldali kimeneti erősítési szint
$42 Right Output Gain Level Jobb oldali kimeneti erősítési szint
$43 Microphone AGC Control Mikrofon AGC engedélyezés/tiltás
$44 Left Treble Level Bal oldali magas szint szabályozás
$45 Right Treble Level Jobb oldali magas szint szabályozás
$46 Left Bass Level Bal oldali mély szint szabályozás
$47 Right Bass Level Jobb oldali mély szint szabályozás
$80 Interrupt Level Select Megszakítási vonal kiválasztása
$81 DMA Channels Select DMA csatornák kiválasztása (8, 16 bit)
$82 Interrupt Request Status Megszakításkérés állapot

A mixer néhány fontosabb regiszterének részletezése

Reg. 7. bit 6. bit 5. bit 4. bit 3. bit 2. bit 1. bit 0. bit Megjegyzés
$3C Line Lf Line Rg CD Lf CD Rg Mic 0: tiltás,1: engedélyezés
$3D 0 (fix) MIDI Lf MIDI Rg Line Lf Line Rg CD Lf CD Rg Mic 0: tiltás,1: engedélyezés
$3E 0 (fix) MIDI Lf MIDI Rg Line Lf Line Rg CD Lf CD Rg Mic 0: tiltás,1: engedélyezés
$42 Gain Gain 00: egyszeres erősítés,01: kétszeres erősítés,10 négyszeres erősítés,

11: nyolcszoros erősítés

$43 FGC/AGC $00: fix erősítés,$01: automatikus erősítésszabályozás
$80 IRQ 10 IRQ 7 IRQ 5 IRQ 2 Mindig csak egy bit legyen 1
$81 DMA 7 DMA 6 DMA 5 DMA 3 DMA 1 DMA 0 Egyszerre csak egy bit lehet 1 mindkét részben
High DMA Low DMA
$82 1 (fix) 0 (fix) 0 (fix) 0 (fix) ? UART DSP 16 DSP 8 A regiszter alsó három bitje azt adja meg, hogy melyik eszköz okozta a megszakítást.

A következő programrészleteknek a mixer programozása során vehetjük hasznát:

procedure SetMixer(RegNum,Value:byte);     assembler;

asm

mov  dx,Base      { Báziscím:$220 }

add  dx,$0004     { Mixer címregiszter:$224 }

mov  al,RegNum

out  dx,al        { Cím kiküldése }

inc  dx           { Mixer adatregiszter:$225 }

mov  al,Value

out  dx,al        { Adat kiküldése }

end;

A SetMixer eljárás segítségével a mixer egy adott regiszterét lehet beállítani.

function  GetMixer(Regnum:byte):byte;      assembler;

asm

mov  dx,Base      { Báziscím:$220 }

add  dx,$0004     { Mixer címregiszter:$224 }

mov  al,Regnum

out  dx,al        { Cím kiküldése }

inc  dx           { Mixer adatregiszter:$225 }

in   al,dx        { Adat beolvasása }

end;

A GetMixer függvény egy adott regiszter értékét kérdezi le.

A fenti rutinokat tartalmazó Pascal unit részletes listája a F.26-ban található [4] alapján. F.32-ben pedig egy olyan program tanulmányozható, amely kiírja az SB mixerének regisztereit, módosítható és fix bitjeikkel.

[4] László József: Hangkártya programozása Pascal és Assembly nyelven, Computer Books, Budapest, 1999

Hangkártyák programozása retro 10.

6. Az impulzusválasz teszt számítógépesítése

6.1. A rendszer leírása

A teremakusztikai bevezetés során megismerkedhettünk az akusztikai tér különböző jellemzőivel. Láttuk, hogy az impulzusválasz teszt az a módszer, amellyel leginkább megkaphatjuk egy bizonyos terem hangzását. Feladatunk tehát, hogy egy pillanatszerű hanglökést keltsünk a vizsgált teremben egy hangforrás segítségével, illetve azt és annak hatását egy mikrofon segítségével rögzítsük további vizsgálódás, illetve felhasználás érdekében. A megvalósításhoz két hangkártyát használtam, – az egyik az impulzus keltéséért, illetve annak a vonali kimeneten, majd a hangszórón keresztül a vizsgált hangtérbe való juttatásáért felelős, a másik pedig a mikrofonon keresztül történő hangfelvételt valósítja meg – így jól elválasztható a két folyamat és szinte egy időben történik meg a lejátszás és a felvétel. Ha a rendszer csak egyetlen kártyát használna, akkor nehezebb lenne az adott kártyán belüli funkcionális egységek összehangolása. A piacon fellelhető hangkártyák széles skálájából a feladat megvalósításához olyan kártyákat választottam, amelyek elég elterjedtek és viszonylag olcsón beszerezhetők. A hangfelvételért felelős kártya egy Sound Blaster 16-os, amely szinte már szabvánnyá nőtte ki magát, és egyben rendelkezik a piacon fellelhető bonyolultabb hangkártyák minden alapvető tulajdonságával. A lejátszást végző kártya szintén egy jól ismert hangkártya család tagja: a Gravis Ultrasound Ace nevű terméke, melynek érdekessége, hogy hangfelvételre nem, csupán lejátszásra alkalmas. Viszont RAM-jába bármilyen hangminta beírható és a kimenetre küldhető. A választás során az sem volt mellékes, hogy a Sound Blaster és a Gravis termékeiről bőven találhatunk programozói leírást és azokat működtető kész programokat is. E két hangkártya ismertetésével programozásuk során részletesebben foglalkozunk. (Lásd F.16.)

F.16. Az impulzusválasz teszt számítógépesítése

Hangkártyák programozása retro 9.

5.6. Hangkártyák összehasonlítása

Ahogy a bevezetésben is utaltam rá, óriásit változott a világ a számítógépes hangfeldolgozás területén. Ma már olyan nagy választék áll rendelkezésünkre hangkártyákból, hogy szint lehetetlen választani. Mégis érdemes röviden áttekinteni, hogy amikor ez a dolgozat íródik (2000), milyen hangkártyák lelhetők fel a piacon és mi alapján érdemes közülük választani. *

A CHIP számítógépes magazin 2000. januári számában [8] egy viszonylag jól alkalmazható módszert találunk a hangkártyák tesztelésére, összehasonlítására. A mérési módszer lényege, hogy a kártyák tesztelése „önmaguk” segítségével történik. Pontosabban arról van szó, hogy a kártya vonali kimentén (Line Out) megjelenő jelet egy erre alkalmas kábellel a kártya vonali bemenetére (Line In) vezetik. Magyarul, a kártyával „bevetetik ” a saját maga által generált jelet. Mindezekből következik, hogy a jelromlás összeadódik, hiszen a jel keresztülmegy mind a kimeneten, mind a bemeneten.

A hangkártya lejátszási (Playback) keverőjén a Master és a Wave kivételével, amelyeket maximális hangerőre állítottak, mindent elcsöndesítettek. A felvételi (Recording) keverőn pedig csak a bemenetet (Line In) állították teljes hangerőre. A keverő beállításaiból következően a mérés során gerjedés nem léphetett fel. A megfelelő kimeneti jel biztosítása érdekében 44100 bit/s mintavételezésű, mono Wav hangállományokat használtak, amelyek a következő módon jellemezhetőek:

  1. Teljes hangerőn 11 perc alatt megy végig a teljes hangfrekvenciás tartományon (0-22kHz).
  2. 10 különálló, 66 másodperces hangállomány, amelyeket 440 Hz-es hangot adnak különböző hangerővel – 10%-tól 100%-ig, 10%-os lépésekben.
  3. 10 különálló, 66 másodperces hangállomány, amelyek 1 kHz-es hangot adnak különböző hangerővel – 10%-tól 100%-ig, 10%-os lépésekben.
  4. 10 különálló, 66 másodperces hangállomány, amelyek 440 Hz-es és 1 kHz-es hangot adnak keverve, különböző hangerővel, – 10%-tól 100%-ig, 10%-os lépésekben.

A hangállományok és a SpectraLab nevű program segítségével a következő vizsgálatokat végezték el:

  • Átvitel: a teljes halható frekvenciatartományban vizsgálták a kiadott maximális hangerőt, visszacsatoló kábel használata nélkül. A magasabb érték jobb, a kártyák átviteli karakterisztikáján ezt jelzi a zöld vonal. (Lásd F.15.b.)
  • Áthallás: visszacsatoló kábel nélkül figyelték meg, hogy a kimenet által kiadott jel milyen mértékben jut vissza a bemenetre a kártya áramkörein keresztül. Az alapzajnál sosem lehet kisebb szintű, de értéke kicsivel az alapzaj alá süllyedhet. (Az alacsonyabb érték a kedvezőbb, a kártyák átviteli karakterisztikáján ez a sárga vonal.)
  • Alapzaj: visszacsatoló kábel nélkül, a hanglejátszás során figyelték a bemenet által összeszedett, illetve az áramkörök által keltett saját zajt. (Az alacsonyabb és „egyenesebb ” érték elfogadhatóbb, a kártyák karakterisztikáján ez a piros vonal.)
  • Jel/zaj viszony: a hangfrekvenciás jelalak mutatója, amely a hang tisztaságát jelzi. A hasznos jel csúcsértékének és a zaj átlagos erősségének hányadosa, mértékegysége decibel (dB). A teszt során a 440 Hz-es és az 1 KHz-es jelet egyszerre lejátszva, különböző hangerőkön figyelték a program által kijelzett jel/zaj viszonyt. (A nagyobb érték a jobb.)
  • Optimális hangerő: az a hangerő, amelyen a legmagasabb jel/zaj viszonyt mérték.

Az összehasonlítás során rájöttek, hogy nagy eltérés van az egyes hangkártyák kimeneti erősítői között: a kártyák optimális hangereje 40% és 80% között mozog. 90-100% közelében már minden hangkártya esetében jelentősen romlott a jel/zaj arány. Ezért használat során az adott hangkártya optimális hangerejét célszerű beállítani a szoftveres keverővel.

Rájöttek még, hogy bár az alapzajnak minél kisebbnek kéne lennie, az áthallás akkor a legkisebb, ha az alapzaj azonos szinten marad.

A T.1. a CHIP magazin hardvertesztje alapján néhány fontosabb hangkártyát foglal magába, azok műszaki paramétereit és mérési eredményeit ismertetve. F.15.b. pedig a Creative Sound Blaster Live! és a Terratec Sound System DMX hangkártya grafikonjait tartalmazza, összehasonlítás céljából.

F.15.b. A Creative Sound Blaster Live! és a Terratec Sound System DMX hangkártyák összehasonlítása grafikonok alapján: (a) jel/zaj a hangerő függvényében, (b) átviteli jelleggörbék, (c) torzítás a hangerő függvényében

T.1. Fontosabb hangkártyák összehasonlítása (2000. január)

Műszaki adatok
Típus Aurel Vortex2 SuperQuad Digital Creative Sound Blaster Live! Terratec Sound System DMX Terratec Xlerate Pro Tracer Sampling Audio System
Chipkészlet Aurel Vortex AU8830A2 Creative EMU 10K1NDF ESS
Canyon3D ES1970M-3D
Aurel Vortex AU8830A2 Crystal CX4237BXQ3
Csatolás PCI PCI PCI PCI PCI
MIDI/Joystick Van Van Van Van Van
Bemenetek Line, mic, aux, CD, TAD Line, mic, aux, CD,  TAD, digitális, digitális optikai Line, mic, aux, CD1, CD2, TAD, optikai digitális Line, mic, aux, CD, TAD 2xline, mic, CD, TAD, video
Kimenetek Duál line, optikai digitális Duál line, digitális, optikai digitális Duál line, optikai digitális Duál line, optikai digitális Line, duál speaker
Hangrendszer Sztereó, A3D, 4 csatornás Sztereó, EAX, 4 csatornás Sztereó, A3D, EAX, 4 csatornás Sztereó, A3D, 4 csatornás Sztereó, SRS, 4 csatornás
Mérési eredmények
Alapzaj (0%-os hangerőn) -78,3 dB -77,3 dB -80,3 dB -78,3 dB -93,0 dB
Áthallás (100%-os hangerőn) -78,0 dB -76,8 dB -61,1 dB -78,0 dB -91,6 dB
Jel/zaj viszony
10%-os hangerőn 39,3 dB 34,2 dB 30,5 dB 39,3 dB 37,7 dB
20%-os hangerőn 44,7 dB 39,1 dB 30,6 dB 44,7 dB 42,9 dB
30%-os hangerőn 50,0 dB 44,2 dB 30,7 dB 50,0 dB 48,3 dB
40%-os hangerőn 55,1 dB 49,3 dB 30,7 dB 55,1 dB 53,5 dB
50%-os hangerőn 60,2 dB 54,3 dB 30,7 dB 60,2 dB 58,6 dB
60%-os hangerőn 64,9 dB 59,3 dB 30,7 dB 64,9 dB 63,4 dB
70%-os hangerőn 69,4 dB 64,2 dB 16,2 dB 69,4 dB 67,8 dB
80%-os hangerőn 44,5 dB 68,8 dB 10,3 dB 44,5 dB 70,8 dB
90%-os hangerőn 14,4 dB 31,2 dB 7,7 dB 14,4 dB 15,8 dB
100%-os hangerőn 9,67 dB 13,5 dB 6,2 dB 9,6 dB 17,2 dB
Optimális hangerő 69,4 db (70%) 68,8 dB (80%) 30,7 dB (60%) 69,4 dB (70%) 70,8 dB (80%)

* Azt tervezem, hogy cikksorozatomnak ezt a részét kiegészítem a legújabb hangkártya típusok összehasonlításával  és azt folyamatosan nyomon is követjük majd a jövőben.

[8]   Giczy Gergő: Hanghatár, CHIP magazin, XII. évf., 1. szám, 92-96. oldal

Hangkártyák programozása retro 8.

5.5. A hangkártyák általános felépítése

Egy hangkártya általában a következő egységeket tartalmazza:

  • Wavetable,
  • ROM,
  • RAM,
  • FM chip,
  • CODEC,
  • MIXER.

A wavetable (hullámtábla) egység az, amely a hangmintákkal kapcsolatos műveletek elvégzéséért felelős, s amely átadja a feldolgozott mintákat a CODEC egységnek. Nem minden hangkártyához tartozik hullámtábla processzor, de magával a processzorral általában rendelkeznek. A ROM tartalmazza azokat a hangmintákat, amelyeket gyárilag adnak a hangkártyához. Amint a nevében is benne van, az egy kizárólag olvasható memória, következésképpen saját hangmintákat nem itt, hanem az erre alkalmas RAM-ban tárolhatjuk. Természetesen a RAM bizonyos határokat szab a tárolandó hangminták méretét illetően. Az FM egység az a chip, amely magát az FM szintézist végzi. A CODEC egység végzi az analóg-digitális, illetve a digitális-analóg átalakítást, vagyis magában foglalja a mintavevő és tartó áramkört, valamint az A/D és D/A konvertereket. A szűrőket általában a CODEC-en kívüli elemek felhasználásával oldják meg a széleskörű felhasználhatóság érdekében. A MIXER feladata, hogy a különböző forrásból származó jeleket a CODEC-re kapcsolja, illetve a CODEC-ről jövő jeleket a megfelelő kimenetre küldje. Ez nem más, mit egy analóg fokozat nélküli kapcsoló, melyet programból vezérelhetünk. Vagyis a kimenetek és a bementek kapcsolása úgy is lehetséges, hogy miközben az egyik fokozatosan elhalkul, a másik fokozatosan felerősödik.

A hangkártya ki- és bemenetei létesítenek kapcsolatot a különböző külsőleg csatlakoztatható eszközökkel. Ezek általában a következők:

  • Line Out: vonali, tehát kis szintű kimenet, melynek kimeneti feszültsége általában maximum 0,745 V, amely alkalmassá teszi erősítő eszközre való csatlakozáshoz.
  • Speaker Out: egy hangszóróra közvetlen csatlakozó erősített kimenet.
  • Aux In: nagyjelű bemenet, amely külső eszközről származó analóg jelek feldolgozását teszi lehetővé.
  • Mic In: erősítőt tartalmazó mikrofon bemenet.
  • Joystick/Midi: botkormány, illetve elektronikus hangszer csatlakozását teszi lehetővé.

(Lásd F.15.)

F.15. Hangkártyák általános felépítése

Hangkártyák programozása retro 7.

5. A digitális jelfeldolgozás alapjai

5.1. Az analóg-digitális átalakítás

Az analóg jelek időben és értéktartományban folytonosak, vagyis a jel megfelelően (hasonlatos módon) képez le egy folytonosan zajló fizikai folyamatot. A jel bizonyos határok között értéktartományban és időben tetszőleges értéket vehet fel.
Előfordulhat, hogy a jel időben folytonos, de csak meghatározott értékeket vehet fel. Ezeket a jeleket időben folytonos, diszkrét értékű jeleknek nevezzük.
Egy másik lehetséges eset, hogy a jel folytonos értékű, azonban időben csak meghatározott pillanatokban jelentkezik, vagyis időben diszkrét.
Ha egy analóg jelből diszkrét időközönként mintákat veszünk, és a jel értékét diszkrét helyeken definiáljuk, vagyis kvantáljuk, diszkrét értékű és diszkrét idejű jelet kapunk eredményül, amely nem más, mint a digitális jel.
Tehát a digitalizálás első művelete a mintavételezés, vagyis folytonos értékű, időben diszkrét jel előállítása. A mintavevő meghatározott időpontokban a bemenetre kapcsolt analóg jellel arányos amplitúdójú feszültségértékeket határoz meg. Ezeket a diszkrét amplitúdó értékeket a tartó elem tárolja el a további fokozatok számára. A pillanatnyi mintavételezés folyamata felfogható az egységlökés impulzussorozat amplitúdó modulációjaként is.
A következő művelet a kvantálás, mely során az időben diszkrétté tett analóg jelet amplitúdóban is diszkrétté tesszük a kvantáló elem segítségével, amely a bemenetére adott feszültségértékeket megadott szélességű bináris adatokká alakítja. Ezt a bináris jelet egy kódoló segítségével tetszőleges adattá alakíthatjuk, amelynek kimenetén megjelenik a digitális jel. (A F.9-ben példákat láthatunk a jelek néhány típusára és struktúrájára.)


F.9. Jelek típusai és struktúrájuk: (a) folytonos idejű és értékű (analóg) jel, (b) időben folytonos, diszkrétértékű függvény, (c) folytonos értékű, időben diszkrét függvény, (d) diszkrét értékű és idejű (digitális) jel.

5.2. Információveszteség az analóg-digitális átalakítás során

Az információveszteség egyik oka, hogy véges hosszúságú adatokká képezzük le az értéktartományt. Ez azt jelenti, hogy minden amplitúdó-értéket csak közvetlenül az adatszavak hosszúságával összefüggő, véges pontossággal lehet kódolni. Így a megengedett amplitúdó-tartomány véges számú, diszkrét amplitúdó-intervallumra oszlik. Ezt a folyamatot nevezzük amplitúdó kvantálásnak. Ez azt jelenti, hogy a mintavételezett értékek egy része eltér a valódi, a mintavételi időpontokban ténylegesen megjelenő értékektől. Ezek az eltérések szabályos működés esetén maximum egy kvantálási lépés felét tehetik ki. Ezek az eltérések a jelértékben mint a valódi szuperponált zavarjelek keletkeznek, s ez a kvantálási zaj. Az elkövetett hiba pedig a kvantálási hiba.
Az analóg-digitális átalakítás problémája az úgynevezett mintavételi törvény megsértése miatt keletkezhet és ez az úgynevezett aliasing zaj. A Shannon által megfogalmazott mintavételi törvény értelmében a mintavételi frekvenciának a jelben előforduló legnagyobb frekvencia kétszeresénél nagyobbnak lennie ahhoz, hogy a jel által tartalmazott információ teljes mértékben megmaradjon, azaz a digitális mintákból az eredeti jel teljes mértékben visszaállítható legyen. Ha egy jelből a szükségesnél kevesebb mintát vesznek, a jelben olyan spektrális összetevők jelenhetnek meg, amelyeket a jel nem is tartalmazott. Ennek okai a periodikusan ismétlődő spektrum időbeni mintavételezése során fellépő átlapolásokban keresendőek. Általában a mintavételi frekvencia előre meghatározott és nem tetszőleges érték. Az aliasing zaj kivédésére jó megoldás lehet az aliasing szűrő alkalmazása, vagyis ha a bemeneti jelet egy olyan aluláteresztő szűrőn bocsátjuk át, melynek törésponti frekvenciája a mintavételi maximum fele. (F.10-ben példákat láthatunk hibás mintavételezésre.)

F. 10. Példák hibás mintavételezésre

5.3. Analóg-digitális átalakítók

Az analóg-digitális átalakítók több csoportját szokás megkülönböztetni. Mindig az adott feladat dönti el, hogy milyen szempontok alapján, illetve mely csoportból választunk.
1. Integráló típusú A/D átalakítók (single slope converter). A bemeneti analóg feszültséget egy összehasonlító referenciafeszültséggel vetjük össze. A referenciafeszültség integrálja lineárisan nő, s amíg a két feszültség egyenlő nem lesz, egy digitális számláló folyamatosan felfelé számol, majd amikor a két feszültség egyenlővé válik, illetve különbségük minimális lesz, leállítjuk a számlálást. Az integrátor áramkör C kondenzátora az inicializálás után az R ellenálláson keresztül el kezd töltődni. Az integrátor kimeneti feszültségét egy komparátor segítségével hasonlítjuk össze a bemeneti feszültséggel. Amíg a két feszültség nem egyenlő, addig a komparátor kimeneti jele logikai magas szintű (1). Az ÉS kapu kimenetén ilyenkor az órajel megjelenhet, vagyis a számláló számol. Ha a két feszültség értéke annyira megközelíti egymást, hogy a komparátor át tud billenni, a komparátor kimenetén logikai alacsony szint (0) jelenik meg. Ekkor az ÉS kapu már nem engedi át az órajelet, ezért a számlálás véget ér. A konverter tehát időbeli integrálást hajtott végre, a számláló digitális kimenete arányos az integrálás idejével, az integrálás ideje pedig arányos a bemeneti feszültséggel. Az átalakítást az inicializálás indítja el. Ekkor a kapcsoló alaphelyzetbe hozza az integrátort, azaz kisüti a kondenzátort, míg a számlálót lenullázza. Ez a típusú átalakítás tehát bizonyos időt vesz igénybe. Ez az idő az integrátor R és C tagjától függ. A kapcsolás előnye, hogy viszonylag egyszerű és olcsón előállítható. Hátránya, hogy bizonyos frekvencia felett nem használható. (Lásd F.11.)

F.11. Integráló típusú A/D konverter

2. Fokozatos közelítés elvén működő A/D átalakító. A bemeneti analóg feszültséget egy mintavevő és tartó áramkörre vezetjük, melynek kimenetén rendelkezésre áll az időben diszkretizált analóg jel, amely egy komparátor egyik bemenetére kerül. A komparátor kimentén pedig megjelenik egy logikai szint, amely egy soros bemenetű regiszterbe kerül, melynek a neve szukcesszív approximációs regiszter. A regiszterben tárolt digitális értéket egy D/A átalakító segítségével visszaalakítjuk, analóg jellé és ezt vezetjük rá a komparátor másik bementére. Vagyis a komparátor összehasonlítja a bemeneti feszültséget a regiszter által tárolt értékkel. Az átalakítás kezdetén a legmagasabb helyértékű bit 1 lesz, a többi pedig 0. Ha bemeneti feszültség nagyobb, mint a D/A átalakító kimenetén lévő feszültség, akkor a bit továbbra is 1 marad, egyébként visszaáll 0-ra. Ez az összehasonlítás minden bitre megtörténik, s a regiszterben olyan érték lesz, amely bizonyos hibaarányon belül egyenlő a bemeneti analóg jel feszültségével. Ez a leggyakrabban használt analóg-digitális átalakító. (Lásd F.12.)


F.12. Fokozatos megközelítésű A/D konverter

3. Közvetlen feszültség-összehasonlításos A/D átalakítók (flash converter). A bemeneti jelet közvetlen több referenciafeszültséggel hasonlítjuk össze, amelyek értékei csupán egy-egy kvantálási léptékkel térnek le egymástól. A bemeneti feszültséget az összes komparátor összehasonlítja a saját referenciafeszültségével, és amely referenciafeszültségeknél a bemenő feszültség nagyobb, ott az adott komparátor átbillen. A komparátor kimeneteit tehát a bemeneti feszültségre jellemző digitális kódnak tekinthetjük. Ezt a kódot a kódoló áramkör a felbontásnak megfelelő bitszámú bináris számmá alakítja. A módszer hátránya, hogy az átalakítónak n bit esetén 2n-1 darab komparátort kell tartalmaznia, és a komparátoroknak igen pontosaknak kell lenniük, így sokkal költségesebb átalakítási módszerről van szó, mint az integráló típusú A/D konverter esetében. Ezzel szemben előnye, hogy sokkal gyorsabb. (Lásd F.13.)

F.13. Közvetlen feszültség-összehasonlításos A/D átalakító

5.4. Digitális-analóg átalakítók

A D/A átalakítás lényege, hogy egy számot vele arányos feszültséggé alakítunk át. Azon-ban maga a D/A átalakítás csak egy lépés a sok közül a jel visszaállítása során. A D/A átalakító kimenetén időben folytonos, de diszkrét értékekből álló lépcsőzetes jel jelenik meg, amelyre jellemző, hogy benne  ugrásszerű változások lelhetők fel. Ennek következtében a jel spektrumában olyan összetevők jelennek meg, amelyek az eredeti analóg jelben nem léteztek. Ezen hamis összetevők eltávolítása érdekében a jelet egy megfelelő határfrekvenciájú aluláteresztő szűrőn kell átvinni.
Általában a D/A átalakítók kapcsolására jellemző, hogy benne bináris súlyozású referenciafeszültségek vannak sorba kötve. A digitális bemenet bitjei egy-egy ilyen referenciafeszültség-forrást kapcsolnak be a hálózatba, ha az adott bit 1 értékű, illetve ezeket kapcsolják ki a hálózatból, ha az adott bit 0 értékű. A bináris kód súlyozásának természetesen megfelel a feszültségértékek súlyozása. Így az adott feszültségek összeadódnak és a kimeneti feszültség a bekapcsolt források feszültségének összege lesz. Ez az eljárás elég gyors hangfrekvenciás felhasználásokhoz. (Lásd F.14.)

F.14. D/A átalakítók működési elve

Hangkártyák programozása retro 6.

4. Kísérletek a mesterséges hangzás megvalósítására

Az egycsatornás mesterséges hangátvitel egyéb hibái mellett teljesen híján van a térhatásnak. Egyetlen kis felületre összpontosítja a bármilyen elhelyezkedésű és kiterjedésű hangforrás térbeli hangját. Ezért régóta foglalkoznak a mesterséges hang térhatású átvitelével. A sztereofon rendszerektől kezdve a már említett mesterséges zengetésig, rengeteg módszerrel próbálkoztak. Ezek közül talán a legfigyelemreméltóbb a műfej alkalmazása volt, amelyet kezdetben nem hangfelvételre, hanem csupán hangközvetítésre használtak. Lényege, hogy az emberi fej alakját utánzó tárgyba a fül bázistávolságában elhelyezett két mikrofonnal készítik a felvételt az adott teremben.

Kezdetben, a közvetítések esetében, a két mikrofon egy fejhallgatón keresztül a hallgató füléhez csatlakozott, tehát az illető úgy érezte, mintha az adott teremben hallgatta volna az adott zeneművet, holott valójában egy másik helyiség volt az, ahol a fejére helyezett készülékkel ült. Ennek a rendszernek nagy előnye, hogy a fej körül kialakuló természetes időkésés és árnyékoló hatás tökéletesen lemásolja, kellemetlen azonban a fejhallgató kötelező használata. Később rájöttek, hogy a műfejjel felvett hangnak megfelelő kapcsolású, két hangszórón keresztül való közvetítése szintén térhatást kelt. Egyedül a távolságérzet lesz egységes, mert minden hallgató ugyanazon a helyen érzi magát, ahol a műfejes hangfelvétel megtörtént, az irányérzet és a kiterjedtség érzete jobb bármelyik más rendszernél, mert az időkülönbségen kívül, amely csak 1 kHz alatti irányhatást eredményez, a fej valódi árnyékoló hatása adta intenzitáskülönbséget is tartalmazza.


F.5. Utózengési idő szabályozása: (a) változtatható elnyeléssel, (b) elektroakusztikailag csatolt terekkel, (c) zengetéssel.

A műfejes hangfelvétel azonban nem csak a hallgatókagylón keresztül létesít irányhatást. Ehhez azonban a hangszórókat kompenzációs szűrőkkel kell ellátni, ami annyit jelent, hogy az ellenoldali hangszóró hatását az érdekelt fülnél erősen kell csökkenteni. (Lásd F.6.)

F.6. A műfejes felvétel hangszórós visszaadásához szükséges szűrő körök

Jelentse B(w) és J(w) a műfej két mikrofonjával felvett hangjel színképét és jelentse A(w) és E(w) a két komplex átviteli függvényt, valamelyik hangszóró és az azonos oldali fül dobhártyája, illetve az ellenoldali fül dobhártyája közötti úton. A megvalósított kísérletek alkalmával a hangszórók távolsága a megfigyelőtől 3m és a hangszórók sugárzási szöge +22,5° és –22,5° volt a megfigyelő szimmetriasíkjához képest. A két komplex átviteli függvényt valóban ki kellett mérni. Vizsgáljuk a jobb fül helyzetét. Ha csak a jobb oldali hangszóró működne és E(w,balra)=0 volna, vagyis nem volna áthallás jobbról balra, egyszerű volna a megoldás. A jobb csatornát A-1(w) inverz függvénnyel kéne szűrni, s ezáltal pontosan J(w) jutna a jobb fülbe. De valójában megvan az áthallás (a fej elhajlító hatása), ezt tehát a másik (baloldali) hangszóró megfelelőképpen szűrt jelével kell ellensúlyozni. Az átalakítások legfontosabb eleme az áthallási kompenzációs szűrő jelleggörbe, amely a következőképpen számolható:

(4.1)

A bal fülbe a bal ágon a következő jelfüggvény jut:


A bal fülbe a jobb ágon jutó jelfüggvény:


Együttesen (4.1) felhasználásával:

(4.2)

Vagyis látható, hogy YB egyenlő a B-vel, a műfej bal fülének bemeneti jelével.

Ez a módszer közvetlen teremhangosításra nem használható, de a segítségével elkészített felvétel tökéletesen olyan hangzást kelt, mint a felvétel helyéül szolgáló terem.

A műfejes hangtér-vizsgálatot jelentősen leegyszerűsíti annak számítógépesített változata, melynek alapelve a 1980-as években alakult ki. (Lásd F.7.)

F.7. A műfejes hangtérvizsgálat és annak számítógépesített változata

Ahelyett, hogy a teljes hanganyagot rögzítenénk a műfejjel, nem teszünk mást, mint végrehajtjuk az impulzusválasz tesztet és a műfejhez érkező összesen négy impulzusválasz függvénnyel „programozzuk” a számítógép, illetve a hangkártya által előzőleg digitalizált hanganyagot. Visszajátszáskor (digitális-analóg átalakításkor) a hanganyag a teremnek megfelelő hangzást fog produkálni. (Lásd F.8.)

F.8. Mesterséges hangzás megvalósításának elve

De mielőtt ebbe részletesebben belemennénk ismerkedjünk meg a digitális jelfeldolgozás alapjaival és a hangkártya felépítésével, programozásával, hiszen ez lesz az impulzusválasz teszt megvalósításának fő eszköze.

Hangkártyák programozása retro 5.

ROOM_ACOUSTICS2.jpg

3. Bevezetés a teremakusztikába

3.1. Visszaverődések sík felületről

A hanghullámok terjedésére is érvényes Fermat elve, amely szerint a hullám két pont között mindig a legrövidebb ideig tartó úton halad. Ha a terjedési sebesség a téren belül nem változik, az előbbi feltétel egyúttal a legrövidebb geometriai távolságot, az egyenest jelenti. A hullámfelületre merőlegesen képzelt sugarak tehát egyenes mentén terjednek. Ha azonban a tér inhomogén, az egyenes vonalú terjedés helyett elhajló hangsugarakkal kell számolnunk.

A különböző hőmérsékletű rétegekben adódó különféle hangsebesség okozta elhajlási jelenségek elsősorban a szabadtéri akusztikában jelentősek, de hatásuk kimutatható a teremakusztikában is. A melegebb levegőrétegben a hang nagyobb sebességgel terjed, így ferde belépéskor a beesési merőleges irányától megtörve folytatja útját.

A Huygens-féle elv kimondja, hogy valamely hullámfelület minden pontjából elemi gömbhullámok indulnak ki, s egy későbbi időpontban észlelhető hullámfelület ezeknek az elemi hullámoknak a burkolója. Ezzel az elvvel magyarázható az akadálytalan hullámterjedés, a törés, a visszaverődés és az elhajlás is. A Fersnel-féle módosítás szerint azonban a burkoló felületek helyett az elemi hullámok interferenciájával magyarázható a hullám terjedése. A módosított Huygens-Fersnel elv alkalmas az elhajlási jelenségek leírására.

3.2. Visszaverődés domború és homorú felületekről

A görbült felületek a hanghullámokra gyűjtő, illetve szóró hatást fejtenek ki. A domború felületek szétszórják a hanghullámokat, míg a homorúak összegyűjtik azokat. Az optikából már jól ismert szerkesztési fogásokkal képileg is ábrázolhatóak a sík-, illetve gömbhullámok visszaverődései. De nem csak az optikára és az akusztikára érvényes az említett jelenség, hanem hőhullámokra is. (Ezekre mutat példát F.4.)



F.4. Homorú felületek gyűjtő hatása fény-, hő- és hanghullámok esetén

A gyakorlati teremakusztikában számos érdekes példa található a görbült felületekről való visszaverődésekre. Ezek közül talán a legérdekesebbek a suttogó galériák. A suttogó galéria minden olyan geometriájú terem, vagy teremrész, amely lehetővé teszi, hogy halk hangokat nagy távolságban is meg lehessen hallani. Lehet, hogy a hallgató és a beszélő a közbeeső akadályok miatt nem is láthatja egymást, vagy hogy a hang nem a természetes irányból jön. Fontos fiziológiai jelenség, hogy az energia összpontosításának hatására rendkívül halk, egyébként a hallásküszöb alatti hangok is hallhatóvá válnak. A halk hang csak a térnek bizonyos helyén hallható, tehát úgy érezzük, mintha ott volna a maga a hangforrás. Különösen áll ez a suttogó beszédre, mert nagy frekvenciájú összetevői miatt jól irányítható, másrészt elég halk ahhoz, hogy a meglepő hatást előidézze. A suttogó galériákat azon elv szerint csoportosítjuk, hogy a hang első-, másod-, vagy magasabb rendű visszaverődések útján jut el a hallgatóhoz.

Elsőrendű suttogó galériák közé tartozik minden kúpszelet metszetű kupola, esetleg nagyobb bolthajtás. Azonban a kupola hatásáról más szempontból is beszélhetünk. A kupola okozta hangfelerősödés a kupolatörvény segítségével előre meghatározható. Ezt a levezetést most itt nem tesszük meg, csupán annak végeredményét közöljük: ha a kupola gömbi középpontja a padló-kupolatető távolság felénél magasabban van, kupolahatástól, vagyis túlzott hangenergia felerősödéstől nem kell félnünk.

Másod-, vagy harmadrendű suttogó galériák például úgy készíthetők, hogy két parabola hangvetítőt állítunk fel egymással szemben, vagy egymáshoz képest ferdén úgy, hogy egy harmadik hangvisszaverő felület is részt vegyen a sugár irányításában.

Tipikusan n-edrendű suttogó galéria a londoni Szent Pál-katedrális kupolája, amelynek erkélyén állva az egymás mellet állók egymás suttogását világosan értik, azonban a kupola átmérője irányában átkiabált szöveg érthetetlen.

3.3. Hangvetők és hangterelők

A hangvetők és a hangterelők a teremakusztika olyan eszközei, amelyekkel a közvetlen hangot az elsőrendű visszaverődések révén erősítjük fel. A hangvetők a hangforrás hangjának közvetlen erősítését és irányítását szolgálják, míg a hangterelők a terem távolabbi részeiben a visszavert hangnak a hangenergiában szegény helyekre való eljuttatását célozzák meg.

3.4. Az egyszerű visszhang

Visszhangot akkor hallunk biztosan, ha valamely hangjelenség visszaverődés következtében 0,1 s  (100 ms) vagy annál nagyobb idő elteltével ismét fülünkhöz jut. Mivel normális állapotú levegőben ez az idő 34m hangutat jelent, egy 17 m távolságban álló visszaverő felület visszhangot okozhat. Ilyen távolságok közepes méretű (2000 m3 fölötti) teremben már előfordulhat. A visszhang megfigyelése azonban a szabadban könnyebb, mert zárt termekben nem csak a visszhangot okozó felületről verődik vissza hang, hanem egyéb helyekről is, s a szabálytalan összevisszaságban érkező rövidebb, vagy hosszabb idejű visszaverődések a tiszta visszhang jelenséget elmossák. (Meg kell jegyezni, hogy ez lehet az egyik megoldása a nem kívánt teremvisszhangok megszüntetésének.)

Például a híres tihanyi visszhang a Visszhangdombon álló megfigyelőhöz az 500 m távolságban álló apátsági templom északi oldaláról verődött vissza. A két domb közötti völgy az elmosódást okozó földi visszaverődéseket régebben kizárta, de ma már a völgy és a Visszhangdomb beépítése miatt a visszhang hallhatóságát jelentősen romlott. Az 1000 m-es hangút 12-13 szótag visszahallását tette lehetővé. Személyes tapasztalat alapján elmondhatom, hogy különböző időjárási viszonyok között változhat az említett visszhang hallhatósága, amely talán magyarázható a hangsugarak különböző elhajlásával különböző hőmérsékletű levegőrétegekben, de ez inkább szubjektív vélemény.

Az impulzusválasz teszt, amelynek számítógépesítésével a későbbiekben foglakozunk, alkalmas a visszhangok vizsgálatára. Elöljáróban annyit, hogy rövididejű hangjelet keltünk, majd a mikrofonnal felfogott jelsort vizsgáljuk, mégpedig úgy, hogy megállapítjuk az egymás után jelentkező impulzusok időtávolságát, és ezt visszaszámítjuk hangútra. A geometriai távolságok ismeretében, vagy lemérésével a hangutak azonosíthatók, így a visszhangok pontos keletkezési helye megállapítható. Ez az úgynevezett oszcillogramos visszhangmérés, amely igen precíz: centiméter pontos eredmények nyerhetők segítségével.

3.5. A többszörös visszhangok

Szabadtéri visszhangok között vannak ismétlődőek is, ahol a hang két párhuzamos fal között ide-oda verődve sokszorosan hallható. Ha olyan sűrűen jönnek a visszaverődések – tehát olyan kicsi a faltávolság –, hogy külön-külön már nem vehetők észre, de csörgésszerűen észlelhető, akkor csörgő visszhangról beszélünk.

Másik lehetőség, a sorozatvisszhang, amikor a hang nem a két akadály között verődik ide-oda, hanem több, térben egymás után következő akadályrendszerről: oszlopsorról, kerítésről, lépcsőzetesen emelkedő üléssorokról. Az egymás után visszaérkező hangimpulzusok időtávolságát az akadályrendszer elemeinek egymástól való távolsága szabja meg.

Csörgő sorozatvisszhang keletkezik, ha az akadályok egymástól 2-3 m távolságra helyezkednek el. A rendes csörgő visszhang elhalása csak az elnyeléstől függ, a csörgő sorozatvisszhangé az akadályok számától.

Zenei visszhangról beszélünk, ha a hangimpulzus visszaverődéseit 85 cm-rel növekedő akadálysorról halljuk vissza, amikor is az egymás után érkező hangimpulzusok időkésése 1/200 s, vagyis 200 Hz frekvenciájú zenei hangot észlelünk.

3.6. Visszaverődés elnyelő anyagokról

Tudnunk kell, hogy a határfelületre beeső hangenergiának mindig csak egy része verődik vissza. Az ütközéskor bekövetkező energiaegyensúly első közelítésben:

Ibeeső=Ielnyelt+Ivisszavert (3.1)

Az a= Ie/Ib hányados az elnyelési fok, vagy tényező. Minden ütközést követően a továbbhaladó hanghullám intenzitása:

Ivisszavert=Ibeeső(1-a) (3.2)

Az elnyelési tényező az anyag minőségének, szerkezetének és vastagságának jellemzője és függ a hang frekvenciájától is. Az elnyelés mértéke ugyanakkor függ a hanghullám beesési szögétől is.

3.7. Az utózengési folyamat

Az egyszeri hang burkológörbéje kapcsán már beszéltünk a hang elhalásáról. Most vizsgáljuk meg ezt a hangtér szempontjából is.

Az utózengési folyamat vizsgálatára a visszhangoktól mentes, úgynevezett diffúz tér az ideális:

  • a hangenergia sűrűsége a terem minden pontjában azonos,
  • bármilyen elemi térrészbe beáramló és a belőle kiáramló hangenergia egyensúlyban legyen egymással,
  • bármilyen kis kiragadott térrész energiakészlete minden irányban egyenletesen sugárzódjon szét.

Berezgéskor a hangtér energiamentes állapotból épül föl az állandósult állapot eléréséig. A kényszerrezgések elméletéből világos, hogy a felnövekedés a folyamat megkezdésekor rohamosabb, később, mind lassúbb (exponenciális megközelítés). Tehát a hangtér kevés ideig létezik kis energiájú állapotban. Viszont a hang megszüntetésekor az energia az ütközésekből adódó százalékos veszteségek következtében rohamosan kezd csökkenni, de elméletileg sohasem fogy el teljesen, és így a kis energiájú állapot tart hosszú ideig (exponenciális csökkenés). Ennek a jelenségnek a neve a hang elhalása, vagy utózengése.

Hangkártyák programozása retro 4.

2.2.9. Hangforrások és hangok csoportosítási lehetőségei

Hangforrásnak nevezzük azt a helyet, amely a hangot produkálja. Azonban gyakorlati szempontból bonyolultabb a kérdés, mivel a hangforrások általában összetettek.

Általában a következő hangforrásokat különböztetjük meg egymástól:

  • Biológiai hangforrások: emberi és állati hangok,
  • Természeti hangforrások: szélzúgás, mennydörgés,
  • Hangszerek: zenei célokat szolgáló, mechanikai rezgés-átalakítók,
  • Technikai hangforrások: gépek, munkaeszközök, közlekedési eszközök,
  • Elektroakusztikai hangforrások: átalakítók, hangszórók.

Tehát a hangforrás valamilyen hangot „ad ki”. Természetesen ezen hangok is különböző típusúak lehetnek. A hangok fizikai felosztása minőségi szempontból:

1)      Forma szerint:

  • Tiszta hang (szinuszos jellegű),
  • Zenei hang (periódusos jellegű),
  • Zörej (statisztikus jellegű, periódus nélküli),
  • Összetett hang (több azonos, vagy nem azonos formából kevert jellegű),

2)      Az időbeli forma változása szerint:

  • Állandó hang (jellegét hosszabb ideig megtartja),
  • Változó hang (átmeneti jellegű bármilyen szempont szerint),
  • Szakaszos hang (szünetek szakítják meg),
  • Egyszeri hang (hosszabb ideig nem ismétlődik),

3)      Időtartam szerint:

  • Impulzus hang (0,1 s-nál rövidebb),
  • Rövid idejű hang (0,1 és 1 s közötti),
  • Tartós hang (1 s-nál hosszabb),
  • Hosszú idejű (60 s-nál nagyobb).

Szerkezeti szempont alapján történő csoportosítás:

1)      Állandósult:

  • Előre meghatározott (determinisztikus),
  • Szinuszos (tisztahang),
  • Periódusos (zenei hang),
  • Kváziperiódusos,
  • Véletlenszerű (sztochasztikus),

2)      Átmeneti:

  • Folyamatos (kvázisztochasztikus: beszéd, zene),
  • Egyszeri esemény,
  • Berezgés,
  • Lecsengés,
  • Átkötés,

3)      Lökésjellegű

  • Egyszeri (impulzus),
  • Többszöri (impulzussorozat),
  • Periódusosan ismétlődő (kváziperiódusos),
  • Szabálytalanul ismétlődő.

2.2.10. Fourier-analízis

Bár a későbbiekben bőven lesz szó e témáról és annak matematikai alapjairól, mégis fontos, hogy legalább bevezető jelleggel ismertessünk néhány alapvető dolgot, amely szükséges a hanghullámok komolyabb megismeréséhez. Jean Baptiste Joseph de Fourier, francia matematikus 1822-ben bebizonyította, hogy bármilyen periodikus függvény felbontható különböző amplitúdójú és fázisú függvényekre. Vagyis bármilyen periodikus jelfüggvény előállítható különböző szinuszhullámok súlyozott összegeként. A Fourier-analízis segítségével meg tudjuk határozni egy összetett hullám alkotóelemeit. A spektrum egy olyan koordinátarendszer, amelynek függőleges tengelyén az amplitúdót, függőleges tengelyén pedig a frekvenciát ábrázoljuk. Az ábrázolást követően kiolvashatjuk, hogy milyen frekvenciájú és amplitúdójú jeleket kell összegeznünk ahhoz, hogy a kívánt jelhez jussunk. A mesterséges hangzás megvalósításában komoly szerepe lesz a Fourier transzformáltnak. F.2-ben néhány fontosabb függvény és Fourier transzformáltja látható. F.33. pedig egy olyan program listáját közli [4] alapján, amely a négyszögjelet állítja elő különböző szinuszhullámok súlyozott összegeként.

Amplitúdómoduláció: az a jelenség, mely során egy hang amplitúdója valamilyen periódussal kismértékben, de szabályosan változik. A F.2. második ábrája erre mutat példát.

Frekvenciamoduláció: az a jelenség, mely során egy hang frekvenciája kismértékben periodikusan változik.

F.2. Néhány jellegzetes függvény és spektruma

2.2.11. Az egyszeri hang burkológörbéje

Amikor hosszabb ideig nem ismétlődő, egyszeri hangokat vizsgálunk, észrevehetjük, hogy azok a megszólalástól az elhalásig folyamatosan változnak. Ha az amplitúdó változását az idő függvényében ábrázoljuk, megkapjuk az adott hang burkológörbéjét, amely a következő szakaszokra osztható általában:
  • felfutási (attack),
  • lecsengési (decay),
  • kitartási (sustain),
  • elhalási (release).

Tehát a hang amplitúdója felfut egy bizonyos szintre, majd lecseng, és a kitartási szinten marad, ezt követően pedig elhal. Természetesen nem csak a fenti szakaszokkal rendelkező úgynevezett ADSR burkológörbe létezik, hanem olyan burkológörbe is, amely ennél lényegesebb több szakasszal jellemezhető. Annyi biztos, hogy minimálisan egy felfutási és egy lecsengési szakasz alkotja az egyszeri hang burkológörbéjét. A burkológörbe jellegzetes szakaszait a F.3-ban tanulmányozhatjuk.

F.3. Az egyszeri hang burkológörbéjének egyes szakaszai

2.3. Bevezetés a fiziológiai hangtanba

Ebben a részben áttekintjük a hang szubjektív érzékelésével kapcsolatos alapokat.  Jellemző a hangtan eme ágára, hogy eredményeit főleg statisztikai módszerek felhasználásával produkálja, ami azt jelenti, hogy rengeteg, lehetőleg különböző, emberen kell elvégezni az adott kísérleteket, s csak azt követően lehet komolyabb következtetéseket levonni. Meg kell jegyezni, hogy a hangok szubjektív érzékelése nem követi a Fechner-Weber-féle logaritmikus törvényt, tehát a szintekkel kifejezett növekedést.

Az egyik kísérlet a hang legfontosabb érzeti jellemzői közül a hangossággal kapcsolatos. Az első mérések végzői után Fletcher-Munson görbéknek nevezték el az azonos hangossági szintek görbéit, melyeket többszőr is megmértek és szabványosítottak. Ezen karakterisztikák az alábbi feltételek teljesülése esetén érvényesek:

  • a hangforrás frontálisan helyezkedik el a hallgatóhoz képest,
  • a hallgató akusztikai szabadtérben van és a hanghullámok síkhullámként érkeznek hozzá,
  • a hangnyomást a megfigyelés helyén, a hallgató távollétében mérik,
  • a hallgatás mindkét füllel egyszerre történik,
  • a hallgató kora 18 és 25 év közötti, és normális hallású.

Az azonos hangossági szintek görbéinél 100 Hz-es frekvenciát választottak. Az egyéb frekvenciákon ugyanakkora hangossághoz szükséges hangnyomásszinteket meghatározták és az így kapott pontokat összekötötték. Az 1000 Hz-en megállapított szintet rendelték ehhez a görbéhez, de ez már a szubjektív érzetet tükröző hangosságszint, ezért megkülönböztetésül 1 phon a mértékegysége. Az 1000 Hz-en érvényes p=p0 –nak megfelelő 0 dB-es vonatkoztatási szinthez tartozó hangosságszint 0 phon. Az új mérések szerint a hallásküszöb 1000 Hz esetén 4,2 dB-el egyenlő, szemben a vonatkoztatási értékhez tartozó 0 dB-el, azaz a korábban meghatározott küszöbértékkel. Ezért ajánlatos különbséget tenni a küszöbérték és a vonatkoztatási érték között.

2.3.1. A hang szubjektív magassága

A hang szubjektív magasságának egysége: 1 mel. 1000 mel: 1000 Hz frekvencián, 40 phon hangosságszintnél. 500 mel szubjektív magasságúnak tekintjük azt a frekvenciát, amelynél az átlagos normális hallású ember fele akkora zenei magasságúnak érzékeli a hangot az eredeti 1000 Hz-es hanghoz képest. Hasonlóképpen 2000 mel szubjektív magasság azt a frekvenciát jelenti, amelynél a szubjektív megítélés kétszeres hangmagasságot állapít meg. A hang szubjektív magassága némileg függ az adott hang hangosságától is. Például 100 Hz-en 40 phon-ról 100 phon-ra növelve a hangosságot, a szubjektív hangmagasság körülbelül 10%-kal csökken, vagyis egy teljes zenei hanggal. Ezzel szemben körülbelül 4 kHz fölötti magas hangoknál a hangosság növekedésekor a hang szubjektív magassága is növekszik.

2.3.2. A fül iránykarakterisztikája

A hangforrás irányának megállapításakor az irányhatás és a két fülre érkező hang időkülönbsége, illetve fáziskülönbsége is érvényesül. Az irány-lokalizálásban jelentős szerepe van a Haas-féle jelenségnek, amely abban nyilvánul meg, hogy a hangforrás helyét aszerint állapítjuk meg, hogy honnét érte fülünket az első hullámfelület. A következő hullámokat már 10dB-lel nagyobb intenzitással és 50 ms-nyi késéssel is érkezhetnek anélkül, hogy az eredeti irányérzékelést helyesbítenénk.

Több hang egyidejű hangzásakor a fülben további, úgynevezett aurális hangok is jelentkeznek, amelyek az eredeti hangok összegéből, illetve különbségéből, egyéb kombinációiból jönnek létre, és a fül nem lineáris átviteli karakterisztikái okozzák. Ezek annál inkább érvényesülnek, minél nagyobb az eredeti hangok hangossága és minél mélyebbek az eredeti hangok. Ha egyszerre több harmonikus hangjel éri el a fülünket, akkor az erősseb jel elfedi a gyengébbet, mert a fül érzékenysége csökken a gyengébb hangra vonatkozóan.

Ha például 1200 Hz közepes frekvenciájú keskenysávú zörejt hozunk létre, és ennek különböző intenzitásaihoz mérjük a többi frekvenciájú harmonikus (szinuszos) jel hallhatósági küszöbét, azt tapasztaljuk, hogy az 1200 Hz körüli tartományban csökken a hallószerv érzékenysége. Az így megállapított küszöbkarakterisztikát elfedési küszöbnek nevezzük. Ez a hatás az elfedő hang intenzitásától is függ.

Sok egyidejű hang eredő hatása bonyolult módon összesíthető. Közelítőleg azt mondjuk, hogy az erősebb hang elfedi a gyengébbet, ha a szintje 10 dB-lel nagyobb és frekvenciájuk 1 oktávon belül van. A mélyebb hangoknál az elfedés nem lépi túl az oktávnyi szélességet, de a magasabb hangoknál az elfedő hang nagyobb intenzitása esetén az elfedési jelenség a legmagasabb hallható frekvenciáig terjed.

Időtartományban is jelentkezik elfedési jelenség. Azt könnyű belátni, hogy nagy intenzitású hangot közvetlenül követő kis intenzitású hang nem hallatszik, mert az erős hang süketít. Azt azonban nehéz felfogni, hogy egy halk hangot elfedhet egy később megszólaló erős hang, mert ez látszólag ellentétes a kauzális rendszerek viselkedésével. De természetesen ennek is megvan a maga ésszerű magyarázata: az erősebb hang ingerülete gyorsabban terjed a hallószervben és az idegekben. Még lineáris rendszer esetén is belátható, hogy ez így történik.

2.3.3. Hangosság

Sem a hangnyomás, sem annak szintje, de még a hangosság szintje sincs összhangban a hangosság szubjektív érzékelésével. Ezek a jellemzők fizikai szempontból jól definiálhatók, de a szubjektív módon érzékelt hangosság ezekkel nem egyezik meg. Tekintettel az elfedési jelenségekre, a hangosság egy egyszerűen nem mérhető, és a phonokban mért azonos hangosságszintekkel csak akkor mutatható ki az egyezés, ha harmonikus hangok esetén mérjük a hangosságot. Például a zajok csaknem mindig összetettek, mivel több, különböző frekvenciájú komponens eredőjeként jönnek létre. Ezért a hangosság számszerű kifejezéséhez a zajspektrumot fel kell bontani adott frekvenciákra, külön kell mérni minden frekvenciát, vagy frekvenciasávot, és csak ezt követően kaphatjuk meg a végeredményt.

Ha szubjektív kísérleteknél azt a kérdést tesszük fel, hogy milyen hangmagasságon érzékel az átlagos megfigyelő az 1000 Hz frekvenciájú hangnál kétszer hangosabbat, akkor eljutunk a phonokban kifejezett hangosságszint és a sonokban megadható hangosság közötti összefüggéshez:

(2.14)
ahol N a hangosság és LN a hangosságszint.

2.3.4. Hangszín

A természetben leginkább összetett hullámokkal találkozhatunk. A hangokat a hangszín szerint lehet megkülönböztetni. A hangszín nem pontosan definiált fizikai fogalom, kimondottan szubjektív érzékelésen alapszik. Gyakorlatilag a hang összetevőinek és átmeneti jelenségeinek, különböző modulációinak összességét nevezhetjük hangszínnek. Látható tehát, hogy rengeteg dologtól függ: mechanikai kialakítástól, a hangkeltő eszköztől, az anyag tulajdonságaitól.

[4]   László József: Hangkártya programozása Pascal és Assembly nyelven, Computer Books, Budapest, 1999

Hangkártyák programozása retro 3.

2.2. A hang alapvető fizikai jellemzőinek rövid áttekintése

2.2.1. Hangsebesség

A hangterjedési sebessége nem más, mint a hanghullám terjedési sebessége adott közegben, a hangsugár irányában. Ez a jellemző nagymértékben függ a hőmérséklettől és az alábbi képelttel számítható:

(2.1)

amelyben:

  • a a hangsebesség m/s-ban,
  • k az adiabatikus kitevő,
  • R a gázállandó m2/s2K-ben és
  • T a közeg abszolút hőmérséklete K-ben.

A levegőre vonatkozó hangterjedési sebesség a következő módszerekkel számolható ki:

al=20,05(T)1/2 [m/s] (2.2)

al=331,8+0,60u [m/s] (2.3)

ahol u a hangtér (általában terem) hőmérséklete C°-ban.

2.2.2. Hangnyomás

A hangnyomás a hang terjedésekor a barometrikus nyomásra szuperponált váltakozó nyomás, és így a barometrikus nyomás nyugalmi értékétől való eltérést fejezi ki. Ennek időbeli változása p0+p(t) alakban jelentkezik, ahol a hangot kifejező második tag valamilyen nyomásváltozást lejegyző berendezéssel rögzíthető, azonban időbeli középértéke zérus. A gyakorlatban mindig a hangnyomás – adott időtartamra vonatkoztatott – effektív


értékét adjuk meg Pa-ban (2.4). A hangnyomás vonatkoztatási értéke: p0=2×10-5Pa=20mPa.

(2.4)

2.2.3. Frekvencia

Az egy másodperc alatti rezgések számát jelenti. A frekvencia a periódusidő reciproka, és ez fordítva is igaz:

T=1/f [Hz] (2.5)

Frekvenciáról csak periodikus (harmonikus) rezgőmozgás esetén illik beszélni. A legegyszerűbb harmonikus rezgés a szinuszos rezgés, amely például egy hangvilla segítségével előállítható. (Lásd F.1.) A hangtanban gyakran használjuk két frekvencia viszonyszámának kifejezésére az oktávot, illetve a dekádot. Az oktáv nyolc egészhangnyi eltérést jelent. Az oktávok egymáshoz való frekvenciaviszonya ½, és mindegyik oktávban tizenkét hang található. Ezek között vannak egész és félhangok egyaránt, amelyek egymástól félhangnyira helyezkednek el. Két szomszédos hang frekvenciaviszonya állandó:

(2.6)

Használatos a frekvenciák 1:10-hez viszonya is (dekád), főleg a Bode-diagram esetében.

2.2.4. Amplitúdó

A rezgések legnagyobb kitérési értéke, tájékoztatást ad a hullám erősségéről, arról az energiáról, amit hangerőként érzékelünk. Minél nagyobb egy hang amplitúdója, annál hangosabbnak halljuk. (Lásd F.1.)


F.1.  Egyszerű szinuszos rezgés jellemzése

2.2.5. Hullámimpedancia

A hangnyomás és a részecskesebesség abszolút értékének hányadosa, amely síkbeli hullám esetén (elhanyagolható a fáziseltolódás, mivel elég nagy távolságra van a hangforrás) a hangellenállással egyenlő, ami a hangsebesség és a közegsűrűség szorzata:

z=p/|vr|=ar [sPa/m] (2.7)

A levegő hullámellenállása 20°C hőmérsékleten: 408[Pa ms-1].

2.2.6. Hangintezitás

A hangtér intenzitása a hanghullám terjedési irányára merőleges felületre vonatkozó felületi energiasűrűség. Síkban terjedő hullámmozgás esetén – azaz a hangforrástól számított nagy távolságban, amikor a hang terjedése akadálytalan – a hangintenzitás a hangnyomás és a részecskesebesség abszolút értékének szorzata:

I=p|vr|=pnr [W/m2] (2.8)

A közeg hullámellenállásának bevezetésével:

I=p2/z0=nr2z0 [W/m2] (2.9)

A hangforrás közelében, ahol a hangnyomás és a részecskesebesség fáziseltérése jelentős:

I=pnrcosj [W/m2] (2.10)

Meg kell jegyezni, hogy szabad térben mindig feltételezhető a hanghullám sík terjedése, vagyis hogy cosj=1. A hangintenzitás vonatkoztatási értéke: I0=10-12W/m2.

2.2.7. Hangteljesítmény

A hangforrás elsődleges adata a hangteljesítmény: a teljes térbe az időegység alatt kisugárzott energia. A hangintenzitás és a felület szorzataként számolható:

P=IS [W] (2.11)

Ha a hangforrás síksugárzó, a távolságtól függetlenül mindig ugyanakkora felületen jelenik meg hangenergia. Pontszerű, minden irányban egyenletesen sugárzó forrás teljesítményének felületi szétterjedése a távolság négyzetével növekszik. Az esetek többségében a hangforrás úgynevezett féltérben helyezkedik el, vagyis az S felületet félgömbnek tekintjük. A hangforrás általában nem egyenletesen sugároz minden irányba és ilyenkor felületi integrálást kell alkalmazni, vagy iránytényezővel korrigálni. Érdemes megemlíteni, hogy például kisméretű hangforrások hangteljesítményének meghatározásánál részesítik előnyben a féltéri vizsgálatokat szabadtéri vizsgálatokkal szemben.


2.2.8. Hangszint

Esetünkben a szint nem más, mint egy adott összehasonlító érték hányadosának a logaritmusa. Más élettani jelenségekhez hasonlóan itt is érvényes a Fechner-Weber-féle törvény, amely szerint a minimálisan észlelhető intenzitásváltozás függ az intenzitásváltozás nagysága és a kiinduló intenzitás közötti viszonytól. Ebből levezethető, hogy az okozat intenzitása arányos az ok intenzitásának logaritmusával. A hangintenzitás szintjének definíciója:

(2.12)


A hangnyomás és a hangintenzitás közötti kapcsolat alapján (2.9), meghatározható a hangnyomásszint:

(2.13)


Hangkártyák programozása retro 2.

2. Hangtani alapfogalmak

2.1. A hang, mint jelentéshordozó médium

Fontos, hogy elsőként tisztázzuk, mit is értünk „hang” alatt, hiszen ez a fogalom is olyan hétköznapinak és egyszerűnek tűnő, hogy amikor meg kell fogalmaznunk miről van szó, szinte  zavarba jövünk  és elgondolkodunk a pontos definíción. Amire biztos, hogy utoljára gondol a hétköznapi ember az a hangnak, mint fizikai jelenségnek  a magyarázata. Pedig az elsősorban nem más, mint valamilyen rugalmas közeg mechanikai zavarási állapota, amely a közeg rugalmassága miatt az energia támadási helyén rezgés alakjában jelentkezik és a hatás a rendelkezésre álló térben továbbterjed. A zavarási állapot gáznemű és folyékony közegben nyomásingadozás, míg szilárd testekben lényegesen bonyolultabb alakváltozási folyamat.

Természetesen az embernek elsőként az jut eszébe, hogy „hang az, amit hallunk”. Az ilyen, élettani szempontú megközelítés pontosabb, tudományos megfogalmazása:  „a hang füllel érzékelhető inger ”. Következésképpen a hang fizikai szempontból sokkal tágabb fogalom, hiszen vannak olyan hangok amit például semmilyen módon nem vagyunk képesek érzékelni.

Azonban a hanghoz nem egyszerűen biológiai szempontból viszonyulunk, vagyis, hogy „hallom-e a hangot, vagy sem”, hanem annak fontos szerepe van a kommunikációban, s az információszerzésben. Hangélményről beszélünk, amikor megkülönböztetjük a fizikai és az emberi fül által hallható hangokat. Látható tehát, hogy a hang számunkra fontos értelmi, érzelmi és esztétikai hatással is bír, mint jelentéshordozó médium.

Fizikai szempontból az általunk „zaj”-nak nevezett és ártalmasként, zavaróként, valamint kellemetlenként jellemzett hang sem más, mint a többi. Az informatív hanggal szemben az ember számára a zaj redundancia, mindamellett, hogy bizonyos típusai akár komoly sérüléseket is okozhatnak – gondoljunk csak a zajártalomra, s a halláskárosodásra. Ahol nincs élőlény, ott értelmetlen a hangérzet és a zaj fogalma.

Érdekes, hogy míg az egyik személy számára egy bizonyos hang információt hordoz, addig a másikat éppenséggel zavarhatja. Ilyen például a házőrző kutya vad ugatása, ami a szomszéd számára zavaró lehet, főleg éjszaka, viszont a tulajdonos számára fontos jelzés, hogy illetéktelen személy kísérel meg behatolást az adott területre. Ha esztétikai szempontból vizsgáljuk a kérdést, más és más a megítélése egy zeneműnek, s ebben szerepe van a különböző ízléseknek is. Azonban lehetséges, hogy egy dal, amit egyébként kedvelek, nem tetszik annyira, ha egy kis szobában bömböl. De akár említhetnénk egy szónoklatot is, amely egyeseket lelkesít, mások közömbösek iránta, megint másokban félelmeket kelt.

Látható tehát, hogy a hangot legalább kétféle szempontból érdemes alaposan tanulmányozni. Az egyik egy természettudományos matematikai és fizikai szempont, a másik egy inkább emberközpontú, úgynevezett fiziológiai szempont.

Folytatjuk.

Hangkártyák programozása retro 1.

Előszó

Régi dolgaim között kutakodva, kezembe került az első dimplamunkám, amelyen pont tíz évvel ezelőtt dolgoztam. Eredeti címe Hangkártyák alkamazása a műszaki akusztika területén volt és arra gondoltam, hogy bár azóta sokat fejlődött a hardverpiac, sorozatban publikálom, mivel aki hardver közeli programozással, netán beágyazott rendszerek fejlesztésével szeretne foglalkozni, annak nagyon jó alapokat adhatnak a bemutatott példák.

Nagyon örülnék, ha az egyes fejezetkehez érve lenne olyan programozó szakember, aki a régi példákat újakkal egészítené ki a hozzászólásokban.

Nos, akkor vágjunk bele!

1. Bevezetés

Az 1990-es évek kezdetekor ritkaságszámba ment, ha egy személyi számítógép rendelkezett hangkártyával, olyan eszközzel, amely valódi hangzást tudott produkálni a beépített hangszóró alapvető és nehézkes hangkeltési lehetőségeivel szemben. Később megjelentek a különböző szabványok, amelyek már előírták az említett eszköz jelenlétét, ezáltal úgynevezett multimédiás környezetet biztosítva az egyre igényesebb felhasználók számára.

Rengeteg játék-, oktató-, zeneszerkesztő- és lejátszó-program jelent meg azóta is a piacon, s ez a gyártókat arra ösztökélte, hogy egymással versengve a jobb hangzás érdekében egyre fejlettebb hangkártyákat készítsenek, bővítve egyben azok egyéb lehetőségeit is. Bár már az 1980-as évek óta foglalkoztak a gondolattal és a gyakorlatban is számos kísérlet létezett az akusztikai mérések, illetve kísérletek számítógépesítésével kapcsolatosan, a viszonylag olcsó hangkártyák alkalmazása nem igazán terjedt el ilyen, vagy más jellegű (például biztonságtechnikai, egészségügyi, környezetvédelmi stb.) alkalmazások területén, talán azért, mert felhasználhatóságukat eléggé behatárolta felépítésük, fejlettségük és elsődleges rendeltetésük. Meg kell azonban említeni, hogy a gyártók és a programozók sem nyitottak komolyan egy ilyenfajta felhasználás lehetősége felé, hiszen maga a hangkártya szakirodalom is inkább szórakoztató jellegű alkalmazások fejlesztésével foglalkozik. Különböző akusztikai célhardverek természetesen léteznek, de tömeges felhasználásuk nem jellemző, mivel drágák. Azonban a legújabb hangkártyák már megcélozzák az akusztikai jellegű felhasználást is, például a mesterséges hangterek hardveres kialakítása kapcsán, ehhez igazítva a lehetőségeket. Manapság (2000-ben) már egy átlagos hangkártya, amely CD minőségű hangzást tud produkálni és rengeteg lehetőséggel rendelkezik, egy hétköznapi felhasználó számára is elérhető, viszonylag olcsón. Így egy hangkártyán alapuló mérőrendszer megalkotása szinte fillérekből megvalósítható, még akkor is, ha hozzávesszük a szükséges tartozékokat (mikrofon, hangdoboz, stb.), tehát akár mindez házilag is kialakítható.

E dolgozat célja, hogy bemutassa a piacon jelenleg fellelhető hangkártyák széles skáláját, rövid összehasonlítást készítsen azok felépítéséről, műszaki paraméteriről és lehetőségeiről, majd a programozhatóságot, illetve a funkcionális egységek összehangolását szemléltesse egy konkrét gyakorlati példán keresztül, mely szorosan kapcsolódik az akusztikai mérésekhez, vagyis megismerkedhetünk a hangkártyáknak egy, az eddigiektől eltérő alkalmazásával.

Az általam választott példa a teremakusztikában jól ismert impulzusválasz teszt számítógépesítésének bemutatása, illetve az impulzusválasz ismeretében úgynevezett mesterséges hangtér kialakítása, vagy más néven mesterséges hangzás megvalósítása szoftveres úton. Ezért a dolgozat minden egyes fejezete arra koncentrál, hogy egy süketszobában felvett, visszhangoktól és utózengéstől mentes hanganyagot olyanná tegyünk, mintha az mondjuk a Mátyás templomban szólalna meg.

Folytatjuk.