Obsah
1. Změna velikosti bitmap a rotace bitmap
2. Demonstrační příklad SDLTest57 – načtení a vykreslení originální bitmapy
3. Demonstrační příklad SDLTest58 – změna měřítka bitmapy
4. Demonstrační příklad SDLTest59 – rotace bitmapy
5. Zvukový subsystém knihovny SDLJava
6. Základ přehrávání zvuků a hudby s využitím třídy SDLMixer
7. Demonstrační příklad SDLTest60 – přehrání zvuku načteného ze souboru .wav
8. Demonstrační příklad SDLTest61 – přehrání hudby načtené ze souboru .mod
9. Repositář se zdrojovými kódy všech pěti dnešních demonstračních příkladů
1. Změna velikosti bitmap a rotace bitmap
V první polovině dnešního článku o knihovně SDLJava dokončíme popis „kreslicích“ funkcí nabízených třídou sdljavax.gfx.SDLGfx. Řekneme si, jakým způsobem je možné měnit velikost bitmap, popř. jak lze bitmapami rotovat o libovolný úhel. Druhá polovina článku bude věnována zvukovému subsystému SDLJava; zaměříme se především na popis základních možností třídy SDLMixer, s jejímž využitím je možné velmi jednoduše přehrávat zvuky ale i hudbu, která může být uložena v různých formátech, včetně Ogg Vorbis či stále populárních formátů založených na amigáckých MODulech.
Ve třídě sdljavax.gfx.SDLGfx nalezneme kromě běžných „kreslicích“ funkcí i dvojici funkcí zoomSurface a rotozoomSurface se zvláštním významem. Tyto funkce slouží ke změně velikosti bitmapy, popř. k rotaci bitmapy společně se změnou velikosti. Bitmapa je reprezentována instancí typu SDLSurface a vzhledem k tomu, že rozměry SDLSurface jsou po jejím vytvoření neměnitelné, vrací obě popisované funkce novou bitmapu, tj. novou instanci typu SDLSurface. Kromě obou zmíněných funkcí zoomSurface a rotozoomSurface nalezneme ve třídě sdljavax.gfx.SDLGfx i funkce pojmenované zoomSurfaceSize a rotozoomSurfaceSize sloužící pro zjištění rozměrů bitmapy po změně měřítka či po její rotaci:
# | Jméno funkce | Návratová hodnota | Parametry | Popis |
---|---|---|---|---|
zoomSurface | SDLSurface | SDLSurface src, double zoomx, double zoomy, boolean smooth | změna měřítka bitmapy (vytvoří se nová bitmapa) | |
zoomSurfaceSize | java.awt.Dimension | int width, int height, double zoomx, double zoomy | výpočet rozměrů nové bitmapy po změně měřítka | |
rotozoomSurface | SDLSurface | SDLSurface src, double angle, double zoom, boolean smooth | rotace a změna měřítka bitmapy (vytvoří se nová bitmapa) | |
rotozoomSurfaceSize | java.awt.Dimension | int width, int height, double angle, double zoom | výpočet rozměrů nové bitmapy po rotaci a změně měřítka |
Způsob použití funkcí zoomSurface a rotozoomSurface si ukážeme na dvojici demonstračních příkladů popsaných ve třetí a ve čtvrté kapitole.
2. Demonstrační příklad SDLTest57 – načtení a vykreslení originální bitmapy
V dnešním prvním demonstračním příkladu pojmenovaném SDLTest58 není ve skutečnosti použita žádná nová funkce. Tento příklad totiž bude sloužit jako základ pro následující dva demonstrační příklady se jmény SDLTest59 a SDLTest60. Implementovaný algoritmus je velmi jednoduchý – po spuštění tohoto demonstračního příkladu se nahraje rastrový obrázek „xscorch.bmp“, jenž můžete nalézt na této adrese: http://icedtea.classpath.org/people/ptisnovs/jvm-tools/raw-file/ce9cbc53b31c/sdljava/img/xscorch.bmp. Následně je tato bitmapa pomocí operace typu BitBLT/BLIT přenesena do framebufferu a zobrazena. Program poté čeká na stisk klávesy ESC, která jeho běh ukončí.
Obrázek 1: Obrazovka demonstračního příkladu SDLTest57.
Zdrojový kód demonstračního příkladu SDLTest57:
import sdljava.SDLMain; import sdljava.SDLException; import sdljava.event.SDLEvent; import sdljava.event.SDLKeyboardEvent; import sdljava.event.SDLKey; import sdljava.event.SDLQuitEvent; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import sdljava.video.SDLRect; import sdljava.x.swig.SDLPressedState; import static sdljavax.gfx.SDLGfx.*; /** * Padesaty sedmy demonstracni priklad vyuzivajici knihovnu SDLjava. * * Vykresleni bitmapy na obrazovku. * * @author Pavel Tisnovsky */ public class SDLTest57 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 480; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 480; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Nazev bitmapy, ktera se ma nacist a nasledne zobrazit. */ private static final String INPUT_IMAGE_NAME = "xscorch.bmp"; /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Smycka pro zpracovani udalosti. */ private static void eventLoop() throws SDLException { while (true) { // precist udalost z fronty SDLEvent event = SDLEvent.waitEvent(); // vyskok ze smycky pro zpracovani udalosti pri vyskytu // udalosti typu SDLQuitEvent if (event instanceof SDLQuitEvent) { return; } // stisk ci pusteni klavesy if (event instanceof SDLKeyboardEvent) { // pretypovani final SDLKeyboardEvent keyEvent = (SDLKeyboardEvent)event; // symbol/kod klavesy final int symbol = keyEvent.getSym(); // ESC ukonci program if (symbol == SDLKey.SDLK_ESCAPE && keyEvent.getState() == SDLPressedState.PRESSED) { return; } } } } /** * Vykresleni sceny na obrazovku ci do okna aplikace. * * @param screen * framebuffer * @param bitmap * bitmapa, ktera se ma vykreslit */ private static void drawOnScreen(SDLSurface screen, SDLSurface bitmap) throws SDLException { // vykresleni bitmapy s obrazkem drawBitmap(screen, bitmap); // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); } /** * Vykresleni bitmapy na obrazovku. * * @param screen * framebuffer * @param bitmap * bitmapa, ktery se ma na obrazovku vykreslit */ private static void drawBitmap(SDLSurface screen, SDLSurface bitmap) throws SDLException { // vypocitat umisteni bitmapy na obrazovce. final SDLRect rect = computePositionOnScreen(screen, bitmap); // provest operaci typu BitBLT bitmap.blitSurface(screen, rect); } /** * Vypocitat umisteni bitmapy na obrazovce. * * @param screen * framebuffer * @param bitmap * bitmapa s pismem, ktera se ma na obrazovku vykreslit * @return obdelnik predstavujici pozici bitmapy na obrazovce */ private static SDLRect computePositionOnScreen(SDLSurface screen, SDLSurface bitmap) { // ziskat rozmery obrazovky i bitmapy final int screenWidth = screen.getWidth(); final int screenHeight = screen.getHeight(); final int bitmapWidth = bitmap.getWidth(); final int bitmapHeight = bitmap.getHeight(); // vypocitat umisteni bitmapy na obrazovce final int x = (screenWidth - bitmapWidth)>>1; final int y = (screenHeight - bitmapHeight)>>1; return new SDLRect(x, y, bitmapWidth, bitmapHeight); } /** * Spusteni padesateho sedmeho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani final SDLSurface screen = initVideo(); // nacteni bitmapy z externiho souboru final SDLSurface bitmap = SDLVideo.loadBMP(INPUT_IMAGE_NAME); // vykresleni sceny na obrazovku drawOnScreen(screen, bitmap); // smycka pro zpracovani udalosti eventLoop(); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } } }
Skript pro překlad tohoto demonstračního příkladu na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest57.java
Dávkový soubor pro překlad tohoto demonstračního příkladu na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest57.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest57
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest57
3. Demonstrační příklad SDLTest58 – změna měřítka bitmapy
V dnešním druhém demonstračním příkladu pojmenovaném SDLTest58 je ukázán způsob použití funkce sdljavax.gfx.SDLGfx.zoomSurface(). Této funkci se předává čtveřice parametrů – zdrojová bitmapa představovaná instancí třídy SDLSurface, měřítko v ose X, měřítko v ose Y a čtvrtým parametrem je pravdivostní příznak určující, zda se má při zvětšování či zmenšování bitmapy použít rychlý algoritmus pro výběr nejbližších pixelů (hodnota false) či poněkud pomalejší algoritmus implementující bilineární interpolaci (hodnota true). Návratovou hodnotou funkce zoomSurface() je nová bitmapa, tj. nová instance třídy SDLSurface. Povšimněte si, že měřítka v ose X i ose Y jsou na sobě nezávislá: při zadání hodnoty menší než jedna dojde ke zmenšení bitmapy v daném směru, při zadání hodnoty větší než jedna naopak ke zvětšení:
/** * Vykresleni sceny na obrazovku ci do okna aplikace. * * @param screen * framebuffer * @param bitmap * bitmapa, ktera se ma zpracovat */ private static void drawOnScreen(SDLSurface screen, SDLSurface bitmap) throws SDLException { // vytvoreni nove bitmapy se zmenou meritka SDLSurface zoomedBitmap = zoomSurface(bitmap, 1.5, 2.3, false); // vykresleni nove bitmapy s obrazkem drawBitmap(screen, zoomedBitmap); // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); }
Obrázek 2: Obrazovka demonstračního příkladu SDLTest58 ve chvíli, kdy se při zvětšování/zmenšování bitmapy použije algoritmus výběru nejbližšího pixelu.
Obrázek 3: Obrazovka demonstračního příkladu SDLTest58 ve chvíli, kdy se při zvětšování/zmenšování bitmapy použije vylepšený algoritmus založený na bilineární transformaci.
Úplný zdrojový kód demonstračního příkladu SDLTest58 vypadá následovně:
import sdljava.SDLMain; import sdljava.SDLException; import sdljava.event.SDLEvent; import sdljava.event.SDLKeyboardEvent; import sdljava.event.SDLKey; import sdljava.event.SDLQuitEvent; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import sdljava.video.SDLRect; import sdljava.x.swig.SDLPressedState; import static sdljavax.gfx.SDLGfx.*; /** * Padesaty osmy demonstracni priklad vyuzivajici knihovnu SDLjava. * * Vytvoreni nove bitmapy se zmenou meritka. * * @author Pavel Tisnovsky */ public class SDLTest58 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 480; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 480; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Nazev bitmapy, ktera se ma nacist a nasledne zobrazit. */ private static final String INPUT_IMAGE_NAME = "xscorch.bmp"; /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Smycka pro zpracovani udalosti. */ private static void eventLoop() throws SDLException { while (true) { // precist udalost z fronty SDLEvent event = SDLEvent.waitEvent(); // vyskok ze smycky pro zpracovani udalosti pri vyskytu // udalosti typu SDLQuitEvent if (event instanceof SDLQuitEvent) { return; } // stisk ci pusteni klavesy if (event instanceof SDLKeyboardEvent) { // pretypovani final SDLKeyboardEvent keyEvent = (SDLKeyboardEvent)event; // symbol/kod klavesy final int symbol = keyEvent.getSym(); // ESC ukonci program if (symbol == SDLKey.SDLK_ESCAPE && keyEvent.getState() == SDLPressedState.PRESSED) { return; } } } } /** * Vykresleni sceny na obrazovku ci do okna aplikace. * * @param screen * framebuffer * @param bitmap * bitmapa, ktera se ma zpracovat */ private static void drawOnScreen(SDLSurface screen, SDLSurface bitmap) throws SDLException { // vytvoreni nove bitmapy se zmenou meritka SDLSurface zoomedBitmap = zoomSurface(bitmap, 1.5, 2.3, false); // vykresleni nove bitmapy s obrazkem drawBitmap(screen, zoomedBitmap); // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); } /** * Vykresleni bitmapy na obrazovku. * * @param screen * framebuffer * @param bitmap * bitmapa, ktery se ma na obrazovku vykreslit */ private static void drawBitmap(SDLSurface screen, SDLSurface bitmap) throws SDLException { // vypocitat umisteni bitmapy na obrazovce. final SDLRect rect = computePositionOnScreen(screen, bitmap); // provest operaci typu BitBLT bitmap.blitSurface(screen, rect); } /** * Vypocitat umisteni bitmapy na obrazovce. * * @param screen * framebuffer * @param bitmap * bitmapa s pismem, ktera se ma na obrazovku vykreslit * @return obdelnik predstavujici pozici bitmapy na obrazovce */ private static SDLRect computePositionOnScreen(SDLSurface screen, SDLSurface bitmap) { // ziskat rozmery obrazovky i bitmapy final int screenWidth = screen.getWidth(); final int screenHeight = screen.getHeight(); final int bitmapWidth = bitmap.getWidth(); final int bitmapHeight = bitmap.getHeight(); // vypocitat umisteni bitmapy na obrazovce final int x = (screenWidth - bitmapWidth)>>1; final int y = (screenHeight - bitmapHeight)>>1; return new SDLRect(x, y, bitmapWidth, bitmapHeight); } /** * Spusteni padesateho osmeho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani final SDLSurface screen = initVideo(); // nacteni bitmapy z externiho souboru final SDLSurface bitmap = SDLVideo.loadBMP(INPUT_IMAGE_NAME); // vykresleni sceny na obrazovku drawOnScreen(screen, bitmap); // smycka pro zpracovani udalosti eventLoop(); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } } }
Skript pro překlad tohoto demonstračního příkladu na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest58.java
Dávkový soubor pro překlad tohoto demonstračního příkladu na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest58.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest58
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest58
4. Demonstrační příklad SDLTest59 – rotace bitmapy
Zatímco v předchozím demonstračním příkladu byla použita funkce sdljavax.gfx.SDLGfx.zoomSurface() pro vytvoření nové zvětšené nebo zmenšené bitmapy, je v dnešním třetím demonstračním příkladu nazvaném SDLTest59 využita funkce sdljavax.gfx.SDLGfx.rotozoomSurface(), kterou je možné použít pro současnou rotaci bitmapy i změnu jejího měřítka. I této funkci se předávají čtyři parametry – zdrojová bitmapa představovaná objektem typu SDLSurface, úhel otočení specifikovaný ve stupních, třetím parametrem lze specifikovat zvětšení či zmenšení bitmapy (ve všech směrech stejné) a ve čtvrtém parametru se opět s využitím pravdivostní hodnoty určuje buď rychlý algoritmus vybírající při otáčení nejbližší pixely, popř. pomalejší algoritmus s antialiasingem, který produkuje kvalitnější výsledky. Úhel otočení je reprezentován hodnotou typu double, což znamená, že otočení je možné specifikovat s větší přesností než jeden stupeň. Míra zvětšení či zmenšení bitmapy je taktéž reprezentována hodnotou typu double: hodnotami většími než 1 se zadává zvětšení, hodnotami menšími než 1 naopak zmenšení bitmapy:
/** * Vykresleni sceny na obrazovku ci do okna aplikace. * * @param screen * framebuffer * @param bitmap * bitmapa, ktera se ma zpracovat */ private static void drawOnScreen(SDLSurface screen, SDLSurface bitmap) throws SDLException { // vytvoreni nove bitmapy s jeji rotaci SDLSurface rotatedBitmap = rotozoomSurface(bitmap, 45, 1.0, false); // vykresleni nove bitmapy s obrazkem drawBitmap(screen, rotatedBitmap); // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); }
Obrázek 4: Obrazovka demonstračního příkladu SDLTest59 ve chvíli, kdy se při zvětšování/zmenšování bitmapy použije algoritmus výběru nejbližšího pixelu.
Obrázek 5: Obrazovka demonstračního příkladu SDLTest59 ve chvíli, kdy se při zvětšování/zmenšování bitmapy použije vylepšený algoritmus založený na bilineární transformaci.
Následuje výpis zdrojového kódu demonstračního příkladu SDLTest59:
import sdljava.SDLMain; import sdljava.SDLException; import sdljava.event.SDLEvent; import sdljava.event.SDLKeyboardEvent; import sdljava.event.SDLKey; import sdljava.event.SDLQuitEvent; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import sdljava.video.SDLRect; import sdljava.x.swig.SDLPressedState; import static sdljavax.gfx.SDLGfx.*; /** * Padesaty devaty demonstracni priklad vyuzivajici knihovnu SDLjava. * * Vytvoreni nove bitmapy s jeji rotaci. * * @author Pavel Tisnovsky */ public class SDLTest59 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 480; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 480; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Nazev bitmapy, ktera se ma nacist a nasledne zobrazit. */ private static final String INPUT_IMAGE_NAME = "xscorch.bmp"; /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Smycka pro zpracovani udalosti. */ private static void eventLoop() throws SDLException { while (true) { // precist udalost z fronty SDLEvent event = SDLEvent.waitEvent(); // vyskok ze smycky pro zpracovani udalosti pri vyskytu // udalosti typu SDLQuitEvent if (event instanceof SDLQuitEvent) { return; } // stisk ci pusteni klavesy if (event instanceof SDLKeyboardEvent) { // pretypovani final SDLKeyboardEvent keyEvent = (SDLKeyboardEvent)event; // symbol/kod klavesy final int symbol = keyEvent.getSym(); // ESC ukonci program if (symbol == SDLKey.SDLK_ESCAPE && keyEvent.getState() == SDLPressedState.PRESSED) { return; } } } } /** * Vykresleni sceny na obrazovku ci do okna aplikace. * * @param screen * framebuffer * @param bitmap * bitmapa, ktera se ma zpracovat */ private static void drawOnScreen(SDLSurface screen, SDLSurface bitmap) throws SDLException { // vytvoreni nove bitmapy s jeji rotaci SDLSurface rotatedBitmap = rotozoomSurface(bitmap, 45, 1.0, false); // vykresleni nove bitmapy s obrazkem drawBitmap(screen, rotatedBitmap); // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); } /** * Vykresleni bitmapy na obrazovku. * * @param screen * framebuffer * @param bitmap * bitmapa, ktery se ma na obrazovku vykreslit */ private static void drawBitmap(SDLSurface screen, SDLSurface bitmap) throws SDLException { // vypocitat umisteni bitmapy na obrazovce. final SDLRect rect = computePositionOnScreen(screen, bitmap); // provest operaci typu BitBLT bitmap.blitSurface(screen, rect); } /** * Vypocitat umisteni bitmapy na obrazovce. * * @param screen * framebuffer * @param bitmap * bitmapa s pismem, ktera se ma na obrazovku vykreslit * @return obdelnik predstavujici pozici bitmapy na obrazovce */ private static SDLRect computePositionOnScreen(SDLSurface screen, SDLSurface bitmap) { // ziskat rozmery obrazovky i bitmapy final int screenWidth = screen.getWidth(); final int screenHeight = screen.getHeight(); final int bitmapWidth = bitmap.getWidth(); final int bitmapHeight = bitmap.getHeight(); // vypocitat umisteni bitmapy na obrazovce final int x = (screenWidth - bitmapWidth)>>1; final int y = (screenHeight - bitmapHeight)>>1; return new SDLRect(x, y, bitmapWidth, bitmapHeight); } /** * Spusteni padesateho devateho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani final SDLSurface screen = initVideo(); // nacteni bitmapy z externiho souboru final SDLSurface bitmap = SDLVideo.loadBMP(INPUT_IMAGE_NAME); // vykresleni sceny na obrazovku drawOnScreen(screen, bitmap); // smycka pro zpracovani udalosti eventLoop(); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } } }
Skript pro překlad tohoto demonstračního příkladu na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest59.java
Dávkový soubor pro překlad tohoto demonstračního příkladu na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest59.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest59
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest59
5. Zvukový subsystém knihovny SDLJava
Při popisu knihovny SDLJava jsme se prozatím soustředili zejména na vykreslování do framebufferu a na systém práce s událostmi. Ovšem tato knihovna v sobě integruje i další – mnohdy neméně významný – zvukový subsystém. Ten je z hlediska programátora implementován v balíčcích sdljava.audio a sdljava.mixer, přičemž se v současné verzi knihovny SDLJava naprostá většina použitelných metod a funkcí nachází ve třídách dostupných v balíčku sdljava.mixer, zejména ve třídě sdljava.mixer.SDLMixer. S využitím této třídy je možné jednoduše přehrávat hudbu či zvuky, nastavovat hlasitost jednotlivých zvukových kanálů, automaticky zajistit postupné snížení hlasitosti, aplikovat další zvukové filtry na přehrávaný zvuk atd. Dnes se seznámíme pouze se základy využití zvukového subsystému, podrobnější informace o problematice nastavení bufferů apod. budou uvedeny v navazující části tohoto seriálu.
6. Základ přehrávání zvuků a hudby s využitím třídy SDLMixer
Podívejme se nyní, jakým způsobem lze zvukový subsystém knihovny SDLJava využít v praxi. Nejprve je nutné provést inicializaci zvukového subsystému, a to podobným způsobem, který již známe z inicializace subsystému grafického:
// inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_AUDIO);
Následně je nutné přes metodu SDLMixer.openAudio() nastavit vzorkovací frekvenci přehrávaných samplů, formát zvuku (zde šestnáctibitové samply se stereo kanály), celkový počet zvukových kanálů vstupujících do mixéru a konečně velikost zvukových bufferů. My zde prozatím použijeme osvědčenou hodnotu 1024, ovšem příště si řekneme, za jakých okolností se vyplatí velikost bufferů změnit:
SDLMixer.openAudio(44100, SDLMixer.AUDIO_S16, 1, 1024);
Dále je nutné nastavit hlasitost pro přehrávání zvuků a/nebo hudby, a to jednou z těchto metod:
SDLMixer.volume(-1, 100); SDLMixer.volumeMusic(100);
Při přehrávání zvuků se musí zvuková data buď vytvořit či načíst ze souboru a následně spustit přehrávání v určeném zvukovém kanálu:
// nacteni samplu z externiho souboru MixChunk effect = SDLMixer.loadWAV("Kitten.wav"); // prehrani samplu SDLMixer.playChannel(-1, effect, 0);
Podobně se postupuje při přehrávání hudby, která je typicky uložena v souborech MIDI, Ogg Vorbis či Amiga MOD:
// nacteni hudby z externiho souboru MixMusic music = SDLMixer.loadMUS("Beast1_2.mod"); // prehrani hudby SDLMixer.playMusic(music, -1);
Aplikace může jednoduše testovat, zda již došlo k ukončení přehrávání zvuku/zvuků (to je nutné pro znovunaplnění bufferu či bufferů):
// musime pockat na ukonceni prehravani while (SDLMixer.playing(ALL_SOUND_CHANNELS) > 0) { SDLTimer.delay(100); }
Taktéž lze testovat, zda se již přehrála celá hudební skladba:
// musime pockat na ukonceni prehravani while (SDLMixer.playingMusic()) { SDLTimer.delay(100); }
Celý postup bude ukázán na dvojici demonstračních příkladů popsaných v navazujících dvou kapitolách.
7. Demonstrační příklad SDLTest60 – přehrání zvuku načteného ze souboru .wav
Dnešní předposlední demonstrační příklad po svém spuštění načte zvukový soubor nazvaný „Kitten.wav“ (tento je konkrétně součástí programu Scratch) a přehraje ho v prvním zvukovém kanálu. Po inicializaci zvukového subsystému a nastavení hlasitosti se načte již zmíněný zvukový soubor pomocí metody SDLMixer.loadWAV(„Kitten.wav“). Následně se spustí přehrávání zvuku s využitím metody SDLMixer.playChannel(ALL_SOUND_CHANNELS, effect, 0), kde je konstantou ALL_SOUND_CHANNELS=-1 určeno, že se má zahájit přehrávání ve všech vytvořených zvukových kanálech a hodnota 0 v posledním parametru udává počet přehrání (jedenkrát). Následně je nutné v programové smyčce počkat na přehrání zvuku; pokud by tato smyčka nebyla v programu použita, byla by aplikace ukončena ještě před inicializací přehrávání. Podívejme se nyní na zdrojový kód tohoto demonstračního příkladu, který není příliš dlouhý, a to díky využití relativně vysokoúrovňové třídy sdljava.mixer.SDLMixer:
import sdljava.SDLMain; import sdljava.SDLException; import sdljava.SDLTimer; import sdljava.mixer.SDLMixer; import sdljava.mixer.MixChunk; /** * Sedesaty demonstracni priklad vyuzivajici knihovnu SDLjava. * * SDLJava a audio. * * @author Pavel Tisnovsky */ public class SDLTest60 { /** * Konstanta zastupujici vsechny zvukove kanaly. */ static final int ALL_SOUND_CHANNELS = -1; /** * Prehrani zvukoveho vzorku. */ public static void playSample() throws SDLException, InterruptedException { // nastaveni vlastnosti zvukoveho subsystemu i vybranych zvukovych kanalu SDLMixer.openAudio(44100, SDLMixer.AUDIO_S16, 1, 1024); SDLMixer.volume(ALL_SOUND_CHANNELS, 100); // nacteni samplu z externiho souboru MixChunk effect = SDLMixer.loadWAV("Kitten.wav"); // prehrani samplu SDLMixer.playChannel(ALL_SOUND_CHANNELS, effect, 0); // musime pockat na ukonceni prehravani while (SDLMixer.playing(ALL_SOUND_CHANNELS) > 0) { SDLTimer.delay(100); } } /** * Spusteni sedesateho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_AUDIO); // prehrani zvukoveho vzorku playSample(); } catch (Exception e) { e.printStackTrace(); } finally { SDLMain.quit(); } } }
Skript pro překlad tohoto demonstračního příkladu na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest60.java
Dávkový soubor pro překlad tohoto demonstračního příkladu na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest60.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest60
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest60
8. Demonstrační příklad SDLTest61 – přehrání hudby načtené ze souboru .mod
V dnešním posledním demonstračním příkladu je ukázáno, jakým způsobem lze využít knihovnu SDLJava pro přehrávání hudby. Povšimněte si, že zdrojový kód tohoto příkladu je velmi krátký, protože díky použití třídy sdljava.mixer.SDLMixer je přehrávání hudby skutečně jednoduché. Nejprve je provedena inicializace zvukového subsystému (což již známe) a posléze se metodou SDLMixer.loadMUS(„Beast1_2.mod“) načte soubor obsahující hudbu – v tomto případě znělku ze známé hry Shadow of the Beast uloženou ve formátu amigáckých modulů, které jsou stále ještě dosti populární. Následně je přes metodu SDLMixer.playMusic(music, –1) spuštěno přehrávání hudby, přičemž hodnota –1 značí, že se má melodie přehrávat neustále dokola a kladná hodnota značí počet přehrání melodie. Následuje programová smyčka, v níž se metodou SDLMixer.playingMusic() zjišťuje, zda již nedošlo k ukončení melodie (což však při loop=-1 nikdy nenastane, takže se program musí ukončit přímo z terminálu:
import sdljava.SDLMain; import sdljava.SDLException; import sdljava.SDLTimer; import sdljava.mixer.SDLMixer; import sdljava.mixer.MixMusic; /** * Sedesaty prvni demonstracni priklad vyuzivajici knihovnu SDLjava. * * SDLJava a audio. * * @author Pavel Tisnovsky */ public class SDLTest61 { /** * Prehrani hudby. */ public static void playMusic() throws SDLException, InterruptedException { // nastaveni vlastnosti zvukoveho subsystemu i vybranych zvukovych kanalu SDLMixer.openAudio(44100, SDLMixer.AUDIO_S16, 1, 1024); SDLMixer.volumeMusic(100); // nacteni hudby z externiho souboru MixMusic music = SDLMixer.loadMUS("Beast1_2.mod"); // prehrani hudby SDLMixer.playMusic(music, -1); // musime pockat na ukonceni prehravani while (SDLMixer.playingMusic()) { SDLTimer.delay(100); } } /** * Spusteni sedesateho prvniho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_AUDIO); // prehrani hudby playMusic(); } catch (Exception e) { e.printStackTrace(); } finally { SDLMain.quit(); } } }
Skript pro překlad tohoto demonstračního příkladu na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest61.java
Dávkový soubor pro překlad tohoto demonstračního příkladu na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest61.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest61
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest61
9. Repositář se zdrojovými kódy všech pěti dnešních demonstračních příkladů
Všech pět dnes popsaných demonstračních příkladů bylo společně s podpůrnými skripty určenými pro jejich překlad a následné spuštění uloženo do Mercurial repositáře dostupného na adrese http://icedtea.classpath.org/people/ptisnovs/jvm-tools/. Podobně jako tomu bylo i v předchozích několika dílech tohoto seriálu, i ke dnešním příkladům jsou přiloženy skripty využitelné pro jejich překlad a spuštění. Navíc byly přidány i skripty využitelné ve Windows:
10. Odkazy na Internetu
- SDL_mixer 2.0
http://www.libsdl.org/projects/SDL_mixer/ - SDLJava: package sdljava.ttf
http://sdljava.sourceforge.net/docs/api/sdljava/ttf/package-summary.html#package_description - SDLJava: class sdljava.ttf.SDLTTF
http://sdljava.sourceforge.net/docs/api/sdljava/ttf/SDLTTF.html - SDLJava: class sdljava.ttf.SDLTrueTypeFont
http://sdljava.sourceforge.net/docs/api/sdljava/ttf/SDLTrueTypeFont.html - SDL_ttf Documentation
http://www.libsdl.org/projects/SDL_ttf/docs/ - SDL_ttf 2.0 (není prozatím součástí SDLJava)
http://www.libsdl.org/projects/SDL_ttf/ - SDL_ttf doc
http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf_frame.html - SDL 1.2 Documentation: SDL_Surface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsurface.html - SDL 1.2 Documentation: SDL_PixelFormat
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlpixelformat.html - SDL 1.2 Documentation: SDL_LockSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdllocksurface.html - SDL 1.2 Documentation: SDL_UnlockSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlunlocksurface.html - SDL 1.2 Documentation: SDL_LoadBMP
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlloadbmp.html - SDL 1.2 Documentation: SDL_SaveBMP
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsavebmp.html - SDL 1.2 Documentation: SDL_BlitSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlblitsurface.html - SDL 1.2 Documentation: SDL_VideoInfo
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlvideoinfo.html - SDL 1.2 Documentation: SDL_GetVideoInfo
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlgetvideoinfo.html - glDrawArrays
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawArrays.xml - glDrawElements
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawElements.xml - glDrawArraysInstanced
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawArraysInstanced.xml - glDrawElementsInstanced
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawElementsInstanced.xml - Root.cz: Seriál Grafická knihovna OpenGL
http://www.root.cz/serialy/graficka-knihovna-opengl/ - Root.cz: Seriál Tvorba přenositelných grafických aplikací využívajících knihovnu GLUT
http://www.root.cz/serialy/tvorba-prenositelnych-grafickych-aplikaci-vyuzivajicich-knihovnu-glut/ - Best Practices for Working with Vertex Data
https://developer.apple.com/library/ios/documentation/3ddrawing/conceptual/opengles_programmingguide/TechniquesforWorkingwithVertexData/TechniquesforWorkingwithVertexData.html - Class BufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/image/BufferStrategy.html - Class Graphics
http://docs.oracle.com/javase/1.5.0/docs/api/java/awt/Graphics.html - Double Buffering and Page Flipping
http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html - BufferStrategy and BufferCapabilities
http://docs.oracle.com/javase/tutorial/extra/fullscreen/bufferstrategy.html - Java:Tutorials:Double Buffering
http://content.gpwiki.org/index.php/Java:Tutorials:Double_Buffering - Double buffer in standard Java AWT
http://www.codeproject.com/Articles/2136/Double-buffer-in-standard-Java-AWT - Java 2D: Hardware Accelerating – Part 1 – Volatile Images
http://www.javalobby.org/forums/thread.jspa?threadID=16840&tstart=0 - Java 2D: Hardware Accelerating – Part 2 – Buffer Strategies
http://www.javalobby.org/java/forums/t16867.html - How does paintComponent work?
http://stackoverflow.com/questions/15544549/how-does-paintcomponent-work - A Swing Architecture Overview
http://www.oracle.com/technetwork/java/architecture-142923.html - Class javax.swing.JComponent
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html - Class java.awt.Component
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html - Class java.awt.Component.BltBufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.BltBufferStrategy.html - Class java.awt.Component.FlipBufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.FlipBufferStrategy.html - Metoda java.awt.Component.isDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html#isDoubleBuffered() - Metoda javax.swing.JComponent.isDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#isDoubleBuffered() - Metoda javax.swing.JComponent.setDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#setDoubleBuffered(boolean) - Javadoc – třída GraphicsDevice
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsDevice.html - Javadoc – třída GraphicsEnvironment
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsEnvironment.html - Javadoc – třída GraphicsConfiguration
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsConfiguration.html - Javadoc – třída DisplayMode
http://docs.oracle.com/javase/7/docs/api/java/awt/DisplayMode.html - Lesson: Full-Screen Exclusive Mode API
http://docs.oracle.com/javase/tutorial/extra/fullscreen/ - Full-Screen Exclusive Mode
http://docs.oracle.com/javase/tutorial/extra/fullscreen/exclusivemode.html - Display Mode
http://docs.oracle.com/javase/tutorial/extra/fullscreen/displaymode.html - Using the Full-Screen Exclusive Mode API in Java
http://www.developer.com/java/other/article.php/3609776/Using-the-Full-Screen-Exclusive-Mode-API-in-Java.htm - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - MultiMedia eXtensions
http://softpixel.com/~cwright/programming/simd/mmx.phpi - SSE (Streaming SIMD Extentions)
http://www.songho.ca/misc/sse/sse.html - Timothy A. Chagnon: SSE and SSE2
http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf - Intel corporation: Extending the Worldr's Most Popular Processor Architecture
http://download.intel.com/technology/architecture/new-instructions-paper.pdf - SIMD architectures:
http://arstechnica.com/old/content/2000/03/simd.ars/ - GC safe-point (or safepoint) and safe-region
http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html - Safepoints in HotSpot JVM
http://blog.ragozin.info/2012/10/safepoints-in-hotspot-jvm.html - Java theory and practice: Synchronization optimizations in Mustang
http://www.ibm.com/developerworks/java/library/j-jtp10185/ - How to build hsdis
http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/tip/src/share/tools/hsdis/README - Java SE 6 Performance White Paper
http://www.oracle.com/technetwork/java/6-performance-137236.html - Lukas Stadler's Blog
http://classparser.blogspot.cz/2010/03/hsdis-i386dll.html - How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
http://dropzone.nfshost.com/hsdis.htm - PrintAssembly
https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly - The Java Virtual Machine Specification: 3.14. Synchronization
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.14 - The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4 - The Java Virtual Machine Specification: 17.4. Memory Model
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 - The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 - Open Source ByteCode Libraries in Java
http://java-source.net/open-source/bytecode-libraries - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - BCEL Home page
http://commons.apache.org/bcel/ - Byte Code Engineering Library (před verzí 5.0)
http://bcel.sourceforge.net/ - Byte Code Engineering Library (verze >= 5.0)
http://commons.apache.org/proper/commons-bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - Javassist
http://www.jboss.org/javassist/ - Byteman
http://www.jboss.org/byteman - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - jclasslib bytecode viewer
http://www.ej-technologies.com/products/jclasslib/overview.html