P R O G R A M U J E M EDelfinárium / 12. èas : Spä k databázam III.Tentoraz sa opä budeme zaobera databázami. Povieme si nieèo o metóde BatchMovea komponente TBatchMove a ukážeme si, ako prenáša údaje z jedného databázovéhoformátu do druhého.METÓDA BatchMove PODROBNEJŠIE. Možno si spomí<strong>na</strong>te, že tútometódu sme pred mesiacom použili, keï sme potrebovali uloži množinu dát, ktorúvytvoril dopyt SQL. Vtedy som to síce nezdôraznil, no metóda BatchMove vie by nesmiernešikovným pomocníkom. V predchádzajúcom èlánku som už spomenul, že jej primárnouúlohou je presúva dáta, zvládne však aj iné „kú<strong>sk</strong>y“. Prvým parametrom tejto metódyje zdroj dát, ktorým je komponent odvodený od triedy TBDEDataSet. Najznámejšímipotomkami tejto triedy sú komponenty TTable a TQuery. Manipulácia s dátami ovplyvnívždy tú tabu¾ku, ktorá metódu BatchMove volá. Druhý parameter metódy BatchMove jetypu TBatchMode a môže <strong>na</strong>dobúda <strong>na</strong>sledujúce hodnoty:l batAppend – táto hodnota z<strong>na</strong>èí, že dôjde k pripojeniu zdrojových údajov k novejtabu¾ke. Štruktúra polí ani indexy zdrojovej tabu¾ky zmenené nebudú, ale poradie polí vzdrojovej i cie¾ovej tabu¾ke musí by rov<strong>na</strong>ké, pretože v opaènom prípade dôjde k<strong>na</strong>rušeniu integrity dát. Inými slovami, ak je <strong>na</strong>príklad v cie¾ovej tabu¾ke prvé pole typuinteger a v zdrojovej tabu¾ke je prvé pole typu string, tento re azec bude jednoduchoprenesený do èíselného po¾a.Parameter batAppend je užitoèný vtedy, ak máme dve tabu¾ky so zhodnou štruktúrou,ale jed<strong>na</strong> z nich obsahuje novšie záz<strong>na</strong>my, ktoré by sme radi presunuli do cie¾ovej tabu¾ky.l batAppendUpdate – pracuje podobne ako batAppend, len s tým rozdielom, že docie¾ovej databázy nielenže pridá <strong>na</strong>jnovšie záz<strong>na</strong>my, ale dôjde aj k aktualizácii týchzáz<strong>na</strong>mov cie¾ovej tabu¾ky, ktoré sa v zdrojovej tabu¾ke zmenili. Inými slovami, novézáz<strong>na</strong>my sa pridajú a staršie sa aktualizujú.l batCopy – dôjde k úplnému zániku pôvodnej štruktúry tabu¾ky, stratia sa pôvodnépolia aj indexy. Lenže pozor, ak je zdrojom údajov komponent TTable, ktorý obsahujenejaké indexy, treba poèíta s tým, že tieto indexy do cie¾ovej tabu¾ky nebudú prenesené.l batDelete – záz<strong>na</strong>my, ktoré sa <strong>na</strong>chádzajú aj v zdrojovej, aj v cie¾ovej tabu¾ke, budú vymazané.l batUpdate – v cie¾ovej tabu¾ke budú aktualizované tie záz<strong>na</strong>my, ktoré sa <strong>na</strong>chádzajú ajv zdrojovej tabu¾ke.Samozrejme, <strong>na</strong> základe takýchto všeobecných informácií nie je možné porozumie ,kde sa <strong>sk</strong>rýva potenciál metódy BatchMove, preto si všetko vysvetlíme <strong>na</strong> príklade.Strýko Držgroš má dve kníhkupectvá. Keïže je to ve¾mi sporivý káèer a nerád vyhadzujepeniaze <strong>na</strong> výplaty, v prvom sedí on sám a v druhom vysedáva jeho synovec Huey.Evidencia predaných kníh sa vedie v jednoduchej tabu¾ke typu Paradox (Držgroša ani lenne<strong>na</strong>padne, aby investoval do nejakého rozumného databázového riešenia). Na boháèovompoèítaèi je hlavná databáza a <strong>na</strong> poèítaèi jeho synovca je umiestnená druhá databáza. Veèer,keï sa obchod zatvára, Huey musí donies záz<strong>na</strong>my o predaných knihách <strong>na</strong> di<strong>sk</strong>ete (pravda,mohol by ich posla cez internet, Držgroš však šetrí aj <strong>na</strong> tom). Držgroš si údaje zladí tak,aby sa záz<strong>na</strong>my z poboèky (teda kníhkupectva, kde pracuje Huey) pripojili k tým, ktoré máon vo svojom hlavnom súbore. Po tomto úkone sa obaja poberú spa .Vytvorme si teraz aj my podobnýmodel. Prvá tabu¾ka sa bude volaDRZGROS.DB a druha HUEY.DB. Vprvej vytvoríme primárny index,keïže pri operácii batAppendUpdatemusí by cie¾ová tabu¾ka indexovaná.Obr. 1Aby bola štruktúra oboch tabuliek èo<strong>na</strong>jjednoduchšia, budú tabu¾ky obsahovaiba dve polia. Obe polia budúre azcové s dåžkou 30 z<strong>na</strong>kov,prièom prvé sa bude vola Nazov(teda názov knihy) a druhé Predal(t. j. kto knihu predal). Do každejObr. 2tabu¾ky pridáme dva záz<strong>na</strong>my ešte vDatabase De<strong>sk</strong>tope (obr. 1 a 2).Potom vytvoríme nový formulár, ktorý bude pomocou komponentu TDBGrid zobrazovaobsah súboru DRZGROS.DB. Vytvoríme dva komponenty TTable, prvý <strong>na</strong>zveme Drzgros adruhý Huey, oba budú pracova s rovnomennými databázami. Potom pridáme tlaèidlo,ktoré <strong>na</strong>zveme Prenos.Pred <strong>na</strong>písaním kódu si ešte raz zopakujme, v èom spoèíva náš problém: Huey priniesolsvoje záz<strong>na</strong>my <strong>na</strong> di<strong>sk</strong>ete a my ich musíme prida k Držgrošovej hlavnej databáze.Naš astie nie je to nijaký problém:procedure TForm1.PrenosClick(Sender: TObject);begintrytryScreen.Cursor:=crHourGlass;with drzgros dobeginClose;BatchMove(Huey,batAppendUpdate);Openend;//withexceptraiseend;//exceptfi<strong>na</strong>llyScreen.Cursor:=crDefault;end;//fi<strong>na</strong>llyMožno si budete klás otázku, preèo som použil parameter batAppendUpdate, keï somdoteraz celý èas hovoril iba o pridávaní. Mohli sme teoreticky použi parameter batAppend,to by však so sebou prinieslo aj jednu ve¾kú nevýhodu: keby sa náhodou Huey „uklepol“ azadal by nesprávny názov knihy, preniesla by sa chyba aj do Držgrošovej databázy. A èo je eštehoršie, nielenže by sa tam preniesla, ale by tam aj ostala, kým by ju starý káèer neopravil. Hueyby preklep vo svojej databáze opravoval <strong>na</strong>darmo, pretože zmeny by sa v Držgrošovom súboreaj tak neprejavili vzh¾adom <strong>na</strong> to, že program by do úvahy bral iba nové záz<strong>na</strong>my (teda dohlavnej databázy by sa pridali <strong>na</strong>jnovšie záz<strong>na</strong>my, tie staré by však ostali nedotknuté). Akpoužijeme parameter batAppendUpdate, nielenže sa nové záz<strong>na</strong>my pridajú, ale dôjde aj kaktualizácii tých starších.Nadmieru šetrný káèer môže metódu BatchMove dokonca aj zneuži . Predstavme si,že jedného dòa Huey donesie nové údaje. Držgroš však chce ušetri <strong>na</strong> jedle (a <strong>na</strong>turáliách,v ktorých svojho synovca vypláca), a tak použije uvedený kód s malou úpravou:miesto parametra batAppendUpdate použije batDelete. Iste tušíte, aký bude výsledok:všetky záz<strong>na</strong>my v hlavnej databáze, ktoré pochádzajú od Hueya, sa vymažú. Držgrošpotom môže svojmu synovcovi tvrdi , že vlastne niè nepredal, a teda veèeru si nezaslúži.KOMPONENT TBatchMove. Problémy s presunom, aktualizáciou èi mazanímdát je možné rieši aj pomocou komponentu TBatchMove. Slúži <strong>na</strong> rov<strong>na</strong>ké úèely akometóda BatchMove, ale je <strong>na</strong> úlohu presúvania, vymazávania èi aktualizovania dát podstatnelepšie pripravený než uvedená metóda. Umožòuje <strong>na</strong>príklad používate¾ovi zadanázov tabu¾ky, do ktorej sa majú umiestni záz<strong>na</strong>my, ktoré sa z nejakého dôvodu nepodariloprekopírova do cie¾ovej tabu¾ky. Môže <strong>na</strong>príklad <strong>na</strong>sta prípad, že sa v cie¾ovej ajzdrojovej tabu¾ke <strong>na</strong>chádzajú rov<strong>na</strong>ké záz<strong>na</strong>my. Pri pokuse o kopírovanie do cie¾ovejtabu¾ky <strong>na</strong>stane výnimka Key violation, pretože daný záz<strong>na</strong>m už v cie¾ovej tabu¾ke existuje.V takom prípade tento záz<strong>na</strong>m komponent TBatchMove presunie do vami urèenejtabu¾ky. Môžete síce toto <strong>na</strong>stavenie ignorova , potom sa však nedozviete, ktorý záz<strong>na</strong>mproblém spôsobil.Okrem toho komponent umožòuje definova aj tabu¾ku pre také záz<strong>na</strong>my, ktorénemohli by spracované z dôvodu nekompatibility polí. Predstavte si, že v cie¾ovejtabu¾ke definujete pole s názvom Meno, ktoré má dåžku 30 z<strong>na</strong>kov, zatia¾ èo v zdrojovejtabu¾ke je to isté pole, ale dlhé 35 z<strong>na</strong>kov. Všetky záz<strong>na</strong>my, v ktorých meno osoby presahuje30 z<strong>na</strong>kov, budú potom presunuté do <strong>na</strong>mi špecifikovanej tabu¾ky. Výhodytakéhoto postupu sú rov<strong>na</strong>ké ako v predchádzajúcom prípade: ak sa vy<strong>sk</strong>ytne problém,vždy je dobré vedie , ktoré záz<strong>na</strong>my ho spôsobili.Ve¾kým prínosom komponentu TBatchMove (a aj metódy BatchMove) je to, žeumožòuje konverziu medzi rôznymi typmi databáz. Ak ste teda práve migrovali zParadoxu <strong>na</strong>príklad <strong>na</strong> MS Access a nechcete prís o staré údaje, bude pre vás tentokomponent ve¾mi užitoèným nástrojom. Mnohých z vás teraz možno <strong>na</strong>padne, že sitrošku pocvièíte prsty a <strong>na</strong>píšete si databázový konvertor. Žia¾, to ani zïaleka nie je takéjednoduché. V urèitom zmysle konverzia databáz možná je, konvertova možno všakiba tie <strong>na</strong>jjednoduchšie tabu¾ky bez indexov. Ak som spomí<strong>na</strong>l prechod z Paradoxu <strong>na</strong>Access, mal som <strong>na</strong> mysli prenos údajov, èo má ku konvertovaniu databázy ve¾mi ïaleko.Databáza totiž nezahàòa iba údaje, ale aj indexy, definíciu referenènej integrity a vzávislosti od databázového systému aj rôzne iné dáta (<strong>na</strong>pr. uložené procedúry). Nie je<strong>na</strong>príklad možné prekonvertova do formátu dBase takú paradoxovú tabu¾ku, ktoráobsahuje primárny index, tie totiž dBase nepodporuje. Preprava dát možná je, ale indexya ostatné dôležité údaje vz ahujúce sa k definícii tabu¾ky je potrebné po konverziidoplni ruène.Ïalšou ve¾mi užitoènou vlastnos ou komponentu TBatchMove je možnos mapovaniapolí. Pri presune dát môže <strong>na</strong>sta problém, keï poèet a typy polí v cie¾ovej i zdrojovejtabu¾ke sú síce rov<strong>na</strong>ké, ich poradie sa však v zdrojovej i cie¾ovej tabu¾ke líši, a preto saúdaje môžu dosta <strong>na</strong> nesprávne miesto. Ak je <strong>na</strong>príklad v cie¾ovej tabu¾ke prvé pole typustring a v zdrojovej tabu¾ke typu integer, budú celé èísla „<strong>na</strong>tvrdo“ vložené do re azcovéhopo¾a. Riešením problému je použitie mapovania, vtedy totiž vieme presne urèi ,ktoré pole sa kam premietne.Uvedený problém si, samozrejme, budeme demonštrova <strong>na</strong> príklade. Vytvoríme program,ktorý nám prekonvertuje údaje z tabu¾ky ANIMALS.DBF do formátu Paradox. Násvšak budú zaujíma iba dve polia: názov zviera a a oblas , v ktorej zviera žije. Aby sme sito ešte viac <strong>sk</strong>omplikovali, vytvoríme takú paradoxovú tabu¾ku, ktorá bude obsahova sloven<strong>sk</strong>énázvy polí (<strong>na</strong>zveme ju ZVER.DB). Ako ste už iste vydedukovali, prvé pole sa budevola Meno (ve¾kos 10 z<strong>na</strong>kov) a druhé Oblast (ve¾kos 15 z<strong>na</strong>kov). Rozmer druhého po¾a(èiže po¾a Oblast) som zámerne <strong>sk</strong>rátil (v pôvodnej tabu¾ke má toto pole dåžku 20 z<strong>na</strong>kov),aby sme si mohli demonštrova prácu s problémovými záz<strong>na</strong>mami.128 PC REVUE 10/2001
P R O G R A M U J E M EVytvorme teda nový formulár, ktorého obyvate¾mi budú komponent TBatchMove,komponent TTable, <strong>na</strong>zvaný Zdroj, a komponent Ttable, <strong>na</strong>zvaný Ciel. VlastnosTableName tabu¾ky Zdroj <strong>na</strong>stavíme <strong>na</strong> ANIMALS.DBF. Vlastnos Source komponentuTBatchMove <strong>na</strong>stavíme <strong>na</strong> Zdroj (ako vidíme, do vlastnosti Source nestaèí jednoduchovpísa cestu k databáze, ako by možno niektorí z vás oèakávali, vždy musíme zada komponenttypu TTable). Podobne budeme postupova aj s cie¾ovou tabu¾kou: vlastnosTableName <strong>na</strong>stavíme <strong>na</strong> Zver; vlastnos Desti<strong>na</strong>tion komponentuTBatchMove <strong>na</strong>stavíme <strong>na</strong> Ciel. Vlastnos Mode<strong>na</strong>stavíme <strong>na</strong> batAppend. Do vlastnosti ProblemTableNamevpíšeme cestu tabu¾ky, do ktorej sa majú uložiproblémové záz<strong>na</strong>my, a vlastnos AbortOnProblem <strong>na</strong>stavíme<strong>na</strong> false. Vlastnos ProblemTable <strong>na</strong>stavíme <strong>na</strong>C:\PROBLEM.DBF. Mapovanie polí <strong>na</strong>teraz vynecháme, abysme si ukázali, èo toto vynechanie spôsobí. Na komponentTBatchMove klikneme pravým tlaèidlom a z kontextovéhoObr. 3menu vyberieme Execute, èím sa spustí presun dát.Výsledný súbor, samozrejme, nie je správne usporiadaný(pozri obr. 3). S prvým po¾om nie je nijaký problém, pretože meno živoèícha je <strong>na</strong>prvom mieste aj v databáze ANIMALS.DBF, aj v <strong>na</strong>šej databáze. Druhé pole je však vzdrojovej a cie¾ovej databáze iné, v zdrojovej databáze druhé pole udáva ve¾kos živoèícha,zatia¾ èo v cie¾ovej to má by oblas , v ktorej zviera žije. Problém vyriešime kliknutím<strong>na</strong> vlastnos Mappings a <strong>na</strong>definovaním mapovania polí (obr. 4). Mapovanie polí jemožné <strong>na</strong>definova ve¾mi jednoducho, a to <strong>na</strong> základe vzorca PoleCie¾ovejTabu¾ky=PoleZdrojovejTabu¾ky. Ak po <strong>na</strong>stavení mapovania z kontextového menu vyberiemepoložku Execute, prenos dát bude u<strong>sk</strong>utoènený korektne, s výnimkou jedného záz<strong>na</strong>mu,ktorý bol presunutý do tabu¾ky PROBLEM.DBF. Príèi<strong>na</strong> tohto javu je ve¾mi jednoduchá:<strong>na</strong>šu databázu sme zámerne vytvorili tak,aby sa do nej tento záz<strong>na</strong>m jednoduchonezmestil; re azec v poli Area je totiždlhší ako <strong>na</strong>mi stanovená dåžka po¾aOblast. Urobili sme tak preto, aby sme simohli demonštrova prácu s problémovýmizáz<strong>na</strong>mami.Pokia¾ budete s programom experimentova, nezabudnite pred každýmObr. 4presunom dát vymaza cie¾ovú tabu¾ku– použili sme totiž parameter bat-Append a staré údaje sa tak samy nevymažú. Parameter batCopy sme nemohli použiz toho dôvodu, aby nedošlo k ovplyvneniu štruktúry tabu¾ky. Síce by neboli pridanénové polia, ale ve¾kos po¾a Oblast by sa zmenila z pôvodných 15 z<strong>na</strong>kov <strong>na</strong> 20 z<strong>na</strong>kov(èo je dåžka po¾a v tabu¾ke ANIMALS.DBF), v dôsledku èoho by nevznikli žiadne problémovézáz<strong>na</strong>my a my by sme si nemohli demonštrova použitie vlastnosti Problem-TableName.Ako vidíme, dokázali sme relatívne bezbolestne presunú dáta z dBase tabu¾ky dotabu¾ky typu Paradox, dokonca sme ani nemuseli <strong>sk</strong>ompilova program. Pre úplnos všakpripájam kód, ktorý vykoná to isté, èo sme pred chví¾kou urobili ruène. Nie je vôbeczložitý:procedure TForm1.BatchBtnClick(Sender: TObject);begintrytryscreen.Cursor:=crHourGlass;with BatchMove dobeginMappings.Clear;Mappings.Add(‘meno=<strong>na</strong>me’);Mappings.Add(‘oblast=area’);Execute;if ProblemCount>0 then ShowMessage(IntToStr(ProblemCount)+’ problémovýchzáz<strong>na</strong>mov’);end;//withexceptraiseend;//exceptfi<strong>na</strong>llyscreen.Cursor:=crDefault;end;//fi<strong>na</strong>llyend;Oproti predchádzajúcemu ruènému postupu je tu jed<strong>na</strong> výhoda: v prípade, že primanipulácii s dátami <strong>na</strong>stali problémy, procedúra nás o tom okamžite informuje (vzniknevýnimka).NABUDÚCE. Nabudúce si ukážeme, ako vytvára tabu¾ky poèas behu programu, apovieme si aj nieèo o poèítaných položkách (calculated fields). Dovidenia o mesiac.Ivan Zernovác ml.Delphi v praxi / 7. èas : ID3 TAG v1Každý z vás, kto aspoò letmo pozná formát hudby MP3, sa iste stretol s pojmom ID3TAG. Nosite¾om tejto informácie o <strong>sk</strong>ladbe je ten istý súbor, teda súbor MP3. V dnešnejdobe, keï pracujeme s väèším množstvom „MP trojok“, urèite nie je <strong>na</strong> škodu struènýzoz<strong>na</strong>m samotných súborov, respektíve informácií o <strong>sk</strong>ladbe. Tak sa ID3 TAG stáva jedinýmmožným riešením. Pomocou prehrávaèa, prípadne playlistu zistíme všetky potrebnéinformácie: interpreta, pomenovanie albumu, názov <strong>sk</strong>ladby. Takéto informácie je dobrévedie zapísa a aj preèíta . Je mnoho editorov ID3 TAG, dokonca aj samy prehrávaèe disponujútakouto možnos ou, ktorá nie je až taká komfortná. Ak vám takéto programy aprehrávaèe nevyhovujú, máte teraz možnos <strong>na</strong>uèi sa, ako si takýto program urobi .ID3 TAG. Rozlišujeme dva druhy, ktoré sa od základu líšia zápisom a umiestnením v súbore.Rov<strong>na</strong>ko ako väèši<strong>na</strong> programov aj popisy súborov MP3 sú oddelené verziami. Nemôžemtu vymenúva všetky doteraz existujúce verzie, lebo ani sám nemám preh¾ad, èosa toho týka. My si len spomenieme dve základné, ktoré sú <strong>na</strong>vzájom úplne odlišné.V tejto èasti si opíšeme iba ID3 TAG v1 a druhú verziu si podrobne rozoberieme v <strong>na</strong>sledujúcomvydaní.ID3 TAG V1. Zápis tejto informácie je umiestnený <strong>na</strong> konci súboru v posledných 128bitoch. Jednotlivé informácie sú obmedzené dåžkou, a to takto (tabu¾ka a obr. 1):Zaèiatok z<strong>na</strong>èený TAG3 bitNázov <strong>sk</strong>ladby30 bitInterpret30 bitNázov albumu30 bitRok4 bitKomentár29 bitÈíslo <strong>sk</strong>ladby1 bitŽáner1 bitObr. 1Urèite sa väèšine z vás nebude páèi jeden bit urèený pre èíslo <strong>sk</strong>ladby. Tým bol o jedenz<strong>na</strong>k <strong>sk</strong>rátený komentár. V opisoch sa tento bit neuvádza, ale väèši<strong>na</strong> prehrávaèov hopoužíva <strong>na</strong> zaz<strong>na</strong>me<strong>na</strong>nie poradového èísla, <strong>na</strong>príklad èísla <strong>sk</strong>ladby <strong>na</strong> CD. V koneènomdôsledku si musíme všetci uvedomi , že takýto opis si môžeme prispôsobi , tak ako chceme,ale si treba uvedomi potrebu používania štandardu. Keby sme chceli zapísa nejakýparameter, ktorý štandard nepodporuje, zásadne by sme ho nemali dáva <strong>na</strong> koniec súboru,ale mali by sme rozmýš¾a o uložení <strong>na</strong> iné miesto v súbore alebo o <strong>sk</strong>rátení niektorejhodnoty tak ako s predposledným bitom <strong>na</strong> uchovanie poradového èísla. Na obrázku 1je zobrazený opis pesnièky Blood Brothers od Iron Maiden z albumu Brawe New World zroku 2000. Komentár v tomto prípade nie je nijaký. Sú jasne vidite¾né všetky string informácie.Informácia o žánri a o poradovom èísle je zapísaná takisto <strong>na</strong> konci súboru vposledných dvoch bitoch. Nastal èas položi si otázku, ako to èo <strong>na</strong>jjednoduchšie preèíta, zobrazi a, samozrejme, aj editova .ÈÍTANIE ID3 TAG V1. Hneï <strong>na</strong> zaèiatok si musíme uvedomi , èo a v akej formebudeme èíta . Všetky hodnoty okrem èísla <strong>sk</strong>ladby a žánru by sme mohli považova zastring. Na zistenie èísla <strong>sk</strong>ladby sa nám <strong>na</strong>jviac hodí premenná typu byte, ako aj <strong>na</strong> zisteniežánru. Žáner je potrebné previes <strong>na</strong> zrozumite¾nejšiu hodnotu, a to pod¾a tabu¾kyžánrov <strong>na</strong> hodnotu string. Na to nám <strong>na</strong>jlepšie poslúži pole string, ktoré bude vyzeratakto.constMaxID3zaner=147;ID3zaner: array[0..MaxID3zaner] of string = (‘Blues’, ‘Classic Rock’, ‘Country’, ‘Dance’, ‘Disco’, ‘Funk’, ‘Grunge’,‘Hip-Hop’, ‘Jazz’, ‘Metal’, ‘New Age’, ‘Oldies’, ‘Other’, ‘Pop’, ‘R&B’,‘Rap’, ‘Reggae’, ‘Rock’, ‘Techno’, ‘Industrial’, ‘Alter<strong>na</strong>tive’, ‘Ska’,‘Death Metal’, ‘Pranks’, ‘Soundtrack’, ‘Euro-Techno’, ‘Ambient’,‘Trip-Hop’, ‘Vocal’, ‘Jazz+Funk’, ‘Fusion’, ‘Trance’, ‘Classical’,‘Instrumental’, ‘Acid’, ‘House’, ‘Game’, ‘Sound Clip’, ‘Gospel’,‘Noise’, ‘AlternRock’, ‘Bass’, ‘Soul’, ‘Punk’, ‘Space’, ‘Meditative’,‘Instrumental Pop’, ‘Instrumental Rock’, ‘Ethnic’, ‘Gothic’,‘Darkwave’, ‘Techno-Industrial’, ‘Electronic’, ‘Pop-Folk’,‘Eurodance’, ‘Dream’, ‘Southern Rock’, ‘Comedy’, ‘Cult’, ‘Gangsta’,‘Top 40’, ‘Christian Rap’, ‘Pop/Funk’, ‘Jungle’, ‘Native American’,‘Cabaret’, ‘New Wave’, ‘Psychadelic’, ‘Rave’, ‘Showtunes’, ‘Trailer’,‘Lo-Fi’, ‘Tribal’, ‘Acid Punk’, ‘Acid Jazz’, ‘Polka’, ‘Retro’,‘Musical’, ‘Rock & Roll’, ‘Hard Rock’, ‘Folk’, ‘Folk-Rock’,‘Natio<strong>na</strong>l Folk’, ‘Swing’, ‘Fast Fusion’, ‘Bebob’, ‘Latin’, ‘Revival’,‘Celtic’, ‘Bluegrass’, ‘Avantgarde’, ‘Gothic Rock’, ‘Progressive Rock’,10/2001 PC REVUE 129