P R O G R A M U J E M Epotom zvýši o jednotku a vzápätí je volaná funkcia GetMem, ktorá alokuje potrebnúpamä. Nasleduje volanie metódy GetInfo1, ktorá tentoraz u urobí to, èo robi má – zistíúdaje o súbore MP3 a vráti hodnotu, ktorá <strong>na</strong> ne ukazuje (adresa údajov je uloená vpremennej TechInfo). Tieto informácie sú však iba takpovediac v surovom formáte, pretoich treba dekódova. Na to nám slúi funkcia DecodeMPEGInfo1, deklarovaná v jednot−ke MPEGDefs. Mono vás prekvapil zápisMPEGDefs.DecodeMPEGInfo1(TechInfo,Layer,Bitrate,Frequency,StereoMode);Tento výraz sa odvoláva <strong>na</strong> jednotku MPEGDefs (v hre teda nie je nijaký objekt, akoby sa <strong>na</strong> prvý poh¾ad mohlo zda). Takýto postup síce nie je èastý, je však dobré vedie,o èo ide. S funkciami èi procedúrami jednotky je moné pracova tak, ako keby to bolimetódy nejakého objektu. Môeme preto poui zápis typu NázovJednoky.NázovFunk−cie. V tomto prípade však takýto zápis vôbec nie je potrebný, úplne staèí, keï jedno−ducho <strong>na</strong>píšemeInitStream;Play;Timer.E<strong>na</strong>bled:=true;Pauza.E<strong>na</strong>bled:=true;Okrem toho je potrebné upravi kód, ktorý sa vykoná po stlaèení tlaèidla Stop, vtedyje toti potrebné èasovaè zastavi:procedure TPlayerWnd.StopClick(Sender: TObject);beginif Player.PlayerMode=pmPlaying then Player.Stop;Pauza.E<strong>na</strong>bled:=false;Timer.E<strong>na</strong>bled:=false;end;DecodeMPEGInfo1(TechInfo,Layer,Bitrate,Frequency,StereoMode);Po dekódovaní informácií sa tie zobrazia v komponente TLabel <strong>na</strong>zvanom Tech.Nepochybujem, e vás pri <strong>sk</strong>úmaní procedúry PlayMP3 zarazil tento zvláštny výraz:StereoModes[StereoMode]Dátový typ StereoMode je v jednotke ElSounds deklarovaný takto:TElSStereoMode = (MPG_MD_STEREO, MPG_MD_JOINT_STEREO, MPG_MD_DUAL_CHANNEL,MPG_MD_MONO);Konštanta StereoModes je definovaná tak, e kadý reazec vlastne zodpovedá jednejz týchto hodnôt, vïaka èomu sme nemuseli poui príkaz Case.Po zí<strong>sk</strong>aní technických informácií o súbore MP3 <strong>na</strong>sleduje volanie funkcie GetInfo2,ktorá vráti ukazovate¾ (pointer) <strong>na</strong> dátovú štruktúru, ktorá opisuje <strong>sk</strong>ladbu. Podobneako v predchádzajúcom prípade aj teraz je potrebné dáta dekódova, tentoraz pomo−cou funkcie DecodeMPEGInfo2. Po tomto úkone u máme k dispozícii údaje ako názov<strong>sk</strong>ladby, meno interpreta, rok atï., ktoré môeme zobrazi pomocou komponentuNazov.Kadý slušný prehrávaè by mal zobrazova dåku <strong>sk</strong>ladby a aj aktuálnu pozíciu v nej.Na hlavný formulár preto pridáme dva komponenty TLabel, prièom prvý <strong>na</strong>zvemeCelkovyCas a druhý Pozicia. Prvý údaj bude k dispozícii hneï po štarte <strong>sk</strong>ladby, ten druhýbude potrebné pravidelne obnovova. Informácie o dåke <strong>sk</strong>ladby a aktuálnej pozícii jemoné zisti prostredníctvom vlastností Size a Position, prièom obe hodnoty sú uvedenév milisekundách. Aby sme po spustení <strong>sk</strong>ladby mohli vidie jej celkovú dåku, musímeupravi metódu PlayMP3. Keïe budeme potrebova nejaké drobné prepoèty, musíme<strong>na</strong>j<strong>sk</strong>ôr deklarova dve nové premenné:Minuty,Sekundy:byte;Potom upravíme koniec metódy, ktorý bude vyzera takto:Nazov.Caption:=Artist + - + Title;minuty:=(Player.Size div 1000) div 60;sekundy:=(Player.Size div 1000) mod 60;CelkovyCas.Caption:=Format( %.2d: %.2d ,[minuty,sekundy]);Zobrazenie celkového èasu u funguje, ostáva u len dorobi priebený indikátor pozí−cie v <strong>sk</strong>ladbe. Ten bude pracova tak, e sa v jednosekundovom intervale bude vola ruti−<strong>na</strong>, ktorá pomocou vlastnosti Position zistí, ko¾ko minút a sekúnd sa zo <strong>sk</strong>ladby prehralo.Iste ste vytušili, e <strong>na</strong> to budeme potrebova komponent TTimer. Keïe ruti<strong>na</strong> bude vola−ná kadú sekundu, ponecháme vlastnos Interval <strong>na</strong>stavenú <strong>na</strong> hodnote 1000. VlastnosE<strong>na</strong>bled <strong>na</strong>stavíme <strong>na</strong> false, pretoe èasovaè bude spustený a po zaèatí prehrávania.V podstate bude táto procedúra totoná s procedúrou <strong>na</strong> zistenie celkovej ve¾kosti <strong>sk</strong>lad−by. Jediný rozdiel tkvie v tom, e kým v predchádzajúcom prípade sme pouili vlastnosSize, teraz pouijeme vlastnos Position:procedure TPlayerWnd.TimerTimer(Sender: TObject);varminuty,sekundy:byte;beginminuty:=(Player.Position div 1000) div 60;sekundy:=(Player.Position div 1000) mod 60;Pozicia.Caption:=Format( %.2d: %.2d ,[minuty,sekundy]);end;Samozrejme, nesmieme zabudnú modifikova metódu PlayMP3, pretoe práve v nejbude èasovaè spustený:Obr. 1Väèši<strong>na</strong> kvalitných MP3 prehrávaèov obsahuje aj Seeking bar, preto ho pridáme ajdo nášho programu. Ako som u spomenul, nie je vhodné <strong>na</strong> tento úèel poui kom−ponent TTrackBar, preto pouijeme komponent TSlider, ktorý sa dodáva s MP3 kom−ponentom. Ten bude pracova podobne ako Seeking bar vo WinAmpe: bude priebeneukazova pozíciu v <strong>sk</strong>ladbe a zároveò sa pomocou neho budeme môc v <strong>sk</strong>ladbe pre−súva. Pri <strong>na</strong>stavovaní polohy jazdca opä vyuijeme komponent TTimer, pretoe tátooperácia sa bude dia súbene so zobrazovaním pozície v <strong>sk</strong>ladbe:procedure TPlayerWnd.TimerTimer(Sender: TObject);varminuty,sekundy:byte;beginminuty:=(Player.Position div 1000) div 60;sekundy:=(Player.Position div 1000) mod 60;Pozicia.Caption:=Format( %.2d: %.2d ,[minuty,sekundy]);Slider.Value:=Round(Player.Position / Player.Size *100);end;Na rade je <strong>na</strong>písanie rutiny, ktorá nám umoní presúva sa v <strong>sk</strong>ladbe. Keï vyz<strong>na</strong>èíme kom−ponent TSlider a vyberieme kartu Events, bude nás láka tento kód umiestni do obslunej ruti−ny udalosti OnChange. To však nie je správne riešenie, pretoe potom by sa táto procedúravyvolala pri akomko¾vek pohybe jazdcom. Ak by sme ho teda uchopili, premiestnili <strong>na</strong> jedenkoniec komponentu, vzápätí <strong>na</strong> druhý koniec a potom pustili myš, zistili by sme, e dekodéru pravdepodobne „zošalel“, keïe udalos OnChange bola vyvolaná pri kadom pohybejazdcom. Našastie však existuje udalos, ktorá vyvolá obslunú rutinu iba vtedy, keï smetlaèidlo myši pustili a poloha jazdca sa ustálila. Táto udalos sa volá OnStopTracking (v kom−ponente TTrackBar neexistuje) a jej obsluha bude spúša <strong>na</strong>sledujúci kód:procedure TPlayerWnd.SliderStopTracking(Sender: TObject);beginif Player.PlayerModepmPlaying then exit;Player.Position:=Round(Slider.Value / 100 * Player.Size);end;Ak teraz jazdca uchopíme a premiestnime, bude to ma rov<strong>na</strong>ký efekt ako pouitieSeeking baru vo WinAmpe. Samozrejme, premiestnenie je moné iba vtedy, ak sa práveprehráva nejaká <strong>sk</strong>ladba, v opaènom prípade sa nestane niè.1/2002 PC REVUE 109
P R O G R A M U J E M EMimochodom, všimnite si, e po dokonèení <strong>sk</strong>ladby program ešte stále pokraèuje vpoèítaní sekúnd, take v komponente Pozicia sa zobrazí väèšie èíslo, ne je dåka <strong>sk</strong>lad−by. Tento problém je moné vyrieši zvýšením presnosti èasovaèa z 1000 ms <strong>na</strong> 250 ms amodifikáciou obsluhy udalosti OnTimer:procedure TPlayerWnd.TimerTimer(Sender: TObject);varminuty,sekundy:byte;beginif Player.Position>=Player.Size thenbeginTimer.E<strong>na</strong>bled:=false;exit;end;minuty:=(Player.Position div 1000) div 60;sekundy:=(Player.Position div 1000) mod 60;Pozicia.Caption:=Format( %.2d: %.2d ,[minuty,sekundy]);Slider.Value:=Round(Player.Position / Player.Size *100);end;Trochu zvláštne je, e <strong>na</strong> zaèiatku procedúry nestaèí jednoduché vypnutie èasovaèa:ten je, bohuia¾, v niektorých prípadoch volaný aj vtedy, keï u nemá by volaný. Pretosom do procedúry umiestnil kód, ktorý v prípade potreby zabráni vyko<strong>na</strong>niu zvyšku obslu−hy èasovaèa, take nedôjde k stavu, keï by program ukazoval takú pozíciu v piesni,ktorá je vyššia ne celková dåka piesne.Posledným zlepšením programu bude okno, ktoré zobrazí údaje o <strong>sk</strong>ladbe. Na roz−diel od WinAmpu však tieto informácie nebude moné meni (komponent to v èasepísania èlánku neumoòoval), preto <strong>na</strong> ich zobrazenie pouijeme iba funkciu Messa−geBox. Jedinou informáciou, ktorú sa o <strong>sk</strong>ladbe nedozvieme, bude áner (Genre): tenje toti definovaný ako celé èíslo a <strong>na</strong> jeho správnu identifikáciu by bol potrebný dlhýzoz<strong>na</strong>m všetkých moných ánrov.Zí<strong>sk</strong>anie informácií známych ako ID3 tag pre nás nebude nijaký problém. Napokonu sme to raz urobili, keï sme po spustení piesne zabezpeèili zobrazenie názvu <strong>sk</strong>lad−by a interpreta v komponente TLabel. Rov<strong>na</strong>ko ako v predchádzajúcom prípade ajteraz pouijeme procedúru DecodeMPEGInfo2, definovanú v jednotke MPEGDefs. Naformulár pridáme tlaèidlo TButton <strong>na</strong>zvané InfoBtn, po ktorého stlaèení sa vyvolá <strong>na</strong>sle−dujúca ruti<strong>na</strong>:procedure TPlayerWnd.InfoBtnClick(Sender: TObject);varInfo:pointer;Title,Artist,Album,Year,Comment:string;Genre:byte;beginInfo:=Player.GetInfo2;if not Assigned(Info) then exit;MPegDefs.DecodeMPEGInfo2(Info, Title,Artist,Album, Year, Comment,Genre);Application.MessageBox(PChar( NÆzov <strong>sk</strong>ladby: + Title+Chr(13)+Interpret: +Artist+Chr(13)+Album: +Album+Chr(13)+Rok vydania: +year), ID3 Tag ,MB_ICONINFORMATION);end;Po tejto úprave nám u neostáva iné ako pokocha sa výsledkami <strong>na</strong>šej práce. Koneènúpodobu MP3 prehrávaèa vidíme <strong>na</strong> obrázku è. 1.Tak, a sme hotoví. Nezabúdajte však, e toto všetko bol iba krátky úvod do problema−tiky prehrávania súborov MP3, pretoe komponent TElPlayer toho dokáe podstatne viac.Okrem iného by sa pomocou neho dali vytvori <strong>na</strong>príklad aj vizualizaèné efekty, známe zWinAmpu. To si však vyaduje nemalé úsilie a z<strong>na</strong>los toho, èo sa deje v zákulisí poèasprehrávania súborov MP3. Komponent ïalej umoòuje meni prioritu vlák<strong>na</strong>, ktoré zabez−peèuje prehrávanie a po<strong>sk</strong>ytuje monosti <strong>na</strong> prehrávanie cez DirectSound. Dokumentáciaje, ia¾, pomerne strohá, take si budete musie pozorne preštudova ukákový program(prípadne programy, ak ich v budúcnosti bude viac). Nie je to však niè, èo by sa nedalozvládnu, take vám drím palce a elám ve¾a pekných piesní, prehraných MP3 prehrá−vaèom z vlastnej produkcie.NABUDÚCE. Nabudúce budeme hovori o funkciách API, ktoré nám pomôu zistirôzne informácie o systéme. Napríklad si ukáeme, ako zisti typ a verziu <strong>na</strong>inštalovanéhooperaèného systému èi typ procesora, ako zisti ve¾kos pamäte a podobne. Teším sa <strong>na</strong>stretnutie opä o mesiac.Ivan Zernovác ml.Java pod lupou17. èas: Thready a synchronizáciaNapriek s¾ubu v závere predošlej èasti sa balíku Swing ani ïalším balíkom v tomtoseriáli nebudeme venova. Pri zloitejších témach toti vôbec nie je jednoduché efek−tívne zhrnú obrov<strong>sk</strong>é mnostvo informácií do jednej èi dvoch èastí seriálu. Ak sa topodarí, výsledok pripomí<strong>na</strong> <strong>sk</strong>ôr referenènú príruèku a o tom seriál predsa nie je.Èitate¾ chtivý podrobnejších informácií preto bude musie h¾ada aj inde, <strong>na</strong>jlepšie <strong>na</strong>internete.MULTITHREADING. Sotva by sa dnes uivila programovacia platforma, kto−rá by neumoòovala paralelné vykonávanie úloh. Pod¾a úrovne, <strong>na</strong> ktorej paraleliz−mus pozorujeme, môeme hovori o multita<strong>sk</strong>ingu alebo o multithreadingu. Prvýpojem oz<strong>na</strong>èuje schopnos súèasného behu viacerých procesov. Pod procesom sipredstavme samostatne vykonávanú exekuènú jednotku, ktorá má pridelený adreso−vý priestor, disponuje vnútorným stavom a môe vlastni prostriedky OS. Skrátka,proces je „beiaci program“. Jeden binárny program môe bea súèasne vo viace−rých kópiách – kadá kópia je potom samostatným procesom. Multita<strong>sk</strong>ing sa dnespovauje za samozrejmos.Pri multithreadingu rozdelíme jeden proces <strong>na</strong> nieko¾ko menších podprocesov,tzv. threadov. Názov pochádza z angliètiny, kde z<strong>na</strong>mená „vlákno“. O threadochpreto niekedy hovoríme ako o „exekuèných vlák<strong>na</strong>ch“. Thready medzi sebou zdie¾ajúadresový priestor, môu však obsahova vlastné, privátne údaje. Dôvodom, preèo sauchy¾ujeme k rozdrobeniu aplikácie <strong>na</strong> thready, je obyèajne s<strong>na</strong>ha o zlepšeniepouívate¾<strong>sk</strong>ého ohlasu. V takom textovom editore sa jeden thread môe stara oeditáciu textu, ïalší o obnovu obrazovky, kontrolu pravopisu èi tlaè <strong>na</strong> pozadí. Kebytoto všetko mal robi jeden thread, nebolo by moné pri tlaèení upravova text, prie−bene kontrolova pravopis a podobne. Existujú však aj typy úloh, pri ktorých jevýhodné poui thready z iného dôvodu – <strong>na</strong>príklad <strong>na</strong> zjednodušenie algoritmualebo implementáciu zloitejšieho správania.Ak máme dosiahnu paralelný beh viacerých procesov èi threadov <strong>na</strong> jednoproceso−rovom systéme, musíme sa uchýli k nejakému triku. Rýchlym prepí<strong>na</strong>ním medzi jed−notlivými threadmi môeme vytvori ilúziu súèasného behu viacerých úloh. V operaè−nom systéme zvyèajne existuje samostatný modul, plánovaè (scheduler), ktorý sa staráo pride¾ovanie procesora jednotlivým threadom <strong>na</strong> základe vhodnej stratégie. Ak je plá−novaè schopný po uplynutí vopred stanoveného èasového kvanta thread pozastavi aspusti iný, hovoríme o preemptívnom plánovaní. (V ére šestnásbitových Windows,ktoré nepo<strong>sk</strong>ytovali preemptívny multita<strong>sk</strong>ing, sa musel kadý beiaci proces explicitnea dobrovo¾ne zriec tohto svojho výsadného postavenia. Ak sa stalo, e sa program zase−kol, spolu s ním išiel pod kytièky aj operaèný systém.)Niektoré thready sú pre beh systému dôleitejšie. Aby sa zabezpeèila plynulos ohla−su, majú thready priradenú celoèíselnú hodnotu priority. Plánovaè threadov preferuje pripride¾ovaní procesora thready s vyššou prioritou. Thready sa môu <strong>na</strong>chádza v rôz−nych stavoch, <strong>na</strong>jhrubšie rozdelenie je <strong>na</strong> thready beiace (môe ich by aj viac akojeden, <strong>na</strong>príklad <strong>na</strong> multiprocesorových systémoch), thready pripravené <strong>na</strong> <strong>na</strong>plánova−nie a thready „spiace“, ktoré èakajú <strong>na</strong> vý<strong>sk</strong>yt nejakej udalosti. Väèši<strong>na</strong> threadov je po−èas svojho ivota v tomto treom stave, pretoe èaká <strong>na</strong> vstup z klávesnice, <strong>na</strong> pohybmyši, <strong>na</strong> vykreslenie obrázka, <strong>na</strong> dodanie údajov z di<strong>sk</strong>u a podobne. Len thready, ktoréintenzívne poèítajú, dokáu vyui svoje èasové kvantum <strong>na</strong>plno.Thread môe zmeni svoj stav aj i<strong>na</strong>k ako vypršaním prideleného èasu – môe sadobrovo¾ne zriec procesora alebo môe vyjadri svoju potrebu èaka <strong>na</strong> splnenienejakej podmienky. Spiaci thread môe by zobudený systémom alebo iným threa−dom, ktorý sa postaral o splnenie podmienky.THREADY A JAVA. Java po<strong>sk</strong>ytuje dva spôsoby, ako vytvori thread. Prvýmje odvodenie vlastnej podtriedy systémovej triedy java.lang.Thread. Jadromthreadu je metóda run() (treba ju, pochopite¾ne, predefinova), ktorá sa zaènevykonáva po spustení threadu. To dosiahneme zavolaním metódy start(). Threadbeí, kým je v metóde run() èo robi. Len èo sa v metóde vy<strong>sk</strong>ytne príkaz return èinezachytená výnimka a aj keï tok riadenia dospeje ku koncovej zátvorke, thread konèía systém ho po èase zlikviduje. Ukáka vytvorenia a spustenia nového threadu:class MyThread extends Thread{// ...}MyThread mt = new MyThread();mt.start();V príklade vytvárame objekt MyThread, ktorý sme odvodili od triedy Thread, a volá−me jeho metódu start().110 PC REVUE 1/2002